From b14c2cbab2af7c23aa845da1b14b2472eda40df9 Mon Sep 17 00:00:00 2001 From: Aryeh Gregor Date: Sun, 28 Apr 2013 14:52:10 +0300 Subject: [PATCH 01/91] Bug 867101 - Fix more implicit conversion to already_AddRefed; r=Ms2ger --- .../src/base/nsAccessibilityService.cpp | 5 ++- content/base/src/nsDocument.cpp | 6 ++-- .../html/content/src/nsGenericHTMLElement.cpp | 4 +-- dom/base/nsGlobalWindow.cpp | 4 +-- dom/base/nsWindowMemoryReporter.cpp | 4 +-- gfx/gl/GLContext.cpp | 2 +- gfx/gl/GLTextureImage.h | 4 +-- gfx/thebes/gfxContext.cpp | 6 ++-- gfx/thebes/gfxFT2Fonts.cpp | 4 +-- gfx/thebes/gfxPattern.cpp | 2 +- gfx/thebes/gfxPlatform.cpp | 2 +- gfx/thebes/gfxPlatformMac.cpp | 13 +++----- gfx/thebes/gfxQuartzImageSurface.cpp | 5 +-- gfx/thebes/gfxQuartzSurface.cpp | 5 ++- gfx/thebes/gfxWindowsPlatform.cpp | 4 ++- gfx/thebes/gfxWindowsSurface.cpp | 12 ++----- intl/unicharutil/src/nsEntityConverter.cpp | 7 ++--- .../device/AndroidCaptureProvider.cpp | 4 +-- parser/html/nsHtml5TreeOperation.h | 2 +- toolkit/components/places/Database.h | 4 +-- .../components/places/nsAnnotationService.h | 2 +- toolkit/components/places/nsFaviconService.h | 2 +- toolkit/components/places/nsNavBookmarks.h | 2 +- toolkit/components/places/nsNavHistory.h | 2 +- toolkit/components/places/nsPlacesMacros.h | 20 ++++++------ .../exthandler/mac/nsOSHelperAppService.mm | 8 ++--- .../exthandler/win/nsOSHelperAppService.cpp | 31 +++++++------------ widget/android/nsIdleServiceAndroid.h | 6 ++-- widget/cocoa/nsChildView.mm | 14 ++++----- widget/cocoa/nsIdleServiceX.h | 6 ++-- widget/tests/TestAppShellSteadyState.cpp | 2 +- widget/windows/nsIdleServiceWin.h | 7 ++--- xpcom/base/nsCycleCollector.cpp | 6 ++-- xpcom/tests/TestRegistrationOrder.cpp | 2 +- 34 files changed, 88 insertions(+), 121 deletions(-) diff --git a/accessible/src/base/nsAccessibilityService.cpp b/accessible/src/base/nsAccessibilityService.cpp index ce5917966a9..76f9fc25078 100644 --- a/accessible/src/base/nsAccessibilityService.cpp +++ b/accessible/src/base/nsAccessibilityService.cpp @@ -287,11 +287,10 @@ nsAccessibilityService::CreatePluginAccessible(nsObjectFrame* aFrame, HWND pluginPort = nullptr; aFrame->GetPluginPort(&pluginPort); - Accessible* accessible = + nsRefPtr accessible = new HTMLWin32ObjectOwnerAccessible(aContent, aContext->Document(), pluginPort); - NS_ADDREF(accessible); - return accessible; + return accessible.forget(); #elif MOZ_ACCESSIBILITY_ATK if (!AtkSocketAccessible::gCanEmbed) diff --git a/content/base/src/nsDocument.cpp b/content/base/src/nsDocument.cpp index c1b852a1cde..80a75c381b4 100644 --- a/content/base/src/nsDocument.cpp +++ b/content/base/src/nsDocument.cpp @@ -4828,7 +4828,7 @@ nsIDocument::CreateElement(const nsAString& aTagName, ErrorResult& rv) if (rv.Failed()) { return nullptr; } - return content.forget().get()->AsElement(); + return dont_AddRef(content.forget().get()->AsElement()); } NS_IMETHODIMP @@ -4865,7 +4865,7 @@ nsIDocument::CreateElementNS(const nsAString& aNamespaceURI, if (rv.Failed()) { return nullptr; } - return content.forget().get()->AsElement(); + return dont_AddRef(content.forget().get()->AsElement()); } NS_IMETHODIMP @@ -6975,7 +6975,7 @@ nsIDocument::CreateEvent(const nsAString& aEventType, ErrorResult& rv) const nsEventDispatcher::CreateEvent(const_cast(this), presContext, nullptr, aEventType, getter_AddRefs(ev)); - return ev ? ev.forget().get()->InternalDOMEvent() : nullptr; + return ev ? dont_AddRef(ev.forget().get()->InternalDOMEvent()) : nullptr; } void diff --git a/content/html/content/src/nsGenericHTMLElement.cpp b/content/html/content/src/nsGenericHTMLElement.cpp index 1f05b85aabe..b47de500625 100644 --- a/content/html/content/src/nsGenericHTMLElement.cpp +++ b/content/html/content/src/nsGenericHTMLElement.cpp @@ -321,8 +321,8 @@ nsGenericHTMLElement::Dataset() slots->mDataset = new nsDOMStringMap(this); } - NS_ADDREF(slots->mDataset); - return slots->mDataset; + nsRefPtr ret = slots->mDataset; + return ret.forget(); } nsresult diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index 7c6aee13dae..af35cffc3aa 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -2791,12 +2791,12 @@ TryGetTabChildGlobalAsEventTarget(nsISupports *aFrom) { nsCOMPtr frameLoaderOwner = do_QueryInterface(aFrom); if (!frameLoaderOwner) { - return NULL; + return nullptr; } nsRefPtr frameLoader = frameLoaderOwner->GetFrameLoader(); if (!frameLoader) { - return NULL; + return nullptr; } nsCOMPtr target = frameLoader->GetTabChildGlobalAsEventTarget(); diff --git a/dom/base/nsWindowMemoryReporter.cpp b/dom/base/nsWindowMemoryReporter.cpp index eb70158f70a..a2a23df8ef0 100644 --- a/dom/base/nsWindowMemoryReporter.cpp +++ b/dom/base/nsWindowMemoryReporter.cpp @@ -56,7 +56,7 @@ static already_AddRefed GetWindowURI(nsIDOMWindow *aWindow) { nsCOMPtr pWindow = do_QueryInterface(aWindow); - NS_ENSURE_TRUE(pWindow, NULL); + NS_ENSURE_TRUE(pWindow, nullptr); nsCOMPtr doc = pWindow->GetExtantDoc(); nsCOMPtr uri; @@ -68,7 +68,7 @@ GetWindowURI(nsIDOMWindow *aWindow) if (!uri) { nsCOMPtr scriptObjPrincipal = do_QueryInterface(aWindow); - NS_ENSURE_TRUE(scriptObjPrincipal, NULL); + NS_ENSURE_TRUE(scriptObjPrincipal, nullptr); // GetPrincipal() will print a warning if the window does not have an outer // window, so check here for an outer window first. This code is diff --git a/gfx/gl/GLContext.cpp b/gfx/gl/GLContext.cpp index a8ccc4f2430..9014cfec0df 100644 --- a/gfx/gl/GLContext.cpp +++ b/gfx/gl/GLContext.cpp @@ -1389,7 +1389,7 @@ GLContext::GetTexImage(GLuint aTexture, bool aYInvert, ShaderProgramType aShader nsRefPtr surf = new gfxImageSurface(size, gfxASurface::ImageFormatARGB32); if (!surf || surf->CairoStatus()) { - return NULL; + return nullptr; } uint32_t currentPackAlignment = 0; diff --git a/gfx/gl/GLTextureImage.h b/gfx/gl/GLTextureImage.h index 799a2218f4f..1a71f1a9df5 100644 --- a/gfx/gl/GLTextureImage.h +++ b/gfx/gl/GLTextureImage.h @@ -214,10 +214,10 @@ public: /** * If this TextureImage has a permanent gfxASurface backing, - * return it. Otherwise return NULL. + * return it. Otherwise return nullptr. */ virtual already_AddRefed GetBackingSurface() - { return NULL; } + { return nullptr; } const nsIntSize& GetSize() const { return mSize; } ContentType GetContentType() const { return mContentType; } diff --git a/gfx/thebes/gfxContext.cpp b/gfx/thebes/gfxContext.cpp index f99b661dc9f..70683a33ba3 100644 --- a/gfx/thebes/gfxContext.cpp +++ b/gfx/thebes/gfxContext.cpp @@ -160,7 +160,7 @@ gfxContext::CurrentSurface(gfxFloat *dx, gfxFloat *dy) *dx = *dy = 0; } // An Azure context doesn't have a surface backing it. - return NULL; + return nullptr; } } @@ -249,7 +249,7 @@ already_AddRefed gfxContext::CopyPath() const return path.forget(); } else { // XXX - This is not yet supported for Azure. - return NULL; + return nullptr; } } @@ -1711,7 +1711,7 @@ gfxContext::GetFlattenedPath() return path.forget(); } else { // XXX - Used by SVG, needs fixing. - return NULL; + return nullptr; } } diff --git a/gfx/thebes/gfxFT2Fonts.cpp b/gfx/thebes/gfxFT2Fonts.cpp index 201935b7286..3da4ad6f357 100644 --- a/gfx/thebes/gfxFT2Fonts.cpp +++ b/gfx/thebes/gfxFT2Fonts.cpp @@ -601,9 +601,7 @@ gfxFT2Font::GetOrMakeFont(FT2FontEntry *aFontEntry, const gfxFontStyle *aStyle, return nullptr; gfxFontCache::GetCache()->AddNew(font); } - gfxFont *f = nullptr; - font.swap(f); - return static_cast(f); + return font.forget().downcast(); } void diff --git a/gfx/thebes/gfxPattern.cpp b/gfx/thebes/gfxPattern.cpp index 7275271553b..dbf52310a74 100644 --- a/gfx/thebes/gfxPattern.cpp +++ b/gfx/thebes/gfxPattern.cpp @@ -388,7 +388,7 @@ gfxPattern::GetSurface() } else { // We should never be trying to get the surface off an Azure gfx Pattern. NS_ERROR("Attempt to get surface off an Azure gfxPattern!"); - return NULL; + return nullptr; } } diff --git a/gfx/thebes/gfxPlatform.cpp b/gfx/thebes/gfxPlatform.cpp index 2a845d8c3d3..a62e2ffddb0 100644 --- a/gfx/thebes/gfxPlatform.cpp +++ b/gfx/thebes/gfxPlatform.cpp @@ -741,7 +741,7 @@ gfxPlatform::GetThebesSurfaceForDrawTarget(DrawTarget *aTarget) RefPtr data = source->GetDataSurface(); if (!data) { - return NULL; + return nullptr; } IntSize size = data->GetSize(); diff --git a/gfx/thebes/gfxPlatformMac.cpp b/gfx/thebes/gfxPlatformMac.cpp index 05087865449..b482129deb8 100644 --- a/gfx/thebes/gfxPlatformMac.cpp +++ b/gfx/thebes/gfxPlatformMac.cpp @@ -95,12 +95,9 @@ already_AddRefed gfxPlatformMac::CreateOffscreenSurface(const gfxIntSize& size, gfxASurface::gfxContentType contentType) { - gfxASurface *newSurface = nullptr; - - newSurface = new gfxQuartzSurface(size, OptimalFormatForContent(contentType)); - - NS_IF_ADDREF(newSurface); - return newSurface; + nsRefPtr newSurface = + new gfxQuartzSurface(size, OptimalFormatForContent(contentType)); + return newSurface.forget(); } already_AddRefed @@ -127,8 +124,8 @@ gfxPlatformMac::OptimizeImage(gfxImageSurface *aSurface, isurf = new gfxImageSurface (surfaceSize, format); if (!isurf->CopyFrom (aSurface)) { // don't even bother doing anything more - NS_ADDREF(aSurface); - return aSurface; + nsRefPtr ret = aSurface; + return ret.forget(); } } diff --git a/gfx/thebes/gfxQuartzImageSurface.cpp b/gfx/thebes/gfxQuartzImageSurface.cpp index bde1c25be4a..51bb0218e93 100644 --- a/gfx/thebes/gfxQuartzImageSurface.cpp +++ b/gfx/thebes/gfxQuartzImageSurface.cpp @@ -67,8 +67,5 @@ gfxQuartzImageSurface::GetAsImageSurface() return nullptr; } - nsRefPtr asurf = gfxASurface::Wrap(isurf); - gfxImageSurface *imgsurf = (gfxImageSurface*) asurf.get(); - NS_ADDREF(imgsurf); - return imgsurf; + return gfxASurface::Wrap(isurf).downcast(); } diff --git a/gfx/thebes/gfxQuartzSurface.cpp b/gfx/thebes/gfxQuartzSurface.cpp index d7ca3bb1574..f50fa0c1e9b 100644 --- a/gfx/thebes/gfxQuartzSurface.cpp +++ b/gfx/thebes/gfxQuartzSurface.cpp @@ -192,10 +192,9 @@ already_AddRefed gfxQuartzSurface::GetAsImageSurface() // shares the refcounts of Cairo surfaces. However, Wrap also adds a // reference to the image. We need to remove one of these references // explicitly so we don't leak. - gfxImageSurface* imgSurface = static_cast (img.forget().get()); - imgSurface->Release(); + img->Release(); - return imgSurface; + return img.forget().downcast(); } gfxQuartzSurface::~gfxQuartzSurface() diff --git a/gfx/thebes/gfxWindowsPlatform.cpp b/gfx/thebes/gfxWindowsPlatform.cpp index e50b5f6160e..310f70fd4f5 100644 --- a/gfx/thebes/gfxWindowsPlatform.cpp +++ b/gfx/thebes/gfxWindowsPlatform.cpp @@ -678,7 +678,9 @@ gfxWindowsPlatform::CreateOffscreenImageSurface(const gfxIntSize& aSize, { #ifdef CAIRO_HAS_D2D_SURFACE if (mRenderMode == RENDER_DIRECT2D) { - return new gfxImageSurface(aSize, OptimalFormatForContent(aContentType)); + nsRefPtr surface = + new gfxImageSurface(aSize, OptimalFormatForContent(aContentType)); + return surface.forget(); } #endif diff --git a/gfx/thebes/gfxWindowsSurface.cpp b/gfx/thebes/gfxWindowsSurface.cpp index df0377d1b76..43942d4a088 100644 --- a/gfx/thebes/gfxWindowsSurface.cpp +++ b/gfx/thebes/gfxWindowsSurface.cpp @@ -178,10 +178,7 @@ gfxWindowsSurface::GetAsImageSurface() if (!isurf) return nullptr; - nsRefPtr asurf = gfxASurface::Wrap(isurf); - gfxImageSurface *imgsurf = (gfxImageSurface*) asurf.get(); - NS_ADDREF(imgsurf); - return imgsurf; + return gfxASurface::Wrap(isurf).downcast(); } already_AddRefed @@ -202,14 +199,11 @@ gfxWindowsSurface::OptimizeToDDB(HDC dc, const gfxIntSize& size, gfxImageFormat tmpCtx.SetSource(this); tmpCtx.Paint(); - gfxWindowsSurface *raw = (gfxWindowsSurface*) (wsurf.get()); - NS_ADDREF(raw); - // we let the new DDB surfaces be converted back to dibsections if // acquire_source_image is called on them - cairo_win32_surface_set_can_convert_to_dib(raw->CairoSurface(), TRUE); + cairo_win32_surface_set_can_convert_to_dib(wsurf->CairoSurface(), TRUE); - return raw; + return wsurf.forget().downcast(); } nsresult diff --git a/intl/unicharutil/src/nsEntityConverter.cpp b/intl/unicharutil/src/nsEntityConverter.cpp index 501b62dcfd4..b9152c728f9 100644 --- a/intl/unicharutil/src/nsEntityConverter.cpp +++ b/intl/unicharutil/src/nsEntityConverter.cpp @@ -92,12 +92,11 @@ nsEntityConverter::LoadEntityBundle(uint32_t version) LossyAppendUTF16toASCII(versionName, url); url.Append(".properties"); - nsIStringBundle* bundle; - rv = bundleService->CreateBundle(url.get(), &bundle); + nsCOMPtr bundle; + rv = bundleService->CreateBundle(url.get(), getter_AddRefs(bundle)); NS_ENSURE_SUCCESS(rv, nullptr); - // does this addref right? - return bundle; + return bundle.forget(); } const PRUnichar* diff --git a/netwerk/protocol/device/AndroidCaptureProvider.cpp b/netwerk/protocol/device/AndroidCaptureProvider.cpp index 749b58cf4f8..145c6000eae 100644 --- a/netwerk/protocol/device/AndroidCaptureProvider.cpp +++ b/netwerk/protocol/device/AndroidCaptureProvider.cpp @@ -296,6 +296,6 @@ already_AddRefed GetAndroidCaptureProvider() { if (!AndroidCaptureProvider::sInstance) { AndroidCaptureProvider::sInstance = new AndroidCaptureProvider(); } - AndroidCaptureProvider::sInstance->AddRef(); - return AndroidCaptureProvider::sInstance; + nsRefPtr ret = AndroidCaptureProvider::sInstance; + return ret.forget(); } diff --git a/parser/html/nsHtml5TreeOperation.h b/parser/html/nsHtml5TreeOperation.h index e1756c61202..f3d7f5c9d5f 100644 --- a/parser/html/nsHtml5TreeOperation.h +++ b/parser/html/nsHtml5TreeOperation.h @@ -351,7 +351,7 @@ class nsHtml5TreeOperation { inline already_AddRefed Reget(nsIAtom* aAtom) { if (!aAtom || aAtom->IsStaticAtom()) { - return aAtom; + return dont_AddRef(aAtom); } nsAutoString str; aAtom->ToString(str); diff --git a/toolkit/components/places/Database.h b/toolkit/components/places/Database.h index 3f8e1c542f4..42ca243e07c 100644 --- a/toolkit/components/places/Database.h +++ b/toolkit/components/places/Database.h @@ -282,10 +282,8 @@ private: /** * Singleton getter, invoked by class instantiation. - * - * Note: does AddRef. */ - static Database* GetSingleton(); + static already_AddRefed GetSingleton(); static Database* gDatabase; diff --git a/toolkit/components/places/nsAnnotationService.h b/toolkit/components/places/nsAnnotationService.h index 630275249df..1b9816886b5 100644 --- a/toolkit/components/places/nsAnnotationService.h +++ b/toolkit/components/places/nsAnnotationService.h @@ -55,7 +55,7 @@ public: /** * Obtains the service's object. */ - static nsAnnotationService* GetSingleton(); + static already_AddRefed GetSingleton(); /** * Initializes the service's object. This should only be called once. diff --git a/toolkit/components/places/nsFaviconService.h b/toolkit/components/places/nsFaviconService.h index 325ec1077af..00406a1faf7 100644 --- a/toolkit/components/places/nsFaviconService.h +++ b/toolkit/components/places/nsFaviconService.h @@ -61,7 +61,7 @@ public: /** * Obtains the service's object. */ - static nsFaviconService* GetSingleton(); + static already_AddRefed GetSingleton(); /** * Initializes the service's object. This should only be called once. diff --git a/toolkit/components/places/nsNavBookmarks.h b/toolkit/components/places/nsNavBookmarks.h index cbed625963c..0ce237594a0 100644 --- a/toolkit/components/places/nsNavBookmarks.h +++ b/toolkit/components/places/nsNavBookmarks.h @@ -104,7 +104,7 @@ public: /** * Obtains the service's object. */ - static nsNavBookmarks* GetSingleton(); + static already_AddRefed GetSingleton(); /** * Initializes the service's object. This should only be called once. diff --git a/toolkit/components/places/nsNavHistory.h b/toolkit/components/places/nsNavHistory.h index 01515b260df..d18c2322000 100644 --- a/toolkit/components/places/nsNavHistory.h +++ b/toolkit/components/places/nsNavHistory.h @@ -87,7 +87,7 @@ public: /** * Obtains the nsNavHistory object. */ - static nsNavHistory* GetSingleton(); + static already_AddRefed GetSingleton(); /** * Initializes the nsNavHistory object. This should only be called once. diff --git a/toolkit/components/places/nsPlacesMacros.h b/toolkit/components/places/nsPlacesMacros.h index 310491f2d77..964704e9527 100644 --- a/toolkit/components/places/nsPlacesMacros.h +++ b/toolkit/components/places/nsPlacesMacros.h @@ -26,22 +26,22 @@ #define PLACES_FACTORY_SINGLETON_IMPLEMENTATION(_className, _sInstance) \ _className * _className::_sInstance = nullptr; \ \ - _className * \ + already_AddRefed<_className> \ _className::GetSingleton() \ { \ if (_sInstance) { \ - NS_ADDREF(_sInstance); \ - return _sInstance; \ + nsRefPtr<_className> ret = _sInstance; \ + return ret.forget(); \ } \ _sInstance = new _className(); \ - if (_sInstance) { \ - NS_ADDREF(_sInstance); \ - if (NS_FAILED(_sInstance->Init())) { \ - NS_RELEASE(_sInstance); \ - _sInstance = nullptr; \ - } \ + nsRefPtr<_className> ret = _sInstance; \ + if (NS_FAILED(_sInstance->Init())) { \ + /* Null out ret before _sInstance so the destructor doesn't assert */ \ + ret = nullptr; \ + _sInstance = nullptr; \ + return nullptr; \ } \ - return _sInstance; \ + return ret.forget(); \ } #define PLACES_WARN_DEPRECATED() \ diff --git a/uriloader/exthandler/mac/nsOSHelperAppService.mm b/uriloader/exthandler/mac/nsOSHelperAppService.mm index ec7c5238cbd..8fc73f07f0b 100644 --- a/uriloader/exthandler/mac/nsOSHelperAppService.mm +++ b/uriloader/exthandler/mac/nsOSHelperAppService.mm @@ -310,10 +310,7 @@ nsOSHelperAppService::GetMIMEInfoFromOS(const nsACString& aMIMEType, flatType.get(), flatExt.get())); // Create a Mac-specific MIME info so we can use Mac-specific members. - nsMIMEInfoMac* mimeInfoMac = new nsMIMEInfoMac(aMIMEType); - if (!mimeInfoMac) - return nullptr; - NS_ADDREF(mimeInfoMac); + nsRefPtr mimeInfoMac = new nsMIMEInfoMac(aMIMEType); NSAutoreleasePool *localPool = [[NSAutoreleasePool alloc] init]; @@ -463,7 +460,6 @@ nsOSHelperAppService::GetMIMEInfoFromOS(const nsACString& aMIMEType, nsCOMPtr app(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID)); if (!app) { - NS_RELEASE(mimeInfoMac); [localPool release]; return nullptr; } @@ -531,7 +527,7 @@ nsOSHelperAppService::GetMIMEInfoFromOS(const nsACString& aMIMEType, PR_LOG(mLog, PR_LOG_DEBUG, ("OS gave us: type '%s' found '%i'\n", mimeType.get(), *aFound)); [localPool release]; - return mimeInfoMac; + return mimeInfoMac.forget(); NS_OBJC_END_TRY_ABORT_BLOCK_NSNULL; } diff --git a/uriloader/exthandler/win/nsOSHelperAppService.cpp b/uriloader/exthandler/win/nsOSHelperAppService.cpp index a9214cd0bc2..3e7865b3a32 100644 --- a/uriloader/exthandler/win/nsOSHelperAppService.cpp +++ b/uriloader/exthandler/win/nsOSHelperAppService.cpp @@ -527,11 +527,7 @@ already_AddRefed nsOSHelperAppService::GetByExtension(const nsAFl LossyAppendUTF16toASCII(temp, typeToUse); } - nsMIMEInfoWin* mimeInfo = new nsMIMEInfoWin(typeToUse); - if (!mimeInfo) - return nullptr; // out of memory - - NS_ADDREF(mimeInfo); + nsRefPtr mimeInfo = new nsMIMEInfoWin(typeToUse); // don't append the '.' mimeInfo->AppendExtension(NS_ConvertUTF16toUTF8(Substring(fileExtToUse, 1))); @@ -569,7 +565,6 @@ already_AddRefed nsOSHelperAppService::GetByExtension(const nsAFl found = false; if (!found) { - NS_IF_RELEASE(mimeInfo); // we failed to really find an entry in the registry return nullptr; } @@ -579,7 +574,6 @@ already_AddRefed nsOSHelperAppService::GetByExtension(const nsAFl if (NS_FAILED(GetDefaultAppInfo(appInfo, defaultDescription, getter_AddRefs(defaultApplication)))) { - NS_IF_RELEASE(mimeInfo); return nullptr; } @@ -589,7 +583,7 @@ already_AddRefed nsOSHelperAppService::GetByExtension(const nsAFl // Grab the general description GetMIMEInfoFromRegistry(appInfo, mimeInfo); - return mimeInfo; + return mimeInfo.forget(); } already_AddRefed nsOSHelperAppService::GetMIMEInfoFromOS(const nsACString& aMIMEType, const nsACString& aFileExt, bool *aFound) @@ -619,10 +613,10 @@ already_AddRefed nsOSHelperAppService::GetMIMEInfoFromOS(const nsAC } } // If we found an extension for the type, do the lookup - nsMIMEInfoWin* mi = nullptr; + nsRefPtr mi; if (!fileExtension.IsEmpty()) - mi = GetByExtension(fileExtension, flatType.get()).get(); - LOG(("Extension lookup on '%s' found: 0x%p\n", fileExtension.get(), mi)); + mi = GetByExtension(fileExtension, flatType.get()); + LOG(("Extension lookup on '%s' found: 0x%p\n", fileExtension.get(), mi.get())); bool hasDefault = false; if (mi) { @@ -646,21 +640,18 @@ already_AddRefed nsOSHelperAppService::GetMIMEInfoFromOS(const nsAC GetByExtension(NS_ConvertUTF8toUTF16(aFileExt), flatType.get()); LOG(("Ext. lookup for '%s' found 0x%p\n", flatExt.get(), miByExt.get())); if (!miByExt && mi) - return mi; + return mi.forget(); if (miByExt && !mi) { - miByExt.swap(mi); - return mi; + return miByExt.forget(); } if (!miByExt && !mi) { *aFound = false; mi = new nsMIMEInfoWin(flatType); - if (mi) { - NS_ADDREF(mi); - if (!aFileExt.IsEmpty()) - mi->AppendExtension(aFileExt); + if (!aFileExt.IsEmpty()) { + mi->AppendExtension(aFileExt); } - return mi; + return mi.forget(); } // if we get here, mi has no default app. copy from extension lookup. @@ -670,7 +661,7 @@ already_AddRefed nsOSHelperAppService::GetMIMEInfoFromOS(const nsAC mi->SetDefaultDescription(desc); } - return mi; + return mi.forget(); } NS_IMETHODIMP diff --git a/widget/android/nsIdleServiceAndroid.h b/widget/android/nsIdleServiceAndroid.h index 797e5b43fef..af7513a91ca 100644 --- a/widget/android/nsIdleServiceAndroid.h +++ b/widget/android/nsIdleServiceAndroid.h @@ -19,14 +19,12 @@ public: static already_AddRefed GetInstance() { - nsIdleServiceAndroid* idleService = - static_cast(nsIdleService::GetInstance().get()); + nsRefPtr idleService = nsIdleService::GetInstance(); if (!idleService) { idleService = new nsIdleServiceAndroid(); - NS_ADDREF(idleService); } - return idleService; + return idleService.forget().downcast(); } protected: diff --git a/widget/cocoa/nsChildView.mm b/widget/cocoa/nsChildView.mm index 763276ca0b0..a6732e6f5d9 100644 --- a/widget/cocoa/nsChildView.mm +++ b/widget/cocoa/nsChildView.mm @@ -2378,19 +2378,19 @@ nsChildView::GetDocumentAccessible() if (!mozilla::a11y::ShouldA11yBeEnabled()) return nullptr; - a11y::Accessible* docAccessible = nullptr; if (mAccessible) { - CallQueryReferent(mAccessible.get(), &docAccessible); - return docAccessible; + nsRefPtr ret; + CallQueryReferent(mAccessible.get(), + static_cast(getter_AddRefs(ret))); + return ret.forget(); } // need to fetch the accessible anew, because it has gone away. // cache the accessible in our weak ptr - a11y::Accessible* acc = GetAccessible(); - mAccessible = do_GetWeakReference(static_cast(acc)); + nsRefPtr acc = GetAccessible(); + mAccessible = do_GetWeakReference(static_cast(acc.get())); - NS_IF_ADDREF(acc); - return acc; + return acc.forget(); } #endif diff --git a/widget/cocoa/nsIdleServiceX.h b/widget/cocoa/nsIdleServiceX.h index c999d3b016a..792da6c7a6b 100644 --- a/widget/cocoa/nsIdleServiceX.h +++ b/widget/cocoa/nsIdleServiceX.h @@ -16,14 +16,12 @@ public: static already_AddRefed GetInstance() { - nsIdleServiceX* idleService = - static_cast(nsIdleService::GetInstance().get()); + nsRefPtr idleService = nsIdleService::GetInstance(); if (!idleService) { idleService = new nsIdleServiceX(); - NS_ADDREF(idleService); } - return idleService; + return idleService.forget().downcast(); } protected: diff --git a/widget/tests/TestAppShellSteadyState.cpp b/widget/tests/TestAppShellSteadyState.cpp index 17dfc51ef03..977b07a4d35 100644 --- a/widget/tests/TestAppShellSteadyState.cpp +++ b/widget/tests/TestAppShellSteadyState.cpp @@ -325,7 +325,7 @@ GetAppShell() } } - return NULL; + return nullptr; } void diff --git a/widget/windows/nsIdleServiceWin.h b/widget/windows/nsIdleServiceWin.h index 6f3e7acdc60..9d1ced6fa8f 100644 --- a/widget/windows/nsIdleServiceWin.h +++ b/widget/windows/nsIdleServiceWin.h @@ -28,14 +28,13 @@ public: static already_AddRefed GetInstance() { - nsIdleServiceWin* idleService = - static_cast(nsIdleService::GetInstance().get()); + nsRefPtr idleService = + nsIdleService::GetInstance().downcast(); if (!idleService) { idleService = new nsIdleServiceWin(); - NS_ADDREF(idleService); } - return idleService; + return idleService.forget(); } protected: diff --git a/xpcom/base/nsCycleCollector.cpp b/xpcom/base/nsCycleCollector.cpp index a928dfe2286..3f17ed586e4 100644 --- a/xpcom/base/nsCycleCollector.cpp +++ b/xpcom/base/nsCycleCollector.cpp @@ -1659,7 +1659,9 @@ private: NS_ConvertUTF16toUTF8(mFilenameIdentifier).get()); // Get the log directory either from $MOZ_CC_LOG_DIRECTORY or from - // the fallback directories in OpenTempFile. + // the fallback directories in OpenTempFile. We don't use an nsCOMPtr + // here because OpenTempFile uses an in/out param and getter_AddRefs + // wouldn't work. nsIFile* logFile = nullptr; if (char* env = PR_GetEnv("MOZ_CC_LOG_DIRECTORY")) { NS_NewNativeLocalFile(nsCString(env), /* followLinks = */ true, @@ -1671,7 +1673,7 @@ private: return nullptr; } - return logFile; + return dont_AddRef(logFile); } FILE *mStream; diff --git a/xpcom/tests/TestRegistrationOrder.cpp b/xpcom/tests/TestRegistrationOrder.cpp index 4f4120f8e9b..05359cd08b6 100644 --- a/xpcom/tests/TestRegistrationOrder.cpp +++ b/xpcom/tests/TestRegistrationOrder.cpp @@ -137,7 +137,7 @@ GetRegDirectory(const char* basename, const char* dirname, const char* leafname) nsresult rv = NS_NewNativeLocalFile(nsDependentCString(basename), true, getter_AddRefs(f)); if (NS_FAILED(rv)) - return NULL; + return nullptr; f->AppendNative(nsDependentCString(dirname)); if (leafname) From b9e70d017a54e5725c82120ea0735b49f9c75832 Mon Sep 17 00:00:00 2001 From: Aryeh Gregor Date: Wed, 10 Apr 2013 17:22:53 +0300 Subject: [PATCH 02/91] Bug 859817 - Eliminate implicit conversion from raw pointer to already_AddRefed; r=bz sr=bsmedberg --- xpcom/glue/nsCOMPtr.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/xpcom/glue/nsCOMPtr.h b/xpcom/glue/nsCOMPtr.h index 29b486d5648..d0829280376 100644 --- a/xpcom/glue/nsCOMPtr.h +++ b/xpcom/glue/nsCOMPtr.h @@ -22,6 +22,7 @@ #include "mozilla/Attributes.h" #include "mozilla/TypeTraits.h" #include "mozilla/Assertions.h" +#include "mozilla/NullPtr.h" // Wrapping includes can speed up compiles (see "Large Scale C++ Software Design") #ifndef nsDebug_h___ @@ -145,6 +146,17 @@ struct already_AddRefed |nsCOMPtr_helper|. */ { +#ifdef MOZ_HAVE_CXX11_NULLPTR + /* We use decltype(nullptr) instead of std::nullptr_t because the standard + * library might be old, and to save including . All compilers + * that support nullptr seem to support decltype. */ + already_AddRefed(decltype(nullptr) aNullPtr) + : mRawPtr(nullptr) + { + } + + explicit +#endif already_AddRefed( T* aRawPtr ) : mRawPtr(aRawPtr) { From aa103372ca5f244e2a15a34e507bea3c74c510df Mon Sep 17 00:00:00 2001 From: Jan de Mooij Date: Fri, 24 May 2013 14:03:28 +0200 Subject: [PATCH 03/91] Bug 874825 - Correctly set HAS_ARGS_OBJ flag when bailing out to baseline. r=djvj --- js/src/ion/BaselineBailouts.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/js/src/ion/BaselineBailouts.cpp b/js/src/ion/BaselineBailouts.cpp index 46a31348025..e1111a5799e 100644 --- a/js/src/ion/BaselineBailouts.cpp +++ b/js/src/ion/BaselineBailouts.cpp @@ -565,13 +565,15 @@ InitFromBailout(JSContext *cx, HandleScript caller, jsbytecode *callerPC, } IonSpew(IonSpew_BaselineBailouts, " ScopeChain=%p", scopeChain); blFrame->setScopeChain(scopeChain); - if (argsObj) - blFrame->initArgsObjUnchecked(*argsObj); + // Do not need to initialize scratchValue or returnValue fields in BaselineFrame. - // No flags are set. blFrame->setFlags(flags); + // initArgsObjUnchecked modifies the frame's flags, so call it after setFlags. + if (argsObj) + blFrame->initArgsObjUnchecked(*argsObj); + // Ion doesn't compile code with try/catch, so the block object will always be // null. blFrame->setBlockChainNull(); From 1cb900eb062cc39e7bc3b437f42ec2e1939fd0d3 Mon Sep 17 00:00:00 2001 From: Jan de Mooij Date: Fri, 24 May 2013 14:03:31 +0200 Subject: [PATCH 04/91] Bug 868431 - Disable Ion when Baseline is disabled, remove bailout-to-interpreter code. r=djvj --- js/src/ion/Bailouts.cpp | 572 +------------------------------ js/src/ion/Bailouts.h | 72 +--- js/src/ion/BaselineBailouts.cpp | 2 +- js/src/ion/Ion.cpp | 71 +--- js/src/ion/Ion.h | 14 +- js/src/ion/IonBuilder.cpp | 5 +- js/src/ion/IonCompartment.h | 20 -- js/src/ion/IonFrames.cpp | 4 - js/src/ion/IonMacroAssembler.cpp | 102 +----- js/src/jit-test/jit_test.py | 4 +- js/src/jsinterp.cpp | 62 +--- js/src/vm/Stack.cpp | 30 -- js/src/vm/Stack.h | 20 -- 13 files changed, 49 insertions(+), 929 deletions(-) diff --git a/js/src/ion/Bailouts.cpp b/js/src/ion/Bailouts.cpp index ec9298a2ea6..4b172f9bc4d 100644 --- a/js/src/ion/Bailouts.cpp +++ b/js/src/ion/Bailouts.cpp @@ -62,286 +62,6 @@ IonBailoutIterator::dump() const } } -static JSScript * -GetBailedJSScript(JSContext *cx) -{ - // Just after the frame conversion, we can safely interpret the ionTop as JS - // frame because it targets the bailed JS frame converted to an exit frame. - IonJSFrameLayout *frame = reinterpret_cast(cx->mainThread().ionTop); - switch (GetCalleeTokenTag(frame->calleeToken())) { - case CalleeToken_Function: { - JSFunction *fun = CalleeTokenToFunction(frame->calleeToken()); - return fun->nonLazyScript(); - } - case CalleeToken_Script: - return CalleeTokenToScript(frame->calleeToken()); - default: - JS_NOT_REACHED("unexpected callee token kind"); - return NULL; - } -} - -void -StackFrame::initFromBailout(JSContext *cx, SnapshotIterator &iter) -{ - uint32_t exprStackSlots = iter.slots() - script()->nfixed; - -#ifdef TRACK_SNAPSHOTS - iter.spewBailingFrom(); -#endif - IonSpew(IonSpew_Bailouts, " expr stack slots %u, is function frame %u", - exprStackSlots, isFunctionFrame()); - - if (iter.bailoutKind() == Bailout_ArgumentCheck) { - // Temporary hack -- skip the (unused) scopeChain, because it could be - // bogus (we can fail before the scope chain slot is set). Strip the - // hasScopeChain flag. If a call object is needed, it will get handled later - // by |ThunkToInterpreter| which call |EnsureHasScopeObjects|. - iter.skip(); - flags_ &= ~StackFrame::HAS_SCOPECHAIN; - - // If the script binds arguments, then skip the snapshot slot reserved to hold - // its value. - if (script()->argumentsHasVarBinding()) - iter.skip(); - flags_ &= ~StackFrame::HAS_ARGS_OBJ; - } else { - Value scopeChain = iter.read(); - JS_ASSERT(scopeChain.isObject() || scopeChain.isUndefined()); - if (scopeChain.isObject()) { - scopeChain_ = &scopeChain.toObject(); - flags_ |= StackFrame::HAS_SCOPECHAIN; - if (isFunctionFrame() && fun()->isHeavyweight()) - flags_ |= StackFrame::HAS_CALL_OBJ; - } - - // The second slot will be an arguments object if the script needs one. - if (script()->argumentsHasVarBinding()) { - Value argsObj = iter.read(); - JS_ASSERT(argsObj.isObject() || argsObj.isUndefined()); - if (argsObj.isObject()) - initArgsObj(argsObj.toObject().asArguments()); - } - } - - // Assume that all new stack frames have had their entry flag set if - // profiling has been turned on. This will be corrected if necessary - // elsewhere. - if (cx->runtime->spsProfiler.enabled()) - setPushedSPSFrame(); - - if (isFunctionFrame()) { - Value thisv = iter.read(); - formals()[-1] = thisv; - - // The new |this| must have already been constructed prior to an Ion - // constructor running. - if (isConstructing()) - JS_ASSERT(!thisv.isPrimitive()); - - JS_ASSERT(iter.slots() >= CountArgSlots(script(), fun())); - IonSpew(IonSpew_Bailouts, " frame slots %u, nargs %u, nfixed %u", - iter.slots(), fun()->nargs, script()->nfixed); - - for (uint32_t i = 0; i < fun()->nargs; i++) { - Value arg = iter.read(); - formals()[i] = arg; - } - } - exprStackSlots -= CountArgSlots(script(), maybeFun()); - - for (uint32_t i = 0; i < script()->nfixed; i++) { - Value slot = iter.read(); - slots()[i] = slot; - } - - IonSpew(IonSpew_Bailouts, " pushing %u expression stack slots", exprStackSlots); - FrameRegs ®s = cx->regs(); - for (uint32_t i = 0; i < exprStackSlots; i++) { - Value v; - - // If coming from an invalidation bailout, and this is the topmost - // value, and a value override has been specified, don't read from the - // iterator. Otherwise, we risk using a garbage value. - if (!iter.moreFrames() && i == exprStackSlots - 1 && cx->runtime->hasIonReturnOverride()) - v = iter.skip(); - else - v = iter.read(); - - *regs.sp++ = v; - } - unsigned pcOff = iter.pcOffset(); - regs.pc = script()->code + pcOff; - - if (iter.resumeAfter()) - regs.pc = GetNextPc(regs.pc); - - IonSpew(IonSpew_Bailouts, " new PC is offset %u within script %p (line %d)", - pcOff, (void *)script(), PCToLineNumber(script(), regs.pc)); - - // For fun.apply({}, arguments) the reconstructStackDepth will be atleast 4, - // but it could be that we inlined the funapply. In that case exprStackSlots, - // will have the real arguments in the slots and not always be equal. - JS_ASSERT_IF(JSOp(*regs.pc) != JSOP_FUNAPPLY, - exprStackSlots == js_ReconstructStackDepth(cx, script(), regs.pc)); -} - -static StackFrame * -PushInlinedFrame(JSContext *cx, StackFrame *callerFrame) -{ - // Grab the callee object out of the caller's frame, which has already been restored. - // N.B. we currently assume that the caller frame is at a JSOP_CALL pc for the caller frames, - // which will not be the case when we inline getters (in which case it would be a - // JSOP_GETPROP). That will have to be handled differently. - FrameRegs ®s = cx->regs(); - JS_ASSERT(js_CodeSpec[*regs.pc].format & JOF_INVOKE); - int callerArgc = GET_ARGC(regs.pc); - if (JSOp(*regs.pc) == JSOP_FUNAPPLY) - callerArgc = callerFrame->nactual(); - const Value &calleeVal = regs.sp[-callerArgc - 2]; - - RootedFunction fun(cx, calleeVal.toObject().toFunction()); - RootedScript script(cx, fun->nonLazyScript()); - CallArgs inlineArgs = CallArgsFromSp(callerArgc, regs.sp); - - // Bump the stack pointer to make it look like the inline args have been pushed, but they will - // really get filled in by RestoreOneFrame. - regs.sp = inlineArgs.end(); - - InitialFrameFlags flags = INITIAL_NONE; - if (JSOp(*regs.pc) == JSOP_NEW) - flags = INITIAL_CONSTRUCT; - - if (!cx->stack.pushInlineFrame(cx, regs, inlineArgs, fun, script, flags, DONT_REPORT_ERROR)) - return NULL; - - StackFrame *fp = cx->stack.fp(); - JS_ASSERT(fp == regs.fp()); - JS_ASSERT(fp->prev() == callerFrame); - - fp->formals()[-2].setObject(*fun); - - return fp; -} - -static uint32_t -ConvertFrames(JSContext *cx, IonActivation *activation, IonBailoutIterator &it) -{ - IonSpew(IonSpew_Bailouts, "Bailing out %s:%u, IonScript %p", - it.script()->filename(), it.script()->lineno, (void *) it.ionScript()); - IonSpew(IonSpew_Bailouts, " reading from snapshot offset %u size %u", - it.snapshotOffset(), it.ionScript()->snapshotsSize()); -#ifdef DEBUG - // Use count is reset after invalidation. Log use count on bailouts to - // determine if we have a critical sequence of bailout. - // - // Note: frame conversion only occurs in sequential mode - if (it.script()->maybeIonScript() == it.ionScript()) { - IonSpew(IonSpew_Bailouts, " Current script use count is %u", - it.script()->getUseCount()); - } -#endif - - // Set a flag to avoid bailing out on every iteration or function call. Ion can - // compile and run the script again after an invalidation. - it.ionScript()->incNumBailouts(); - it.script()->updateBaselineOrIonRaw(); - - // We use OffTheBooks instead of cx because at this time we cannot iterate - // on the stack safely and the reported error attempts to walk the IonMonkey - // frames. We cannot iterate on the stack because we have no exit frame to - // start Ion frames iteratons. - BailoutClosure *br = js_new(); - if (!br) - return BAILOUT_RETURN_FATAL_ERROR; - activation->setBailout(br); - - StackFrame *fp; - if (it.isEntryJSFrame() && cx->fp()->runningInIon() && activation->entryfp()) { - // Avoid creating duplicate interpreter frames. This is necessary to - // avoid blowing out the interpreter stack, and must be used in - // conjunction with inline-OSR from within bailouts (since each Ion - // activation must be tied to a unique StackFrame for ScriptFrameIter to - // work). - // - // Note: If the entry frame is a placeholder (a stub frame pushed for - // JM -> Ion calls), then we cannot re-use it as it does not have - // enough slots. - JS_ASSERT(cx->fp() == activation->entryfp()); - fp = cx->fp(); - cx->regs().sp = fp->base(); - } else { - br->constructFrame(); - if (!cx->stack.pushBailoutArgs(cx, it, br->argsGuard())) - return BAILOUT_RETURN_FATAL_ERROR; - - fp = cx->stack.pushBailoutFrame(cx, it, *br->argsGuard(), br->frameGuard()); - } - - if (!fp) - return BAILOUT_RETURN_OVERRECURSED; - - br->setEntryFrame(fp); - - JSFunction *callee = it.maybeCallee(); - if (callee) - fp->formals()[-2].setObject(*callee); - - if (it.isConstructing()) - fp->setConstructing(); - - SnapshotIterator iter(it); - - while (true) { - IonSpew(IonSpew_Bailouts, " restoring frame"); - fp->initFromBailout(cx, iter); - // If the IonScript wasn't compiled with SPS enabled, make sure that the StackFrame - // frame isn't marked as having a pushed SPS frame. - if (!it.ionScript()->hasSPSInstrumentation()) - fp->unsetPushedSPSFrame(); - - if (!iter.moreFrames()) - break; - iter.nextFrame(); - - fp = PushInlinedFrame(cx, fp); - if (!fp) - return BAILOUT_RETURN_OVERRECURSED; - } - - fp->clearRunningInIon(); - - jsbytecode *bailoutPc = fp->script()->code + iter.pcOffset(); - br->setBailoutPc(bailoutPc); - - switch (iter.bailoutKind()) { - case Bailout_Normal: - return BAILOUT_RETURN_OK; - case Bailout_TypeBarrier: - return BAILOUT_RETURN_TYPE_BARRIER; - case Bailout_Monitor: - return BAILOUT_RETURN_MONITOR; - case Bailout_BoundsCheck: - return BAILOUT_RETURN_BOUNDS_CHECK; - case Bailout_ShapeGuard: - return BAILOUT_RETURN_SHAPE_GUARD; - case Bailout_CachedShapeGuard: - return BAILOUT_RETURN_CACHED_SHAPE_GUARD; - - // When bailing out from an argument check, none of the code of the - // function has run yet. When profiling, this means that the function - // hasn't flagged its entry just yet. It has been "entered," however, so - // we flag it here manually that the entry has happened. - case Bailout_ArgumentCheck: - fp->unsetPushedSPSFrame(); - Probes::enterScript(cx, fp->script(), fp->script()->function(), fp); - return BAILOUT_RETURN_ARGUMENT_CHECK; - } - - JS_NOT_REACHED("bad bailout kind"); - return BAILOUT_RETURN_FATAL_ERROR; -} - uint32_t ion::Bailout(BailoutStack *sp, BaselineBailoutInfo **bailoutInfo) { @@ -353,25 +73,18 @@ ion::Bailout(BailoutStack *sp, BaselineBailoutInfo **bailoutInfo) IonBailoutIterator iter(ionActivations, sp); IonActivation *activation = ionActivations.activation(); - // IonCompartment *ioncompartment = cx->compartment->ionCompartment(); - // IonActivation *activation = cx->runtime->ionActivation; - // FrameRecovery in = FrameRecoveryFromBailout(ioncompartment, sp); - IonSpew(IonSpew_Bailouts, "Took bailout! Snapshot offset: %d", iter.snapshotOffset()); - uint32_t retval; - if (IsBaselineEnabled(cx)) { - *bailoutInfo = NULL; - retval = BailoutIonToBaseline(cx, activation, iter, false, bailoutInfo); - JS_ASSERT(retval == BAILOUT_RETURN_BASELINE || - retval == BAILOUT_RETURN_FATAL_ERROR || - retval == BAILOUT_RETURN_OVERRECURSED); - JS_ASSERT_IF(retval == BAILOUT_RETURN_BASELINE, *bailoutInfo != NULL); - } else { - retval = ConvertFrames(cx, activation, iter); - } + JS_ASSERT(IsBaselineEnabled(cx)); - if (retval != BAILOUT_RETURN_BASELINE) + *bailoutInfo = NULL; + uint32_t retval = BailoutIonToBaseline(cx, activation, iter, false, bailoutInfo); + JS_ASSERT(retval == BAILOUT_RETURN_OK || + retval == BAILOUT_RETURN_FATAL_ERROR || + retval == BAILOUT_RETURN_OVERRECURSED); + JS_ASSERT_IF(retval == BAILOUT_RETURN_OK, *bailoutInfo != NULL); + + if (retval != BAILOUT_RETURN_OK) EnsureExitFrame(iter.jsFrame()); return retval; @@ -396,36 +109,16 @@ ion::InvalidationBailout(InvalidationBailoutStack *sp, size_t *frameSizeOut, // Note: the frame size must be computed before we return from this function. *frameSizeOut = iter.topFrameSize(); - uint32_t retval; - if (IsBaselineEnabled(cx)) { - *bailoutInfo = NULL; - retval = BailoutIonToBaseline(cx, activation, iter, true, bailoutInfo); - JS_ASSERT(retval == BAILOUT_RETURN_BASELINE || - retval == BAILOUT_RETURN_FATAL_ERROR || - retval == BAILOUT_RETURN_OVERRECURSED); - JS_ASSERT_IF(retval == BAILOUT_RETURN_BASELINE, *bailoutInfo != NULL); + JS_ASSERT(IsBaselineEnabled(cx)); - if (retval != BAILOUT_RETURN_BASELINE) { - IonJSFrameLayout *frame = iter.jsFrame(); - IonSpew(IonSpew_Invalidate, "converting to exit frame"); - IonSpew(IonSpew_Invalidate, " orig calleeToken %p", (void *) frame->calleeToken()); - IonSpew(IonSpew_Invalidate, " orig frameSize %u", unsigned(frame->prevFrameLocalSize())); - IonSpew(IonSpew_Invalidate, " orig ra %p", (void *) frame->returnAddress()); - - frame->replaceCalleeToken(NULL); - EnsureExitFrame(frame); - - IonSpew(IonSpew_Invalidate, " new calleeToken %p", (void *) frame->calleeToken()); - IonSpew(IonSpew_Invalidate, " new frameSize %u", unsigned(frame->prevFrameLocalSize())); - IonSpew(IonSpew_Invalidate, " new ra %p", (void *) frame->returnAddress()); - } - - iter.ionScript()->decref(cx->runtime->defaultFreeOp()); - - return retval; - } else { - retval = ConvertFrames(cx, activation, iter); + *bailoutInfo = NULL; + uint32_t retval = BailoutIonToBaseline(cx, activation, iter, true, bailoutInfo); + JS_ASSERT(retval == BAILOUT_RETURN_OK || + retval == BAILOUT_RETURN_FATAL_ERROR || + retval == BAILOUT_RETURN_OVERRECURSED); + JS_ASSERT_IF(retval == BAILOUT_RETURN_OK, *bailoutInfo != NULL); + if (retval != BAILOUT_RETURN_OK) { IonJSFrameLayout *frame = iter.jsFrame(); IonSpew(IonSpew_Invalidate, "converting to exit frame"); IonSpew(IonSpew_Invalidate, " orig calleeToken %p", (void *) frame->calleeToken()); @@ -438,83 +131,11 @@ ion::InvalidationBailout(InvalidationBailoutStack *sp, size_t *frameSizeOut, IonSpew(IonSpew_Invalidate, " new calleeToken %p", (void *) frame->calleeToken()); IonSpew(IonSpew_Invalidate, " new frameSize %u", unsigned(frame->prevFrameLocalSize())); IonSpew(IonSpew_Invalidate, " new ra %p", (void *) frame->returnAddress()); - - iter.ionScript()->decref(cx->runtime->defaultFreeOp()); - - // Only need to take ion return override if resuming to interpreter. - if (cx->runtime->hasIonReturnOverride()) - cx->regs().sp[-1] = cx->runtime->takeIonReturnOverride(); - - if (retval != BAILOUT_RETURN_FATAL_ERROR) { - // If invalidation was triggered inside a stub call, we may still have to - // monitor the result, since the bailout happens before the MMonitorTypes - // instruction is executed. - jsbytecode *pc = activation->bailout()->bailoutPc(); - - // If this is not a ResumeAfter bailout, there's nothing to monitor, - // we will redo the op in the interpreter. - bool isResumeAfter = GetNextPc(pc) == cx->regs().pc; - - if ((js_CodeSpec[*pc].format & JOF_TYPESET) && isResumeAfter) { - JS_ASSERT(retval == BAILOUT_RETURN_OK); - return BAILOUT_RETURN_MONITOR; - } - - return retval; - } - - return BAILOUT_RETURN_FATAL_ERROR; - } -} - -static void -ReflowArgTypes(JSContext *cx) -{ - StackFrame *fp = cx->fp(); - unsigned nargs = fp->fun()->nargs; - RootedScript script(cx, fp->script()); - - types::AutoEnterAnalysis enter(cx); - - if (!fp->isConstructing()) - types::TypeScript::SetThis(cx, script, fp->thisValue()); - for (unsigned i = 0; i < nargs; ++i) - types::TypeScript::SetArgument(cx, script, i, fp->unaliasedFormal(i, DONT_CHECK_ALIASING)); -} - -uint32_t -ion::ReflowTypeInfo(uint32_t bailoutResult) -{ - JSContext *cx = GetIonContext()->cx; - IonActivation *activation = cx->mainThread().ionActivation; - - IonSpew(IonSpew_Bailouts, "reflowing type info"); - - if (bailoutResult == BAILOUT_RETURN_ARGUMENT_CHECK) { - IonSpew(IonSpew_Bailouts, "reflowing type info at argument-checked entry"); - ReflowArgTypes(cx); - return true; } - RootedScript script(cx, cx->fp()->script()); - jsbytecode *pc = activation->bailout()->bailoutPc(); + iter.ionScript()->decref(cx->runtime->defaultFreeOp()); - JS_ASSERT(js_CodeSpec[*pc].format & JOF_TYPESET); - - IonSpew(IonSpew_Bailouts, "reflowing type info at %s:%d pcoff %d", script->filename(), - script->lineno, pc - script->code); - - types::AutoEnterAnalysis enter(cx); - if (bailoutResult == BAILOUT_RETURN_TYPE_BARRIER) - script->analysis()->breakTypeBarriers(cx, pc - script->code, false); - else - JS_ASSERT(bailoutResult == BAILOUT_RETURN_MONITOR); - - // When a type barrier fails, the bad value is at the top of the stack. - Value &result = cx->regs().sp[-1]; - types::TypeScript::Monitor(cx, script, pc, result); - - return true; + return retval; } // Initialize the decl env Object, call object, and any arguments obj of the current frame. @@ -530,161 +151,6 @@ ion::EnsureHasScopeObjects(JSContext *cx, AbstractFramePtr fp) return true; } -uint32_t -ion::BoundsCheckFailure() -{ - JSContext *cx = GetIonContext()->cx; - JSScript *script = GetBailedJSScript(cx); - - IonSpew(IonSpew_Bailouts, "Bounds check failure %s:%d", script->filename(), - script->lineno); - - if (!script->failedBoundsCheck) { - script->failedBoundsCheck = true; - - // Invalidate the script to force a recompile. - IonSpew(IonSpew_Invalidate, "Invalidating due to bounds check failure"); - - return Invalidate(cx, script); - } - - return true; -} - -uint32_t -ion::ShapeGuardFailure() -{ - JSContext *cx = GetIonContext()->cx; - JSScript *script = GetBailedJSScript(cx); - - JS_ASSERT(!script->ionScript()->invalidated()); - - script->failedShapeGuard = true; - - IonSpew(IonSpew_Invalidate, "Invalidating due to shape guard failure"); - - return Invalidate(cx, script); -} - -uint32_t -ion::CachedShapeGuardFailure() -{ - JSContext *cx = GetIonContext()->cx; - JSScript *script = GetBailedJSScript(cx); - - JS_ASSERT(!script->ionScript()->invalidated()); - - script->failedShapeGuard = true; - - IonSpew(IonSpew_Invalidate, "Invalidating due to shape guard failure"); - - return Invalidate(cx, script); -} - -uint32_t -ion::ThunkToInterpreter(Value *vp) -{ - JSContext *cx = GetIonContext()->cx; - IonActivation *activation = cx->mainThread().ionActivation; - BailoutClosure *br = activation->takeBailout(); - InterpMode resumeMode = JSINTERP_BAILOUT; - - if (!EnsureHasScopeObjects(cx, cx->fp())) - resumeMode = JSINTERP_RETHROW; - - // By default we set the forbidOsr flag on the ion script, but if a GC - // happens just after we re-enter the interpreter, the ion script get - // invalidated and we do not see the forbidOsr flag. This may cause a loop - // which apear with eager compilation and gc zeal enabled. This code is a - // workaround to avoid recompiling with OSR just after a bailout followed by - // a GC. (see Bug 746691 & Bug 751383) - jsbytecode *pc = cx->regs().pc; - while (JSOp(*pc) == JSOP_GOTO) - pc += GET_JUMP_OFFSET(pc); - if (JSOp(*pc) == JSOP_LOOPENTRY) - cx->regs().pc = GetNextPc(pc); - - // When JSScript::argumentsOptimizationFailed, we cannot access Ion frames - // in order to create an arguments object for them. However, there is an - // invariant that script->needsArgsObj() implies fp->hasArgsObj() (after the - // prologue), so we must create one now for each inlined frame which needs - // one. - { - ScriptFrameIter iter(cx); - StackFrame *fp = NULL; - Rooted script(cx); - do { - fp = iter.interpFrame(); - script = iter.script(); - if (script->needsArgsObj()) { - ArgumentsObject *argsObj; - if (fp->hasArgsObj()) { - argsObj = &fp->argsObj(); - } else { - argsObj = ArgumentsObject::createExpected(cx, fp); - if (!argsObj) { - resumeMode = JSINTERP_RETHROW; - break; - } - } - - // The arguments is a local binding and needsArgsObj does not - // check if it is clobbered. Ensure that the local binding - // restored during bailout before storing the arguments object - // to the slot. - SetFrameArgumentsObject(cx, fp, script, argsObj); - } - ++iter; - } while (fp != br->entryfp()); - } - - if (activation->entryfp() == br->entryfp()) { - // If the bailout entry fp is the same as the activation entryfp, then - // there are no scripted frames below us. In this case, just shortcut - // out with a special return code, and resume interpreting in the - // original Interpret activation. - vp->setMagic(JS_ION_BAILOUT); - js_delete(br); - return resumeMode == JSINTERP_RETHROW ? Interpret_Error : Interpret_Ok; - } - - InterpretStatus status = Interpret(cx, br->entryfp(), resumeMode); - JS_ASSERT_IF(resumeMode == JSINTERP_RETHROW, status == Interpret_Error); - - if (status == Interpret_OSR) { - // The interpreter currently does not ask to perform inline OSR, so - // this path is unreachable. - JS_NOT_REACHED("invalid"); - - IonSpew(IonSpew_Bailouts, "Performing inline OSR %s:%d", - cx->fp()->script()->filename(), - PCToLineNumber(cx->fp()->script(), cx->regs().pc)); - - // We want to OSR again. We need to avoid the problem where frequent - // bailouts cause recursive nestings of Interpret and EnterIon. The - // interpreter therefore shortcuts out, and now we're responsible for - // completing the OSR inline. - // - // Note that we set runningInIon so that if we re-enter C++ from within - // the inlined OSR, ScriptFrameIter will know to traverse these frames. - StackFrame *fp = cx->fp(); - - fp->setRunningInIon(); - vp->setPrivate(fp); - js_delete(br); - return Interpret_OSR; - } - - if (status == Interpret_Ok) - *vp = br->entryfp()->returnValue(); - - // The BailoutFrameGuard's destructor will ensure that the frame is - // removed. - js_delete(br); - - return status; -} - bool ion::CheckFrequentBailouts(JSContext *cx, JSScript *script) { diff --git a/js/src/ion/Bailouts.h b/js/src/ion/Bailouts.h index 73c8409f55b..a67e3d2aff1 100644 --- a/js/src/ion/Bailouts.h +++ b/js/src/ion/Bailouts.h @@ -100,65 +100,7 @@ static const uint32_t BAILOUT_TABLE_SIZE = 16; // N.B. the relative order of these values is hard-coded into ::GenerateBailoutThunk. static const uint32_t BAILOUT_RETURN_OK = 0; static const uint32_t BAILOUT_RETURN_FATAL_ERROR = 1; -static const uint32_t BAILOUT_RETURN_ARGUMENT_CHECK = 2; -static const uint32_t BAILOUT_RETURN_TYPE_BARRIER = 3; -static const uint32_t BAILOUT_RETURN_MONITOR = 4; -static const uint32_t BAILOUT_RETURN_BOUNDS_CHECK = 5; -static const uint32_t BAILOUT_RETURN_SHAPE_GUARD = 6; -static const uint32_t BAILOUT_RETURN_OVERRECURSED = 7; -static const uint32_t BAILOUT_RETURN_CACHED_SHAPE_GUARD = 8; -static const uint32_t BAILOUT_RETURN_BASELINE = 9; - -// Attached to the compartment for easy passing through from ::Bailout to -// ::ThunkToInterpreter. -class BailoutClosure -{ - // These class are used to control the stack usage and the order of - // declaration is used by the destructor to restore the stack in the - // expected order when classes are created. This class is only created - // when we need a new stack frame. - struct Guards { - InvokeArgsGuard iag; - BailoutFrameGuard bfg; - }; - - mozilla::Maybe guards_; - - StackFrame *entryfp_; - jsbytecode *bailoutPc_; - - public: - BailoutClosure() - : bailoutPc_(NULL) - { } - - void constructFrame() { - guards_.construct(); - } - InvokeArgsGuard *argsGuard() { - return &guards_.ref().iag; - } - BailoutFrameGuard *frameGuard() { - return &guards_.ref().bfg; - } - StackFrame *entryfp() const { - return entryfp_; - } - void setEntryFrame(StackFrame *fp) { - entryfp_ = fp; - } - - // The bailout pc is the pc associated with the MResuemPoint this bailout - // is restoring. If the resume point is "ResumeAfter", we'll actually start - // executing from the *next* bytecode. This lets us recover the pc used for - // type inference reflows. - void setBailoutPc(jsbytecode *pc) { - bailoutPc_ = pc; - } - jsbytecode *bailoutPc() const { - return bailoutPc_; - } -}; +static const uint32_t BAILOUT_RETURN_OVERRECURSED = 2; class IonCompartment; @@ -214,18 +156,6 @@ uint32_t Bailout(BailoutStack *sp, BaselineBailoutInfo **info); uint32_t InvalidationBailout(InvalidationBailoutStack *sp, size_t *frameSizeOut, BaselineBailoutInfo **info); -// Called from a bailout thunk. Interprets the frame(s) that have been bailed -// out. -uint32_t ThunkToInterpreter(Value *vp); - -uint32_t ReflowTypeInfo(uint32_t bailoutResult); - -uint32_t BoundsCheckFailure(); - -uint32_t ShapeGuardFailure(); - -uint32_t CachedShapeGuardFailure(); - uint32_t FinishBailoutToBaseline(BaselineBailoutInfo *bailoutInfo); bool CheckFrequentBailouts(JSContext *cx, JSScript *script); diff --git a/js/src/ion/BaselineBailouts.cpp b/js/src/ion/BaselineBailouts.cpp index e1111a5799e..136aeef620b 100644 --- a/js/src/ion/BaselineBailouts.cpp +++ b/js/src/ion/BaselineBailouts.cpp @@ -1119,7 +1119,7 @@ ion::BailoutIonToBaseline(JSContext *cx, IonActivation *activation, IonBailoutIt info->bailoutKind = bailoutKind; *bailoutInfo = info; - return BAILOUT_RETURN_BASELINE; + return BAILOUT_RETURN_OK; } static bool diff --git a/js/src/ion/Ion.cpp b/js/src/ion/Ion.cpp index d5633c53a3d..fed1dd07793 100644 --- a/js/src/ion/Ion.cpp +++ b/js/src/ion/Ion.cpp @@ -413,7 +413,6 @@ IonActivation::IonActivation(JSContext *cx, StackFrame *fp) compartment_(cx->compartment), prev_(cx->mainThread().ionActivation), entryfp_(fp), - bailout_(NULL), prevIonTop_(cx->mainThread().ionTop), prevIonJSContext_(cx->mainThread().ionJSContext), prevpc_(NULL) @@ -427,7 +426,6 @@ IonActivation::IonActivation(JSContext *cx, StackFrame *fp) IonActivation::~IonActivation() { JS_ASSERT(cx_->mainThread().ionActivation == this); - JS_ASSERT(!bailout_); if (entryfp_) entryfp_->clearRunningInIon(); @@ -1564,17 +1562,13 @@ Compile(JSContext *cx, HandleScript script, AbstractFramePtr fp, jsbytecode *osr bool constructing, CompileContext &compileContext) { JS_ASSERT(ion::IsEnabled(cx)); + JS_ASSERT(ion::IsBaselineEnabled(cx)); JS_ASSERT_IF(osrPc != NULL, (JSOp)*osrPc == JSOP_LOOPENTRY); ExecutionMode executionMode = compileContext.executionMode(); - if (executionMode == SequentialExecution && - IsBaselineEnabled(cx) && !script->hasBaselineScript()) - { - //IonSpew(IonSpew_Abort, "Aborted compilation of %s:%d (has no baseline script)", - // script->filename(), script->lineno); + if (executionMode == SequentialExecution && !script->hasBaselineScript()) return Method_Skipped; - } if (cx->compartment->debugMode()) { IonSpew(IonSpew_Abort, "debugging"); @@ -1600,15 +1594,10 @@ Compile(JSContext *cx, HandleScript script, AbstractFramePtr fp, jsbytecode *osr } if (executionMode == SequentialExecution) { - if (IsBaselineEnabled(cx)) { - // If Baseline is enabled we use getUseCount instead of incUseCount to avoid - // bumping the use count twice. - if (script->getUseCount() < js_IonOptions.usesBeforeCompile) - return Method_Skipped; - } else { - if (script->incUseCount() < js_IonOptions.usesBeforeCompileNoJaeger) - return Method_Skipped; - } + // Use getUseCount instead of incUseCount to avoid bumping the + // use count twice. + if (script->getUseCount() < js_IonOptions.usesBeforeCompile) + return Method_Skipped; } AbortReason reason = IonCompile(cx, script, fp, osrPc, constructing, compileContext); @@ -1716,6 +1705,15 @@ ion::CanEnter(JSContext *cx, JSScript *script, AbstractFramePtr fp, bool isConst return Method_CantCompile; } + // If --ion-eager is used, compile with Baseline first, so that we + // can directly enter IonMonkey. + if (js_IonOptions.eagerCompilation && !script->hasBaselineScript()) { + bool newType = isConstructing && fp.asStackFrame()->useNewType(); + MethodStatus status = CanEnterBaselineJIT(cx, script, fp.asStackFrame(), newType); + if (status != Method_Compiled) + return status; + } + // Attempt compilation. Returns Method_Compiled if already compiled. SequentialCompileContext compileContext; RootedScript rscript(cx, script); @@ -2003,12 +2001,6 @@ EnterIon(JSContext *cx, StackFrame *fp, void *jitcode) result.address()); } - if (result.isMagic() && result.whyMagic() == JS_ION_BAILOUT) { - if (!EnsureHasScopeObjects(cx, cx->fp())) - return IonExec_Error; - return IonExec_Bailout; - } - JS_ASSERT(fp == cx->fp()); JS_ASSERT(!cx->runtime->hasIonReturnOverride()); @@ -2054,39 +2046,6 @@ ion::Cannon(JSContext *cx, StackFrame *fp) return status; } -IonExecStatus -ion::SideCannon(JSContext *cx, StackFrame *fp, jsbytecode *pc) -{ - RootedScript script(cx, fp->script()); - IonScript *ion = script->ionScript(); - IonCode *code = ion->method(); - void *osrcode = code->raw() + ion->osrEntryOffset(); - - JS_ASSERT(ion->osrPc() == pc); - -#if JS_TRACE_LOGGING - TraceLog(TraceLogging::defaultLogger(), - TraceLogging::ION_SIDE_CANNON_START, - script); -#endif - - IonExecStatus status = EnterIon(cx, fp, osrcode); - -#if JS_TRACE_LOGGING - if (status == IonExec_Bailout) { - TraceLog(TraceLogging::defaultLogger(), - TraceLogging::ION_SIDE_CANNON_BAIL, - script); - } else { - TraceLog(TraceLogging::defaultLogger(), - TraceLogging::ION_SIDE_CANNON_STOP, - script); - } -#endif - - return status; -} - IonExecStatus ion::FastInvoke(JSContext *cx, HandleFunction fun, CallArgs &args) { diff --git a/js/src/ion/Ion.h b/js/src/ion/Ion.h index bbe9da3ac72..813fc2b3753 100644 --- a/js/src/ion/Ion.h +++ b/js/src/ion/Ion.h @@ -296,11 +296,7 @@ enum IonExecStatus IonExec_Error, // The method call succeeed and returned a value. - IonExec_Ok, - - // A guard triggered in IonMonkey and we must resume execution in - // the interpreter. - IonExec_Bailout + IonExec_Ok }; static inline bool @@ -310,7 +306,6 @@ IsErrorStatus(IonExecStatus status) } IonExecStatus Cannon(JSContext *cx, StackFrame *fp); -IonExecStatus SideCannon(JSContext *cx, StackFrame *fp, jsbytecode *pc); // Used to enter Ion from C++ natives like Array.map. Called from FastInvokeGuard. IonExecStatus FastInvoke(JSContext *cx, HandleFunction fun, CallArgs &args); @@ -340,9 +335,12 @@ CodeGenerator *CompileBackEnd(MIRGenerator *mir, MacroAssembler *maybeMasm = NUL void AttachFinishedCompilations(JSContext *cx); void FinishOffThreadBuilder(IonBuilder *builder); -static inline bool IsEnabled(JSContext *cx) +static inline bool +IsEnabled(JSContext *cx) { - return cx->hasOption(JSOPTION_ION) && cx->typeInferenceEnabled(); + return cx->hasOption(JSOPTION_ION) && + cx->hasOption(JSOPTION_BASELINE) && + cx->typeInferenceEnabled(); } void ForbidCompilation(JSContext *cx, JSScript *script); diff --git a/js/src/ion/IonBuilder.cpp b/js/src/ion/IonBuilder.cpp index f3dfdab6a81..29f119a9417 100644 --- a/js/src/ion/IonBuilder.cpp +++ b/js/src/ion/IonBuilder.cpp @@ -241,10 +241,7 @@ IonBuilder::canInlineTarget(JSFunction *target, CallInfo &callInfo) } // Don't inline functions which don't have baseline scripts compiled for them. - if (executionMode == SequentialExecution && - ion::IsBaselineEnabled(cx) && - !inlineScript->hasBaselineScript()) - { + if (executionMode == SequentialExecution && !inlineScript->hasBaselineScript()) { IonSpew(IonSpew_Inlining, "%s:%d Cannot inline target with no baseline jitcode", inlineScript->filename(), inlineScript->lineno); return false; diff --git a/js/src/ion/IonCompartment.h b/js/src/ion/IonCompartment.h index b7114e4b429..cf232b6b096 100644 --- a/js/src/ion/IonCompartment.h +++ b/js/src/ion/IonCompartment.h @@ -310,8 +310,6 @@ class IonCompartment } }; -class BailoutClosure; - class IonActivation { private: @@ -319,7 +317,6 @@ class IonActivation JSCompartment *compartment_; IonActivation *prev_; StackFrame *entryfp_; - BailoutClosure *bailout_; uint8_t *prevIonTop_; JSContext *prevIonJSContext_; @@ -352,23 +349,6 @@ class IonActivation JS_ASSERT_IF(pc, !prevpc_); prevpc_ = pc; } - void setBailout(BailoutClosure *bailout) { - JS_ASSERT(!bailout_); - bailout_ = bailout; - } - BailoutClosure *maybeTakeBailout() { - BailoutClosure *br = bailout_; - bailout_ = NULL; - return br; - } - BailoutClosure *takeBailout() { - JS_ASSERT(bailout_); - return maybeTakeBailout(); - } - BailoutClosure *bailout() const { - JS_ASSERT(bailout_); - return bailout_; - } JSCompartment *compartment() const { return compartment_; } diff --git a/js/src/ion/IonFrames.cpp b/js/src/ion/IonFrames.cpp index 46efbb147a4..16ef0529bf0 100644 --- a/js/src/ion/IonFrames.cpp +++ b/js/src/ion/IonFrames.cpp @@ -456,10 +456,6 @@ HandleException(ResumeFromException *rfe) IonSpew(IonSpew_Invalidate, "handling exception"); - // Immediately remove any bailout frame guard that might be left over from - // an error in between ConvertFrames and ThunkToInterpreter. - js_delete(cx->mainThread().ionActivation->maybeTakeBailout()); - // Clear any Ion return override that's been set. // This may happen if a callVM function causes an invalidation (setting the // override), and then fails, bypassing the bailout handlers that would diff --git a/js/src/ion/IonMacroAssembler.cpp b/js/src/ion/IonMacroAssembler.cpp index 89177420423..a4069ce3298 100644 --- a/js/src/ion/IonMacroAssembler.cpp +++ b/js/src/ion/IonMacroAssembler.cpp @@ -713,79 +713,18 @@ MacroAssembler::generateBailoutTail(Register scratch, Register bailoutInfo) { enterExitFrame(); - Label reflow; - Label interpret; Label exception; - Label osr; - Label boundscheck; - Label overrecursed; - Label invalidate; Label baseline; // The return value from Bailout is tagged as: - // - 0x0: done (thunk to interpreter) + // - 0x0: done (enter baseline) // - 0x1: error (handle exception) - // - 0x2: reflow args - // - 0x3: reflow barrier - // - 0x4: monitor types - // - 0x5: bounds check failure - // - 0x6: force invalidation - // - 0x7: overrecursed - // - 0x8: cached shape guard failure - // - 0x9: bailout to baseline + // - 0x2: overrecursed - branch32(LessThan, ReturnReg, Imm32(BAILOUT_RETURN_FATAL_ERROR), &interpret); + branch32(Equal, ReturnReg, Imm32(BAILOUT_RETURN_OK), &baseline); branch32(Equal, ReturnReg, Imm32(BAILOUT_RETURN_FATAL_ERROR), &exception); - branch32(LessThan, ReturnReg, Imm32(BAILOUT_RETURN_BOUNDS_CHECK), &reflow); - - branch32(Equal, ReturnReg, Imm32(BAILOUT_RETURN_BOUNDS_CHECK), &boundscheck); - branch32(Equal, ReturnReg, Imm32(BAILOUT_RETURN_OVERRECURSED), &overrecursed); - branch32(Equal, ReturnReg, Imm32(BAILOUT_RETURN_SHAPE_GUARD), &invalidate); - branch32(Equal, ReturnReg, Imm32(BAILOUT_RETURN_BASELINE), &baseline); - - // Fall-through: cached shape guard failure. - { - setupUnalignedABICall(0, scratch); - callWithABI(JS_FUNC_TO_DATA_PTR(void *, CachedShapeGuardFailure)); - - branchTest32(Zero, ReturnReg, ReturnReg, &exception); - jump(&interpret); - } - - // Force invalidation. - bind(&invalidate); - { - setupUnalignedABICall(0, scratch); - callWithABI(JS_FUNC_TO_DATA_PTR(void *, ShapeGuardFailure)); - - branchTest32(Zero, ReturnReg, ReturnReg, &exception); - jump(&interpret); - } - - // Bounds-check failure. - bind(&boundscheck); - { - setupUnalignedABICall(0, scratch); - callWithABI(JS_FUNC_TO_DATA_PTR(void *, BoundsCheckFailure)); - - branchTest32(Zero, ReturnReg, ReturnReg, &exception); - jump(&interpret); - } - - // Reflow types. - bind(&reflow); - { - setupUnalignedABICall(1, scratch); - passABIArg(ReturnReg); - callWithABI(JS_FUNC_TO_DATA_PTR(void *, ReflowTypeInfo)); - - branchTest32(Zero, ReturnReg, ReturnReg, &exception); - jump(&interpret); - } - - // Throw an over-recursion error. - bind(&overrecursed); + // Fall-through: overrecursed. { loadJSContext(ReturnReg); setupUnalignedABICall(1, scratch); @@ -794,39 +733,6 @@ MacroAssembler::generateBailoutTail(Register scratch, Register bailoutInfo) jump(&exception); } - bind(&interpret); - { - // Reserve space for Interpret() to store a Value. - subPtr(Imm32(sizeof(Value)), StackPointer); - mov(StackPointer, ReturnReg); - - // Call out to the interpreter. - setupUnalignedABICall(1, scratch); - passABIArg(ReturnReg); - callWithABI(JS_FUNC_TO_DATA_PTR(void *, ThunkToInterpreter)); - - // Load the value the interpreter returned. - popValue(JSReturnOperand); - - // Check for an exception. - JS_STATIC_ASSERT(!Interpret_Error); - branchTest32(Zero, ReturnReg, ReturnReg, &exception); - - // Remove the exitCode pointer from the stack. - leaveExitFrame(); - - branch32(Equal, ReturnReg, Imm32(Interpret_OSR), &osr); - - // Return to the caller. - ret(); - } - - bind(&osr); - { - unboxPrivate(JSReturnOperand, OsrFrameReg); - performOsr(); - } - bind(&exception); { handleException(); diff --git a/js/src/jit-test/jit_test.py b/js/src/jit-test/jit_test.py index 5220c6a8b58..786ae1f359f 100755 --- a/js/src/jit-test/jit_test.py +++ b/js/src/jit-test/jit_test.py @@ -145,8 +145,6 @@ def main(argv): ['--ion-eager'], # implies --baseline-eager ['--baseline-eager'], ['--baseline-eager', '--no-ti', '--no-fpu'], - ['--no-baseline'], - ['--no-baseline', '--ion-eager'], ['--no-baseline', '--no-ion'], ['--no-baseline', '--no-ion', '--no-ti'], ] @@ -156,7 +154,7 @@ def main(argv): new_test.jitflags.extend(variant) job_list.append(new_test) elif options.ion: - flags = [['--no-jm'], ['--ion-eager']] + flags = [['--baseline-eager'], ['--ion-eager']] for test in test_list: for variant in flags: new_test = test.copy() diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index 63e380c4086..569c8af60d2 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -334,12 +334,6 @@ js::RunScript(JSContext *cx, StackFrame *fp) return false; if (status == ion::Method_Compiled) { ion::IonExecStatus status = ion::Cannon(cx, fp); - - // Note that if we bailed out, new inline frames may have been - // pushed, so we interpret with the current fp. - if (status == ion::IonExec_Bailout) - return Interpret(cx, fp, JSINTERP_REJOIN); - return !IsErrorStatus(status); } } @@ -350,13 +344,6 @@ js::RunScript(JSContext *cx, StackFrame *fp) return false; if (status == ion::Method_Compiled) { ion::IonExecStatus status = ion::EnterBaselineMethod(cx, fp); - - // For now, we can never bail out from the baseline jit. - // TODO: This may need to be removed when we want to add support for - // OSR into Ion, which will be implemented by bailing out to the interpreter - // from baseline. - JS_ASSERT(status != ion::IonExec_Bailout); - return !IsErrorStatus(status); } } @@ -1344,39 +1331,7 @@ check_backedge: BEGIN_CASE(JSOP_LOOPENTRY) #ifdef JS_ION - // Attempt on-stack replacement with Ion code. IonMonkey OSR takes place at - // the point of the initial loop entry, to consolidate hoisted code between - // entry points. - if (ion::IsEnabled(cx)) { - ion::MethodStatus status = - ion::CanEnterAtBranch(cx, script, AbstractFramePtr(regs.fp()), regs.pc, - regs.fp()->isConstructing()); - if (status == ion::Method_Error) - goto error; - if (status == ion::Method_Compiled) { - ion::IonExecStatus maybeOsr = ion::SideCannon(cx, regs.fp(), regs.pc); - if (maybeOsr == ion::IonExec_Bailout) { - // We hit a deoptimization path in the first Ion frame, so now - // we've just replaced the entire Ion activation. - SET_SCRIPT(regs.fp()->script()); - op = JSOp(*regs.pc); - DO_OP(); - } - - // We failed to call into Ion at all, so treat as an error. - if (maybeOsr == ion::IonExec_Aborted) - goto error; - - interpReturnOK = (maybeOsr == ion::IonExec_Ok); - - if (entryFrame != regs.fp()) - goto jit_return; - - regs.fp()->setFinishedInInterpreter(); - goto leave_on_safe_point; - } - } - + // Attempt on-stack replacement with Baseline code. if (ion::IsBaselineEnabled(cx)) { ion::MethodStatus status = ion::CanEnterBaselineJIT(cx, script, regs.fp(), false); if (status == ion::Method_Error) @@ -1384,9 +1339,6 @@ BEGIN_CASE(JSOP_LOOPENTRY) if (status == ion::Method_Compiled) { ion::IonExecStatus maybeOsr = ion::EnterBaselineAtBranch(cx, regs.fp(), regs.pc); - // We can never bail out from the baseline jit. - JS_ASSERT(maybeOsr != ion::IonExec_Bailout); - // We failed to call into baseline at all, so treat as an error. if (maybeOsr == ion::IonExec_Aborted) goto error; @@ -2293,11 +2245,6 @@ BEGIN_CASE(JSOP_FUNCALL) if (status == ion::Method_Compiled) { ion::IonExecStatus exec = ion::Cannon(cx, regs.fp()); CHECK_BRANCH(); - if (exec == ion::IonExec_Bailout) { - SET_SCRIPT(regs.fp()->script()); - op = JSOp(*regs.pc); - DO_OP(); - } interpReturnOK = !IsErrorStatus(exec); goto jit_return; } @@ -2310,13 +2257,6 @@ BEGIN_CASE(JSOP_FUNCALL) if (status == ion::Method_Compiled) { ion::IonExecStatus exec = ion::EnterBaselineMethod(cx, regs.fp()); CHECK_BRANCH(); - - // For now, we can never bail out from the baseline jit. - // TODO: This may need to be removed when we want to add support for - // OSR into Ion, which will be implemented by bailing out to the interpreter - // from baseline. - JS_ASSERT(exec != ion::IonExec_Bailout); - interpReturnOK = !IsErrorStatus(exec); goto jit_return; } diff --git a/js/src/vm/Stack.cpp b/js/src/vm/Stack.cpp index 2dabc417c30..e38bc1b726b 100644 --- a/js/src/vm/Stack.cpp +++ b/js/src/vm/Stack.cpp @@ -13,7 +13,6 @@ #include "ion/BaselineFrame.h" #include "ion/IonFrames.h" #include "ion/IonCompartment.h" -#include "ion/Bailouts.h" #endif #include "Stack.h" #include "ForkJoin.h" @@ -968,35 +967,6 @@ ContextStack::pushExecuteFrame(JSContext *cx, HandleScript script, const Value & return true; } -#ifdef JS_ION -bool -ContextStack::pushBailoutArgs(JSContext *cx, const ion::IonBailoutIterator &it, InvokeArgsGuard *iag) -{ - unsigned argc = it.numActualArgs(); - - if (!pushInvokeArgs(cx, argc, iag, DONT_REPORT_ERROR)) - return false; - - ion::SnapshotIterator s(it); - JSFunction *fun = it.callee(); - iag->setCallee(ObjectValue(*fun)); - - CopyTo dst(iag->array()); - Value *src = it.actualArgs(); - Value thisv = iag->thisv(); - s.readFrameArgs(dst, src, NULL, &thisv, 0, fun->nargs, argc, it.script()); - return true; -} - -StackFrame * -ContextStack::pushBailoutFrame(JSContext *cx, const ion::IonBailoutIterator &it, - const CallArgs &args, BailoutFrameGuard *bfg) -{ - JSFunction *fun = it.callee(); - return pushInvokeFrame(cx, DONT_REPORT_ERROR, args, fun, INITIAL_NONE, bfg); -} -#endif - void ContextStack::popFrame(const FrameGuard &fg) { diff --git a/js/src/vm/Stack.h b/js/src/vm/Stack.h index 53a706b35b8..8dc5e54ef7b 100644 --- a/js/src/vm/Stack.h +++ b/js/src/vm/Stack.h @@ -29,7 +29,6 @@ class InvokeArgsGuard; class InvokeFrameGuard; class FrameGuard; class ExecuteFrameGuard; -class BailoutFrameGuard; class GeneratorFrameGuard; class CallIter; @@ -42,11 +41,6 @@ class StaticBlockObject; struct ScopeCoordinate; -namespace ion { - class IonBailoutIterator; - class SnapshotIterator; -} - /*****************************************************************************/ /* @@ -412,8 +406,6 @@ class StackFrame bool prologue(JSContext *cx); void epilogue(JSContext *cx); - /* Called from IonMonkey to transition from bailouts. */ - void initFromBailout(JSContext *cx, ion::SnapshotIterator &iter); bool initFunctionScopeObjects(JSContext *cx); /* Initialize local variables of newly-pushed frame. */ @@ -1428,7 +1420,6 @@ class ContextStack bool pushInvokeArgs(JSContext *cx, unsigned argc, InvokeArgsGuard *ag, MaybeReportError report = REPORT_ERROR); - /* Factor common code between pushInvokeFrame and pushBailoutFrame */ StackFrame *pushInvokeFrame(JSContext *cx, MaybeReportError report, const CallArgs &args, JSFunction *fun, InitialFrameFlags initial, FrameGuard *fg); @@ -1442,14 +1433,6 @@ class ContextStack HandleObject scopeChain, ExecuteType type, AbstractFramePtr evalInFrame, ExecuteFrameGuard *efg); - /* Allocate actual argument space for the bailed frame */ - bool pushBailoutArgs(JSContext *cx, const ion::IonBailoutIterator &it, - InvokeArgsGuard *iag); - - /* Bailout for normal functions. */ - StackFrame *pushBailoutFrame(JSContext *cx, const ion::IonBailoutIterator &it, - const CallArgs &args, BailoutFrameGuard *bfg); - /* * Called by SendToGenerator to resume a yielded generator. In addition to * pushing a frame onto the VM stack, this function copies over the @@ -1538,9 +1521,6 @@ class InvokeFrameGuard : public FrameGuard class ExecuteFrameGuard : public FrameGuard {}; -class BailoutFrameGuard : public FrameGuard -{}; - class DummyFrameGuard : public FrameGuard {}; From 15de9fbbf119003f5e61e98d84aeca30ac2c1126 Mon Sep 17 00:00:00 2001 From: Jan de Mooij Date: Tue, 21 May 2013 16:09:01 +0200 Subject: [PATCH 05/91] Bug 873155 - Remove StackFrame argument duplication. r=luke --- js/src/ion/BaselineFrame.cpp | 5 +- js/src/ion/BaselineFrame.h | 9 +-- js/src/ion/BaselineIC.cpp | 4 +- js/src/ion/BaselineJIT.cpp | 26 +-------- js/src/ion/Ion.cpp | 26 +-------- js/src/vm/ArgumentsObject.cpp | 20 ++----- js/src/vm/Stack-inl.h | 102 +++++++--------------------------- js/src/vm/Stack.cpp | 2 +- js/src/vm/Stack.h | 68 +++++++++-------------- 9 files changed, 62 insertions(+), 200 deletions(-) diff --git a/js/src/ion/BaselineFrame.cpp b/js/src/ion/BaselineFrame.cpp index 85c207c60e3..c0fa105e974 100644 --- a/js/src/ion/BaselineFrame.cpp +++ b/js/src/ion/BaselineFrame.cpp @@ -27,8 +27,7 @@ BaselineFrame::trace(JSTracer *trc) // Mark actual and formal args. if (isNonEvalFunctionFrame()) { unsigned numArgs = js::Max(numActualArgs(), numFormalArgs()); - JS_ASSERT(actuals() == formals()); - gc::MarkValueRootRange(trc, numArgs, actuals(), "baseline-args"); + gc::MarkValueRootRange(trc, numArgs, argv(), "baseline-args"); } // Mark scope chain. @@ -62,7 +61,7 @@ BaselineFrame::copyRawFrameSlots(AutoValueVector *vec) const if (!vec->resize(nformals + nfixed)) return false; - mozilla::PodCopy(vec->begin(), formals(), nformals); + mozilla::PodCopy(vec->begin(), argv(), nformals); for (unsigned i = 0; i < nfixed; i++) (*vec)[nformals + i] = *valueSlot(i); return true; diff --git a/js/src/ion/BaselineFrame.h b/js/src/ion/BaselineFrame.h index 85fdb925e54..3a10069331d 100644 --- a/js/src/ion/BaselineFrame.h +++ b/js/src/ion/BaselineFrame.h @@ -158,14 +158,14 @@ class BaselineFrame JS_ASSERT(i < numFormalArgs()); JS_ASSERT_IF(checkAliasing, !script()->argsObjAliasesFormals()); JS_ASSERT_IF(checkAliasing, !script()->formalIsAliased(i)); - return formals()[i]; + return argv()[i]; } Value &unaliasedActual(unsigned i, MaybeCheckAliasing checkAliasing) const { JS_ASSERT(i < numActualArgs()); JS_ASSERT_IF(checkAliasing, !script()->argsObjAliasesFormals()); JS_ASSERT_IF(checkAliasing && i < numFormalArgs(), !script()->formalIsAliased(i)); - return actuals()[i]; + return argv()[i]; } Value &unaliasedLocal(unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING) const { @@ -188,14 +188,11 @@ class BaselineFrame BaselineFrame::Size() + offsetOfThis()); } - Value *formals() const { + Value *argv() const { return (Value *)(reinterpret_cast(this) + BaselineFrame::Size() + offsetOfArg(0)); } - Value *actuals() const { - return formals(); - } bool copyRawFrameSlots(AutoValueVector *vec) const; diff --git a/js/src/ion/BaselineIC.cpp b/js/src/ion/BaselineIC.cpp index 60dcb084585..16edc74233d 100644 --- a/js/src/ion/BaselineIC.cpp +++ b/js/src/ion/BaselineIC.cpp @@ -785,7 +785,7 @@ PrepareOsrTempData(JSContext *cx, ICUseCount_Fallback *stub, BaselineFrame *fram // // Copy formal args and thisv. - memcpy(stackFrameStart, frame->formals() - 1, (numFormalArgs + 1) * sizeof(Value)); + memcpy(stackFrameStart, frame->argv() - 1, (numFormalArgs + 1) * sizeof(Value)); // Initialize ScopeChain, Exec, and Flags fields in StackFrame struct. uint8_t *stackFrame = info->stackFrame; @@ -8019,7 +8019,7 @@ DoCreateRestParameter(JSContext *cx, BaselineFrame *frame, ICRest_Fallback *stub unsigned numActuals = frame->numActualArgs(); unsigned numRest = numActuals > numFormals ? numActuals - numFormals : 0; - JSObject *obj = NewDenseCopiedArray(cx, numRest, frame->actuals() + numFormals, NULL); + JSObject *obj = NewDenseCopiedArray(cx, numRest, frame->argv() + numFormals, NULL); if (!obj) return false; obj->setType(type); diff --git a/js/src/ion/BaselineJIT.cpp b/js/src/ion/BaselineJIT.cpp index b475cedc03e..a729741e613 100644 --- a/js/src/ion/BaselineJIT.cpp +++ b/js/src/ion/BaselineJIT.cpp @@ -89,34 +89,14 @@ EnterBaseline(JSContext *cx, StackFrame *fp, void *jitcode, bool osr) // arguments and the number of formal arguments. It accounts for |this|. int maxArgc = 0; Value *maxArgv = NULL; - int numActualArgs = 0; + unsigned numActualArgs = 0; RootedValue thisv(cx); void *calleeToken; if (fp->isNonEvalFunctionFrame()) { - // CountArgSlot include |this| and the |scopeChain|, and maybe |argumentsObj| - // Want to keep including this, but remove the scopeChain and any argumentsObj. - maxArgc = CountArgSlots(fp->script(), fp->fun()) - StartArgSlot(fp->script(), fp->fun()); - maxArgv = fp->formals() - 1; // -1 = include |this| - - // Formal arguments are the argument corresponding to the function - // definition and actual arguments are corresponding to the call-site - // arguments. numActualArgs = fp->numActualArgs(); - - // We do not need to handle underflow because formal arguments are pad - // with |undefined| values but we need to distinguish between the - if (fp->hasOverflowArgs()) { - int formalArgc = maxArgc; - Value *formalArgv = maxArgv; - maxArgc = numActualArgs + 1; // +1 = include |this| - maxArgv = fp->actuals() - 1; // -1 = include |this| - - // The beginning of the actual args is not updated, so we just copy - // the formal args into the actual args to get a linear vector which - // can be copied by generateEnterJit. - memcpy(maxArgv, formalArgv, formalArgc * sizeof(Value)); - } + maxArgc = Max(numActualArgs, fp->numFormalArgs()) + 1; // +1 = include |this| + maxArgv = fp->argv() - 1; // -1 = include |this| calleeToken = CalleeToToken(&fp->callee()); } else { // For eval function frames, set the callee token to the enclosing function. diff --git a/js/src/ion/Ion.cpp b/js/src/ion/Ion.cpp index fed1dd07793..68ef551e9ad 100644 --- a/js/src/ion/Ion.cpp +++ b/js/src/ion/Ion.cpp @@ -1951,34 +1951,14 @@ EnterIon(JSContext *cx, StackFrame *fp, void *jitcode) // arguments and the number of formal arguments. It accounts for |this|. int maxArgc = 0; Value *maxArgv = NULL; - int numActualArgs = 0; + unsigned numActualArgs = 0; RootedValue thisv(cx); void *calleeToken; if (fp->isFunctionFrame()) { - // CountArgSlot include |this| and the |scopeChain| and maybe |argumentsObj|. - // Keep |this|, but discard the others. - maxArgc = CountArgSlots(fp->script(), fp->fun()) - StartArgSlot(fp->script(), fp->fun()); - maxArgv = fp->formals() - 1; // -1 = include |this| - - // Formal arguments are the argument corresponding to the function - // definition and actual arguments are corresponding to the call-site - // arguments. numActualArgs = fp->numActualArgs(); - - // We do not need to handle underflow because formal arguments are pad - // with |undefined| values but we need to distinguish between the - if (fp->hasOverflowArgs()) { - int formalArgc = maxArgc; - Value *formalArgv = maxArgv; - maxArgc = numActualArgs + 1; // +1 = include |this| - maxArgv = fp->actuals() - 1; // -1 = include |this| - - // The beginning of the actual args is not updated, so we just copy - // the formal args into the actual args to get a linear vector which - // can be copied by generateEnterJit. - memcpy(maxArgv, formalArgv, formalArgc * sizeof(Value)); - } + maxArgc = Max(numActualArgs, fp->numFormalArgs()) + 1; // +1 = include |this| + maxArgv = fp->argv() - 1; // -1 = include |this| calleeToken = CalleeToToken(&fp->callee()); } else { calleeToken = CalleeToToken(fp->script()); diff --git a/js/src/vm/ArgumentsObject.cpp b/js/src/vm/ArgumentsObject.cpp index f3372aad646..2500d953f14 100644 --- a/js/src/vm/ArgumentsObject.cpp +++ b/js/src/vm/ArgumentsObject.cpp @@ -30,25 +30,13 @@ CopyStackFrameArguments(const AbstractFramePtr frame, HeapValue *dst, unsigned t { JS_ASSERT_IF(frame.isStackFrame(), !frame.asStackFrame()->runningInIon()); - unsigned numActuals = frame.numActualArgs(); - unsigned numFormals = frame.callee()->nargs; - JS_ASSERT(numActuals <= totalArgs); - JS_ASSERT(numFormals <= totalArgs); - JS_ASSERT(Max(numActuals, numFormals) == totalArgs); + JS_ASSERT(Max(frame.numActualArgs(), frame.numFormalArgs()) == totalArgs); - /* Copy formal arguments. */ - Value *src = frame.formals(); - Value *end = src + numFormals; + /* Copy arguments. */ + Value *src = frame.argv(); + Value *end = src + totalArgs; while (src != end) (dst++)->init(*src++); - - /* Copy actual argument which are not contignous. */ - if (numFormals < numActuals) { - src = frame.actuals() + numFormals; - end = src + (numActuals - numFormals); - while (src != end) - (dst++)->init(*src++); - } } /* static */ void diff --git a/js/src/vm/Stack-inl.h b/js/src/vm/Stack-inl.h index 384e5ec159a..ff024bcc9e8 100644 --- a/js/src/vm/Stack-inl.h +++ b/js/src/vm/Stack-inl.h @@ -99,9 +99,7 @@ inline void StackFrame::initCallFrame(JSContext *cx, JSFunction &callee, JSScript *script, uint32_t nactual, StackFrame::Flags flagsArg) { - JS_ASSERT((flagsArg & ~(CONSTRUCTING | - OVERFLOW_ARGS | - UNDERFLOW_ARGS)) == 0); + JS_ASSERT((flagsArg & ~CONSTRUCTING) == 0); JS_ASSERT(callee.nonLazyScript() == script); /* Initialize stack frame members. */ @@ -129,7 +127,7 @@ StackFrame::createRestParameter(JSContext *cx) JS_ASSERT(fun()->hasRest()); unsigned nformal = fun()->nargs - 1, nactual = numActualArgs(); unsigned nrest = (nactual > nformal) ? nactual - nformal : 0; - return NewDenseCopiedArray(cx, nrest, actuals() + nformal, NULL); + return NewDenseCopiedArray(cx, nrest, argv() + nformal, NULL); } inline Value & @@ -155,7 +153,7 @@ StackFrame::unaliasedFormal(unsigned i, MaybeCheckAliasing checkAliasing) JS_ASSERT(i < numFormalArgs()); JS_ASSERT_IF(checkAliasing, !script()->argsObjAliasesFormals()); JS_ASSERT_IF(checkAliasing, !script()->formalIsAliased(i)); - return formals()[i]; + return argv()[i]; } inline Value & @@ -164,7 +162,7 @@ StackFrame::unaliasedActual(unsigned i, MaybeCheckAliasing checkAliasing) JS_ASSERT(i < numActualArgs()); JS_ASSERT_IF(checkAliasing, !script()->argsObjAliasesFormals()); JS_ASSERT_IF(checkAliasing && i < numFormalArgs(), !script()->formalIsAliased(i)); - return i < numFormalArgs() ? formals()[i] : actuals()[i]; + return argv()[i]; } template @@ -174,25 +172,9 @@ StackFrame::forEachUnaliasedActual(Op op) JS_ASSERT(!script()->funHasAnyAliasedFormal); JS_ASSERT(!script()->needsArgsObj()); - unsigned nformal = numFormalArgs(); - unsigned nactual = numActualArgs(); - - const Value *formalsEnd = (const Value *)this; - const Value *formals = formalsEnd - nformal; - - if (nactual <= nformal) { - const Value *actualsEnd = formals + nactual; - for (const Value *p = formals; p < actualsEnd; ++p) - op(*p); - } else { - for (const Value *p = formals; p < formalsEnd; ++p) - op(*p); - - const Value *actualsEnd = formals - 2; - const Value *actuals = actualsEnd - nactual; - for (const Value *p = actuals + nformal; p < actualsEnd; ++p) - op(*p); - } + const Value *argsEnd = argv() + numActualArgs(); + for (const Value *p = argv(); p < argsEnd; ++p) + op(*p); } struct CopyTo @@ -209,30 +191,6 @@ struct CopyToHeap void operator()(const Value &src) { dst->init(src); ++dst; } }; -inline unsigned -StackFrame::numFormalArgs() const -{ - JS_ASSERT(hasArgs()); - return fun()->nargs; -} - -inline unsigned -StackFrame::numActualArgs() const -{ - /* - * u.nactual is always coherent, except for method JIT frames where the - * callee does not access its arguments and the number of actual arguments - * matches the number of formal arguments. The JIT requires that all frames - * which do not have an arguments object and use their arguments have a - * coherent u.nactual (even though the below code may not use it), as - * JIT code may access the field directly. - */ - JS_ASSERT(hasArgs()); - if (JS_UNLIKELY(flags_ & (OVERFLOW_ARGS | UNDERFLOW_ARGS))) - return u.nactual; - return numFormalArgs(); -} - inline ArgumentsObject & StackFrame::argsObj() const { @@ -315,31 +273,19 @@ ContextStack::getCallFrame(JSContext *cx, MaybeReportError report, const CallArg unsigned nvals = VALUES_PER_STACK_FRAME + script->nslots; - /* Maintain layout invariant: &formals[0] == ((Value *)fp) - nformal. */ - - if (args.length() == nformal) { + if (args.length() >= nformal) { if (!space().ensureSpace(cx, report, firstUnused, nvals)) return NULL; return reinterpret_cast(firstUnused); } - if (args.length() < nformal) { - *flags = StackFrame::Flags(*flags | StackFrame::UNDERFLOW_ARGS); - unsigned nmissing = nformal - args.length(); - if (!space().ensureSpace(cx, report, firstUnused, nmissing + nvals)) - return NULL; - SetValueRangeToUndefined(firstUnused, nmissing); - return reinterpret_cast(firstUnused + nmissing); - } - - *flags = StackFrame::Flags(*flags | StackFrame::OVERFLOW_ARGS); - unsigned ncopy = 2 + nformal; - if (!space().ensureSpace(cx, report, firstUnused, ncopy + nvals)) + /* Pad any missing arguments with |undefined|. */ + JS_ASSERT(args.length() < nformal); + unsigned nmissing = nformal - args.length(); + if (!space().ensureSpace(cx, report, firstUnused, nmissing + nvals)) return NULL; - Value *dst = firstUnused; - Value *src = args.base(); - mozilla::PodCopy(dst, src, ncopy); - return reinterpret_cast(firstUnused + ncopy); + SetValueRangeToUndefined(firstUnused, nmissing); + return reinterpret_cast(firstUnused + nmissing); } JS_ALWAYS_INLINE bool @@ -386,7 +332,7 @@ ContextStack::popInlineFrame(FrameRegs ®s) JS_ASSERT(®s == &seg_->regs()); StackFrame *fp = regs.fp(); - Value *newsp = fp->actuals() - 1; + Value *newsp = fp->argv() - 1; JS_ASSERT(newsp >= fp->prev()->base()); newsp[-1] = fp->returnValue(); @@ -815,27 +761,17 @@ AbstractFramePtr::isStrictEvalFrame() const } inline Value * -AbstractFramePtr::formals() const +AbstractFramePtr::argv() const { if (isStackFrame()) - return asStackFrame()->formals(); + return asStackFrame()->argv(); #ifdef JS_ION - return asBaselineFrame()->formals(); -#else - JS_NOT_REACHED("Invalid frame"); -#endif -} -inline Value * -AbstractFramePtr::actuals() const -{ - if (isStackFrame()) - return asStackFrame()->actuals(); -#ifdef JS_ION - return asBaselineFrame()->actuals(); + return asBaselineFrame()->argv(); #else JS_NOT_REACHED("Invalid frame"); #endif } + inline bool AbstractFramePtr::hasArgsObj() const { diff --git a/js/src/vm/Stack.cpp b/js/src/vm/Stack.cpp index e38bc1b726b..4ed0e8fa88f 100644 --- a/js/src/vm/Stack.cpp +++ b/js/src/vm/Stack.cpp @@ -221,7 +221,7 @@ StackFrame::copyRawFrameSlots(AutoValueVector *vec) { if (!vec->resize(numFormalArgs() + script()->nfixed)) return false; - PodCopy(vec->begin(), formals(), numFormalArgs()); + PodCopy(vec->begin(), argv(), numFormalArgs()); PodCopy(vec->begin() + numFormalArgs(), slots(), script()->nfixed); return true; } diff --git a/js/src/vm/Stack.h b/js/src/vm/Stack.h index 8dc5e54ef7b..4e70b4f0e42 100644 --- a/js/src/vm/Stack.h +++ b/js/src/vm/Stack.h @@ -212,8 +212,7 @@ class AbstractFramePtr inline unsigned numActualArgs() const; inline unsigned numFormalArgs() const; - inline Value *formals() const; - inline Value *actuals() const; + inline Value *argv() const; inline bool hasArgsObj() const; inline ArgumentsObject &argsObj() const; @@ -287,33 +286,29 @@ class StackFrame YIELDING = 0x40, /* Interpret dispatched JSOP_YIELD */ FINISHED_IN_INTERP = 0x80, /* set if frame finished in Interpret() */ - /* Function arguments */ - OVERFLOW_ARGS = 0x100, /* numActualArgs > numFormalArgs */ - UNDERFLOW_ARGS = 0x200, /* numActualArgs < numFormalArgs */ - /* Function prologue state */ - HAS_CALL_OBJ = 0x400, /* CallObject created for heavyweight fun */ - HAS_ARGS_OBJ = 0x800, /* ArgumentsObject created for needsArgsObj script */ + HAS_CALL_OBJ = 0x100, /* CallObject created for heavyweight fun */ + HAS_ARGS_OBJ = 0x200, /* ArgumentsObject created for needsArgsObj script */ /* Lazy frame initialization */ - HAS_HOOK_DATA = 0x1000, /* frame has hookData_ set */ - HAS_RVAL = 0x2000, /* frame has rval_ set */ - HAS_SCOPECHAIN = 0x4000, /* frame has scopeChain_ set */ - HAS_PREVPC = 0x8000, /* frame has prevpc_ and prevInline_ set */ - HAS_BLOCKCHAIN = 0x10000, /* frame has blockChain_ set */ + HAS_HOOK_DATA = 0x400, /* frame has hookData_ set */ + HAS_RVAL = 0x800, /* frame has rval_ set */ + HAS_SCOPECHAIN = 0x1000, /* frame has scopeChain_ set */ + HAS_PREVPC = 0x2000, /* frame has prevpc_ and prevInline_ set */ + HAS_BLOCKCHAIN = 0x4000, /* frame has blockChain_ set */ /* Debugger state */ - PREV_UP_TO_DATE = 0x20000, /* see DebugScopes::updateLiveScopes */ + PREV_UP_TO_DATE = 0x8000, /* see DebugScopes::updateLiveScopes */ /* Used in tracking calls and profiling (see vm/SPSProfiler.cpp) */ - HAS_PUSHED_SPS_FRAME = 0x40000, /* SPS was notified of enty */ + HAS_PUSHED_SPS_FRAME = 0x10000, /* SPS was notified of enty */ /* Ion frame state */ - RUNNING_IN_ION = 0x80000, /* frame is running in Ion */ - CALLING_INTO_ION = 0x100000, /* frame is calling into Ion */ + RUNNING_IN_ION = 0x20000, /* frame is running in Ion */ + CALLING_INTO_ION = 0x40000, /* frame is calling into Ion */ /* Miscellaneous state. */ - USE_NEW_TYPE = 0x200000 /* Use new type for constructed |this| object. */ + USE_NEW_TYPE = 0x80000 /* Use new type for constructed |this| object. */ }; private: @@ -356,9 +351,7 @@ class StackFrame public: Value *slots() const { return (Value *)(this + 1); } Value *base() const { return slots() + script()->nfixed; } - Value *formals() const { return (Value *)this - fun()->nargs; } - Value *actuals() const { return formals() - (flags_ & OVERFLOW_ARGS ? 2 + u.nactual : 0); } - unsigned nactual() const { return u.nactual; } + Value *argv() const { return (Value *)this - Max(numActualArgs(), numFormalArgs()); } private: friend class FrameRegs; @@ -506,15 +499,8 @@ class StackFrame * * Only non-eval function frames have arguments. The arguments pushed by * the caller are the 'actual' arguments. The declared arguments of the - * callee are the 'formal' arguments. When the caller passes less or equal - * actual arguments, the actual and formal arguments are the same array - * (but with different extents). When the caller passes too many arguments, - * the formal subset of the actual arguments is copied onto the top of the - * stack. This allows the engine to maintain a jit-time constant offset of - * arguments from the frame pointer. Since the formal subset of the actual - * arguments is potentially on the stack twice, it is important for all - * reads/writes to refer to the same canonical memory location. This is - * abstracted by the unaliased{Formal,Actual} methods. + * callee are the 'formal' arguments. When the caller passes less actual + * arguments, missing formal arguments are padded with |undefined|. * * When a local/formal variable is "aliased" (accessed by nested closures, * dynamic scope operations, or 'arguments), the canonical location for @@ -536,8 +522,8 @@ class StackFrame bool copyRawFrameSlots(AutoValueVector *v); - inline unsigned numFormalArgs() const; - inline unsigned numActualArgs() const; + unsigned numFormalArgs() const { JS_ASSERT(hasArgs()); return fun()->nargs; } + unsigned numActualArgs() const { JS_ASSERT(hasArgs()); return u.nactual; } inline Value &canonicalActualArg(unsigned i) const; template @@ -706,18 +692,18 @@ class StackFrame JS_ASSERT(isFunctionFrame()); if (isEvalFrame()) return ((Value *)this)[-1]; - return formals()[-1]; + return argv()[-1]; } JSObject &constructorThis() const { JS_ASSERT(hasArgs()); - return formals()[-1].toObject(); + return argv()[-1].toObject(); } Value &thisValue() const { if (flags_ & (EVAL | GLOBAL)) return ((Value *)this)[-1]; - return formals()[-1]; + return argv()[-1]; } /* @@ -742,7 +728,7 @@ class StackFrame const Value &maybeCalleev() const { Value &calleev = flags_ & (EVAL | GLOBAL) ? ((Value *)this)[-2] - : formals()[-2]; + : argv()[-2]; JS_ASSERT(calleev.isObjectOrNull()); return calleev; } @@ -751,11 +737,11 @@ class StackFrame JS_ASSERT(isFunctionFrame()); if (isEvalFrame()) return ((Value *)this)[-2]; - return formals()[-2]; + return argv()[-2]; } CallReceiver callReceiver() const { - return CallReceiverFromArgv(formals()); + return CallReceiverFromArgv(argv()); } /* @@ -850,7 +836,7 @@ class StackFrame Value *generatorArgsSnapshotBegin() const { JS_ASSERT(isGeneratorFrame()); - return actuals() - 2; + return argv() - 2; } Value *generatorArgsSnapshotEnd() const { @@ -947,10 +933,6 @@ class StackFrame flags_ |= PREV_UP_TO_DATE; } - bool hasOverflowArgs() const { - return !!(flags_ & OVERFLOW_ARGS); - } - bool isYielding() { return !!(flags_ & YIELDING); } From ffa528e2a708accf1194e39b77426099559eb15c Mon Sep 17 00:00:00 2001 From: Jan de Mooij Date: Fri, 24 May 2013 14:03:36 +0200 Subject: [PATCH 06/91] Bug 875473 - Add saveFrameChain option to shell evaluate function. r=luke --- js/src/jit-test/tests/basic/save-frame-chain.js | 16 ++++++++++++++++ js/src/shell/js.cpp | 16 ++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 js/src/jit-test/tests/basic/save-frame-chain.js diff --git a/js/src/jit-test/tests/basic/save-frame-chain.js b/js/src/jit-test/tests/basic/save-frame-chain.js new file mode 100644 index 00000000000..1b577061c03 --- /dev/null +++ b/js/src/jit-test/tests/basic/save-frame-chain.js @@ -0,0 +1,16 @@ +// Error().stack (ScriptFrameIter) should not see through JS_SaveFrameChain. +function h() { + stack = Error().stack; +} +function g() { + evaluate("h()", {saveFrameChain: true}); +} +function f() { + g(); +} +f(); +var lines = stack.split("\n"); +assertEq(lines.length, 3); +assertEq(lines[0].startsWith("h@"), true); +assertEq(lines[1].startsWith("@@evaluate"), true); +assertEq(lines[2], ""); diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index e01694ac5ef..1d3227c5f32 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -908,6 +908,7 @@ Evaluate(JSContext *cx, unsigned argc, jsval *vp) unsigned lineNumber = 1; RootedObject global(cx, NULL); bool catchTermination = false; + bool saveFrameChain = false; RootedObject callerGlobal(cx, cx->global()); global = JS_GetGlobalForObject(cx, &args.callee()); @@ -999,6 +1000,15 @@ Evaluate(JSContext *cx, unsigned argc, jsval *vp) return false; catchTermination = b; } + + if (!JS_GetProperty(cx, options, "saveFrameChain", v.address())) + return false; + if (!JSVAL_IS_VOID(v)) { + JSBool b; + if (!JS_ValueToBoolean(cx, v, &b)) + return false; + saveFrameChain = b; + } } RootedString code(cx, args[0].toString()); @@ -1015,6 +1025,9 @@ Evaluate(JSContext *cx, unsigned argc, jsval *vp) cx = ancx.get(); } + if (saveFrameChain && !JS_SaveFrameChain(cx)) + return false; + { JSAutoCompartment ac(cx, global); uint32_t saved = JS_GetOptions(cx); @@ -1051,6 +1064,9 @@ Evaluate(JSContext *cx, unsigned argc, jsval *vp) } } + if (saveFrameChain) + JS_RestoreFrameChain(cx); + return JS_WrapValue(cx, vp); } From 015e795d678e2397a8ae5c63f04cb433c274be1b Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Fri, 24 May 2013 09:15:55 -0400 Subject: [PATCH 07/91] Bug 875622 part 1. Pass through mutable handles to all the various places where we have to work with JS::Value in codegen. r=smaug --- dom/bindings/Codegen.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index c137c39ed8b..f641c751a7b 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -2440,6 +2440,7 @@ class JSToNativeConversionInfo(): ${val} replaced by an expression for the JS::Value in question ${valPtr} is a pointer to the JS::Value in question ${valHandle} is a handle to the JS::Value in question + ${valMutableHandle} is a mutable handle to the JS::Value in question ${holderName} replaced by the holder's name, if any ${declName} replaced by the declaration's name ${haveValue} replaced by an expression that evaluates to a boolean @@ -2770,6 +2771,7 @@ for (uint32_t i = 0; i < length; ++i) { "val" : "temp", "valPtr": "temp.address()", "valHandle": "temp", + "valMutableHandle": "&temp", "declName" : "slot", # We only need holderName here to handle isExternal() # interfaces, which use an internal holder for the @@ -3717,6 +3719,9 @@ class CGArgumentConverter(CGThing): self.replacementVariables["valHandle"] = ( "JS::Handle::fromMarkedLocation(%s)" % self.replacementVariables["valPtr"]) + self.replacementVariables["valMutableHandle"] = ( + "JS::MutableHandle::fromMarkedLocation(%s)" % + self.replacementVariables["valPtr"]) if argument.defaultValue: self.replacementVariables["haveValue"] = string.Template( "${index} < ${argc}").substitute(replacer) @@ -3787,6 +3792,8 @@ class CGArgumentConverter(CGThing): "valPtr": "&" + val, "valHandle" : ("JS::Handle::fromMarkedLocation(&%s)" % val), + "valMutableHandle" : ("JS::MutableHandle::fromMarkedLocation(&%s)" % + val), "declName" : "slot", # We only need holderName here to handle isExternal() # interfaces, which use an internal holder for the @@ -4831,6 +4838,8 @@ class CGMethodCall(CGThing): "val" : distinguishingArg, "valHandle" : ("JS::Handle::fromMarkedLocation(&%s)" % distinguishingArg), + "valMutableHandle" : ("JS::MutableHandle::fromMarkedLocation(&%s)" % + distinguishingArg), "obj" : "obj" }) caseBody.append(CGIndenter(testCode, indent)); @@ -5750,6 +5759,7 @@ return true;""" { "val": "value", "valHandle": "value", + "valMutableHandle": "JS::MutableHandle::fromMarkedLocation(pvalue)", "valPtr": "pvalue", "declName": "SetAs" + name + "()", "holderName": "m" + name + "Holder", @@ -6587,6 +6597,7 @@ class CGProxySpecialOperation(CGPerSignatureCall): "val": "desc->value", "valPtr": "&desc->value", "valHandle" : "JS::Handle::fromMarkedLocation(&desc->value)", + "valMutableHandle" : "JS::MutableHandle::fromMarkedLocation(&desc->value)", "obj": "obj" } self.cgRoot.prepend(instantiateJSToNativeConversion(info, templateValues)) @@ -7781,6 +7792,7 @@ class CGDictionary(CGThing): replacements = { "val": "temp", "valPtr": "temp.address()", "valHandle": "temp", + "valMutableHandle": "&temp", "declName": self.makeMemberName(member.identifier.name), # We need a holder name for external interfaces, but # it's scoped down to the conversion so we can just use @@ -9438,6 +9450,7 @@ class CallbackMember(CGNativeMember): "val": "rval", "valPtr": "rval.address()", "valHandle": "rval", + "valMutableHandle": "&rval", "holderName" : "rvalHolder", "declName" : "rvalDecl", # We actually want to pass in a null scope object here, because From e9e2dca8a41ac710fb74b320263775816b0e7612 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Fri, 24 May 2013 09:16:00 -0400 Subject: [PATCH 08/91] Bug 875622 part 2. Use handles in ConvertJSValueToString. r=smaug --- content/html/content/src/nsGenericHTMLElement.cpp | 3 ++- dom/bindings/BindingUtils.h | 5 +++-- dom/bindings/Codegen.py | 4 ++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/content/html/content/src/nsGenericHTMLElement.cpp b/content/html/content/src/nsGenericHTMLElement.cpp index b47de500625..373c64641d8 100644 --- a/content/html/content/src/nsGenericHTMLElement.cpp +++ b/content/html/content/src/nsGenericHTMLElement.cpp @@ -3174,7 +3174,8 @@ nsGenericHTMLElement::SetItemValue(JSContext* aCx, JS::Value aValue, } FakeDependentString string; - if (!ConvertJSValueToString(aCx, aValue, &aValue, eStringify, eStringify, string)) { + JS::Rooted value(aCx, aValue); + if (!ConvertJSValueToString(aCx, value, &value, eStringify, eStringify, string)) { aError.Throw(NS_ERROR_UNEXPECTED); return; } diff --git a/dom/bindings/BindingUtils.h b/dom/bindings/BindingUtils.h index 98fa1309a52..511c2ee605f 100644 --- a/dom/bindings/BindingUtils.h +++ b/dom/bindings/BindingUtils.h @@ -1504,7 +1504,8 @@ enum StringificationBehavior { // pval must not be null and must point to a rooted JS::Value static inline bool -ConvertJSValueToString(JSContext* cx, const JS::Value& v, JS::Value* pval, +ConvertJSValueToString(JSContext* cx, JS::Handle v, + JS::MutableHandle pval, StringificationBehavior nullBehavior, StringificationBehavior undefinedBehavior, FakeDependentString& result) @@ -1535,7 +1536,7 @@ ConvertJSValueToString(JSContext* cx, const JS::Value& v, JS::Value* pval, if (!s) { return false; } - pval->setString(s); // Root the new string. + pval.set(JS::StringValue(s)); // Root the new string. } size_t len; diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index f641c751a7b..4a3ff4bdc51 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -3225,7 +3225,7 @@ for (uint32_t i = 0; i < length; ++i) { def getConversionCode(varName): conversionCode = ( - "if (!ConvertJSValueToString(cx, ${val}, ${valPtr}, %s, %s, %s)) {\n" + "if (!ConvertJSValueToString(cx, ${valHandle}, ${valMutableHandle}, %s, %s, %s)) {\n" "%s\n" "}" % (nullBehavior, undefinedBehavior, varName, exceptionCodeIndented.define())) @@ -6679,7 +6679,7 @@ class CGProxyNamedOperation(CGProxySpecialOperation): def define(self): # Our first argument is the id we're getting. argName = self.arguments[0].identifier.name - return (("JS::Value nameVal = %s;\n" + return (("JS::Rooted nameVal(cx, %s);\n" "FakeDependentString %s;\n" "if (!ConvertJSValueToString(cx, nameVal, &nameVal,\n" " eStringify, eStringify, %s)) {\n" From 3fd594fe4c076be5f75e847ecc6781ba1a2d0e3c Mon Sep 17 00:00:00 2001 From: Geoff Brown Date: Fri, 24 May 2013 07:27:00 -0600 Subject: [PATCH 09/91] Bug 875363 - Improve VKB clearing in testAddSearchEngine; r=jmaher --- .../base/tests/testAddSearchEngine.java.in | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/mobile/android/base/tests/testAddSearchEngine.java.in b/mobile/android/base/tests/testAddSearchEngine.java.in index 97f47f42fc4..995c1865854 100644 --- a/mobile/android/base/tests/testAddSearchEngine.java.in +++ b/mobile/android/base/tests/testAddSearchEngine.java.in @@ -36,7 +36,7 @@ public class testAddSearchEngine extends BaseTest { loadUrl(url); waitForText("Robocop Search Engine"); - // Open the context menu for the intput field + // Open the context menu for the input field height = mDriver.getGeckoTop() + 10; width = mDriver.getGeckoLeft() + 20; mAsserter.dumpLog("Long Clicking at width = " + String.valueOf(width) + " and height = " + String.valueOf(height)); @@ -51,16 +51,16 @@ public class testAddSearchEngine extends BaseTest { // Add the search engine mSolo.clickOnText("Add Search Engine"); waitForText("Cancel"); - if (mDevice.version.equals("4.x")) { - // If the OS is ICS the vkb is opened when the popup is triggered so we need to close it in order to click the OK button + if (mDevice.type.equals("tablet")) { + // On tablets, the vkb is opened when the popup is triggered so we need to close it in order to click the OK button mActions.sendSpecialKey(Actions.SpecialKey.BACK); waitForText("OK"); // Make sure the OK button is visible } mSolo.clickOnButton("OK"); - mAsserter.ok(!mSolo.searchText("Add Search Engine"), "Adding the search engine", "The add serach engine pop-up has been cloesed"); + mAsserter.ok(!mSolo.searchText("Add Search Engine"), "Adding the search engine", "The add search engine pop-up has been closed"); // Check that the number of search results has increased - mAsserter.is(getNumSearchEngines("Robocop Search Engine") ,initialNumSearchEngines + 1 , "The number of search results has increased"); + mAsserter.is(getNumSearchEngines("Robocop Search Engine"), initialNumSearchEngines + 1 , "The number of search results has increased"); } public int getNumSearchEngines(String waitText) { @@ -96,8 +96,8 @@ public class testAddSearchEngine extends BaseTest { } } - // Since the number of Search Engine count was different on every try fail the test - mAsserter.is(searchEngineCount,oldSearchEngineCount, "The search engine count could not be established correctly"); - return searchEngineCount; + // Since the number of Search Engine count was different on every try fail the test + mAsserter.is(searchEngineCount,oldSearchEngineCount, "The search engine count could not be established correctly"); + return searchEngineCount; } } From c2cd7e77c2ee76e16a478b0f6f067682bf28e312 Mon Sep 17 00:00:00 2001 From: Cameron McCormack Date: Fri, 24 May 2013 23:27:51 +1000 Subject: [PATCH 10/91] Bug 875705 - Fix odd numbering of nsAttrValue::ValueType. r=smaug --- content/base/src/nsAttrValue.h | 40 +++++++++++++++++----------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/content/base/src/nsAttrValue.h b/content/base/src/nsAttrValue.h index d622bbdde67..7298a66e799 100644 --- a/content/base/src/nsAttrValue.h +++ b/content/base/src/nsAttrValue.h @@ -82,26 +82,26 @@ public: ePercent = 0x0F, // 1111 // Values below here won't matter, they'll be always stored in the 'misc' // struct. - eCSSStyleRule = 0x10 - ,eURL = 0x11 - ,eImage = 0x12 - ,eAtomArray = 0x13 - ,eDoubleValue = 0x14 - ,eIntMarginValue = 0x15 - ,eSVGTypesBegin = 0x16 - ,eSVGAngle = eSVGTypesBegin - ,eSVGIntegerPair = 0x17 - ,eSVGLength = 0x18 - ,eSVGLengthList = 0x19 - ,eSVGNumberList = 0x20 - ,eSVGNumberPair = 0x21 - ,eSVGPathData = 0x22 - ,eSVGPointList = 0x23 - ,eSVGPreserveAspectRatio = 0x24 - ,eSVGStringList = 0x25 - ,eSVGTransformList = 0x26 - ,eSVGViewBox = 0x27 - ,eSVGTypesEnd = 0x34 + eCSSStyleRule = 0x10 + ,eURL = 0x11 + ,eImage = 0x12 + ,eAtomArray = 0x13 + ,eDoubleValue = 0x14 + ,eIntMarginValue = 0x15 + ,eSVGAngle = 0x16 + ,eSVGTypesBegin = eSVGAngle + ,eSVGIntegerPair = 0x17 + ,eSVGLength = 0x18 + ,eSVGLengthList = 0x19 + ,eSVGNumberList = 0x1A + ,eSVGNumberPair = 0x1B + ,eSVGPathData = 0x1C + ,eSVGPointList = 0x1D + ,eSVGPreserveAspectRatio = 0x1E + ,eSVGStringList = 0x1F + ,eSVGTransformList = 0x20 + ,eSVGViewBox = 0x21 + ,eSVGTypesEnd = eSVGViewBox }; nsAttrValue(); From ea0d6e617cc40328e4f28b825227fd6f4aaa54bd Mon Sep 17 00:00:00 2001 From: Jonathan Watt Date: Fri, 24 May 2013 14:51:50 +0100 Subject: [PATCH 11/91] Bug 875175 - Stop continuous reflow and invalidation of entire SVG when an element that is partially/wholly outside the viewport is subject to a transform animation. r=dbaron --- layout/svg/nsSVGOuterSVGFrame.cpp | 63 ++++++++++++++++++++++++++++--- layout/svg/nsSVGOuterSVGFrame.h | 2 + 2 files changed, 60 insertions(+), 5 deletions(-) diff --git a/layout/svg/nsSVGOuterSVGFrame.cpp b/layout/svg/nsSVGOuterSVGFrame.cpp index add0562ae1d..da312ce6118 100644 --- a/layout/svg/nsSVGOuterSVGFrame.cpp +++ b/layout/svg/nsSVGOuterSVGFrame.cpp @@ -479,14 +479,46 @@ nsSVGOuterSVGFrame::Reflow(nsPresContext* aPresContext, mCallingReflowSVG = false; } - // Make sure we scroll if we're too big: - // XXX Use the bounding box of our descendants? (See bug 353460 comment 14.) - aDesiredSize.SetOverflowAreasToDesiredBounds(); - FinishAndStoreOverflow(&aDesiredSize); - // Set our anonymous kid's offset from our border box: anonKid->SetPosition(GetContentRectRelativeToSelf().TopLeft()); + // Including our size in our overflow rects regardless of the value of + // 'background', 'border', etc. makes sure that we usually (when we clip to + // our content area) don't have to keep changing our overflow rects as our + // descendants move about (see perf comment below). Including our size in our + // scrollable overflow rect also makes sure that we scroll if we're too big + // for our viewport. + // + // never allows scrolling to anything outside its mRect (only panning), + // so we must always keep our scrollable overflow set to our size. + // + // With regards to visual overflow, we always clip root- (see our + // BuildDisplayList method) regardless of the value of the 'overflow' + // property since that is per-spec, even for the initial 'visible' value. For + // that reason there's no point in adding descendant visual overflow to our + // own when this frame is for a root-. That said, there's also a very + // good performance reason for us wanting to avoid doing so. If we did, then + // the frame's overflow would often change as descendants that are partially + // or fully outside its rect moved (think animation on/off screen), and that + // would cause us to do a full NS_FRAME_IS_DIRTY reflow and repaint of the + // entire document tree each such move (see bug 875175). + // + // So it's only non-root outer- that has the visual overflow of its + // descendants added to its own. (Note that the default user-agent style + // sheet makes 'hidden' the default value for :not(root(svg)), so usually + // FinishAndStoreOverflow will still clip this back to the frame's rect.) + // + // WARNING!! Keep UpdateBounds below in sync with whatever we do for our + // overflow rects here! (Again, see bug 875175.) + // + aDesiredSize.SetOverflowAreasToDesiredBounds(); + if (!mIsRootContent) { + aDesiredSize.mOverflowAreas.VisualOverflow().UnionRect( + aDesiredSize.mOverflowAreas.VisualOverflow(), + anonKid->GetVisualOverflowRect() + anonKid->GetPosition()); + } + FinishAndStoreOverflow(&aDesiredSize); + NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, ("exit nsSVGOuterSVGFrame::Reflow: size=%d,%d", aDesiredSize.width, aDesiredSize.height)); @@ -508,6 +540,27 @@ nsSVGOuterSVGFrame::DidReflow(nsPresContext* aPresContext, return rv; } +/* virtual */ bool +nsSVGOuterSVGFrame::UpdateOverflow() +{ + // See the comments in Reflow above. + + // WARNING!! Keep this in sync with Reflow above! + + nsRect rect(nsPoint(0, 0), GetSize()); + nsOverflowAreas overflowAreas(rect, rect); + + if (!mIsRootContent) { + nsIFrame *anonKid = GetFirstPrincipalChild(); + overflowAreas.VisualOverflow().UnionRect( + overflowAreas.VisualOverflow(), + anonKid->GetVisualOverflowRect() + anonKid->GetPosition()); + } + + return FinishAndStoreOverflow(overflowAreas, GetSize()); +} + + //---------------------------------------------------------------------- // container methods diff --git a/layout/svg/nsSVGOuterSVGFrame.h b/layout/svg/nsSVGOuterSVGFrame.h index d6eb2192558..482505ed9e6 100644 --- a/layout/svg/nsSVGOuterSVGFrame.h +++ b/layout/svg/nsSVGOuterSVGFrame.h @@ -58,6 +58,8 @@ public: const nsHTMLReflowState* aReflowState, nsDidReflowStatus aStatus); + virtual bool UpdateOverflow() MOZ_OVERRIDE; + virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder, const nsRect& aDirtyRect, const nsDisplayListSet& aLists) MOZ_OVERRIDE; From 19a9fa2f5762bab2abadcf7017031a383fe2f689 Mon Sep 17 00:00:00 2001 From: Joe Drew Date: Fri, 24 May 2013 21:52:34 +0800 Subject: [PATCH 12/91] Bug 855221 - Don't flush from imgFrame::ImageUpdated(), which can be called from the decoder thread. r=jrmuizel --HG-- extra : rebase_source : a52a6ed8dc4d0477593f0af8289bb14a61594a3a --- image/src/imgFrame.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/image/src/imgFrame.cpp b/image/src/imgFrame.cpp index e0ecaf66089..d024914e688 100644 --- a/image/src/imgFrame.cpp +++ b/image/src/imgFrame.cpp @@ -228,6 +228,8 @@ nsresult imgFrame::Init(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight, nsresult imgFrame::Optimize() { + MOZ_ASSERT(NS_IsMainThread()); + if (gDisableOptimize) return NS_OK; @@ -486,6 +488,7 @@ void imgFrame::Draw(gfxContext *aContext, gfxPattern::GraphicsFilter aFilter, } } +// This can be called from any thread, but not simultaneously. nsresult imgFrame::ImageUpdated(const nsIntRect &aUpdateRect) { mDecoded.UnionRect(mDecoded, aUpdateRect); @@ -495,10 +498,6 @@ nsresult imgFrame::ImageUpdated(const nsIntRect &aUpdateRect) nsIntRect boundsRect(mOffset, mSize); mDecoded.IntersectRect(mDecoded, boundsRect); -#ifdef XP_MACOSX - if (mQuartzSurface) - mQuartzSurface->Flush(); -#endif return NS_OK; } @@ -575,6 +574,8 @@ void imgFrame::GetPaletteData(uint32_t **aPalette, uint32_t *length) const nsresult imgFrame::LockImageData() { + MOZ_ASSERT(NS_IsMainThread()); + NS_ABORT_IF_FALSE(mLockCount >= 0, "Unbalanced locks and unlocks"); if (mLockCount < 0) { return NS_ERROR_FAILURE; @@ -630,6 +631,8 @@ nsresult imgFrame::LockImageData() nsresult imgFrame::UnlockImageData() { + MOZ_ASSERT(NS_IsMainThread()); + NS_ABORT_IF_FALSE(mLockCount != 0, "Unlocking an unlocked image!"); if (mLockCount == 0) { return NS_ERROR_FAILURE; @@ -672,6 +675,8 @@ nsresult imgFrame::UnlockImageData() void imgFrame::MarkImageDataDirty() { + MOZ_ASSERT(NS_IsMainThread()); + if (mImageSurface) mImageSurface->Flush(); From 060d253855a8337b7e090b408d5188f89498c9e6 Mon Sep 17 00:00:00 2001 From: Nicholas Cameron Date: Fri, 24 May 2013 22:19:28 +0800 Subject: [PATCH 13/91] No bug - fix incorrect comment on nsStyleAnimation::UncomputeValue r=me --- layout/style/nsStyleAnimation.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/layout/style/nsStyleAnimation.h b/layout/style/nsStyleAnimation.h index 262751c2179..2a5e5a69e47 100644 --- a/layout/style/nsStyleAnimation.h +++ b/layout/style/nsStyleAnimation.h @@ -160,8 +160,6 @@ public: * must keep |aComputedValue| alive longer than |aSpecifiedValue|. * * @param aProperty The property whose value we're uncomputing. - * @param aPresContext The presentation context for the document in - * which we're working. * @param aComputedValue The computed value to be converted. * @param [out] aSpecifiedValue The resulting specified value. * @return true on success, false on failure. From 0cd5f226a36d3e4bedb189d7fe0cf36cc0db5d61 Mon Sep 17 00:00:00 2001 From: Chris Lord Date: Fri, 24 May 2013 15:53:58 +0100 Subject: [PATCH 14/91] Bug 870055 - Fix AsynPanZoomController continuing movement when touch point doesn't move. r=kats --- gfx/layers/ipc/Axis.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/gfx/layers/ipc/Axis.cpp b/gfx/layers/ipc/Axis.cpp index b7b0e8dc5a6..6d2241a7e29 100644 --- a/gfx/layers/ipc/Axis.cpp +++ b/gfx/layers/ipc/Axis.cpp @@ -102,11 +102,6 @@ Axis::Axis(AsyncPanZoomController* aAsyncPanZoomController) } void Axis::UpdateWithTouchAtDevicePoint(int32_t aPos, const TimeDuration& aTimeDelta) { - if (mPos == aPos) { - // Does not make sense to calculate velocity when distance is 0 - return; - } - float newVelocity = (mPos - aPos) / aTimeDelta.ToMilliseconds(); bool curVelocityBelowThreshold = fabsf(newVelocity) < gVelocityThreshold; From 91ab86ff26857db6bc69f9a8dcdfd4be387d9a4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Tigeot?= Date: Fri, 24 May 2013 08:29:53 +0200 Subject: [PATCH 15/91] Bug 875666 - add || defined(__DragonFly__) to jsnativestack.cpp (r=luke) --HG-- extra : rebase_source : 23bc7c6997e0e2b1c174f4f7a75f44f6a0155a42 --- js/src/jsnativestack.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/jsnativestack.cpp b/js/src/jsnativestack.cpp index 7b1f602b2d3..562c89a58cf 100644 --- a/js/src/jsnativestack.cpp +++ b/js/src/jsnativestack.cpp @@ -16,7 +16,7 @@ #elif defined(XP_MACOSX) || defined(DARWIN) || defined(XP_UNIX) # include -# if defined(__FreeBSD__) || defined(__OpenBSD__) +# if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) # include # endif From bd06df3c4e309ee79ca288acb3e3b747f8ec52d2 Mon Sep 17 00:00:00 2001 From: Andrew Halberstadt Date: Fri, 24 May 2013 11:01:06 -0400 Subject: [PATCH 16/91] Bug 872164 - Create in-tree mozharness config, r=jgriffin --- testing/config/Makefile.in | 24 ++++++++++++++++++++++++ testing/config/moz.build | 5 +++++ testing/config/mozharness_config.py | 28 ++++++++++++++++++++++++++++ testing/testsuite-targets.mk | 6 ++++++ toolkit/toolkit.mozbuild | 1 + 5 files changed, 64 insertions(+) create mode 100644 testing/config/Makefile.in create mode 100644 testing/config/moz.build create mode 100644 testing/config/mozharness_config.py diff --git a/testing/config/Makefile.in b/testing/config/Makefile.in new file mode 100644 index 00000000000..da303aa7399 --- /dev/null +++ b/testing/config/Makefile.in @@ -0,0 +1,24 @@ +# 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/. + +DEPTH = @DEPTH@ +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(DEPTH)/config/autoconf.mk +include $(topsrcdir)/config/rules.mk + +CONFIG_FILES = \ + mozharness_config.py \ + $(NULL) + +_DEST_DIR = $(DEPTH)/_tests/config +libs:: $(CONFIG_FILES) + $(PYTHON) $(topsrcdir)/config/nsinstall.py $^ $(_DEST_DIR) + +stage-package: PKG_STAGE = $(DIST)/test-package-stage +stage-package: + $(NSINSTALL) -D $(PKG_STAGE)/config + @(cd $(srcdir) && tar $(TAR_CREATE_FLAGS) - $(CONFIG_FILES)) | (cd $(PKG_STAGE)/config && tar -xf -) diff --git a/testing/config/moz.build b/testing/config/moz.build new file mode 100644 index 00000000000..58ce5e27339 --- /dev/null +++ b/testing/config/moz.build @@ -0,0 +1,5 @@ +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + diff --git a/testing/config/mozharness_config.py b/testing/config/mozharness_config.py new file mode 100644 index 00000000000..1f8e36aa78c --- /dev/null +++ b/testing/config/mozharness_config.py @@ -0,0 +1,28 @@ +# 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/. + +"""The config defined in this module is read by mozharness, which is a +tool used to run many of the tests reporting to TBPL. This file is +primarily useful for pushing custom test harness configurations to try. + +For example you can define a custom mochitest configuration by adding: + "mochitest_options": [ + "--appname=%(binary_path)s", "--utility-path=tests/bin", + "--extra-profile-file=tests/bin/plugins", "--symbols-path=%(symbols_path)s", + "--certificate-path=tests/certs", "--autorun", "--close-when-done", + "--console-level=INFO", "--setpref=webgl.force-enabled=true" + ], + +Be warned that these values will be picked up by all platforms and changing them +may result in unexpected behaviour. For example, the above will break B2G +mochitests. + +You must also provide the complete command line to avoid errors. The official +configuration files containing the default values live in: + https://hg.mozilla.org/build/mozharness/configs +""" + +config = { + # Add custom mozharness config options here +} diff --git a/testing/testsuite-targets.mk b/testing/testsuite-targets.mk index f4f1cf315ff..de3c4d3eb46 100644 --- a/testing/testsuite-targets.mk +++ b/testing/testsuite-targets.mk @@ -388,6 +388,7 @@ include $(topsrcdir)/toolkit/mozapps/installer/package-name.mk ifndef UNIVERSAL_BINARY PKG_STAGE = $(DIST)/test-package-stage package-tests: \ + stage-config \ stage-mochitest \ stage-reftest \ stage-xpcshell \ @@ -431,6 +432,7 @@ make-stage-dir: $(NSINSTALL) -D $(PKG_STAGE)/bin $(NSINSTALL) -D $(PKG_STAGE)/bin/components $(NSINSTALL) -D $(PKG_STAGE)/certs + $(NSINSTALL) -D $(PKG_STAGE)/config $(NSINSTALL) -D $(PKG_STAGE)/jetpack $(NSINSTALL) -D $(PKG_STAGE)/peptest $(NSINSTALL) -D $(PKG_STAGE)/mozbase @@ -439,6 +441,9 @@ make-stage-dir: stage-b2g: make-stage-dir $(NSINSTALL) $(topsrcdir)/b2g/test/b2g-unittest-requirements.txt $(PKG_STAGE)/b2g +stage-config: make-stage-dir + $(MAKE) -C $(DEPTH)/testing/config stage-package + stage-mochitest: make-stage-dir $(MAKE) -C $(DEPTH)/testing/mochitest stage-package ifeq ($(MOZ_BUILD_APP),mobile/android) @@ -507,6 +512,7 @@ stage-mozbase: make-stage-dir package-tests \ make-stage-dir \ stage-b2g \ + stage-config \ stage-mochitest \ stage-reftest \ stage-xpcshell \ diff --git a/toolkit/toolkit.mozbuild b/toolkit/toolkit.mozbuild index 58f0c41ae22..341afc106dc 100644 --- a/toolkit/toolkit.mozbuild +++ b/toolkit/toolkit.mozbuild @@ -233,6 +233,7 @@ if CONFIG['ENABLE_MARIONETTE']: if CONFIG['ENABLE_TESTS']: add_tier_dir('platform', [ + 'testing/config', 'testing/mochitest', 'testing/xpcshell', 'testing/tools/screenshot', From ab4c2dc8b3eba4fe31b362b358280c09c69a29c9 Mon Sep 17 00:00:00 2001 From: Wes Johnston Date: Thu, 23 May 2013 15:50:56 -0700 Subject: [PATCH 17/91] Bug 870062 - Provide an async way to use the PromptService. r=blassey --HG-- rename : mobile/android/base/PromptService.java => mobile/android/base/Prompt.java --- .../android/base/ActivityHandlerHelper.java | 18 +- mobile/android/base/GeckoApp.java | 5 +- mobile/android/base/JavaAddonManager.java | 2 +- mobile/android/base/Makefile.in | 1 + mobile/android/base/Prompt.java | 743 ++++++++++++++++++ mobile/android/base/PromptService.java | 704 +---------------- .../android/base/SharedPreferencesHelper.java | 2 +- mobile/android/base/util/EventDispatcher.java | 2 +- .../base/util/GeckoEventResponder.java | 4 +- 9 files changed, 796 insertions(+), 685 deletions(-) create mode 100644 mobile/android/base/Prompt.java diff --git a/mobile/android/base/ActivityHandlerHelper.java b/mobile/android/base/ActivityHandlerHelper.java index 6c24f2db1a3..70fd3e46193 100644 --- a/mobile/android/base/ActivityHandlerHelper.java +++ b/mobile/android/base/ActivityHandlerHelper.java @@ -66,7 +66,7 @@ public class ActivityHandlerHelper { return mActivityResultHandlerMap.put(aHandler); } - private int addIntentActivitiesToList(Context context, Intent intent, ArrayList items, ArrayList aIntents) { + private int addIntentActivitiesToList(Context context, Intent intent, ArrayList items, ArrayList aIntents) { PackageManager pm = context.getPackageManager(); List lri = pm.queryIntentActivityOptions(GeckoApp.mAppContext.getComponentName(), null, intent, 0); @@ -80,7 +80,7 @@ public class ActivityHandlerHelper { ri.activityInfo.applicationInfo.packageName, ri.activityInfo.name)); - PromptService.PromptListItem item = new PromptService.PromptListItem(ri.loadLabel(pm).toString()); + Prompt.PromptListItem item = new Prompt.PromptListItem(ri.loadLabel(pm).toString()); item.icon = ri.loadIcon(pm); items.add(item); aIntents.add(rintent); @@ -89,7 +89,7 @@ public class ActivityHandlerHelper { return lri.size(); } - private int addFilePickingActivities(Context context, ArrayList aItems, String aType, ArrayList aIntents) { + private int addFilePickingActivities(Context context, ArrayList aItems, String aType, ArrayList aIntents) { Intent intent = new Intent(Intent.ACTION_GET_CONTENT); intent.setType(aType); intent.addCategory(Intent.CATEGORY_OPENABLE); @@ -97,8 +97,8 @@ public class ActivityHandlerHelper { return addIntentActivitiesToList(context, intent, aItems, aIntents); } - private PromptService.PromptListItem[] getItemsAndIntentsForFilePicker(Context context, String aMimeType, ArrayList aIntents) { - ArrayList items = new ArrayList(); + private Prompt.PromptListItem[] getItemsAndIntentsForFilePicker(Context context, String aMimeType, ArrayList aIntents) { + ArrayList items = new ArrayList(); if (aMimeType.equals("audio/*")) { if (addFilePickingActivities(context, items, "audio/*", aIntents) <= 0) { @@ -134,7 +134,7 @@ public class ActivityHandlerHelper { addFilePickingActivities(context, items, "*/*", aIntents); } - return items.toArray(new PromptService.PromptListItem[] {}); + return items.toArray(new Prompt.PromptListItem[] {}); } private String getFilePickerTitle(Context context, String aMimeType) { @@ -151,7 +151,7 @@ public class ActivityHandlerHelper { private Intent getFilePickerIntent(Context context, String aMimeType) { ArrayList intents = new ArrayList(); - final PromptService.PromptListItem[] items = + final Prompt.PromptListItem[] items = getItemsAndIntentsForFilePicker(context, aMimeType, intents); if (intents.size() == 0) { @@ -170,11 +170,11 @@ public class ActivityHandlerHelper { // context menu UI using the PromptService. ThreadUtils.postToUiThread(new Runnable() { @Override public void run() { - ps.show(title, "", items, false); + ps.show(title, "", items, false, null); } }); - String promptServiceResult = ps.getResponse(); + String promptServiceResult = ps.getResponse(null); int itemId = -1; try { itemId = new JSONObject(promptServiceResult).getInt("button"); diff --git a/mobile/android/base/GeckoApp.java b/mobile/android/base/GeckoApp.java index 537608b6f14..4b56299620d 100644 --- a/mobile/android/base/GeckoApp.java +++ b/mobile/android/base/GeckoApp.java @@ -807,8 +807,7 @@ abstract public class GeckoApp } } - @Override - public String getResponse() { + public String getResponse(JSONObject origMessage) { String res = mCurrentResponse; mCurrentResponse = ""; return res; @@ -1585,7 +1584,7 @@ abstract public class GeckoApp SmsManager.getInstance().start(); } - mPromptService = new PromptService(); + mPromptService = new PromptService(this); mTextSelection = new TextSelection((TextSelectionHandle) findViewById(R.id.start_handle), (TextSelectionHandle) findViewById(R.id.middle_handle), diff --git a/mobile/android/base/JavaAddonManager.java b/mobile/android/base/JavaAddonManager.java index c3142c310ec..9ab2906a001 100644 --- a/mobile/android/base/JavaAddonManager.java +++ b/mobile/android/base/JavaAddonManager.java @@ -190,7 +190,7 @@ class JavaAddonManager implements GeckoEventListener { } @Override - public String getResponse() { + public String getResponse(JSONObject origMessage) { String response = mBundle.getString("response"); mBundle = null; return response; diff --git a/mobile/android/base/Makefile.in b/mobile/android/base/Makefile.in index a8425e1bdfe..4c778e9e7d6 100644 --- a/mobile/android/base/Makefile.in +++ b/mobile/android/base/Makefile.in @@ -127,6 +127,7 @@ FENNEC_JAVA_FILES = \ PrivateDataPreference.java \ PrivateTab.java \ ProfileMigrator.java \ + Prompt.java \ PromptService.java \ Restarter.java \ SearchEngine.java \ diff --git a/mobile/android/base/Prompt.java b/mobile/android/base/Prompt.java new file mode 100644 index 00000000000..80bb18e1708 --- /dev/null +++ b/mobile/android/base/Prompt.java @@ -0,0 +1,743 @@ +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- + * 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/. */ + +package org.mozilla.gecko; + +import org.mozilla.gecko.util.GeckoEventResponder; +import org.mozilla.gecko.util.ThreadUtils; +import org.mozilla.gecko.widget.DateTimePicker; + +import org.json.JSONArray; +import org.json.JSONObject; +import org.json.JSONException; + +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.DialogInterface.OnCancelListener; +import android.content.DialogInterface.OnClickListener; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.text.Html; +import android.text.InputType; +import android.text.TextUtils; +import android.text.format.DateFormat; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewGroup.LayoutParams; +import android.view.inputmethod.InputMethodManager; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemClickListener; +import android.widget.ArrayAdapter; +import android.widget.CheckBox; +import android.widget.CheckedTextView; +import android.widget.DatePicker; +import android.widget.EditText; +import android.widget.LinearLayout; +import android.widget.ListView; +import android.widget.ScrollView; +import android.widget.Spinner; +import android.widget.TextView; +import android.widget.TimePicker; + +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.GregorianCalendar; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.TimeUnit; + +public class Prompt implements OnClickListener, OnCancelListener, OnItemClickListener { + private static final String LOGTAG = "GeckoPromptService"; + + private String[] mButtons; + private PromptInput[] mInputs; + private boolean[] mSelected; + private AlertDialog mDialog; + + private final LayoutInflater mInflater; + private ConcurrentLinkedQueue mPromptQueue; + private final Context mContext; + private PromptCallback mCallback; + private String mGuid; + + private static boolean mInitialized = false; + private static int mGroupPaddingSize; + private static int mLeftRightTextWithIconPadding; + private static int mTopBottomTextWithIconPadding; + private static int mIconTextPadding; + private static int mIconSize; + private static int mInputPaddingSize; + private static int mMinRowSize; + + + public Prompt(Context context, ConcurrentLinkedQueue queue) { + this(context); + mCallback = null; + mPromptQueue = queue; + } + + public Prompt(Context context, PromptCallback callback) { + this(context); + mCallback = callback; + mPromptQueue = null; + } + + private Prompt(Context context) { + mContext = context; + mInflater = LayoutInflater.from(mContext); + + if (!mInitialized) { + Resources res = mContext.getResources(); + mGroupPaddingSize = (int) (res.getDimension(R.dimen.prompt_service_group_padding_size)); + mLeftRightTextWithIconPadding = (int) (res.getDimension(R.dimen.prompt_service_left_right_text_with_icon_padding)); + mTopBottomTextWithIconPadding = (int) (res.getDimension(R.dimen.prompt_service_top_bottom_text_with_icon_padding)); + mIconTextPadding = (int) (res.getDimension(R.dimen.prompt_service_icon_text_padding)); + mIconSize = (int) (res.getDimension(R.dimen.prompt_service_icon_size)); + mInputPaddingSize = (int) (res.getDimension(R.dimen.prompt_service_inputs_padding)); + mMinRowSize = (int) (res.getDimension(R.dimen.prompt_service_min_list_item_height)); + mInitialized = true; + } + } + + private static String formatDateString(String dateFormat, Calendar calendar) { + return new SimpleDateFormat(dateFormat).format(calendar.getTime()); + } + + private class PromptInput { + private final JSONObject mJSONInput; + + private final String mLabel; + private final String mType; + private final String mId; + private final String mHint; + private final boolean mAutofocus; + private final String mValue; + + private View mView; + + public PromptInput(JSONObject aJSONInput) { + mJSONInput = aJSONInput; + mLabel = getSafeString(aJSONInput, "label"); + mType = getSafeString(aJSONInput, "type"); + String id = getSafeString(aJSONInput, "id"); + mId = TextUtils.isEmpty(id) ? mType : id; + mHint = getSafeString(aJSONInput, "hint"); + mValue = getSafeString(aJSONInput, "value"); + mAutofocus = getSafeBool(aJSONInput, "autofocus"); + } + + public View getView() throws UnsupportedOperationException { + if (mType.equals("checkbox")) { + CheckBox checkbox = new CheckBox(mContext); + checkbox.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT)); + checkbox.setText(mLabel); + checkbox.setChecked(getSafeBool(mJSONInput, "checked")); + mView = (View)checkbox; + } else if (mType.equals("date")) { + try { + DateTimePicker input = new DateTimePicker(mContext, "yyyy-MM-dd", mValue, + DateTimePicker.PickersState.DATE); + input.toggleCalendar(true); + mView = (View)input; + } catch (UnsupportedOperationException ex) { + // We can't use our custom version of the DatePicker widget because the sdk is too old. + // But we can fallback on the native one. + DatePicker input = new DatePicker(mContext); + try { + if (!TextUtils.isEmpty(mValue)) { + GregorianCalendar calendar = new GregorianCalendar(); + calendar.setTime(new SimpleDateFormat("yyyy-MM-dd").parse(mValue)); + input.updateDate(calendar.get(Calendar.YEAR), + calendar.get(Calendar.MONTH), + calendar.get(Calendar.DAY_OF_MONTH)); + } + } catch (Exception e) { + Log.e(LOGTAG, "error parsing format string: " + e); + } + mView = (View)input; + } + } else if (mType.equals("week")) { + DateTimePicker input = new DateTimePicker(mContext, "yyyy-'W'ww", mValue, + DateTimePicker.PickersState.WEEK); + mView = (View)input; + } else if (mType.equals("time")) { + TimePicker input = new TimePicker(mContext); + input.setIs24HourView(DateFormat.is24HourFormat(mContext)); + + GregorianCalendar calendar = new GregorianCalendar(); + if (!TextUtils.isEmpty(mValue)) { + try { + calendar.setTime(new SimpleDateFormat("kk:mm").parse(mValue)); + } catch (Exception e) { } + } + input.setCurrentHour(calendar.get(GregorianCalendar.HOUR_OF_DAY)); + input.setCurrentMinute(calendar.get(GregorianCalendar.MINUTE)); + mView = (View)input; + } else if (mType.equals("datetime-local") || mType.equals("datetime")) { + DateTimePicker input = new DateTimePicker(mContext, "yyyy-MM-dd kk:mm", mValue, + DateTimePicker.PickersState.DATETIME); + input.toggleCalendar(true); + mView = (View)input; + } else if (mType.equals("month")) { + DateTimePicker input = new DateTimePicker(mContext, "yyyy-MM", mValue, + DateTimePicker.PickersState.MONTH); + mView = (View)input; + } else if (mType.equals("textbox") || mType.equals("password")) { + EditText input = new EditText(mContext); + int inputtype = InputType.TYPE_CLASS_TEXT; + if (mType.equals("password")) { + inputtype |= InputType.TYPE_TEXT_VARIATION_PASSWORD | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS; + } + input.setInputType(inputtype); + input.setText(mValue); + + if (!TextUtils.isEmpty(mHint)) { + input.setHint(mHint); + } + + if (mAutofocus) { + input.setOnFocusChangeListener(new View.OnFocusChangeListener() { + @Override + public void onFocusChange(View v, boolean hasFocus) { + if (hasFocus) { + ((InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE)).showSoftInput(v, 0); + } + } + }); + input.requestFocus(); + } + + mView = (View)input; + } else if (mType.equals("menulist")) { + Spinner spinner = new Spinner(mContext); + try { + String[] listitems = getStringArray(mJSONInput, "values"); + if (listitems.length > 0) { + ArrayAdapter adapter = new ArrayAdapter(mContext, R.layout.simple_dropdown_item_1line, listitems); + spinner.setAdapter(adapter); + int selectedIndex = getSafeInt(mJSONInput, "selected"); + spinner.setSelection(selectedIndex); + } + } catch(Exception ex) { } + mView = (View)spinner; + } else if (mType.equals("label")) { + // not really an input, but a way to add labels and such to the dialog + TextView view = new TextView(mContext); + view.setText(Html.fromHtml(mLabel)); + mView = view; + } + return mView; + } + + public String getId() { + return mId; + } + + public String getValue() { + if (mType.equals("checkbox")) { + CheckBox checkbox = (CheckBox)mView; + return checkbox.isChecked() ? "true" : "false"; + } else if (mType.equals("textbox") || mType.equals("password")) { + EditText edit = (EditText)mView; + return edit.getText().toString(); + } else if (mType.equals("menulist")) { + Spinner spinner = (Spinner)mView; + return Integer.toString(spinner.getSelectedItemPosition()); + } else if (mType.equals("time")) { + TimePicker tp = (TimePicker)mView; + GregorianCalendar calendar = + new GregorianCalendar(0,0,0,tp.getCurrentHour(),tp.getCurrentMinute()); + return formatDateString("kk:mm",calendar); + } else if (mType.equals("label")) { + return ""; + } else if (android.os.Build.VERSION.SDK_INT < 11 && mType.equals("date")) { + // We can't use the custom DateTimePicker with a sdk older than 11. + // Fallback on the native DatePicker. + DatePicker dp = (DatePicker)mView; + GregorianCalendar calendar = + new GregorianCalendar(dp.getYear(),dp.getMonth(),dp.getDayOfMonth()); + return formatDateString("yyyy-MM-dd",calendar); + } else { + DateTimePicker dp = (DateTimePicker)mView; + GregorianCalendar calendar = new GregorianCalendar(); + calendar.setTimeInMillis(dp.getTimeInMillis()); + if (mType.equals("date")) { + return formatDateString("yyyy-MM-dd",calendar); + } else if (mType.equals("week")) { + return formatDateString("yyyy-'W'ww",calendar); + } else if (mType.equals("datetime-local")) { + return formatDateString("yyyy-MM-dd kk:mm",calendar); + } else if (mType.equals("datetime")) { + calendar.set(GregorianCalendar.ZONE_OFFSET,0); + calendar.setTimeInMillis(dp.getTimeInMillis()); + return formatDateString("yyyy-MM-dd kk:mm",calendar); + } else if (mType.equals("month")) { + return formatDateString("yyyy-MM",calendar); + } + } + return ""; + } + } + + private View applyInputStyle(View view) { + view.setPadding(mInputPaddingSize, 0, mInputPaddingSize, 0); + return view; + } + + public void show(JSONObject message) { + processMessage(message); + } + + public void show(String aTitle, String aText, PromptListItem[] aMenuList, boolean aMultipleSelection) { + ThreadUtils.assertOnUiThread(); + + // treat actions that show a dialog as if preventDefault by content to prevent panning + if (mContext instanceof GeckoApp) { + ((GeckoApp)mContext).getLayerView().abortPanning(); + } + + AlertDialog.Builder builder = new AlertDialog.Builder(mContext); + if (!TextUtils.isEmpty(aTitle)) { + builder.setTitle(aTitle); + } + + if (!TextUtils.isEmpty(aText)) { + builder.setMessage(aText); + } + + int length = mInputs == null ? 0 : mInputs.length; + if (aMenuList != null && aMenuList.length > 0) { + int resourceId = android.R.layout.simple_list_item_1; + if (mSelected != null && mSelected.length > 0) { + if (aMultipleSelection) { + resourceId = R.layout.select_dialog_multichoice; + } else { + resourceId = R.layout.select_dialog_singlechoice; + } + } + PromptListAdapter adapter = new PromptListAdapter(mContext, resourceId, aMenuList); + if (mSelected != null && mSelected.length > 0) { + if (aMultipleSelection) { + adapter.listView = (ListView) mInflater.inflate(R.layout.select_dialog_list, null); + adapter.listView.setOnItemClickListener(this); + builder.setInverseBackgroundForced(true); + adapter.listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); + adapter.listView.setAdapter(adapter); + builder.setView(adapter.listView); + } else { + int selectedIndex = -1; + for (int i = 0; i < mSelected.length; i++) { + if (mSelected[i]) { + selectedIndex = i; + break; + } + } + mSelected = null; + builder.setSingleChoiceItems(adapter, selectedIndex, this); + } + } else { + builder.setAdapter(adapter, this); + mSelected = null; + } + } else if (length == 1) { + try { + ScrollView view = new ScrollView(mContext); + view.addView(mInputs[0].getView()); + builder.setView(applyInputStyle(view)); + } catch(UnsupportedOperationException ex) { + // We cannot display these input widgets with this sdk version, + // do not display any dialog and finish the prompt now. + try { + finishDialog(new JSONObject("{\"button\": -1}")); + } catch(JSONException e) { } + return; + } + } else if (length > 1) { + try { + LinearLayout linearLayout = new LinearLayout(mContext); + linearLayout.setOrientation(LinearLayout.VERTICAL); + for (int i = 0; i < length; i++) { + View content = mInputs[i].getView(); + linearLayout.addView(content); + } + ScrollView view = new ScrollView(mContext); + view.addView(linearLayout); + builder.setView(applyInputStyle(view)); + } catch(UnsupportedOperationException ex) { + // We cannot display these input widgets with this sdk version, + // do not display any dialog and finish the prompt now. + try { + finishDialog(new JSONObject("{\"button\": -1}")); + } catch(JSONException e) { } + return; + } + } + + length = mButtons == null ? 0 : mButtons.length; + if (length > 0) { + builder.setPositiveButton(mButtons[0], this); + if (length > 1) { + builder.setNeutralButton(mButtons[1], this); + if (length > 2) { + builder.setNegativeButton(mButtons[2], this); + } + } + } + + mDialog = builder.create(); + mDialog.setOnCancelListener(Prompt.this); + mDialog.show(); + } + + @Override + public void onClick(DialogInterface aDialog, int aWhich) { + ThreadUtils.assertOnUiThread(); + JSONObject ret = new JSONObject(); + try { + int button = -1; + ListView list = mDialog.getListView(); + if (list != null || mSelected != null) { + button = aWhich; + if (mSelected != null) { + JSONArray selected = new JSONArray(); + for (int i = 0; i < mSelected.length; i++) { + selected.put(mSelected[i]); + } + ret.put("button", selected); + } else { + ret.put("button", button); + } + } else { + switch(aWhich) { + case DialogInterface.BUTTON_POSITIVE : button = 0; break; + case DialogInterface.BUTTON_NEUTRAL : button = 1; break; + case DialogInterface.BUTTON_NEGATIVE : button = 2; break; + } + ret.put("button", button); + } + if (mInputs != null) { + for (int i = 0; i < mInputs.length; i++) { + ret.put(mInputs[i].getId(), mInputs[i].getValue()); + } + } + } catch(Exception ex) { + Log.i(LOGTAG, "Error building return: " + ex); + } + + if (mDialog != null) { + mDialog.dismiss(); + } + + finishDialog(ret); + } + + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + ThreadUtils.assertOnUiThread(); + mSelected[position] = !mSelected[position]; + } + + @Override + public void onCancel(DialogInterface aDialog) { + ThreadUtils.assertOnUiThread(); + JSONObject ret = new JSONObject(); + try { + ret.put("button", -1); + } catch(Exception ex) { } + finishDialog(ret); + } + + public void finishDialog(JSONObject aReturn) { + mInputs = null; + mButtons = null; + mDialog = null; + mSelected = null; + try { + aReturn.put("guid", mGuid); + } catch(JSONException ex) { } + + if (mPromptQueue != null) { + mPromptQueue.offer(aReturn.toString()); + } + + // poke the Gecko thread in case it's waiting for new events + GeckoAppShell.sendEventToGecko(GeckoEvent.createNoOpEvent()); + + if (mCallback != null) { + mCallback.onPromptFinished(aReturn.toString()); + } + mGuid = null; + } + + private void processMessage(JSONObject geckoObject) { + String title = getSafeString(geckoObject, "title"); + String text = getSafeString(geckoObject, "text"); + mGuid = getSafeString(geckoObject, "guid"); + + mButtons = getStringArray(geckoObject, "buttons"); + + JSONArray inputs = getSafeArray(geckoObject, "inputs"); + mInputs = new PromptInput[inputs.length()]; + for (int i = 0; i < mInputs.length; i++) { + try { + mInputs[i] = new PromptInput(inputs.getJSONObject(i)); + } catch(Exception ex) { } + } + + PromptListItem[] menuitems = getListItemArray(geckoObject, "listitems"); + mSelected = getBooleanArray(geckoObject, "selected"); + boolean multiple = getSafeBool(geckoObject, "multiple"); + show(title, text, menuitems, multiple); + } + + private static String getSafeString(JSONObject json, String key) { + try { + return json.getString(key); + } catch (Exception e) { + return ""; + } + } + + private static JSONArray getSafeArray(JSONObject json, String key) { + try { + return json.getJSONArray(key); + } catch (Exception e) { + return new JSONArray(); + } + } + + private static boolean getSafeBool(JSONObject json, String key) { + try { + return json.getBoolean(key); + } catch (Exception e) { + return false; + } + } + + private static int getSafeInt(JSONObject json, String key ) { + try { + return json.getInt(key); + } catch (Exception e) { + return 0; + } + } + + private String[] getStringArray(JSONObject aObject, String aName) { + JSONArray items = getSafeArray(aObject, aName); + int length = items.length(); + String[] list = new String[length]; + for (int i = 0; i < length; i++) { + try { + list[i] = items.getString(i); + } catch(Exception ex) { } + } + return list; + } + + private boolean[] getBooleanArray(JSONObject aObject, String aName) { + JSONArray items = new JSONArray(); + try { + items = aObject.getJSONArray(aName); + } catch(Exception ex) { return null; } + int length = items.length(); + boolean[] list = new boolean[length]; + for (int i = 0; i < length; i++) { + try { + list[i] = items.getBoolean(i); + } catch(Exception ex) { } + } + return list; + } + + private PromptListItem[] getListItemArray(JSONObject aObject, String aName) { + JSONArray items = getSafeArray(aObject, aName); + int length = items.length(); + PromptListItem[] list = new PromptListItem[length]; + for (int i = 0; i < length; i++) { + try { + list[i] = new PromptListItem(items.getJSONObject(i)); + } catch(Exception ex) { } + } + return list; + } + + public static class PromptListItem { + public final String label; + public final boolean isGroup; + public final boolean inGroup; + public final boolean disabled; + public final int id; + public final boolean isParent; + + // This member can't be accessible from JS, see bug 733749. + public Drawable icon; + + PromptListItem(JSONObject aObject) { + label = getSafeString(aObject, "label"); + isGroup = getSafeBool(aObject, "isGroup"); + inGroup = getSafeBool(aObject, "inGroup"); + disabled = getSafeBool(aObject, "disabled"); + id = getSafeInt(aObject, "id"); + isParent = getSafeBool(aObject, "isParent"); + } + + public PromptListItem(String aLabel) { + label = aLabel; + isGroup = false; + inGroup = false; + disabled = false; + id = 0; + isParent = false; + } + } + + public interface PromptCallback { + public void onPromptFinished(String jsonResult); + } + + public class PromptListAdapter extends ArrayAdapter { + private static final int VIEW_TYPE_ITEM = 0; + private static final int VIEW_TYPE_GROUP = 1; + private static final int VIEW_TYPE_COUNT = 2; + + public ListView listView; + private int mResourceId = -1; + private Drawable mBlankDrawable = null; + private Drawable mMoreDrawable = null; + + PromptListAdapter(Context context, int textViewResourceId, PromptListItem[] objects) { + super(context, textViewResourceId, objects); + mResourceId = textViewResourceId; + } + + @Override + public int getItemViewType(int position) { + PromptListItem item = getItem(position); + return (item.isGroup ? VIEW_TYPE_GROUP : VIEW_TYPE_ITEM); + } + + @Override + public int getViewTypeCount() { + return VIEW_TYPE_COUNT; + } + + private Drawable getMoreDrawable(Resources res) { + if (mMoreDrawable == null) { + mMoreDrawable = res.getDrawable(android.R.drawable.ic_menu_more); + } + return mMoreDrawable; + } + + private Drawable getBlankDrawable(Resources res) { + if (mBlankDrawable == null) { + mBlankDrawable = res.getDrawable(R.drawable.blank); + } + return mBlankDrawable; + } + + private void maybeUpdateIcon(PromptListItem item, TextView t) { + if (item.icon == null && !item.inGroup && !item.isParent) { + t.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null); + return; + } + + Drawable d = null; + Resources res = mContext.getResources(); + // Set the padding between the icon and the text. + t.setCompoundDrawablePadding(mIconTextPadding); + if (item.icon != null) { + // We want the icon to be of a specific size. Some do not + // follow this rule so we have to resize them. + Bitmap bitmap = ((BitmapDrawable) item.icon).getBitmap(); + d = new BitmapDrawable(Bitmap.createScaledBitmap(bitmap, mIconSize, mIconSize, true)); + } else if (item.inGroup) { + // We don't currently support "indenting" items with icons + d = getBlankDrawable(res); + } + + Drawable moreDrawable = null; + if (item.isParent) { + moreDrawable = getMoreDrawable(res); + } + + if (d != null || moreDrawable != null) { + t.setCompoundDrawablesWithIntrinsicBounds(d, null, moreDrawable, null); + } + } + + private void maybeUpdateCheckedState(int position, PromptListItem item, ViewHolder viewHolder) { + viewHolder.textView.setEnabled(!item.disabled && !item.isGroup); + viewHolder.textView.setClickable(item.isGroup || item.disabled); + + if (mSelected == null) { + return; + } + + CheckedTextView ct; + try { + ct = (CheckedTextView) viewHolder.textView; + // Apparently just using ct.setChecked(true) doesn't work, so this + // is stolen from the android source code as a way to set the checked + // state of these items + if (listView != null) { + listView.setItemChecked(position, mSelected[position]); + } + } catch (Exception e) { + return; + } + + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + PromptListItem item = getItem(position); + ViewHolder viewHolder = null; + + if (convertView == null) { + int resourceId = mResourceId; + if (item.isGroup) { + resourceId = R.layout.list_item_header; + } + + convertView = mInflater.inflate(resourceId, null); + convertView.setMinimumHeight(mMinRowSize); + + TextView tv = (TextView) convertView.findViewById(android.R.id.text1); + viewHolder = new ViewHolder(tv, tv.getPaddingLeft(), tv.getPaddingRight(), + tv.getPaddingTop(), tv.getPaddingBottom()); + + convertView.setTag(viewHolder); + } else { + viewHolder = (ViewHolder) convertView.getTag(); + } + + viewHolder.textView.setText(item.label); + maybeUpdateCheckedState(position, item, viewHolder); + maybeUpdateIcon(item, viewHolder.textView); + + return convertView; + } + + private class ViewHolder { + public final TextView textView; + public final int paddingLeft; + public final int paddingRight; + public final int paddingTop; + public final int paddingBottom; + + ViewHolder(TextView aTextView, int aLeft, int aRight, int aTop, int aBottom) { + textView = aTextView; + paddingLeft = aLeft; + paddingRight = aRight; + paddingTop = aTop; + paddingBottom = aBottom; + } + } + } +} diff --git a/mobile/android/base/PromptService.java b/mobile/android/base/PromptService.java index 523d912f3bd..6a2d36522a7 100644 --- a/mobile/android/base/PromptService.java +++ b/mobile/android/base/PromptService.java @@ -7,266 +7,44 @@ package org.mozilla.gecko; import org.mozilla.gecko.util.GeckoEventResponder; import org.mozilla.gecko.util.ThreadUtils; -import org.mozilla.gecko.widget.DateTimePicker; -import org.json.JSONArray; import org.json.JSONObject; -import android.app.AlertDialog; import android.content.Context; -import android.content.DialogInterface; -import android.content.DialogInterface.OnCancelListener; -import android.content.DialogInterface.OnClickListener; -import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; -import android.text.Html; -import android.text.InputType; -import android.text.TextUtils; -import android.text.format.DateFormat; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.view.ViewGroup.LayoutParams; -import android.view.inputmethod.InputMethodManager; -import android.widget.AdapterView; -import android.widget.AdapterView.OnItemClickListener; -import android.widget.ArrayAdapter; -import android.widget.CheckBox; -import android.widget.CheckedTextView; -import android.widget.DatePicker; -import android.widget.EditText; -import android.widget.LinearLayout; -import android.widget.ListView; -import android.widget.ScrollView; -import android.widget.Spinner; -import android.widget.TextView; -import android.widget.TimePicker; -import java.text.SimpleDateFormat; -import java.util.Calendar; -import java.util.GregorianCalendar; import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.TimeUnit; -public class PromptService implements OnClickListener, OnCancelListener, OnItemClickListener, GeckoEventResponder { +public class PromptService implements GeckoEventResponder { private static final String LOGTAG = "GeckoPromptService"; - private String[] mButtons; - private PromptInput[] mInputs; - private boolean[] mSelected; - private AlertDialog mDialog; - - private final LayoutInflater mInflater; private final ConcurrentLinkedQueue mPromptQueue; - private final int mGroupPaddingSize; - private final int mLeftRightTextWithIconPadding; - private final int mTopBottomTextWithIconPadding; - private final int mIconTextPadding; - private final int mIconSize; - private final int mInputPaddingSize; - private final int mMinRowSize; - - PromptService() { - mInflater = LayoutInflater.from(GeckoApp.mAppContext); - mPromptQueue = new ConcurrentLinkedQueue(); - - Resources res = GeckoApp.mAppContext.getResources(); - mGroupPaddingSize = (int) (res.getDimension(R.dimen.prompt_service_group_padding_size)); - mLeftRightTextWithIconPadding = (int) (res.getDimension(R.dimen.prompt_service_left_right_text_with_icon_padding)); - mTopBottomTextWithIconPadding = (int) (res.getDimension(R.dimen.prompt_service_top_bottom_text_with_icon_padding)); - mIconTextPadding = (int) (res.getDimension(R.dimen.prompt_service_icon_text_padding)); - mIconSize = (int) (res.getDimension(R.dimen.prompt_service_icon_size)); - mInputPaddingSize = (int) (res.getDimension(R.dimen.prompt_service_inputs_padding)); - mMinRowSize = (int) (res.getDimension(R.dimen.prompt_service_min_list_item_height)); + private final Context mContext; + public PromptService(Context context) { GeckoAppShell.getEventDispatcher().registerEventListener("Prompt:Show", this); + mPromptQueue = new ConcurrentLinkedQueue(); + mContext = context; } void destroy() { GeckoAppShell.getEventDispatcher().unregisterEventListener("Prompt:Show", this); } - private static String formatDateString(String dateFormat, Calendar calendar) { - return new SimpleDateFormat(dateFormat).format(calendar.getTime()); - } - - private class PromptInput { - private final JSONObject mJSONInput; - - private final String mLabel; - private final String mType; - private final String mId; - private final String mHint; - private final boolean mAutofocus; - private final String mValue; - - private View mView; - - public PromptInput(JSONObject aJSONInput) { - mJSONInput = aJSONInput; - mLabel = getSafeString(aJSONInput, "label"); - mType = getSafeString(aJSONInput, "type"); - String id = getSafeString(aJSONInput, "id"); - mId = TextUtils.isEmpty(id) ? mType : id; - mHint = getSafeString(aJSONInput, "hint"); - mValue = getSafeString(aJSONInput, "value"); - mAutofocus = getSafeBool(aJSONInput, "autofocus"); - } - - public View getView() throws UnsupportedOperationException { - if (mType.equals("checkbox")) { - CheckBox checkbox = new CheckBox(GeckoApp.mAppContext); - checkbox.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT)); - checkbox.setText(mLabel); - checkbox.setChecked(getSafeBool(mJSONInput, "checked")); - mView = (View)checkbox; - } else if (mType.equals("date")) { - try { - DateTimePicker input = new DateTimePicker(GeckoApp.mAppContext, "yyyy-MM-dd", mValue, - DateTimePicker.PickersState.DATE); - input.toggleCalendar(true); - mView = (View)input; - } catch (UnsupportedOperationException ex) { - // We can't use our custom version of the DatePicker widget because the sdk is too old. - // But we can fallback on the native one. - DatePicker input = new DatePicker(GeckoApp.mAppContext); - try { - if (!TextUtils.isEmpty(mValue)) { - GregorianCalendar calendar = new GregorianCalendar(); - calendar.setTime(new SimpleDateFormat("yyyy-MM-dd").parse(mValue)); - input.updateDate(calendar.get(Calendar.YEAR), - calendar.get(Calendar.MONTH), - calendar.get(Calendar.DAY_OF_MONTH)); - } - } catch (Exception e) { - Log.e(LOGTAG, "error parsing format string: " + e); - } - mView = (View)input; + public void show(final String aTitle, final String aText, final Prompt.PromptListItem[] aMenuList, + final boolean aMultipleSelection, final Prompt.PromptCallback callback) { + // The dialog must be created on the UI thread. + ThreadUtils.postToUiThread(new Runnable() { + @Override + public void run() { + Prompt p; + if (callback != null) { + p = new Prompt(mContext, callback); + } else { + p = new Prompt(mContext, mPromptQueue); } - } else if (mType.equals("week")) { - DateTimePicker input = new DateTimePicker(GeckoApp.mAppContext, "yyyy-'W'ww", mValue, - DateTimePicker.PickersState.WEEK); - mView = (View)input; - } else if (mType.equals("time")) { - TimePicker input = new TimePicker(GeckoApp.mAppContext); - input.setIs24HourView(DateFormat.is24HourFormat(GeckoApp.mAppContext)); - - GregorianCalendar calendar = new GregorianCalendar(); - if (!TextUtils.isEmpty(mValue)) { - try { - calendar.setTime(new SimpleDateFormat("kk:mm").parse(mValue)); - } catch (Exception e) { } - } - input.setCurrentHour(calendar.get(GregorianCalendar.HOUR_OF_DAY)); - input.setCurrentMinute(calendar.get(GregorianCalendar.MINUTE)); - mView = (View)input; - } else if (mType.equals("datetime-local") || mType.equals("datetime")) { - DateTimePicker input = new DateTimePicker(GeckoApp.mAppContext, "yyyy-MM-dd kk:mm", mValue, - DateTimePicker.PickersState.DATETIME); - input.toggleCalendar(true); - mView = (View)input; - } else if (mType.equals("month")) { - DateTimePicker input = new DateTimePicker(GeckoApp.mAppContext, "yyyy-MM", mValue, - DateTimePicker.PickersState.MONTH); - mView = (View)input; - } else if (mType.equals("textbox") || mType.equals("password")) { - EditText input = new EditText(GeckoApp.mAppContext); - int inputtype = InputType.TYPE_CLASS_TEXT; - if (mType.equals("password")) { - inputtype |= InputType.TYPE_TEXT_VARIATION_PASSWORD | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS; - } - input.setInputType(inputtype); - input.setText(mValue); - - if (!TextUtils.isEmpty(mHint)) { - input.setHint(mHint); - } - - if (mAutofocus) { - input.setOnFocusChangeListener(new View.OnFocusChangeListener() { - @Override - public void onFocusChange(View v, boolean hasFocus) { - if (hasFocus) { - ((InputMethodManager) GeckoApp.mAppContext.getSystemService(Context.INPUT_METHOD_SERVICE)).showSoftInput(v, 0); - } - } - }); - input.requestFocus(); - } - - mView = (View)input; - } else if (mType.equals("menulist")) { - Spinner spinner = new Spinner(GeckoApp.mAppContext); - try { - String[] listitems = getStringArray(mJSONInput, "values"); - if (listitems.length > 0) { - ArrayAdapter adapter = new ArrayAdapter(GeckoApp.mAppContext, R.layout.simple_dropdown_item_1line, listitems); - spinner.setAdapter(adapter); - int selectedIndex = getSafeInt(mJSONInput, "selected"); - spinner.setSelection(selectedIndex); - } - } catch(Exception ex) { } - mView = (View)spinner; - } else if (mType.equals("label")) { - // not really an input, but a way to add labels and such to the dialog - TextView view = new TextView(GeckoApp.mAppContext); - view.setText(Html.fromHtml(mLabel)); - mView = view; + p.show(aTitle, aText, aMenuList, aMultipleSelection); } - return mView; - } - - public String getId() { - return mId; - } - - public String getValue() { - if (mType.equals("checkbox")) { - CheckBox checkbox = (CheckBox)mView; - return checkbox.isChecked() ? "true" : "false"; - } else if (mType.equals("textbox") || mType.equals("password")) { - EditText edit = (EditText)mView; - return edit.getText().toString(); - } else if (mType.equals("menulist")) { - Spinner spinner = (Spinner)mView; - return Integer.toString(spinner.getSelectedItemPosition()); - } else if (mType.equals("time")) { - TimePicker tp = (TimePicker)mView; - GregorianCalendar calendar = - new GregorianCalendar(0,0,0,tp.getCurrentHour(),tp.getCurrentMinute()); - return formatDateString("kk:mm",calendar); - } else if (mType.equals("label")) { - return ""; - } else if (android.os.Build.VERSION.SDK_INT < 11 && mType.equals("date")) { - // We can't use the custom DateTimePicker with a sdk older than 11. - // Fallback on the native DatePicker. - DatePicker dp = (DatePicker)mView; - GregorianCalendar calendar = - new GregorianCalendar(dp.getYear(),dp.getMonth(),dp.getDayOfMonth()); - return formatDateString("yyyy-MM-dd",calendar); - } else { - DateTimePicker dp = (DateTimePicker)mView; - GregorianCalendar calendar = new GregorianCalendar(); - calendar.setTimeInMillis(dp.getTimeInMillis()); - if (mType.equals("date")) { - return formatDateString("yyyy-MM-dd",calendar); - } else if (mType.equals("week")) { - return formatDateString("yyyy-'W'ww",calendar); - } else if (mType.equals("datetime-local")) { - return formatDateString("yyyy-MM-dd kk:mm",calendar); - } else if (mType.equals("datetime")) { - calendar.set(GregorianCalendar.ZONE_OFFSET,0); - calendar.setTimeInMillis(dp.getTimeInMillis()); - return formatDateString("yyyy-MM-dd kk:mm",calendar); - } else if (mType.equals("month")) { - return formatDateString("yyyy-MM",calendar); - } - } - return ""; - } + }); } // GeckoEventListener implementation @@ -276,14 +54,29 @@ public class PromptService implements OnClickListener, OnCancelListener, OnItemC ThreadUtils.postToUiThread(new Runnable() { @Override public void run() { - processMessage(message); + boolean isAsync = message.optBoolean("async"); + Prompt p; + if (isAsync) { + p = new Prompt(mContext, new Prompt.PromptCallback() { + public void onPromptFinished(String jsonResult) { + GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Prompt:Reply", jsonResult)); + } + }); + } else { + p = new Prompt(mContext, mPromptQueue); + } + p.show(message); } }); } // GeckoEventResponder implementation @Override - public String getResponse() { + public String getResponse(final JSONObject origMessage) { + if (origMessage.optBoolean("async")) { + return ""; + } + // we only handle one kind of message in handleMessage, and this is the // response we provide for that message String result; @@ -292,431 +85,4 @@ public class PromptService implements OnClickListener, OnCancelListener, OnItemC } return result; } - - private View applyInputStyle(View view) { - view.setPadding(mInputPaddingSize, 0, mInputPaddingSize, 0); - return view; - } - - public void show(String aTitle, String aText, PromptListItem[] aMenuList, boolean aMultipleSelection) { - ThreadUtils.assertOnUiThread(); - - // treat actions that show a dialog as if preventDefault by content to prevent panning - GeckoApp.mAppContext.getLayerView().abortPanning(); - - AlertDialog.Builder builder = new AlertDialog.Builder(GeckoApp.mAppContext); - if (!TextUtils.isEmpty(aTitle)) { - builder.setTitle(aTitle); - } - - if (!TextUtils.isEmpty(aText)) { - builder.setMessage(aText); - } - - int length = mInputs == null ? 0 : mInputs.length; - if (aMenuList != null && aMenuList.length > 0) { - int resourceId = android.R.layout.simple_list_item_1; - if (mSelected != null && mSelected.length > 0) { - if (aMultipleSelection) { - resourceId = R.layout.select_dialog_multichoice; - } else { - resourceId = R.layout.select_dialog_singlechoice; - } - } - PromptListAdapter adapter = new PromptListAdapter(GeckoApp.mAppContext, resourceId, aMenuList); - if (mSelected != null && mSelected.length > 0) { - if (aMultipleSelection) { - adapter.listView = (ListView) mInflater.inflate(R.layout.select_dialog_list, null); - adapter.listView.setOnItemClickListener(this); - builder.setInverseBackgroundForced(true); - adapter.listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); - adapter.listView.setAdapter(adapter); - builder.setView(adapter.listView); - } else { - int selectedIndex = -1; - for (int i = 0; i < mSelected.length; i++) { - if (mSelected[i]) { - selectedIndex = i; - break; - } - } - mSelected = null; - builder.setSingleChoiceItems(adapter, selectedIndex, this); - } - } else { - builder.setAdapter(adapter, this); - mSelected = null; - } - } else if (length == 1) { - try { - ScrollView view = new ScrollView(GeckoApp.mAppContext); - view.addView(mInputs[0].getView()); - builder.setView(applyInputStyle(view)); - } catch(UnsupportedOperationException ex) { - // We cannot display these input widgets with this sdk version, - // do not display any dialog and finish the prompt now. - finishDialog("{\"button\": -1}"); - return; - } - } else if (length > 1) { - try { - LinearLayout linearLayout = new LinearLayout(GeckoApp.mAppContext); - linearLayout.setOrientation(LinearLayout.VERTICAL); - for (int i = 0; i < length; i++) { - View content = mInputs[i].getView(); - linearLayout.addView(content); - } - ScrollView view = new ScrollView(GeckoApp.mAppContext); - view.addView(linearLayout); - builder.setView(applyInputStyle(view)); - } catch(UnsupportedOperationException ex) { - // We cannot display these input widgets with this sdk version, - // do not display any dialog and finish the prompt now. - finishDialog("{\"button\": -1}"); - return; - } - } - - length = mButtons == null ? 0 : mButtons.length; - if (length > 0) { - builder.setPositiveButton(mButtons[0], this); - if (length > 1) { - builder.setNeutralButton(mButtons[1], this); - if (length > 2) { - builder.setNegativeButton(mButtons[2], this); - } - } - } - - mDialog = builder.create(); - mDialog.setOnCancelListener(PromptService.this); - mDialog.show(); - } - - @Override - public void onClick(DialogInterface aDialog, int aWhich) { - ThreadUtils.assertOnUiThread(); - JSONObject ret = new JSONObject(); - try { - int button = -1; - ListView list = mDialog.getListView(); - if (list != null || mSelected != null) { - button = aWhich; - if (mSelected != null) { - JSONArray selected = new JSONArray(); - for (int i = 0; i < mSelected.length; i++) { - selected.put(mSelected[i]); - } - ret.put("button", selected); - } else { - ret.put("button", button); - } - } else { - switch(aWhich) { - case DialogInterface.BUTTON_POSITIVE : button = 0; break; - case DialogInterface.BUTTON_NEUTRAL : button = 1; break; - case DialogInterface.BUTTON_NEGATIVE : button = 2; break; - } - ret.put("button", button); - } - if (mInputs != null) { - for (int i = 0; i < mInputs.length; i++) { - ret.put(mInputs[i].getId(), mInputs[i].getValue()); - } - } - } catch(Exception ex) { - Log.i(LOGTAG, "Error building return: " + ex); - } - - if (mDialog != null) { - mDialog.dismiss(); - } - - finishDialog(ret.toString()); - } - - @Override - public void onItemClick(AdapterView parent, View view, int position, long id) { - ThreadUtils.assertOnUiThread(); - mSelected[position] = !mSelected[position]; - } - - @Override - public void onCancel(DialogInterface aDialog) { - ThreadUtils.assertOnUiThread(); - JSONObject ret = new JSONObject(); - try { - ret.put("button", -1); - } catch(Exception ex) { } - finishDialog(ret.toString()); - } - - public void finishDialog(String aReturn) { - mInputs = null; - mButtons = null; - mDialog = null; - mSelected = null; - mPromptQueue.offer(aReturn); - // poke the Gecko thread in case it's waiting for new events - GeckoAppShell.sendEventToGecko(GeckoEvent.createNoOpEvent()); - } - - private void processMessage(JSONObject geckoObject) { - String title = getSafeString(geckoObject, "title"); - String text = getSafeString(geckoObject, "text"); - - mButtons = getStringArray(geckoObject, "buttons"); - - JSONArray inputs = getSafeArray(geckoObject, "inputs"); - mInputs = new PromptInput[inputs.length()]; - for (int i = 0; i < mInputs.length; i++) { - try { - mInputs[i] = new PromptInput(inputs.getJSONObject(i)); - } catch(Exception ex) { } - } - - PromptListItem[] menuitems = getListItemArray(geckoObject, "listitems"); - mSelected = getBooleanArray(geckoObject, "selected"); - boolean multiple = getSafeBool(geckoObject, "multiple"); - show(title, text, menuitems, multiple); - } - - private static String getSafeString(JSONObject json, String key) { - try { - return json.getString(key); - } catch (Exception e) { - return ""; - } - } - - private static JSONArray getSafeArray(JSONObject json, String key) { - try { - return json.getJSONArray(key); - } catch (Exception e) { - return new JSONArray(); - } - } - - private static boolean getSafeBool(JSONObject json, String key) { - try { - return json.getBoolean(key); - } catch (Exception e) { - return false; - } - } - - private static int getSafeInt(JSONObject json, String key ) { - try { - return json.getInt(key); - } catch (Exception e) { - return 0; - } - } - - private String[] getStringArray(JSONObject aObject, String aName) { - JSONArray items = getSafeArray(aObject, aName); - int length = items.length(); - String[] list = new String[length]; - for (int i = 0; i < length; i++) { - try { - list[i] = items.getString(i); - } catch(Exception ex) { } - } - return list; - } - - private boolean[] getBooleanArray(JSONObject aObject, String aName) { - JSONArray items = new JSONArray(); - try { - items = aObject.getJSONArray(aName); - } catch(Exception ex) { return null; } - int length = items.length(); - boolean[] list = new boolean[length]; - for (int i = 0; i < length; i++) { - try { - list[i] = items.getBoolean(i); - } catch(Exception ex) { } - } - return list; - } - - private PromptListItem[] getListItemArray(JSONObject aObject, String aName) { - JSONArray items = getSafeArray(aObject, aName); - int length = items.length(); - PromptListItem[] list = new PromptListItem[length]; - for (int i = 0; i < length; i++) { - try { - list[i] = new PromptListItem(items.getJSONObject(i)); - } catch(Exception ex) { } - } - return list; - } - - public static class PromptListItem { - public final String label; - public final boolean isGroup; - public final boolean inGroup; - public final boolean disabled; - public final int id; - public final boolean isParent; - - // This member can't be accessible from JS, see bug 733749. - public Drawable icon; - - PromptListItem(JSONObject aObject) { - label = getSafeString(aObject, "label"); - isGroup = getSafeBool(aObject, "isGroup"); - inGroup = getSafeBool(aObject, "inGroup"); - disabled = getSafeBool(aObject, "disabled"); - id = getSafeInt(aObject, "id"); - isParent = getSafeBool(aObject, "isParent"); - } - - public PromptListItem(String aLabel) { - label = aLabel; - isGroup = false; - inGroup = false; - disabled = false; - id = 0; - isParent = false; - } - } - - public class PromptListAdapter extends ArrayAdapter { - private static final int VIEW_TYPE_ITEM = 0; - private static final int VIEW_TYPE_GROUP = 1; - private static final int VIEW_TYPE_COUNT = 2; - - public ListView listView; - private int mResourceId = -1; - private Drawable mBlankDrawable = null; - private Drawable mMoreDrawable = null; - - PromptListAdapter(Context context, int textViewResourceId, PromptListItem[] objects) { - super(context, textViewResourceId, objects); - mResourceId = textViewResourceId; - } - - @Override - public int getItemViewType(int position) { - PromptListItem item = getItem(position); - return (item.isGroup ? VIEW_TYPE_GROUP : VIEW_TYPE_ITEM); - } - - @Override - public int getViewTypeCount() { - return VIEW_TYPE_COUNT; - } - - private Drawable getMoreDrawable(Resources res) { - if (mMoreDrawable == null) { - mMoreDrawable = res.getDrawable(android.R.drawable.ic_menu_more); - } - return mMoreDrawable; - } - - private Drawable getBlankDrawable(Resources res) { - if (mBlankDrawable == null) { - mBlankDrawable = res.getDrawable(R.drawable.blank); - } - return mBlankDrawable; - } - - private void maybeUpdateIcon(PromptListItem item, TextView t) { - if (item.icon == null && !item.inGroup && !item.isParent) { - t.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null); - return; - } - - Drawable d = null; - Resources res = GeckoApp.mAppContext.getResources(); - // Set the padding between the icon and the text. - t.setCompoundDrawablePadding(mIconTextPadding); - if (item.icon != null) { - // We want the icon to be of a specific size. Some do not - // follow this rule so we have to resize them. - Bitmap bitmap = ((BitmapDrawable) item.icon).getBitmap(); - d = new BitmapDrawable(Bitmap.createScaledBitmap(bitmap, mIconSize, mIconSize, true)); - } else if (item.inGroup) { - // We don't currently support "indenting" items with icons - d = getBlankDrawable(res); - } - - Drawable moreDrawable = null; - if (item.isParent) { - moreDrawable = getMoreDrawable(res); - } - - if (d != null || moreDrawable != null) { - t.setCompoundDrawablesWithIntrinsicBounds(d, null, moreDrawable, null); - } - } - - private void maybeUpdateCheckedState(int position, PromptListItem item, ViewHolder viewHolder) { - viewHolder.textView.setEnabled(!item.disabled && !item.isGroup); - viewHolder.textView.setClickable(item.isGroup || item.disabled); - - if (mSelected == null) - return; - - CheckedTextView ct; - try { - ct = (CheckedTextView) viewHolder.textView; - // Apparently just using ct.setChecked(true) doesn't work, so this - // is stolen from the android source code as a way to set the checked - // state of these items - if (listView != null) - listView.setItemChecked(position, mSelected[position]); - } catch (Exception e) { - return; - } - - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - PromptListItem item = getItem(position); - ViewHolder viewHolder = null; - - if (convertView == null) { - int resourceId = mResourceId; - if (item.isGroup) { - resourceId = R.layout.list_item_header; - } - - convertView = mInflater.inflate(resourceId, null); - convertView.setMinimumHeight(mMinRowSize); - - TextView tv = (TextView) convertView.findViewById(android.R.id.text1); - viewHolder = new ViewHolder(tv, tv.getPaddingLeft(), tv.getPaddingRight(), - tv.getPaddingTop(), tv.getPaddingBottom()); - - convertView.setTag(viewHolder); - } else { - viewHolder = (ViewHolder) convertView.getTag(); - } - - viewHolder.textView.setText(item.label); - maybeUpdateCheckedState(position, item, viewHolder); - maybeUpdateIcon(item, viewHolder.textView); - - return convertView; - } - - private class ViewHolder { - public final TextView textView; - public final int paddingLeft; - public final int paddingRight; - public final int paddingTop; - public final int paddingBottom; - - ViewHolder(TextView aTextView, int aLeft, int aRight, int aTop, int aBottom) { - textView = aTextView; - paddingLeft = aLeft; - paddingRight = aRight; - paddingTop = aTop; - paddingBottom = aBottom; - } - } - } } diff --git a/mobile/android/base/SharedPreferencesHelper.java b/mobile/android/base/SharedPreferencesHelper.java index c480a2d9d4c..696bc4587dd 100644 --- a/mobile/android/base/SharedPreferencesHelper.java +++ b/mobile/android/base/SharedPreferencesHelper.java @@ -256,7 +256,7 @@ public final class SharedPreferencesHelper } @Override - public String getResponse() { + public String getResponse(JSONObject origMessage) { return mResponse; } } diff --git a/mobile/android/base/util/EventDispatcher.java b/mobile/android/base/util/EventDispatcher.java index 2b4e070b123..2f76f21301f 100644 --- a/mobile/android/base/util/EventDispatcher.java +++ b/mobile/android/base/util/EventDispatcher.java @@ -85,7 +85,7 @@ public final class EventDispatcher { for (GeckoEventListener listener : listeners) { listener.handleMessage(type, json); if (listener instanceof GeckoEventResponder) { - String newResponse = ((GeckoEventResponder)listener).getResponse(); + String newResponse = ((GeckoEventResponder)listener).getResponse(json); if (response != null && newResponse != null) { Log.e(LOGTAG, "Received two responses for message of type " + type); } diff --git a/mobile/android/base/util/GeckoEventResponder.java b/mobile/android/base/util/GeckoEventResponder.java index 3ce41f50030..dc4561561c8 100644 --- a/mobile/android/base/util/GeckoEventResponder.java +++ b/mobile/android/base/util/GeckoEventResponder.java @@ -9,6 +9,8 @@ package org.mozilla.gecko.util; +import org.json.JSONObject; + public interface GeckoEventResponder extends GeckoEventListener { - String getResponse(); + String getResponse(JSONObject response); } From 50afd752be19793bd4855c1dc1114bacf215db6a Mon Sep 17 00:00:00 2001 From: Daniel Holbert Date: Sat, 25 May 2013 00:21:53 +0800 Subject: [PATCH 18/91] Bug 842065: Remove no-longer-necessary method nsIFrame::StyleDataExternal(). r=dbaron --- layout/generic/nsFrame.cpp | 7 ------- layout/generic/nsFrame.h | 3 --- layout/generic/nsIFrame.h | 22 +++------------------- 3 files changed, 3 insertions(+), 29 deletions(-) diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index 716a41e1d78..96acc21fdb3 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -7291,13 +7291,6 @@ nsFrame::GetFirstLeaf(nsPresContext* aPresContext, nsIFrame **aFrame) } } -/* virtual */ const void* -nsFrame::StyleDataExternal(nsStyleStructID aSID) const -{ - NS_ASSERTION(mStyleContext, "unexpected null pointer"); - return mStyleContext->StyleData(aSID); -} - /* virtual */ bool nsIFrame::IsFocusable(int32_t *aTabIndex, bool aWithMouse) { diff --git a/layout/generic/nsFrame.h b/layout/generic/nsFrame.h index 3b04b335ee2..48d0ad83842 100644 --- a/layout/generic/nsFrame.h +++ b/layout/generic/nsFrame.h @@ -413,9 +413,6 @@ public: void ConsiderChildOverflow(nsOverflowAreas& aOverflowAreas, nsIFrame* aChildFrame); - virtual const void* StyleDataExternal(nsStyleStructID aSID) const MOZ_OVERRIDE; - - /** * @return true if we should avoid a page/column break in this frame. */ diff --git a/layout/generic/nsIFrame.h b/layout/generic/nsIFrame.h index 8631e2c8468..d677f75708f 100644 --- a/layout/generic/nsIFrame.h +++ b/layout/generic/nsIFrame.h @@ -773,37 +773,21 @@ public: // Use PeekStyleData instead. virtual void DidSetStyleContext(nsStyleContext* aOldStyleContext) = 0; - /** - * Get the style data associated with this frame. This returns a - * const style struct pointer that should never be modified. See - * |nsStyleContext::StyleData| for more information. - * - * The use of the typesafe functions below is preferred to direct use - * of this function. - */ - virtual const void* StyleDataExternal(nsStyleStructID aSID) const = 0; - /** * Define typesafe getter functions for each style struct by * preprocessing the list of style structs. These functions are the * preferred way to get style data. The macro creates functions like: * const nsStyleBorder* StyleBorder(); * const nsStyleColor* StyleColor(); + * + * Callers outside of libxul should use nsIDOMWindow::GetComputedStyle() + * instead of these accessors. */ - -#ifdef _IMPL_NS_LAYOUT #define STYLE_STRUCT(name_, checkdata_cb_) \ const nsStyle##name_ * Style##name_ () const { \ NS_ASSERTION(mStyleContext, "No style context found!"); \ return mStyleContext->Style##name_ (); \ } -#else - #define STYLE_STRUCT(name_, checkdata_cb_) \ - const nsStyle##name_ * Style##name_ () const { \ - return static_cast( \ - StyleDataExternal(eStyleStruct_##name_)); \ - } -#endif #include "nsStyleStructList.h" #undef STYLE_STRUCT From 9f1395d4f8172d5ec7450c880e85bd354049ec44 Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Sat, 25 May 2013 01:27:52 +0900 Subject: [PATCH 19/91] Bug 807893 part.1 Remove nsIWidget::BeginSecureInput() and nsIWidget::EndSecureInput() r=roc, sr=roc --- content/events/src/nsIMEStateManager.cpp | 24 -------------------- content/events/src/nsIMEStateManager.h | 1 - widget/cocoa/nsChildView.h | 3 --- widget/cocoa/nsChildView.mm | 28 ------------------------ widget/cocoa/nsCocoaWindow.h | 2 -- widget/cocoa/nsCocoaWindow.mm | 26 ---------------------- widget/nsIWidget.h | 22 ++----------------- widget/xpwidgets/nsBaseWidget.cpp | 21 ------------------ widget/xpwidgets/nsBaseWidget.h | 2 -- 9 files changed, 2 insertions(+), 127 deletions(-) diff --git a/content/events/src/nsIMEStateManager.cpp b/content/events/src/nsIMEStateManager.cpp index c4772d89fbe..f02a675dbd1 100644 --- a/content/events/src/nsIMEStateManager.cpp +++ b/content/events/src/nsIMEStateManager.cpp @@ -90,7 +90,6 @@ private: nsIContent* nsIMEStateManager::sContent = nullptr; nsPresContext* nsIMEStateManager::sPresContext = nullptr; bool nsIMEStateManager::sInstalledMenuKeyboardListener = false; -bool nsIMEStateManager::sInSecureInputMode = false; bool nsIMEStateManager::sIsTestingIME = false; nsTextStateManager* nsIMEStateManager::sTextStateObserver = nullptr; @@ -243,29 +242,6 @@ nsIMEStateManager::OnChangeFocusInternal(nsPresContext* aPresContext, return NS_OK; } - // Handle secure input mode for password field input. - bool contentIsPassword = false; - if (aContent && aContent->GetNameSpaceID() == kNameSpaceID_XHTML) { - if (aContent->Tag() == nsGkAtoms::input) { - nsAutoString type; - aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::type, type); - contentIsPassword = type.LowerCaseEqualsLiteral("password"); - } - } - if (sInSecureInputMode) { - if (!contentIsPassword) { - if (NS_SUCCEEDED(widget->EndSecureKeyboardInput())) { - sInSecureInputMode = false; - } - } - } else { - if (contentIsPassword) { - if (NS_SUCCEEDED(widget->BeginSecureKeyboardInput())) { - sInSecureInputMode = true; - } - } - } - IMEState newState = GetNewIMEState(aPresContext, aContent); if (!focusActuallyChanging) { // actual focus isn't changing, but if IME enabled state is changing, diff --git a/content/events/src/nsIMEStateManager.h b/content/events/src/nsIMEStateManager.h index d6f20be154a..9f6bd7c510e 100644 --- a/content/events/src/nsIMEStateManager.h +++ b/content/events/src/nsIMEStateManager.h @@ -128,7 +128,6 @@ protected: static nsIContent* sContent; static nsPresContext* sPresContext; static bool sInstalledMenuKeyboardListener; - static bool sInSecureInputMode; static bool sIsTestingIME; static nsTextStateManager* sTextStateObserver; diff --git a/widget/cocoa/nsChildView.h b/widget/cocoa/nsChildView.h index 7c3b9be26ff..c2a730ed12a 100644 --- a/widget/cocoa/nsChildView.h +++ b/widget/cocoa/nsChildView.h @@ -519,9 +519,6 @@ public: virtual void UpdateThemeGeometries(const nsTArray& aThemeGeometries); - NS_IMETHOD BeginSecureKeyboardInput(); - NS_IMETHOD EndSecureKeyboardInput(); - void HidePlugin(); void UpdatePluginPort(); diff --git a/widget/cocoa/nsChildView.mm b/widget/cocoa/nsChildView.mm index a6732e6f5d9..71dddf94a0c 100644 --- a/widget/cocoa/nsChildView.mm +++ b/widget/cocoa/nsChildView.mm @@ -2343,34 +2343,6 @@ nsChildView::UpdateThemeGeometries(const nsTArray& aThemeGeometri [win setUnifiedToolbarHeight:DevPixelsToCocoaPoints(devUnifiedHeight)]; } -NS_IMETHODIMP -nsChildView::BeginSecureKeyboardInput() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - nsresult rv = nsBaseWidget::BeginSecureKeyboardInput(); - if (NS_SUCCEEDED(rv)) { - ::EnableSecureEventInput(); - } - return rv; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -NS_IMETHODIMP -nsChildView::EndSecureKeyboardInput() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - nsresult rv = nsBaseWidget::EndSecureKeyboardInput(); - if (NS_SUCCEEDED(rv)) { - ::DisableSecureEventInput(); - } - return rv; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - #ifdef ACCESSIBILITY already_AddRefed nsChildView::GetDocumentAccessible() diff --git a/widget/cocoa/nsCocoaWindow.h b/widget/cocoa/nsCocoaWindow.h index 483af270d85..4a1918e1f94 100644 --- a/widget/cocoa/nsCocoaWindow.h +++ b/widget/cocoa/nsCocoaWindow.h @@ -316,8 +316,6 @@ public: } return mInputContext; } - NS_IMETHOD BeginSecureKeyboardInput(); - NS_IMETHOD EndSecureKeyboardInput(); void SetPopupWindowLevel(); diff --git a/widget/cocoa/nsCocoaWindow.mm b/widget/cocoa/nsCocoaWindow.mm index db873800aec..8270ac21d8d 100644 --- a/widget/cocoa/nsCocoaWindow.mm +++ b/widget/cocoa/nsCocoaWindow.mm @@ -2029,32 +2029,6 @@ gfxASurface* nsCocoaWindow::GetThebesSurface() return nullptr; } -NS_IMETHODIMP nsCocoaWindow::BeginSecureKeyboardInput() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - nsresult rv = nsBaseWidget::BeginSecureKeyboardInput(); - if (NS_SUCCEEDED(rv)) { - ::EnableSecureEventInput(); - } - return rv; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -NS_IMETHODIMP nsCocoaWindow::EndSecureKeyboardInput() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - nsresult rv = nsBaseWidget::EndSecureKeyboardInput(); - if (NS_SUCCEEDED(rv)) { - ::DisableSecureEventInput(); - } - return rv; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - void nsCocoaWindow::SetPopupWindowLevel() { if (!mWindow) diff --git a/widget/nsIWidget.h b/widget/nsIWidget.h index 76edf233d13..eba0042470a 100644 --- a/widget/nsIWidget.h +++ b/widget/nsIWidget.h @@ -92,8 +92,8 @@ typedef nsEventStatus (* EVENT_CALLBACK)(nsGUIEvent *event); #endif #define NS_IWIDGET_IID \ -{ 0xa7d1e8d4, 0xe2c1, 0x45cb, \ - { 0xab, 0x72, 0xb3, 0xe9, 0xf9, 0xcc, 0xb2, 0xce } } +{ 0x5b9152, 0x56c8, 0x4a2d, \ + { 0x94, 0x9e, 0xec, 0xf5, 0x3, 0x83, 0x3d, 0x48 } } /* * Window shadow styles @@ -1310,24 +1310,6 @@ class nsIWidget : public nsISupports { */ virtual bool HasPendingInputEvent() = 0; - /** - * Called when when we need to begin secure keyboard input, such as when a password field - * gets focus. - * - * NOTE: Calls to this method may not be nested and you can only enable secure keyboard input - * for one widget at a time. - */ - NS_IMETHOD BeginSecureKeyboardInput() = 0; - - /** - * Called when when we need to end secure keyboard input, such as when a password field - * loses focus. - * - * NOTE: Calls to this method may not be nested and you can only enable secure keyboard input - * for one widget at a time. - */ - NS_IMETHOD EndSecureKeyboardInput() = 0; - /** * Set the background color of the window titlebar for this widget. On Mac, * for example, this will remove the grey gradient and bottom border and diff --git a/widget/xpwidgets/nsBaseWidget.cpp b/widget/xpwidgets/nsBaseWidget.cpp index 336f059b04c..ad8b62752b5 100644 --- a/widget/xpwidgets/nsBaseWidget.cpp +++ b/widget/xpwidgets/nsBaseWidget.cpp @@ -47,7 +47,6 @@ static void debug_RegisterPrefCallbacks(); -static bool debug_InSecureKeyboardInputMode = false; #endif #ifdef NOISY_WIDGET_LEAKS @@ -1172,26 +1171,6 @@ nsBaseWidget::SetIcon(const nsAString&) return NS_OK; } -NS_IMETHODIMP -nsBaseWidget::BeginSecureKeyboardInput() -{ -#ifdef DEBUG - NS_ASSERTION(!debug_InSecureKeyboardInputMode, "Attempting to nest call to BeginSecureKeyboardInput!"); - debug_InSecureKeyboardInputMode = true; -#endif - return NS_OK; -} - -NS_IMETHODIMP -nsBaseWidget::EndSecureKeyboardInput() -{ -#ifdef DEBUG - NS_ASSERTION(debug_InSecureKeyboardInputMode, "Calling EndSecureKeyboardInput when it hasn't been enabled!"); - debug_InSecureKeyboardInputMode = false; -#endif - return NS_OK; -} - NS_IMETHODIMP nsBaseWidget::SetWindowTitlebarColor(nscolor aColor, bool aActive) { diff --git a/widget/xpwidgets/nsBaseWidget.h b/widget/xpwidgets/nsBaseWidget.h index 36e90a73027..87ff5eedc75 100644 --- a/widget/xpwidgets/nsBaseWidget.h +++ b/widget/xpwidgets/nsBaseWidget.h @@ -148,8 +148,6 @@ public: NS_IMETHOD GetAttention(int32_t aCycleCount); virtual bool HasPendingInputEvent(); NS_IMETHOD SetIcon(const nsAString &anIconSpec); - NS_IMETHOD BeginSecureKeyboardInput(); - NS_IMETHOD EndSecureKeyboardInput(); NS_IMETHOD SetWindowTitlebarColor(nscolor aColor, bool aActive); virtual void SetDrawsInTitlebar(bool aState) {} virtual bool ShowsResizeIndicator(nsIntRect* aResizerRect); From 400296b5fd01798e5e14ea3162994974b01a07f7 Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Sat, 25 May 2013 01:27:52 +0900 Subject: [PATCH 20/91] Bug 807893 part.2 Cocoa widget should manage secure input mode with input context at changing the context and changing focus r=smichaud --- widget/cocoa/TextInputHandler.h | 26 +++++++++++++++++++++++ widget/cocoa/TextInputHandler.mm | 35 +++++++++++++++++++++++++++++++ widget/cocoa/nsChildView.mm | 19 ++++++++++++++++- widget/cocoa/nsCocoaWindow.h | 9 ++++---- widget/cocoa/nsCocoaWindow.mm | 36 ++++++++++++++++++++++++++++++++ widget/nsIWidget.h | 5 +++++ 6 files changed, 124 insertions(+), 6 deletions(-) diff --git a/widget/cocoa/TextInputHandler.h b/widget/cocoa/TextInputHandler.h index e23c32411ff..32737c565f1 100644 --- a/widget/cocoa/TextInputHandler.h +++ b/widget/cocoa/TextInputHandler.h @@ -378,6 +378,30 @@ public: */ static bool IsSpecialGeckoKey(UInt32 aNativeKeyCode); + + /** + * EnableSecureEventInput() and DisableSecureEventInput() wrap the Carbon + * Event Manager APIs with the same names. In addition they keep track of + * how many times we've called them (in the same process) -- unlike the + * Carbon Event Manager APIs, which only keep track of how many times they've + * been called from any and all processes. + * + * The Carbon Event Manager's IsSecureEventInputEnabled() returns whether + * secure event input mode is enabled (in any process). This class's + * IsSecureEventInputEnabled() returns whether we've made any calls to + * EnableSecureEventInput() that are not (yet) offset by the calls we've + * made to DisableSecureEventInput(). + */ + static void EnableSecureEventInput(); + static void DisableSecureEventInput(); + static bool IsSecureEventInputEnabled(); + + /** + * EnsureSecureEventInputDisabled() calls DisableSecureEventInput() until + * our call count becomes 0. + */ + static void EnsureSecureEventInputDisabled(); + protected: nsAutoRefCnt mRefCnt; @@ -605,6 +629,8 @@ private: }; KeyboardLayoutOverride mKeyboardOverride; + + static int32_t sSecureEventInputCount; }; /** diff --git a/widget/cocoa/TextInputHandler.mm b/widget/cocoa/TextInputHandler.mm index eb3e337c44c..c0bc5b1fca6 100644 --- a/widget/cocoa/TextInputHandler.mm +++ b/widget/cocoa/TextInputHandler.mm @@ -3980,6 +3980,8 @@ PluginTextInputHandler::DispatchCocoaNPAPITextEvent(NSString* aString) * ******************************************************************************/ +int32_t TextInputHandlerBase::sSecureEventInputCount = 0; + TextInputHandlerBase::TextInputHandlerBase(nsChildView* aWidget, NSView *aNativeView) : mWidget(aWidget) @@ -4217,3 +4219,36 @@ TextInputHandlerBase::IsModifierKey(UInt32 aNativeKeyCode) } return false; } + +/* static */ void +TextInputHandlerBase::EnableSecureEventInput() +{ + sSecureEventInputCount++; + ::EnableSecureEventInput(); +} + +/* static */ void +TextInputHandlerBase::DisableSecureEventInput() +{ + if (!sSecureEventInputCount) { + return; + } + sSecureEventInputCount--; + ::DisableSecureEventInput(); +} + +/* static */ bool +TextInputHandlerBase::IsSecureEventInputEnabled() +{ + NS_ASSERTION(!!sSecureEventInputCount == !!::IsSecureEventInputEnabled(), + "Some other process has enabled secure event input"); + return !!sSecureEventInputCount; +} + +/* static */ void +TextInputHandlerBase::EnsureSecureEventInputDisabled() +{ + while (sSecureEventInputCount) { + TextInputHandlerBase::DisableSecureEventInput(); + } +} diff --git a/widget/cocoa/nsChildView.mm b/widget/cocoa/nsChildView.mm index 71dddf94a0c..910bfcd7bde 100644 --- a/widget/cocoa/nsChildView.mm +++ b/widget/cocoa/nsChildView.mm @@ -1678,10 +1678,18 @@ nsChildView::NotifyIME(NotificationToIME aNotification) mTextInputHandler->CancelIMEComposition(); return NS_OK; case NOTIFY_IME_OF_FOCUS: + if (mInputContext.IsPasswordEditor()) { + TextInputHandler::EnableSecureEventInput(); + } + NS_ENSURE_TRUE(mTextInputHandler, NS_ERROR_NOT_AVAILABLE); mTextInputHandler->OnFocusChangeInGecko(true); return NS_OK; case NOTIFY_IME_OF_BLUR: + // When we're going to be deactive, we must disable the secure event input + // mode, see the Carbon Event Manager Reference. + TextInputHandler::EnsureSecureEventInputDisabled(); + NS_ENSURE_TRUE(mTextInputHandler, NS_ERROR_NOT_AVAILABLE); mTextInputHandler->OnFocusChangeInGecko(false); return NS_OK; @@ -1694,7 +1702,16 @@ NS_IMETHODIMP_(void) nsChildView::SetInputContext(const InputContext& aContext, const InputContextAction& aAction) { - NS_ENSURE_TRUE(mTextInputHandler, ); + // XXX Ideally, we should check if this instance has focus or not. + // However, this is called only when this widget has focus, so, + // it's not problem at least for now. + if (aContext.IsPasswordEditor()) { + TextInputHandler::EnableSecureEventInput(); + } else { + TextInputHandler::EnsureSecureEventInputDisabled(); + } + + NS_ENSURE_TRUE_VOID(mTextInputHandler); mInputContext = aContext; switch (aContext.mIMEState.mEnabled) { case IMEState::ENABLED: diff --git a/widget/cocoa/nsCocoaWindow.h b/widget/cocoa/nsCocoaWindow.h index 4a1918e1f94..43da15c7cf0 100644 --- a/widget/cocoa/nsCocoaWindow.h +++ b/widget/cocoa/nsCocoaWindow.h @@ -297,11 +297,10 @@ public: void SetMenuBar(nsMenuBarX* aMenuBar); nsMenuBarX *GetMenuBar(); - NS_IMETHOD_(void) SetInputContext(const InputContext& aContext, - const InputContextAction& aAction) - { - mInputContext = aContext; - } + NS_IMETHOD NotifyIME(NotificationToIME aNotification) MOZ_OVERRIDE; + NS_IMETHOD_(void) SetInputContext( + const InputContext& aContext, + const InputContextAction& aAction) MOZ_OVERRIDE; NS_IMETHOD_(InputContext) GetInputContext() { NSView* view = mWindow ? [mWindow contentView] : nil; diff --git a/widget/cocoa/nsCocoaWindow.mm b/widget/cocoa/nsCocoaWindow.mm index 8270ac21d8d..8b8faabab86 100644 --- a/widget/cocoa/nsCocoaWindow.mm +++ b/widget/cocoa/nsCocoaWindow.mm @@ -11,6 +11,7 @@ #include "nsGUIEvent.h" #include "nsIRollupListener.h" #include "nsChildView.h" +#include "TextInputHandler.h" #include "nsWindowMap.h" #include "nsAppShell.h" #include "nsIAppShellService.h" @@ -44,6 +45,7 @@ class LayerManager; } } using namespace mozilla::layers; +using namespace mozilla::widget; using namespace mozilla; // defined in nsAppShell.mm @@ -2078,6 +2080,40 @@ bool nsCocoaWindow::ShouldFocusPlugin() return true; } +NS_IMETHODIMP +nsCocoaWindow::NotifyIME(NotificationToIME aNotification) +{ + switch (aNotification) { + case NOTIFY_IME_OF_FOCUS: + if (mInputContext.IsPasswordEditor()) { + TextInputHandler::EnableSecureEventInput(); + } + return NS_OK; + case NOTIFY_IME_OF_BLUR: + // When we're going to be deactive, we must disable the secure event input + // mode, see the Carbon Event Manager Reference. + TextInputHandler::EnsureSecureEventInputDisabled(); + return NS_OK; + default: + return NS_ERROR_NOT_IMPLEMENTED; + } +} + +NS_IMETHODIMP_(void) +nsCocoaWindow::SetInputContext(const InputContext& aContext, + const InputContextAction& aAction) +{ + // XXX Ideally, we should check if this instance has focus or not. + // However, this is called only when this widget has focus, so, + // it's not problem at least for now. + if (aContext.IsPasswordEditor()) { + TextInputHandler::EnableSecureEventInput(); + } else { + TextInputHandler::EnsureSecureEventInputDisabled(); + } + mInputContext = aContext; +} + @implementation WindowDelegate // We try to find a gecko menu bar to paint. If one does not exist, just paint diff --git a/widget/nsIWidget.h b/widget/nsIWidget.h index eba0042470a..a8a25388cf4 100644 --- a/widget/nsIWidget.h +++ b/widget/nsIWidget.h @@ -306,6 +306,11 @@ struct IMEState { struct InputContext { InputContext() : mNativeIMEContext(nullptr) {} + bool IsPasswordEditor() const + { + return mHTMLInputType.LowerCaseEqualsLiteral("password"); + } + IMEState mIMEState; /* The type of the input if the input is a html input field */ From 3fd2b773dd6328209eb6bcc4ae7e2160dcf8ce90 Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Sat, 25 May 2013 01:27:52 +0900 Subject: [PATCH 21/91] Bug 807893 part.3 Add tests for secure input mode of Cocoa widget r=smichaud --- widget/cocoa/nsChildView.mm | 8 ++ widget/tests/Makefile.in | 1 + widget/tests/test_secure_input.html | 151 ++++++++++++++++++++++++++++ 3 files changed, 160 insertions(+) create mode 100644 widget/tests/test_secure_input.html diff --git a/widget/cocoa/nsChildView.mm b/widget/cocoa/nsChildView.mm index 910bfcd7bde..575ba29ee25 100644 --- a/widget/cocoa/nsChildView.mm +++ b/widget/cocoa/nsChildView.mm @@ -4761,6 +4761,14 @@ static int32_t RoundUp(double aDouble) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK; +#if !defined(RELEASE_BUILD) || defined(DEBUG) + if (mGeckoChild && + mGeckoChild->GetInputContext().IsPasswordEditor() != + TextInputHandler::IsSecureEventInputEnabled()) { + MOZ_NOT_REACHED("in wrong secure input mode"); + } +#endif // #if !defined(RELEASE_BUILD) || defined(DEBUG) + if (mGeckoChild && mTextInputHandler && mIsPluginView) { mTextInputHandler->HandleKeyDownEventForPlugin(theEvent); return; diff --git a/widget/tests/Makefile.in b/widget/tests/Makefile.in index ece7c9a8f8e..b2932165daf 100644 --- a/widget/tests/Makefile.in +++ b/widget/tests/Makefile.in @@ -78,6 +78,7 @@ MOCHITEST_CHROME_FILES += native_menus_window.xul \ test_bug596600.xul \ test_bug673301.xul \ test_taskbar_progress.xul \ + test_secure_input.html \ $(NULL) endif diff --git a/widget/tests/test_secure_input.html b/widget/tests/test_secure_input.html new file mode 100644 index 00000000000..be9ff970425 --- /dev/null +++ b/widget/tests/test_secure_input.html @@ -0,0 +1,151 @@ + + + + Test for secure input mode + + + + + + + + +
+
+ +

+
+
+
+
+
+
+

+
+ + + + From 021af0fcd24fa0df4da97bebf930ccdaba679185 Mon Sep 17 00:00:00 2001 From: Dave Townsend Date: Fri, 24 May 2013 09:29:33 -0700 Subject: [PATCH 22/91] Bug 875812 - Uplift Add-on SDK integration branch to Firefox https://github.com/mozilla/addon-sdk/compare/2593d0a01b77...007eb037f040 --- .../source/doc/dev-guide-source/cfx-tool.md | 160 ++++++++++++------ .../source/doc/module-source/sdk/request.md | 26 ++- addon-sdk/source/lib/sdk/addon/runner.js | 24 ++- .../source/lib/sdk/deprecated/tab-browser.js | 10 +- .../source/lib/sdk/deprecated/window-utils.js | 16 +- addon-sdk/source/lib/sdk/panel.js | 21 +-- addon-sdk/source/lib/sdk/request.js | 20 ++- addon-sdk/source/lib/sdk/url.js | 34 ++++ addon-sdk/source/lib/sdk/worker/utils.js | 28 ++- .../test-selection.js | 6 +- .../addons/privileged-panel/data/index.html | 3 + .../test/addons/privileged-panel/main.js | 26 +++ .../test/addons/privileged-panel/package.json | 3 + addon-sdk/source/test/test-request.js | 20 +++ addon-sdk/source/test/test-selection.js | 6 +- addon-sdk/source/test/test-url.js | 116 ++++++++++++- 16 files changed, 405 insertions(+), 114 deletions(-) create mode 100644 addon-sdk/source/test/addons/privileged-panel/data/index.html create mode 100644 addon-sdk/source/test/addons/privileged-panel/main.js create mode 100644 addon-sdk/source/test/addons/privileged-panel/package.json diff --git a/addon-sdk/source/doc/dev-guide-source/cfx-tool.md b/addon-sdk/source/doc/dev-guide-source/cfx-tool.md index b0f4e9b781f..f07540b8822 100644 --- a/addon-sdk/source/doc/dev-guide-source/cfx-tool.md +++ b/addon-sdk/source/doc/dev-guide-source/cfx-tool.md @@ -20,12 +20,69 @@ commands (for example `--help`). `cfx` supports the following global options: -v, --verbose - enable lots of output -"Command-specific options" are only -applicable to a subset of the commands. +"Command-specific options" are documented alongside the commands. -## Supported Commands ## +There are five supported cfx commands: -### cfx docs ### + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ cfx docs + + Display the documentation for the SDK. +
+ cfx init + + Create a skeleton add-on as a starting point for your own add-on. +
+ cfx run + + Launch an instance of Firefox with your add-on installed. +
+ cfx test + + Runs your add-on's unit tests. +
+ cfx xpi + + Package your add-on as an XPI + file, which is the install file format for Firefox add-ons. +
+ +There are also a number of +[internal commands](dev-guide/cfx-tool.html#internal-commands), +which are more likely to be useful to SDK developers than to add-on developers. + +## cfx docs ## This command displays the documentation for the SDK. The documentation is shipped with the SDK in [Markdown](http://daringfireball.net/projects/markdown/) @@ -46,7 +103,8 @@ This command will regenerate only the HTML page you're reading. This is useful if you're iteratively editing a single file, and don't want to wait for cfx to regenerate the complete documentation tree. -### cfx init #### +## cfx init ## + Create a new directory called "my-addon", change into it, and run `cfx init`. This command will create an skeleton add-on, as a starting point for your @@ -73,14 +131,13 @@ own add-on development, with the following file structure:
-### cfx run ### - +## cfx run ## This command is used to run the add-on. Called with no options it looks for a file called `package.json` in the current directory, loads the corresponding add-on, and runs it under the version of Firefox it finds in the platform's default install path. -#### Supported Options ##### +### Supported Options #### You can point `cfx run` at a different `package.json` file using the `--pkgdir` option, and pass arguments to your add-on using the @@ -190,7 +247,7 @@ See -#### Experimental Options #### +### Experimental Options ### @@ -233,6 +290,25 @@ To launch the application, enter the following command: + + + + +
+ -o, --overload-modules + +

In early versions of the SDK, the SDK modules used by an add-on + were themselves included in the add-on. The SDK modules now ship as + part of Firefox. From Firefox 21 onwards, SDK add-ons built with + SDK 1.14 or higher will use the SDK modules that are built into Firefox, + even if the add-on includes its own copies of the SDK modules.

+

Use this flag to reverse that behavior: if this flag is set and + the add-on includes its own copies of the SDK modules, then the add-on + will use the SDK modules in the add-on, not the ones built into Firefox.

+

This flag is particularly useful for SDK developers or people working with + the development version of the SDK, who may want to run an add-on using newer + versions of the modules than than those shipping in Firefox.

+
--templatedir=TEMPLATEDIR @@ -249,7 +325,7 @@ To launch the application, enter the following command:
-#### Internal Options #### +### Internal Options ### @@ -291,8 +367,7 @@ To launch the application, enter the following command:
-### cfx test ### - +##
cfx test ## Run available tests for the specified package. Note the hyphen after "test" in the module name. @@ -310,7 +385,7 @@ See the [reference documentation for the `assert` module](modules/sdk/test/assert.html) for details. -#### Supported Options ##### +### Supported Options ### As with `cfx run` you can use options to control which host application binary version to use, and to select a profile. @@ -416,7 +491,7 @@ times. -#### Experimental Options #### +### Experimental Options ### @@ -461,16 +536,26 @@ To launch the application, enter the following command:
- --use-server + -o, --overload-modules - Run tests using a server previously started with cfx develop. +

In early versions of the SDK, the SDK modules used by an add-on + were themselves included in the add-on. The SDK modules now ship as + part of Firefox. From Firefox 21 onwards, SDK add-ons built with + SDK 1.14 or higher will use the SDK modules that are built into Firefox, + even if the add-on includes its own copies of the SDK modules.

+

Use this flag to reverse that behavior: if this flag is set and + the add-on includes its own copies of the SDK modules, then the add-on + will use the SDK modules in the add-on, not the ones built into Firefox.

+

This flag is particularly useful for SDK developers or people working with + the development version of the SDK, who may want to run an add-on using newer + versions of the modules than than those shipping in Firefox.

-#### Internal Options #### +### Internal Options ### @@ -545,8 +630,7 @@ To launch the application, enter the following command:
-### cfx xpi ### - +## cfx xpi ## This tool is used to package your add-on as an [XPI](https://developer.mozilla.org/en/XPI) file, which is the install file format for Mozilla add-ons. @@ -557,7 +641,7 @@ the current directory and creates the corresponding XPI file. Once you have built an XPI file you can distribute your add-on by submitting it to [addons.mozilla.org](http://addons.mozilla.org). -#### updateURL and updateLink #### +### updateURL and updateLink ### If you choose to host the XPI yourself you should enable the host application to find new versions of your add-on. @@ -597,7 +681,7 @@ So if we run the following command: * an RDF file which embeds `https://example.com/addon/latest` as the value of `updateLink`. -#### Supported Options #### +### Supported Options ### As with `cfx run` you can point `cfx` at a different `package.json` file using the `--pkgdir` option. You can also embed arguments in the XPI using the @@ -675,7 +759,7 @@ add-on whenever it is run. -#### Experimental Options #### +### Experimental Options ### @@ -699,7 +783,7 @@ add-on whenever it is run.
-#### Internal Options #### +### Internal Options ### @@ -721,35 +805,7 @@ add-on whenever it is run.
-## Experimental Commands ## - -### cfx develop ### - -This initiates an instance of a host application in development mode, -and allows you to pipe commands into it from another shell without -having to constantly restart it. Aside from convenience, for SDK -Platform developers this has the added benefit of making it easier to -detect leaks. - -For example, in shell A, type: - -
-  cfx develop
-
- -In shell B, type: - -
-  cfx test --use-server
-
- -This will send `cfx test --use-server` output to shell A. If you repeat the -command in shell B, `cfx test --use-server` output will appear again in shell A -without restarting the host application. - -`cfx develop` doesn't take any options. - -## Internal Commands ## +## Internal Commands ## ### cfx sdocs ### diff --git a/addon-sdk/source/doc/module-source/sdk/request.md b/addon-sdk/source/doc/module-source/sdk/request.md index cc439caec69..3f87aefb361 100644 --- a/addon-sdk/source/doc/module-source/sdk/request.md +++ b/addon-sdk/source/doc/module-source/sdk/request.md @@ -6,20 +6,22 @@ The `request` module lets you make simple yet powerful network requests. @class -The `Request` object is used to make `GET`, `POST` or `PUT` network requests. -It is constructed with a URL to which the request is sent. Optionally the user -may specify a collection of headers and content to send alongside the request -and a callback which will be executed once the request completes. +The `Request` object is used to make `GET`, `POST`, `PUT`, or `HEAD` +network requests. It is constructed with a URL to which the request is sent. +Optionally the user may specify a collection of headers and content to send +alongside the request and a callback which will be executed once the +request completes. Once a `Request` object has been created a `GET` request can be executed by calling its `get()` method, a `POST` request by calling its `post()` method, -or a `PUT` request by calling its `put()` method. +a `PUT` request by calling its `put()` method, or a `HEAD` request by calling +its `head()` method. When the server completes the request, the `Request` object emits a "complete" event. Registered event listeners are passed a `Response` object. -Each `Request` object is designed to be used once. Once `GET`, `POST` or `PUT` -are called, attempting to call either will throw an error. +Each `Request` object is designed to be used once. Once `GET`, `POST`, `PUT`, +or `HEAD` are called, attempting to call either will throw an error. Since the request is not being made by any particular website, requests made here are not subject to the same-domain restriction that requests made in web @@ -150,6 +152,12 @@ Make a `PUT` request. @returns {Request} + +@method +Make a `HEAD` request. +@returns {Request} + + @event The `Request` object emits this event when the request has completed and a @@ -165,8 +173,8 @@ Listener functions are passed the response to the request as a `Response` object @class The Response object contains the response to a network request issued using a -`Request` object. It is returned by the `get()`, `post()` or `put()` method of a -`Request` object. +`Request` object. It is returned by the `get()`, `post()`, `put()`, or `head()` +method of a `Request` object. All members of a `Response` object are read-only. diff --git a/addon-sdk/source/lib/sdk/addon/runner.js b/addon-sdk/source/lib/sdk/addon/runner.js index 3429e23f553..62fc97dfeff 100644 --- a/addon-sdk/source/lib/sdk/addon/runner.js +++ b/addon-sdk/source/lib/sdk/addon/runner.js @@ -11,11 +11,12 @@ module.metadata = { const { Cc, Ci } = require('chrome'); const { descriptor, Sandbox, evaluate, main, resolveURI } = require('toolkit/loader'); const { once } = require('../system/events'); -const { exit, env, staticArgs, name } = require('../system'); +const { exit, env, staticArgs } = require('../system'); const { when: unload } = require('../system/unload'); const { loadReason } = require('../self'); const { rootURI } = require("@loader/options"); const globals = require('../system/globals'); +const xulApp = require('../system/xul-app'); const appShellService = Cc['@mozilla.org/appshell/appShellService;1']. getService(Ci.nsIAppShellService); @@ -23,13 +24,20 @@ const NAME2TOPIC = { 'Firefox': 'sessionstore-windows-restored', 'Fennec': 'sessionstore-windows-restored', 'SeaMonkey': 'sessionstore-windows-restored', - 'Thunderbird': 'mail-startup-done', - '*': 'final-ui-startup' + 'Thunderbird': 'mail-startup-done' }; +// Set 'final-ui-startup' as default topic for unknown applications +let appStartup = 'final-ui-startup'; + // Gets the topic that fit best as application startup event, in according with // the current application (e.g. Firefox, Fennec, Thunderbird...) -const APP_STARTUP = NAME2TOPIC[name] || NAME2TOPIC['*']; +for (let name of Object.keys(NAME2TOPIC)) { + if (xulApp.is(name)) { + appStartup = NAME2TOPIC[name]; + break; + } +} // Initializes default preferences function setDefaultPrefs(prefsURI) { @@ -66,7 +74,7 @@ function definePseudo(loader, id, exports) { } function wait(reason, options) { - once(APP_STARTUP, function() { + once(appStartup, function() { startup(null, options); }); } @@ -145,10 +153,10 @@ function run(options) { program.main({ loadReason: loadReason, - staticArgs: staticArgs - }, { + staticArgs: staticArgs + }, { print: function print(_) { dump(_ + '\n') }, - quit: exit + quit: exit }); } } catch (error) { diff --git a/addon-sdk/source/lib/sdk/deprecated/tab-browser.js b/addon-sdk/source/lib/sdk/deprecated/tab-browser.js index 738ad88941c..a9c8da828d3 100644 --- a/addon-sdk/source/lib/sdk/deprecated/tab-browser.js +++ b/addon-sdk/source/lib/sdk/deprecated/tab-browser.js @@ -566,7 +566,7 @@ ModuleTabTracker.prototype = { _TAB_EVENTS: ["TabOpen", "TabClose", "TabSelect", "DOMContentLoaded", "load", "MozAfterPaint"], _safeTrackTab: function safeTrackTab(tab) { - tab.addEventListener("load", this, false); + tab.linkedBrowser.addEventListener("load", this, true); tab.linkedBrowser.addEventListener("MozAfterPaint", this, false); this._tabs.push(tab); try { @@ -576,7 +576,7 @@ ModuleTabTracker.prototype = { } }, _safeUntrackTab: function safeUntrackTab(tab) { - tab.removeEventListener("load", this, false); + tab.linkedBrowser.removeEventListener("load", this, true); tab.linkedBrowser.removeEventListener("MozAfterPaint", this, false); var index = this._tabs.indexOf(tab); if (index == -1) @@ -617,7 +617,11 @@ ModuleTabTracker.prototype = { } }, _safeLoad: function safeLoad(event) { - let tab = event.target; + let win = event.currentTarget.ownerDocument.defaultView; + let tabIndex = win.gBrowser.getBrowserIndexForDocument(event.target); + if (tabIndex == -1) + return; + let tab = win.gBrowser.tabContainer.getItemAtIndex(tabIndex); let index = this._tabs.indexOf(tab); if (index == -1) console.error("internal error: tab not found"); diff --git a/addon-sdk/source/lib/sdk/deprecated/window-utils.js b/addon-sdk/source/lib/sdk/deprecated/window-utils.js index 554c1513d8e..78758ecea1c 100644 --- a/addon-sdk/source/lib/sdk/deprecated/window-utils.js +++ b/addon-sdk/source/lib/sdk/deprecated/window-utils.js @@ -11,6 +11,7 @@ const { Cc, Ci } = require('chrome'); const { EventEmitter } = require('../deprecated/events'); const { Trait } = require('../deprecated/traits'); const { when } = require('../system/unload'); +const events = require('../system/events'); const { getInnerId, getOuterId, windows, isDocumentLoaded, isBrowser, getMostRecentBrowserWindow, getMostRecentWindow } = require('../window/utils'); const errors = require('../deprecated/errors'); @@ -68,6 +69,8 @@ function WindowTracker(delegate) { for each (let window in getWindows()) this._regWindow(window); windowWatcher.registerNotification(this); + this._onToplevelWindowReady = this._onToplevelWindowReady.bind(this); + events.on('toplevel-window-ready', this._onToplevelWindowReady); require('../system/unload').ensure(this); @@ -116,6 +119,7 @@ WindowTracker.prototype = { unload: function unload() { windowWatcher.unregisterNotification(this); + events.off('toplevel-window-ready', this._onToplevelWindowReady); for each (let window in getWindows()) this._unregWindow(window); }, @@ -128,14 +132,20 @@ WindowTracker.prototype = { } }), + _onToplevelWindowReady: function _onToplevelWindowReady({subject}) { + let window = subject; + // ignore private windows if they are not supported + if (ignoreWindow(window)) + return; + this._regWindow(window); + }, + observe: errors.catchAndLog(function observe(subject, topic, data) { var window = subject.QueryInterface(Ci.nsIDOMWindow); // ignore private windows if they are not supported if (ignoreWindow(window)) return; - if (topic == 'domwindowopened') - this._regWindow(window); - else + if (topic == 'domwindowclosed') this._unregWindow(window); }) }; diff --git a/addon-sdk/source/lib/sdk/panel.js b/addon-sdk/source/lib/sdk/panel.js index 68be5673573..bde1b7ea530 100644 --- a/addon-sdk/source/lib/sdk/panel.js +++ b/addon-sdk/source/lib/sdk/panel.js @@ -19,7 +19,8 @@ const { isPrivateBrowsingSupported } = require('./self'); const { isWindowPBSupported } = require('./private-browsing/utils'); const { Class } = require("./core/heritage"); const { merge } = require("./util/object"); -const { WorkerHost, Worker, detach, attach } = require("./worker/utils"); +const { WorkerHost, Worker, detach, attach, + requiresAddonGlobal } = require("./worker/utils"); const { Disposable } = require("./core/disposable"); const { contract: loaderContract } = require("./content/loader"); const { contract } = require("./util/contract"); @@ -32,24 +33,6 @@ const { filter, pipe } = require("./event/utils"); const { getNodeView, getActiveView } = require("./view/core"); const { isNil, isObject } = require("./lang/type"); -let isArray = Array.isArray; -let assetsURI = require("./self").data.url(); - -function isAddonContent({ contentURL }) { - return typeof(contentURL) === "string" && contentURL.indexOf(assetsURI) === 0; -} - -function hasContentScript({ contentScript, contentScriptFile }) { - return (isArray(contentScript) ? contentScript.length > 0 : - !!contentScript) || - (isArray(contentScriptFile) ? contentScriptFile.length > 0 : - !!contentScriptFile); -} - -function requiresAddonGlobal(model) { - return isAddonContent(model) && !hasContentScript(model); -} - function getAttachEventType(model) { let when = model.contentScriptWhen; return requiresAddonGlobal(model) ? "sdk-panel-content-changed" : diff --git a/addon-sdk/source/lib/sdk/request.js b/addon-sdk/source/lib/sdk/request.js index 514631f0384..28cced413c8 100644 --- a/addon-sdk/source/lib/sdk/request.js +++ b/addon-sdk/source/lib/sdk/request.js @@ -50,12 +50,14 @@ const { validateOptions, validateSingleOption } = new OptionsValidator({ const REUSE_ERROR = "This request object has been used already. You must " + "create a new one to make a new request." -// Utility function to prep the request since it's the same between GET and -// POST +// Utility function to prep the request since it's the same between +// request types function runRequest(mode, target) { let source = request(target) let { xhr, url, content, contentType, headers, overrideMimeType } = source; + let isGetOrHead = (mode == "GET" || mode == "HEAD"); + // If this request has already been used, then we can't reuse it. // Throw an error. if (xhr) @@ -63,11 +65,11 @@ function runRequest(mode, target) { xhr = source.xhr = new XMLHttpRequest(); - // Build the data to be set. For GET requests, we want to append that to - // the URL before opening the request. + // Build the data to be set. For GET or HEAD requests, we want to append that + // to the URL before opening the request. let data = stringify(content); // If the URL already has ? in it, then we want to just use & - if (mode == "GET" && data) + if (isGetOrHead && data) url = url + (/\?/.test(url) ? "&" : "?") + data; // open the request @@ -97,8 +99,8 @@ function runRequest(mode, target) { }; // actually send the request. - // We don't want to send data on GET requests. - xhr.send(mode !== "GET" ? data : null); + // We don't want to send data on GET or HEAD requests. + xhr.send(!isGetOrHead ? data : null); } const Request = Class({ @@ -138,6 +140,10 @@ const Request = Class({ put: function() { runRequest('PUT', this); return this; + }, + head: function() { + runRequest('HEAD', this); + return this; } }); exports.Request = Request; diff --git a/addon-sdk/source/lib/sdk/url.js b/addon-sdk/source/lib/sdk/url.js index de3caf2bf9b..87bca368697 100644 --- a/addon-sdk/source/lib/sdk/url.js +++ b/addon-sdk/source/lib/sdk/url.js @@ -12,6 +12,8 @@ const { Cc, Ci, Cr } = require("chrome"); const { Class } = require("./core/heritage"); const base64 = require("./base64"); +var tlds = Cc["@mozilla.org/network/effective-tld-service;1"] + .getService(Ci.nsIEffectiveTLDService); var ios = Cc['@mozilla.org/network/io-service;1'] .getService(Ci.nsIIOService); @@ -19,6 +21,9 @@ var ios = Cc['@mozilla.org/network/io-service;1'] var resProt = ios.getProtocolHandler("resource") .QueryInterface(Ci.nsIResProtocolHandler); +var URLParser = Cc["@mozilla.org/network/url-parser;1?auth=no"] + .getService(Ci.nsIURLParser); + function newURI(uriStr, base) { try { let baseURI = base ? ios.newURI(base, null, null) : null; @@ -92,11 +97,29 @@ function URL(url, base) { port = uri.port == -1 ? null : uri.port; } catch (e if e.result == Cr.NS_ERROR_FAILURE) {} + let uriData = [uri.path, uri.path.length, {}, {}, {}, {}, {}, {}]; + URLParser.parsePath.apply(URLParser, uriData); + let [{ value: filepathPos }, { value: filepathLen }, + { value: queryPos }, { value: queryLen }, + { value: refPos }, { value: refLen }] = uriData.slice(2); + + let hash = uri.ref ? "#" + uri.ref : ""; + let pathname = uri.path.substr(filepathPos, filepathLen); + let search = uri.path.substr(queryPos, queryLen); + search = search ? "?" + search : ""; + this.__defineGetter__("scheme", function() uri.scheme); this.__defineGetter__("userPass", function() userPass); this.__defineGetter__("host", function() host); + this.__defineGetter__("hostname", function() host); this.__defineGetter__("port", function() port); this.__defineGetter__("path", function() uri.path); + this.__defineGetter__("pathname", function() pathname); + this.__defineGetter__("hash", function() hash); + this.__defineGetter__("href", function() uri.spec); + this.__defineGetter__("origin", function() uri.prePath); + this.__defineGetter__("protocol", function() uri.scheme + ":"); + this.__defineGetter__("search", function() search); Object.defineProperties(this, { toString: { @@ -235,6 +258,17 @@ const DataURL = Class({ exports.DataURL = DataURL; +let getTLD = exports.getTLD = function getTLD (url) { + let uri = newURI(url.toString()); + let tld = null; + try { + tld = tlds.getPublicSuffix(uri); + } catch (e if + e.result == Cr.NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS || + e.result == Cr.NS_ERROR_HOST_IS_IP_ADDRESS) {} + return tld; +}; + let isValidURI = exports.isValidURI = function (uri) { try { newURI(uri); diff --git a/addon-sdk/source/lib/sdk/worker/utils.js b/addon-sdk/source/lib/sdk/worker/utils.js index a7d46767ef2..26052aa690b 100644 --- a/addon-sdk/source/lib/sdk/worker/utils.js +++ b/addon-sdk/source/lib/sdk/worker/utils.js @@ -17,10 +17,34 @@ const { Loader } = require("../content/loader"); const { merge } = require("../util/object"); const { emit } = require("../event/core"); -const LegacyWorker = WorkerTrait.resolve({ +let assetsURI = require("../self").data.url(); +let isArray = Array.isArray; + +function isAddonContent({ contentURL }) { + return typeof(contentURL) === "string" && contentURL.indexOf(assetsURI) === 0; +} + +function hasContentScript({ contentScript, contentScriptFile }) { + return (isArray(contentScript) ? contentScript.length > 0 : + !!contentScript) || + (isArray(contentScriptFile) ? contentScriptFile.length > 0 : + !!contentScriptFile); +} + +function requiresAddonGlobal(model) { + return isAddonContent(model) && !hasContentScript(model); +} +exports.requiresAddonGlobal = requiresAddonGlobal; + + +const LegacyWorker = WorkerTrait.compose(Loader).resolve({ _setListeners: "__setListeners", -}).compose(Loader, { + _injectInDocument: "__injectInDocument", + contentURL: "__contentURL" +}).compose({ _setListeners: function() {}, + get contentURL() this._window.document.URL, + get _injectInDocument() requiresAddonGlobal(this), attach: function(window) this._attach(window), detach: function() this._workerCleanup() }); diff --git a/addon-sdk/source/test/addons/private-browsing-supported/test-selection.js b/addon-sdk/source/test/addons/private-browsing-supported/test-selection.js index 51e18bd58a2..5bf033a002a 100644 --- a/addon-sdk/source/test/addons/private-browsing-supported/test-selection.js +++ b/addon-sdk/source/test/addons/private-browsing-supported/test-selection.js @@ -49,8 +49,8 @@ function open(url, options) { let tab = getActiveTab(chromeWindow); - tab.addEventListener("load", function ready(event) { - let { document } = getTabContentWindow(this); + tab.linkedBrowser.addEventListener("load", function ready(event) { + let { document } = getTabContentWindow(tab); if (document.readyState === "complete" && document.URL === url) { this.removeEventListener(event.type, ready); @@ -60,7 +60,7 @@ function open(url, options) { resolve(document.defaultView); } - }) + }, true); setTabURL(tab, url); }); diff --git a/addon-sdk/source/test/addons/privileged-panel/data/index.html b/addon-sdk/source/test/addons/privileged-panel/data/index.html new file mode 100644 index 00000000000..f37c96b2eb3 --- /dev/null +++ b/addon-sdk/source/test/addons/privileged-panel/data/index.html @@ -0,0 +1,3 @@ + diff --git a/addon-sdk/source/test/addons/privileged-panel/main.js b/addon-sdk/source/test/addons/privileged-panel/main.js new file mode 100644 index 00000000000..c010de9242b --- /dev/null +++ b/addon-sdk/source/test/addons/privileged-panel/main.js @@ -0,0 +1,26 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +const { Panel } = require("sdk/panel") +const { data } = require("sdk/self") + +exports["test addon global"] = function(assert, done) { + let panel = Panel({ + contentURL: //"data:text/html,now?", + data.url("./index.html"), + onMessage: function(message) { + assert.pass("got message from panel script"); + panel.destroy(); + done(); + }, + onError: function(error) { + asser.fail(Error("failed to recieve message")); + done(); + } + }); +}; + +require("sdk/test/runner").runTestsFromModule(module); diff --git a/addon-sdk/source/test/addons/privileged-panel/package.json b/addon-sdk/source/test/addons/privileged-panel/package.json new file mode 100644 index 00000000000..e35c3a0041a --- /dev/null +++ b/addon-sdk/source/test/addons/privileged-panel/package.json @@ -0,0 +1,3 @@ +{ + "id": "test-privileged-addon" +} diff --git a/addon-sdk/source/test/test-request.js b/addon-sdk/source/test/test-request.js index 9b5ed3b3279..b5751b8bb71 100644 --- a/addon-sdk/source/test/test-request.js +++ b/addon-sdk/source/test/test-request.js @@ -218,6 +218,26 @@ exports.testInvalidJSON = function (test) { }); } +exports.testHead = function (test) { + let srv = startServerAsync(port, basePath); + + srv.registerPathHandler("/test-head", + function handle(request, response) { + response.setHeader("Content-Type", "text/plain", false); + }); + + test.waitUntilDone(); + Request({ + url: "http://localhost:" + port + "/test-head", + onComplete: function (response) { + test.assertEqual(response.text, ""); + test.assertEqual(response.statusText, "OK"); + test.assertEqual(response.headers["Content-Type"], "text/plain"); + srv.stop(function() test.done()); + } + }).head(); +} + function runMultipleURLs (srv, test, options) { let urls = [options.url, URL(options.url)]; let cb = options.onComplete; diff --git a/addon-sdk/source/test/test-selection.js b/addon-sdk/source/test/test-selection.js index 4adcc74c15e..3127664711c 100644 --- a/addon-sdk/source/test/test-selection.js +++ b/addon-sdk/source/test/test-selection.js @@ -50,15 +50,15 @@ function open(url, options) { let tab = getActiveTab(chromeWindow); - tab.addEventListener("load", function ready(event) { - let { document } = getTabContentWindow(this); + tab.linkedBrowser.addEventListener("load", function ready(event) { + let { document } = getTabContentWindow(tab); if (document.readyState === "complete" && document.URL === url) { this.removeEventListener(event.type, ready); resolve(document.defaultView); } - }) + }, true); setTabURL(tab, url); }); diff --git a/addon-sdk/source/test/test-url.js b/addon-sdk/source/test/test-url.js index 6f7a2398bcd..de175240dca 100644 --- a/addon-sdk/source/test/test-url.js +++ b/addon-sdk/source/test/test-url.js @@ -3,6 +3,13 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var url = require("sdk/url"); +var { Loader } = require("sdk/test/loader"); +var { pathFor } = require("sdk/system"); +var file = require("sdk/io/file"); +var loader = Loader(module); +var httpd = loader.require("sdk/test/httpd"); +var port = 8099; +var tabs = require("sdk/tabs"); exports.testResolve = function(test) { test.assertEqual(url.URL("bar", "http://www.foo.com/").toString(), @@ -33,12 +40,49 @@ exports.testResolve = function(test) { }; exports.testParseHttp = function(test) { - var info = url.URL("http://foo.com/bar"); + var aUrl = "http://sub.foo.com/bar?locale=en-US&otherArg=%20x%20#myhash"; + var info = url.URL(aUrl); test.assertEqual(info.scheme, "http"); - test.assertEqual(info.host, "foo.com"); + test.assertEqual(info.protocol, "http:"); + test.assertEqual(info.host, "sub.foo.com"); + test.assertEqual(info.hostname, "sub.foo.com"); test.assertEqual(info.port, null); test.assertEqual(info.userPass, null); - test.assertEqual(info.path, "/bar"); + test.assertEqual(info.path, "/bar?locale=en-US&otherArg=%20x%20#myhash"); + test.assertEqual(info.pathname, "/bar"); + test.assertEqual(info.href, aUrl); + test.assertEqual(info.hash, "#myhash"); + test.assertEqual(info.search, "?locale=en-US&otherArg=%20x%20"); +}; + +exports.testParseHttpSearchAndHash = function (test) { + var info = url.URL("https://www.moz.com/some/page.html"); + test.assertEqual(info.hash, ""); + test.assertEqual(info.search, ""); + + var hashOnly = url.URL("https://www.sub.moz.com/page.html#justhash"); + test.assertEqual(hashOnly.search, ""); + test.assertEqual(hashOnly.hash, "#justhash"); + + var queryOnly = url.URL("https://www.sub.moz.com/page.html?my=query"); + test.assertEqual(queryOnly.search, "?my=query"); + test.assertEqual(queryOnly.hash, ""); + + var qMark = url.URL("http://www.moz.org?"); + test.assertEqual(qMark.search, ""); + test.assertEqual(qMark.hash, ""); + + var hash = url.URL("http://www.moz.org#"); + test.assertEqual(hash.search, ""); + test.assertEqual(hash.hash, ""); + + var empty = url.URL("http://www.moz.org?#"); + test.assertEqual(hash.search, ""); + test.assertEqual(hash.hash, ""); + + var strange = url.URL("http://moz.org?test1#test2?test3"); + test.assertEqual(strange.search, "?test1"); + test.assertEqual(strange.hash, "#test2?test3"); }; exports.testParseHttpWithPort = function(test) { @@ -163,10 +207,12 @@ exports.testStringInterface = function(test) { var a = URL(EM); // make sure the standard URL properties are enumerable and not the String interface bits - test.assertEqual(Object.keys(a), "scheme,userPass,host,port,path", "enumerable key list check for URL."); + test.assertEqual(Object.keys(a), + "scheme,userPass,host,hostname,port,path,pathname,hash,href,origin,protocol,search", + "enumerable key list check for URL."); test.assertEqual( JSON.stringify(a), - "{\"scheme\":\"about\",\"userPass\":null,\"host\":null,\"port\":null,\"path\":\"addons\"}", + "{\"scheme\":\"about\",\"userPass\":null,\"host\":null,\"hostname\":null,\"port\":null,\"path\":\"addons\",\"pathname\":\"addons\",\"hash\":\"\",\"href\":\"about:addons\",\"origin\":\"about:\",\"protocol\":\"about:\",\"search\":\"\"}", "JSON.stringify should return a object with correct props and vals."); // make sure that the String interface exists and works as expected @@ -261,6 +307,53 @@ exports.testURLFromURL = function(test) { test.assertEqual(aURL.toString(), bURL.toString(), 'Making a URL from a URL works'); }; +exports.testTLD = function(test) { + let urls = [ + { url: 'http://my.sub.domains.mozilla.co.uk', tld: 'co.uk' }, + { url: 'http://my.mozilla.com', tld: 'com' }, + { url: 'http://my.domains.mozilla.org.hk', tld: 'org.hk' }, + { url: 'chrome://global/content/blah', tld: 'global' }, + { url: 'data:text/plain;base64,QXdlc29tZSE=', tld: null }, + { url: 'https://1.2.3.4', tld: null } + ]; + + urls.forEach(function (uri) { + test.assertEqual(url.getTLD(uri.url), uri.tld); + test.assertEqual(url.getTLD(url.URL(uri.url)), uri.tld); + }); +} + +exports.testWindowLocationMatch = function (test) { + let srv = serve(); + let aUrl = 'http://localhost:' + port + '/index.html?q=aQuery#somehash'; + let urlObject = url.URL(aUrl); + test.waitUntilDone(); + + tabs.open({ + url: aUrl, + onReady: function (tab) { + tab.attach({ + onMessage: function (loc) { + for (let prop in loc) { + test.assertEqual(urlObject[prop], loc[prop], prop + ' matches'); + } + tab.close(); + srv.stop(test.done.bind(test)); + }, + contentScript: '(' + function () { + let res = {}; + // `origin` is `null` in this context??? + let props = 'hostname,port,pathname,hash,href,protocol,search'.split(','); + props.forEach(function (prop) { + res[prop] = window.location[prop]; + }); + self.postMessage(res); + } + ')()' + }); + } + }) +}; + function validURIs() { return [ 'http://foo.com/blah_blah', @@ -352,3 +445,16 @@ function invalidURIs () { // 'http://10.1.1.254' ]; } + +function serve () { + let basePath = pathFor("ProfD"); + let filePath = file.join(basePath, 'index.html'); + let content = "

url tests

"; + let fileStream = file.open(filePath, 'w'); + fileStream.write(content); + fileStream.close(); + + let srv = httpd.startServerAsync(port, basePath); + return srv; +} + From d1662d421899f00cc83ac27323a38590bf58c6ff Mon Sep 17 00:00:00 2001 From: Sachin Hosmani Date: Fri, 24 May 2013 09:37:17 -0700 Subject: [PATCH 23/91] Bug 590347 - Expose the softblock notification in preference to the incompatible notification when compatibility checking is disabled. r=Mossop --- .../mozapps/extensions/content/extensions.js | 3 +- .../mozapps/extensions/content/extensions.xml | 3 +- .../extensions/test/browser/Makefile.in | 1 + .../test/browser/browser_bug590347.js | 120 ++++++++++++++++++ 4 files changed, 125 insertions(+), 2 deletions(-) create mode 100644 toolkit/mozapps/extensions/test/browser/browser_bug590347.js diff --git a/toolkit/mozapps/extensions/content/extensions.js b/toolkit/mozapps/extensions/content/extensions.js index 425f8ad1b4e..41c23c21615 100644 --- a/toolkit/mozapps/extensions/content/extensions.js +++ b/toolkit/mozapps/extensions/content/extensions.js @@ -2845,7 +2845,8 @@ var gDetailView = { errorLink.value = gStrings.ext.GetStringFromName("details.notification.blocked.link"); errorLink.href = this._addon.blocklistURL; errorLink.hidden = false; - } else if (!this._addon.isCompatible) { + } else if (!this._addon.isCompatible && (AddonManager.checkCompatibility || + (this._addon.blocklistState != Ci.nsIBlocklistService.STATE_SOFTBLOCKED))) { this.node.setAttribute("notification", "warning"); document.getElementById("detail-warning").textContent = gStrings.ext.formatStringFromName( "details.notification.incompatible", diff --git a/toolkit/mozapps/extensions/content/extensions.xml b/toolkit/mozapps/extensions/content/extensions.xml index 7c94545d722..92871ec5c70 100644 --- a/toolkit/mozapps/extensions/content/extensions.xml +++ b/toolkit/mozapps/extensions/content/extensions.xml @@ -1223,7 +1223,8 @@ this._errorLink.value = gStrings.ext.GetStringFromName("notification.blocked.link"); this._errorLink.href = this.mAddon.blocklistURL; this._errorLink.hidden = false; - } else if (!isUpgrade && !this.mAddon.isCompatible) { + } else if ((!isUpgrade && !this.mAddon.isCompatible) && (AddonManager.checkCompatibility + || (this.mAddon.blocklistState != Ci.nsIBlocklistService.STATE_SOFTBLOCKED))) { this.setAttribute("notification", "warning"); this._warning.textContent = gStrings.ext.formatStringFromName( "notification.incompatible", diff --git a/toolkit/mozapps/extensions/test/browser/Makefile.in b/toolkit/mozapps/extensions/test/browser/Makefile.in index 5ddc09537ec..8206197ebc7 100644 --- a/toolkit/mozapps/extensions/test/browser/Makefile.in +++ b/toolkit/mozapps/extensions/test/browser/Makefile.in @@ -40,6 +40,7 @@ MOCHITEST_BROWSER_MAIN = \ browser_bug618502.js \ browser_bug679604.js \ browser_bug714593.js \ + browser_bug590347.js \ browser_details.js \ browser_discovery.js \ browser_dragdrop.js \ diff --git a/toolkit/mozapps/extensions/test/browser/browser_bug590347.js b/toolkit/mozapps/extensions/test/browser/browser_bug590347.js new file mode 100644 index 00000000000..8fe9c715e59 --- /dev/null +++ b/toolkit/mozapps/extensions/test/browser/browser_bug590347.js @@ -0,0 +1,120 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +// Bug 590347 +// Tests if softblock notifications are exposed in preference to incompatible +// notifications when compatibility checking is disabled + +var gProvider; +var gManagerWindow; +var gCategoryUtilities; + +var gApp = document.getElementById("bundle_brand").getString("brandShortName"); +var gVersion = Services.appinfo.version; + +// Opens the details view of an add-on +function open_details(aId, aType, aCallback) { + requestLongerTimeout(2); + + gCategoryUtilities.openType(aType, function() { + var list = gManagerWindow.document.getElementById("addon-list"); + var item = list.firstChild; + while (item) { + if ("mAddon" in item && item.mAddon.id == aId) { + list.ensureElementIsVisible(item); + EventUtils.synthesizeMouseAtCenter(item, { clickCount: 1 }, gManagerWindow); + EventUtils.synthesizeMouseAtCenter(item, { clickCount: 2 }, gManagerWindow); + wait_for_view_load(gManagerWindow, aCallback); + return; + } + item = item.nextSibling; + } + ok(false, "Should have found the add-on in the list"); + }); +} + +function get_list_view_warning_node() { + let item = gManagerWindow.document.getElementById("addon-list").firstChild; + let found = false; + while (item) { + if (item.mAddon.name == "Test add-on") { + found = true; + break; + } + item = item.nextSibling; + } + ok(found, "Test add-on node should have been found."); + return item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "warning"); +} + +function get_detail_view_warning_node(aManagerWindow) { + if(aManagerWindow) + return aManagerWindow.document.getElementById("detail-warning"); +} + +function test() { + waitForExplicitFinish(); + + gProvider = new MockProvider(); + + gProvider.createAddons([{ + id: "addon1@tests.mozilla.org", + name: "Test add-on", + description: "A test add-on", + isCompatible: false, + blocklistState: Ci.nsIBlocklistService.STATE_SOFTBLOCKED, + }]); + + open_manager(null, function(aWindow) { + gManagerWindow = aWindow; + gCategoryUtilities = new CategoryUtilities(gManagerWindow); + run_next_test(); + }); +} + +function end_test() { + close_manager(gManagerWindow, function() { + finish(); + }); +} + +// Check with compatibility checking enabled +add_test(function() { + gCategoryUtilities.openType("extension", function() { + Services.prefs.setBoolPref(PREF_CHECK_COMPATIBILITY, true); + let warning_node = get_list_view_warning_node(); + is_element_visible(warning_node, "Warning message should be visible"); + is(warning_node.textContent, "Test add-on is incompatible with " + gApp + " " + gVersion + ".", "Warning message should be correct"); + run_next_test(); + }); +}); + +add_test(function() { + open_details("addon1@tests.mozilla.org", "extension", function() { + let warning_node = get_detail_view_warning_node(gManagerWindow); + is_element_visible(warning_node, "Warning message should be visible"); + is(warning_node.textContent, "Test add-on is incompatible with " + gApp + " " + gVersion + ".", "Warning message should be correct"); + Services.prefs.setBoolPref(PREF_CHECK_COMPATIBILITY, false); + run_next_test(); + }); +}); + +// Check with compatibility checking disabled +add_test(function() { + gCategoryUtilities.openType("extension", function() { + let warning_node = get_list_view_warning_node(); + is_element_visible(warning_node, "Warning message should be visible"); + is(warning_node.textContent, "Test add-on is known to cause security or stability issues.", "Warning message should be correct"); + run_next_test(); + }); +}); + +add_test(function() { + open_details("addon1@tests.mozilla.org", "extension", function() { + let warning_node = get_detail_view_warning_node(gManagerWindow); + is_element_visible(warning_node, "Warning message should be visible"); + is(warning_node.textContent, "Test add-on is known to cause security or stability issues.", "Warning message should be correct"); + run_next_test(); + }); +}); From be5a9d6aefc789cf94cd68507415958bfd833f63 Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Fri, 24 May 2013 12:53:41 -0400 Subject: [PATCH 24/91] Bug 875402 - Do the work for initializing a MediaInputPort even if the message is handled during graph shutdown; r=roc --- content/media/MediaStreamGraph.cpp | 7 +++--- content/media/test/crashtests/875402.html | 24 +++++++++++++++++++ content/media/test/crashtests/crashtests.list | 1 + 3 files changed, 29 insertions(+), 3 deletions(-) create mode 100644 content/media/test/crashtests/875402.html diff --git a/content/media/MediaStreamGraph.cpp b/content/media/MediaStreamGraph.cpp index 06ba543a9c6..bfc6520b107 100644 --- a/content/media/MediaStreamGraph.cpp +++ b/content/media/MediaStreamGraph.cpp @@ -1885,9 +1885,6 @@ MediaInputPort::Destroy() { Run(); } - // This does not need to be strongly referenced; the graph is holding - // a strong reference to the port, which we will remove. This will be the - // last message for the port. MediaInputPort* mPort; }; GraphImpl()->AppendMessage(new Message(this)); @@ -1929,6 +1926,10 @@ ProcessedMediaStream::AllocateInputPort(MediaStream* aStream, uint32_t aFlags, // The graph holds its reference implicitly mPort.forget(); } + virtual void RunDuringShutdown() + { + Run(); + } nsRefPtr mPort; }; nsRefPtr port = new MediaInputPort(aStream, this, aFlags, diff --git a/content/media/test/crashtests/875402.html b/content/media/test/crashtests/875402.html new file mode 100644 index 00000000000..d641bc2b58e --- /dev/null +++ b/content/media/test/crashtests/875402.html @@ -0,0 +1,24 @@ + \ No newline at end of file diff --git a/content/media/test/crashtests/crashtests.list b/content/media/test/crashtests/crashtests.list index 9918dbcf4e7..281f257de76 100644 --- a/content/media/test/crashtests/crashtests.list +++ b/content/media/test/crashtests/crashtests.list @@ -20,3 +20,4 @@ load 874915.html load 874934.html load 874952.html load 875144.html +load 875402.html From dcc3e40184f415ac37f6db83894aa606fbbacfd3 Mon Sep 17 00:00:00 2001 From: Jonathan Watt Date: Fri, 24 May 2013 17:56:26 +0100 Subject: [PATCH 25/91] Bug 378923 - Make non-root outer- respect |overflow:visible| (overflow="visible"). r=roc --- .../svg/overflow-on-outer-svg-01-ref.svg | 7 +++++ .../reftests/svg/overflow-on-outer-svg-01.svg | 11 ++++++++ .../svg/overflow-on-outer-svg-02-ref.xhtml | 10 +++++++ .../svg/overflow-on-outer-svg-02a.xhtml | 16 +++++++++++ .../svg/overflow-on-outer-svg-02b.xhtml | 16 +++++++++++ .../svg/overflow-on-outer-svg-02c.xhtml | 16 +++++++++++ .../svg/overflow-on-outer-svg-02d.xhtml | 28 +++++++++++++++++++ .../svg/overflow-on-outer-svg-03-ref.xhtml | 10 +++++++ .../svg/overflow-on-outer-svg-03a.xhtml | 16 +++++++++++ .../svg/overflow-on-outer-svg-03b.xhtml | 28 +++++++++++++++++++ layout/reftests/svg/reftest.list | 7 +++++ layout/svg/nsSVGOuterSVGFrame.cpp | 8 +++++- 12 files changed, 172 insertions(+), 1 deletion(-) create mode 100644 layout/reftests/svg/overflow-on-outer-svg-01-ref.svg create mode 100644 layout/reftests/svg/overflow-on-outer-svg-01.svg create mode 100644 layout/reftests/svg/overflow-on-outer-svg-02-ref.xhtml create mode 100644 layout/reftests/svg/overflow-on-outer-svg-02a.xhtml create mode 100644 layout/reftests/svg/overflow-on-outer-svg-02b.xhtml create mode 100644 layout/reftests/svg/overflow-on-outer-svg-02c.xhtml create mode 100644 layout/reftests/svg/overflow-on-outer-svg-02d.xhtml create mode 100644 layout/reftests/svg/overflow-on-outer-svg-03-ref.xhtml create mode 100644 layout/reftests/svg/overflow-on-outer-svg-03a.xhtml create mode 100644 layout/reftests/svg/overflow-on-outer-svg-03b.xhtml diff --git a/layout/reftests/svg/overflow-on-outer-svg-01-ref.svg b/layout/reftests/svg/overflow-on-outer-svg-01-ref.svg new file mode 100644 index 00000000000..a3908ab1124 --- /dev/null +++ b/layout/reftests/svg/overflow-on-outer-svg-01-ref.svg @@ -0,0 +1,7 @@ + + + + diff --git a/layout/reftests/svg/overflow-on-outer-svg-01.svg b/layout/reftests/svg/overflow-on-outer-svg-01.svg new file mode 100644 index 00000000000..f18b8595b07 --- /dev/null +++ b/layout/reftests/svg/overflow-on-outer-svg-01.svg @@ -0,0 +1,11 @@ + + + Test that we always clip root-<svg> regardless of 'overflow' + + + diff --git a/layout/reftests/svg/overflow-on-outer-svg-02-ref.xhtml b/layout/reftests/svg/overflow-on-outer-svg-02-ref.xhtml new file mode 100644 index 00000000000..96f8aa1250a --- /dev/null +++ b/layout/reftests/svg/overflow-on-outer-svg-02-ref.xhtml @@ -0,0 +1,10 @@ + + + + +
+ + diff --git a/layout/reftests/svg/overflow-on-outer-svg-02a.xhtml b/layout/reftests/svg/overflow-on-outer-svg-02a.xhtml new file mode 100644 index 00000000000..aadf9b54306 --- /dev/null +++ b/layout/reftests/svg/overflow-on-outer-svg-02a.xhtml @@ -0,0 +1,16 @@ + + + + + Test that non-root outer-<svg> clips for overflow="auto" + + + + + + + diff --git a/layout/reftests/svg/overflow-on-outer-svg-02b.xhtml b/layout/reftests/svg/overflow-on-outer-svg-02b.xhtml new file mode 100644 index 00000000000..48ae9dd5713 --- /dev/null +++ b/layout/reftests/svg/overflow-on-outer-svg-02b.xhtml @@ -0,0 +1,16 @@ + + + + + Test that non-root outer-<svg> clips for overflow="scroll" + + + + + + + diff --git a/layout/reftests/svg/overflow-on-outer-svg-02c.xhtml b/layout/reftests/svg/overflow-on-outer-svg-02c.xhtml new file mode 100644 index 00000000000..151f04c3049 --- /dev/null +++ b/layout/reftests/svg/overflow-on-outer-svg-02c.xhtml @@ -0,0 +1,16 @@ + + + + + Test that non-root outer-<svg> clips for overflow="hidden" + + + + + + + diff --git a/layout/reftests/svg/overflow-on-outer-svg-02d.xhtml b/layout/reftests/svg/overflow-on-outer-svg-02d.xhtml new file mode 100644 index 00000000000..02788d1e003 --- /dev/null +++ b/layout/reftests/svg/overflow-on-outer-svg-02d.xhtml @@ -0,0 +1,28 @@ + + + + + Test that non-root outer-<svg> repaints correctly changing to overflow="hidden" + + + + + + + + diff --git a/layout/reftests/svg/overflow-on-outer-svg-03-ref.xhtml b/layout/reftests/svg/overflow-on-outer-svg-03-ref.xhtml new file mode 100644 index 00000000000..9b9a433d238 --- /dev/null +++ b/layout/reftests/svg/overflow-on-outer-svg-03-ref.xhtml @@ -0,0 +1,10 @@ + + + + +
+ + diff --git a/layout/reftests/svg/overflow-on-outer-svg-03a.xhtml b/layout/reftests/svg/overflow-on-outer-svg-03a.xhtml new file mode 100644 index 00000000000..4eb948f1a6e --- /dev/null +++ b/layout/reftests/svg/overflow-on-outer-svg-03a.xhtml @@ -0,0 +1,16 @@ + + + + + Test that non-root outer-<svg> clips for overflow="auto" + + + + + + + diff --git a/layout/reftests/svg/overflow-on-outer-svg-03b.xhtml b/layout/reftests/svg/overflow-on-outer-svg-03b.xhtml new file mode 100644 index 00000000000..3c41dd97a28 --- /dev/null +++ b/layout/reftests/svg/overflow-on-outer-svg-03b.xhtml @@ -0,0 +1,28 @@ + + + + + Test that non-root outer-<svg> repaints correctly changing to overflow="visible" + + + + + + + + diff --git a/layout/reftests/svg/reftest.list b/layout/reftests/svg/reftest.list index 2e976e49d16..04b27651a28 100644 --- a/layout/reftests/svg/reftest.list +++ b/layout/reftests/svg/reftest.list @@ -216,6 +216,13 @@ skip-if(d2d) == opacity-and-gradient-02.svg opacity-and-gradient-02-ref.svg == opacity-and-pattern-01.svg pass.svg == opacity-and-transform-01.svg opacity-and-transform-01-ref.svg == outer-svg-border-and-padding-01.svg outer-svg-border-and-padding-01-ref.svg +== overflow-on-outer-svg-01.svg overflow-on-outer-svg-01-ref.svg +== overflow-on-outer-svg-02a.xhtml overflow-on-outer-svg-02-ref.xhtml +== overflow-on-outer-svg-02b.xhtml overflow-on-outer-svg-02-ref.xhtml +== overflow-on-outer-svg-02c.xhtml overflow-on-outer-svg-02-ref.xhtml +== overflow-on-outer-svg-02d.xhtml overflow-on-outer-svg-02-ref.xhtml +== overflow-on-outer-svg-03a.xhtml overflow-on-outer-svg-03-ref.xhtml +== overflow-on-outer-svg-03b.xhtml overflow-on-outer-svg-03-ref.xhtml pref(svg.paint-order.enabled,true) == paint-order-01.svg paint-order-01-ref.svg pref(svg.paint-order.enabled,true) == paint-order-02.svg paint-order-02-ref.svg pref(svg.paint-order.enabled,true) == paint-order-03.svg paint-order-03-ref.svg diff --git a/layout/svg/nsSVGOuterSVGFrame.cpp b/layout/svg/nsSVGOuterSVGFrame.cpp index da312ce6118..3b2e4cda4bf 100644 --- a/layout/svg/nsSVGOuterSVGFrame.cpp +++ b/layout/svg/nsSVGOuterSVGFrame.cpp @@ -765,7 +765,13 @@ nsSVGOuterSVGFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, DisplayBorderBackgroundOutline(aBuilder, aLists); - DisplayListClipState::AutoClipContainingBlockDescendantsToContentBox clip(aBuilder, this); + // Per-spec, we always clip root- even when 'overflow' has its initial + // value of 'visible'. See also the "visual overflow" comments in Reflow. + DisplayListClipState::AutoSaveRestore autoSR(aBuilder); + if (mIsRootContent || + StyleDisplay()->IsScrollableOverflow()) { + autoSR.ClipContainingBlockDescendantsToContentBox(aBuilder, this); + } if ((aBuilder->IsForEventDelivery() && NS_SVGDisplayListHitTestingEnabled()) || From d5daf7eeff4c1e7b4225287d1ababca3af843e52 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Fri, 24 May 2013 13:03:13 -0400 Subject: [PATCH 26/91] Bug 875628. NewProxyObject should take a handle for the private value. r=till --- dom/bindings/Codegen.py | 5 +++-- js/src/jsproxy.cpp | 23 ++++++++++++----------- js/src/jsproxy.h | 2 +- js/src/jswrapper.cpp | 5 +++-- js/src/vm/ScopeObject.cpp | 3 ++- js/xpconnect/src/XPCComponents.cpp | 7 ++++--- 6 files changed, 25 insertions(+), 20 deletions(-) diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index 4a3ff4bdc51..c725a0bd3d4 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -2024,8 +2024,9 @@ def CreateBindingJSObject(descriptor, properties, parent): else: objDecl = " JSObject *obj;\n" if descriptor.proxy: - create = """ obj = NewProxyObject(aCx, DOMProxyHandler::getInstance(), - JS::PrivateValue(aObject), proto, %s); + create = """ JS::Rooted proxyPrivateVal(aCx, JS::PrivateValue(aObject)); + obj = NewProxyObject(aCx, DOMProxyHandler::getInstance(), + proxyPrivateVal, proto, %s); if (!obj) { return nullptr; } diff --git a/js/src/jsproxy.cpp b/js/src/jsproxy.cpp index 653dabfba8d..226c4b4ac9d 100644 --- a/js/src/jsproxy.cpp +++ b/js/src/jsproxy.cpp @@ -3236,10 +3236,9 @@ JS_FRIEND_DATA(Class) js::FunctionProxyClass = { }; static JSObject * -NewProxyObject(JSContext *cx, BaseProxyHandler *handler, const Value &priv_, TaggedProto proto_, +NewProxyObject(JSContext *cx, BaseProxyHandler *handler, HandleValue priv, TaggedProto proto_, JSObject *parent_, ProxyCallable callable) { - RootedValue priv(cx, priv_); Rooted proto(cx, proto_); RootedObject parent(cx, parent_); @@ -3280,23 +3279,23 @@ NewProxyObject(JSContext *cx, BaseProxyHandler *handler, const Value &priv_, Tag } JS_FRIEND_API(JSObject *) -js::NewProxyObject(JSContext *cx, BaseProxyHandler *handler, const Value &priv_, JSObject *proto_, +js::NewProxyObject(JSContext *cx, BaseProxyHandler *handler, HandleValue priv, JSObject *proto_, JSObject *parent_, ProxyCallable callable) { - return NewProxyObject(cx, handler, priv_, TaggedProto(proto_), parent_, callable); + return NewProxyObject(cx, handler, priv, TaggedProto(proto_), parent_, callable); } static JSObject * -NewProxyObject(JSContext *cx, BaseProxyHandler *handler, const Value &priv_, JSObject *proto_, +NewProxyObject(JSContext *cx, BaseProxyHandler *handler, HandleValue priv, JSObject *proto_, JSObject *parent_, JSObject *call_, JSObject *construct_) { RootedObject call(cx, call_); RootedObject construct(cx, construct_); JS_ASSERT_IF(construct, cx->compartment == construct->compartment()); - JS_ASSERT_IF(call && cx->compartment != call->compartment(), priv_ == ObjectValue(*call)); + JS_ASSERT_IF(call && cx->compartment != call->compartment(), priv == ObjectValue(*call)); - JSObject *proxy = NewProxyObject(cx, handler, priv_, TaggedProto(proto_), parent_, + JSObject *proxy = NewProxyObject(cx, handler, priv, TaggedProto(proto_), parent_, call || construct ? ProxyIsCallable : ProxyNotCallable); if (!proxy) return NULL; @@ -3349,8 +3348,9 @@ proxy(JSContext *cx, unsigned argc, jsval *vp) if (!JSObject::getProto(cx, target, &proto)) return false; RootedObject fun(cx, target->isCallable() ? target : (JSObject *) NULL); + RootedValue priv(cx, ObjectValue(*target)); JSObject *proxy = NewProxyObject(cx, &ScriptedDirectProxyHandler::singleton, - ObjectValue(*target), proto, cx->global(), + priv, proto, cx->global(), fun, fun); if (!proxy) return false; @@ -3397,8 +3397,9 @@ proxy_create(JSContext *cx, unsigned argc, Value *vp) } if (!parent) parent = vp[0].toObject().getParent(); + RootedValue priv(cx, ObjectValue(*handler)); JSObject *proxy = NewProxyObject(cx, &ScriptedIndirectProxyHandler::singleton, - ObjectValue(*handler), proto, parent); + priv, proto, parent); if (!proxy) return false; @@ -3434,9 +3435,9 @@ proxy_createFunction(JSContext *cx, unsigned argc, Value *vp) return false; } + RootedValue priv(cx, ObjectValue(*handler)); JSObject *proxy = NewProxyObject(cx, &ScriptedIndirectProxyHandler::singleton, - ObjectValue(*handler), proto, parent, - call, construct); + priv, proto, parent, call, construct); if (!proxy) return false; diff --git a/js/src/jsproxy.h b/js/src/jsproxy.h index 399490eddc9..a35efbaf019 100644 --- a/js/src/jsproxy.h +++ b/js/src/jsproxy.h @@ -346,7 +346,7 @@ enum ProxyCallable { }; JS_FRIEND_API(JSObject *) -NewProxyObject(JSContext *cx, BaseProxyHandler *handler, const Value &priv, +NewProxyObject(JSContext *cx, BaseProxyHandler *handler, HandleValue priv, JSObject *proto, JSObject *parent, ProxyCallable callable = ProxyNotCallable); JSObject * diff --git a/js/src/jswrapper.cpp b/js/src/jswrapper.cpp index f990b7e173d..aa5633ea072 100644 --- a/js/src/jswrapper.cpp +++ b/js/src/jswrapper.cpp @@ -36,7 +36,8 @@ Wrapper::New(JSContext *cx, JSObject *obj, JSObject *proto, JSObject *parent, AutoMarkInDeadZone amd(cx->zone()); - return NewProxyObject(cx, handler, ObjectValue(*obj), proto, parent, + RootedValue priv(cx, ObjectValue(*obj)); + return NewProxyObject(cx, handler, priv, proto, parent, obj->isCallable() ? ProxyIsCallable : ProxyNotCallable); } @@ -828,7 +829,7 @@ int DeadObjectProxy::sDeadObjectFamily; JSObject * js::NewDeadProxyObject(JSContext *cx, JSObject *parent) { - return NewProxyObject(cx, &DeadObjectProxy::singleton, NullValue(), + return NewProxyObject(cx, &DeadObjectProxy::singleton, JS::NullHandleValue, NULL, parent, ProxyNotCallable); } diff --git a/js/src/vm/ScopeObject.cpp b/js/src/vm/ScopeObject.cpp index a11b0ddcf1c..61fc679e1b9 100644 --- a/js/src/vm/ScopeObject.cpp +++ b/js/src/vm/ScopeObject.cpp @@ -1527,7 +1527,8 @@ DebugScopeProxy DebugScopeProxy::singleton; DebugScopeObject::create(JSContext *cx, ScopeObject &scope, HandleObject enclosing) { JS_ASSERT(scope.compartment() == cx->compartment); - JSObject *obj = NewProxyObject(cx, &DebugScopeProxy::singleton, ObjectValue(scope), + RootedValue priv(cx, ObjectValue(scope)); + JSObject *obj = NewProxyObject(cx, &DebugScopeProxy::singleton, priv, NULL /* proto */, &scope.global(), ProxyNotCallable); if (!obj) return NULL; diff --git a/js/xpconnect/src/XPCComponents.cpp b/js/xpconnect/src/XPCComponents.cpp index 9bb68bc91aa..ead0c542ff1 100644 --- a/js/xpconnect/src/XPCComponents.cpp +++ b/js/xpconnect/src/XPCComponents.cpp @@ -3133,8 +3133,9 @@ WrapCallable(JSContext *cx, JSObject *callable, JSObject *sandboxProtoProxy) js::GetProxyHandler(sandboxProtoProxy) == &xpc::sandboxProxyHandler); + RootedValue priv(cx, ObjectValue(*callable)); return js::NewProxyObject(cx, &xpc::sandboxCallableProxyHandler, - ObjectValue(*callable), nullptr, + priv, nullptr, sandboxProtoProxy, js::ProxyIsCallable); } @@ -3358,9 +3359,9 @@ xpc_CreateSandboxObject(JSContext *cx, jsval *vp, nsISupports *prinOrSop, Sandbo mozilla::dom::IsDOMClass(Jsvalify(unwrappedClass))) { // Wrap it up in a proxy that will do the right thing in terms // of this-binding for methods. + RootedValue priv(cx, ObjectValue(*options.proto)); options.proto = js::NewProxyObject(cx, &xpc::sandboxProxyHandler, - ObjectValue(*options.proto), nullptr, - sandbox); + priv, nullptr, sandbox); if (!options.proto) return NS_ERROR_OUT_OF_MEMORY; } From e133627d072872bde23cbae5126ce6ffd339c3ea Mon Sep 17 00:00:00 2001 From: Brad Lassey Date: Fri, 24 May 2013 12:21:01 -0400 Subject: [PATCH 27/91] bug 867517 - Gecko-based WebView for Android, refactor patch r=mfinkle --- .../videoengine/VideoCaptureAndroid.java | 10 +- mobile/android/base/ANRReporter.java | 8 +- .../android/base/ActivityHandlerHelper.java | 4 +- .../android/base/AndroidImportPreference.java | 2 +- mobile/android/base/BrowserApp.java | 2 +- mobile/android/base/BrowserToolbar.java | 2 +- mobile/android/base/BrowserToolbarLayout.java | 2 +- .../base/CameraVideoResultHandler.java | 2 +- mobile/android/base/ContextGetter.java | 6 + mobile/android/base/Favicons.java | 2 +- .../android/base/FilePickerResultHandler.java | 4 +- mobile/android/base/FormAssistPopup.java | 4 +- mobile/android/base/GeckoAccessibility.java | 8 +- mobile/android/base/GeckoApp.java | 306 +++-------- mobile/android/base/GeckoAppShell.java | 473 ++++++++++++++---- mobile/android/base/GeckoEditable.java | 4 +- mobile/android/base/GeckoEvent.java | 6 +- mobile/android/base/GeckoInputConnection.java | 12 +- mobile/android/base/GeckoPreferences.java | 6 +- .../base/GeckoScreenOrientationListener.java | 22 +- mobile/android/base/GeckoSmsManager.java | 20 +- mobile/android/base/GeckoThread.java | 21 +- mobile/android/base/GeckoUpdateReceiver.java | 4 +- mobile/android/base/GlobalHistory.java | 6 +- mobile/android/base/Makefile.in | 1 + mobile/android/base/SiteIdentityPopup.java | 6 +- mobile/android/base/SuggestClient.java | 2 +- mobile/android/base/Tabs.java | 9 +- mobile/android/base/TabsPanel.java | 2 +- mobile/android/base/ThumbnailHelper.java | 6 +- mobile/android/base/WebAppAllocator.java | 2 +- .../android/base/awesomebar/AllPagesTab.java | 2 +- mobile/android/base/gfx/LayerView.java | 4 +- mobile/android/base/gfx/PluginLayer.java | 4 +- 34 files changed, 555 insertions(+), 419 deletions(-) create mode 100644 mobile/android/base/ContextGetter.java diff --git a/media/webrtc/trunk/webrtc/modules/video_capture/android/java/org/webrtc/videoengine/VideoCaptureAndroid.java b/media/webrtc/trunk/webrtc/modules/video_capture/android/java/org/webrtc/videoengine/VideoCaptureAndroid.java index f6e3ca1da70..b9f5216a49f 100644 --- a/media/webrtc/trunk/webrtc/modules/video_capture/android/java/org/webrtc/videoengine/VideoCaptureAndroid.java +++ b/media/webrtc/trunk/webrtc/modules/video_capture/android/java/org/webrtc/videoengine/VideoCaptureAndroid.java @@ -73,13 +73,13 @@ public class VideoCaptureAndroid implements PreviewCallback, Callback { captureAndroid.camera = null; captureAndroid.context = 0; - GeckoApp.mAppContext.cameraView.getHolder(). + GeckoAppShell.getGeckoInterface().getCameraView().getHolder(). removeCallback(captureAndroid); ThreadUtils.getUiHandler().post(new Runnable() { @Override public void run() { try { - GeckoApp.mAppContext.disableCameraView(); + GeckoAppShell.getGeckoInterface().disableCameraView(); } catch (Exception e) { Log.e(TAG, "VideoCaptureAndroid disableCameraView exception: " + @@ -99,12 +99,12 @@ public class VideoCaptureAndroid implements PreviewCallback, Callback { currentDevice = in_device; try { - GeckoApp.mAppContext.cameraView.getHolder().addCallback(this); + GeckoAppShell.getGeckoInterface().getCameraView().getHolder().addCallback(this); ThreadUtils.getUiHandler().post(new Runnable() { @Override public void run() { try { - GeckoApp.mAppContext.enableCameraView(); + GeckoAppShell.getGeckoInterface().enableCameraView(); } catch (Exception e) { Log.e(TAG, "VideoCaptureAndroid enableCameraView exception: " @@ -122,7 +122,7 @@ public class VideoCaptureAndroid implements PreviewCallback, Callback { android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo(); android.hardware.Camera.getCameraInfo(cameraId, info); - int rotation = GeckoApp.mAppContext.getWindowManager().getDefaultDisplay().getRotation(); + int rotation = GeckoAppShell.getGeckoInterface().getActivity().getWindowManager().getDefaultDisplay().getRotation(); int degrees = 0; switch (rotation) { case Surface.ROTATION_0: degrees = 0; break; diff --git a/mobile/android/base/ANRReporter.java b/mobile/android/base/ANRReporter.java index d1fbdc703e3..213f1fecddd 100644 --- a/mobile/android/base/ANRReporter.java +++ b/mobile/android/base/ANRReporter.java @@ -181,10 +181,10 @@ public final class ANRReporter extends BroadcastReceiver } private static File getPingFile() { - if (GeckoApp.mAppContext == null) { + if (GeckoAppShell.getContext() == null) { return null; } - GeckoProfile profile = GeckoApp.mAppContext.getProfile(); + GeckoProfile profile = GeckoAppShell.getGeckoInterface().getProfile(); if (profile == null) { return null; } @@ -263,9 +263,9 @@ public final class ANRReporter extends BroadcastReceiver private static long getTotalMem() { - if (Build.VERSION.SDK_INT >= 16 && GeckoApp.mAppContext != null) { + if (Build.VERSION.SDK_INT >= 16 && GeckoAppShell.getContext() != null) { ActivityManager am = (ActivityManager) - GeckoApp.mAppContext.getSystemService(Context.ACTIVITY_SERVICE); + GeckoAppShell.getContext().getSystemService(Context.ACTIVITY_SERVICE); ActivityManager.MemoryInfo mi = new ActivityManager.MemoryInfo(); am.getMemoryInfo(mi); mi.totalMem /= 1024L * 1024L; diff --git a/mobile/android/base/ActivityHandlerHelper.java b/mobile/android/base/ActivityHandlerHelper.java index 70fd3e46193..5d356601b53 100644 --- a/mobile/android/base/ActivityHandlerHelper.java +++ b/mobile/android/base/ActivityHandlerHelper.java @@ -68,7 +68,7 @@ public class ActivityHandlerHelper { private int addIntentActivitiesToList(Context context, Intent intent, ArrayList items, ArrayList aIntents) { PackageManager pm = context.getPackageManager(); - List lri = pm.queryIntentActivityOptions(GeckoApp.mAppContext.getComponentName(), null, intent, 0); + List lri = pm.queryIntentActivityOptions(GeckoAppShell.getGeckoInterface().getActivity().getComponentName(), null, intent, 0); if (lri == null) { return 0; @@ -163,7 +163,7 @@ public class ActivityHandlerHelper { return intents.get(0); } - final PromptService ps = GeckoApp.mAppContext.getPromptService(); + final PromptService ps = GeckoAppShell.getGeckoInterface().getPromptService(); final String title = getFilePickerTitle(context, aMimeType); // Runnable has to be called to show an intent-like diff --git a/mobile/android/base/AndroidImportPreference.java b/mobile/android/base/AndroidImportPreference.java index 045ce7fdbbe..65923a1e5d6 100644 --- a/mobile/android/base/AndroidImportPreference.java +++ b/mobile/android/base/AndroidImportPreference.java @@ -76,7 +76,7 @@ class AndroidImportPreference extends MultiChoicePreference { final Runnable stopCallback = new Runnable() { @Override public void run() { - GeckoApp.mAppContext.runOnUiThread(new Runnable() { + ThreadUtils.postToUiThread(new Runnable() { @Override public void run() { dialog.dismiss(); diff --git a/mobile/android/base/BrowserApp.java b/mobile/android/base/BrowserApp.java index 3033f681136..b07b143d093 100644 --- a/mobile/android/base/BrowserApp.java +++ b/mobile/android/base/BrowserApp.java @@ -957,7 +957,7 @@ abstract public class BrowserApp extends GeckoApp // Display notification for Mozilla data reporting, if data should be collected. if (AppConstants.MOZ_DATA_REPORTING) { - DataReportingNotification.checkAndNotifyPolicy(BrowserApp.mAppContext); + DataReportingNotification.checkAndNotifyPolicy(GeckoAppShell.getContext()); } } else if (event.equals("Telemetry:Gather")) { diff --git a/mobile/android/base/BrowserToolbar.java b/mobile/android/base/BrowserToolbar.java index 289ae78e166..be39922e02b 100644 --- a/mobile/android/base/BrowserToolbar.java +++ b/mobile/android/base/BrowserToolbar.java @@ -1178,7 +1178,7 @@ public class BrowserToolbar implements Tabs.OnTabsChangedListener, if (!mHasSoftMenuButton) return false; - GeckoApp.mAppContext.invalidateOptionsMenu(); + GeckoAppShell.getGeckoInterface().invalidateOptionsMenu(); if (mMenuPopup != null && !mMenuPopup.isShowing()) mMenuPopup.showAsDropDown(mMenu); diff --git a/mobile/android/base/BrowserToolbarLayout.java b/mobile/android/base/BrowserToolbarLayout.java index 1895e054170..cb3e906283c 100644 --- a/mobile/android/base/BrowserToolbarLayout.java +++ b/mobile/android/base/BrowserToolbarLayout.java @@ -39,7 +39,7 @@ public class BrowserToolbarLayout extends GeckoRelativeLayout { post(new Runnable() { @Override public void run() { - ((BrowserApp)GeckoApp.mAppContext).refreshToolbarHeight(); + ((BrowserApp)GeckoAppShell.getContext()).refreshToolbarHeight(); } }); } diff --git a/mobile/android/base/CameraVideoResultHandler.java b/mobile/android/base/CameraVideoResultHandler.java index c8f9ce8b947..bf99e2cd5cc 100644 --- a/mobile/android/base/CameraVideoResultHandler.java +++ b/mobile/android/base/CameraVideoResultHandler.java @@ -30,7 +30,7 @@ class CameraVideoResultHandler implements ActivityResultHandler { return; } - Cursor cursor = GeckoApp.mAppContext.managedQuery(data.getData(), + Cursor cursor = GeckoAppShell.getGeckoInterface().getActivity().managedQuery(data.getData(), new String[] { MediaStore.Video.Media.DATA }, null, null, diff --git a/mobile/android/base/ContextGetter.java b/mobile/android/base/ContextGetter.java new file mode 100644 index 00000000000..5432000784a --- /dev/null +++ b/mobile/android/base/ContextGetter.java @@ -0,0 +1,6 @@ +package org.mozilla.gecko; +import android.content.Context; + +interface ContextGetter { + Context getContext(); +} diff --git a/mobile/android/base/Favicons.java b/mobile/android/base/Favicons.java index 0e91c8b9e33..eaf217cde3f 100644 --- a/mobile/android/base/Favicons.java +++ b/mobile/android/base/Favicons.java @@ -51,7 +51,7 @@ public class Favicons { private LruCache mFaviconsCache; private LruCache mFailedCache; private LruCache mColorCache; - private static final String USER_AGENT = GeckoApp.mAppContext.getDefaultUAString(); + private static final String USER_AGENT = GeckoAppShell.getGeckoInterface().getDefaultUAString(); private AndroidHttpClient mHttpClient; public interface OnFaviconLoadedListener { diff --git a/mobile/android/base/FilePickerResultHandler.java b/mobile/android/base/FilePickerResultHandler.java index 75f3138115d..e515929a49f 100644 --- a/mobile/android/base/FilePickerResultHandler.java +++ b/mobile/android/base/FilePickerResultHandler.java @@ -40,7 +40,7 @@ abstract class FilePickerResultHandler implements ActivityResultHandler { return path == null ? "" : path; } try { - ContentResolver cr = GeckoApp.mAppContext.getContentResolver(); + ContentResolver cr = GeckoAppShell.getContext().getContentResolver(); Cursor cursor = cr.query(uri, new String[] { OpenableColumns.DISPLAY_NAME }, null, null, null); String name = null; @@ -63,7 +63,7 @@ abstract class FilePickerResultHandler implements ActivityResultHandler { fileExt = name.substring(period); fileName = name.substring(0, period); } - File file = File.createTempFile(fileName, fileExt, GeckoLoader.getGREDir(GeckoApp.mAppContext)); + File file = File.createTempFile(fileName, fileExt, GeckoLoader.getGREDir(GeckoAppShell.getContext())); FileOutputStream fos = new FileOutputStream(file); InputStream is = cr.openInputStream(uri); byte[] buf = new byte[4096]; diff --git a/mobile/android/base/FormAssistPopup.java b/mobile/android/base/FormAssistPopup.java index 187db73414e..69712533125 100644 --- a/mobile/android/base/FormAssistPopup.java +++ b/mobile/android/base/FormAssistPopup.java @@ -188,7 +188,7 @@ public class FormAssistPopup extends RelativeLayout implements GeckoEventListene private void positionAndShowPopup(JSONObject rect, boolean isAutoComplete) { // Don't show the form assist popup when using fullscreen VKB InputMethodManager imm = - (InputMethodManager) GeckoApp.mAppContext.getSystemService(Context.INPUT_METHOD_SERVICE); + (InputMethodManager) GeckoAppShell.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); if (imm.isFullscreenMode()) return; @@ -205,7 +205,7 @@ public class FormAssistPopup extends RelativeLayout implements GeckoEventListene sValidationMessageHeight = (int) (res.getDimension(R.dimen.validation_message_height)); } - ImmutableViewportMetrics viewportMetrics = GeckoApp.mAppContext.getLayerView().getViewportMetrics(); + ImmutableViewportMetrics viewportMetrics = GeckoAppShell.getGeckoInterface().getLayerView().getViewportMetrics(); float zoom = viewportMetrics.zoomFactor; // These values correspond to the input box for which we want to diff --git a/mobile/android/base/GeckoAccessibility.java b/mobile/android/base/GeckoAccessibility.java index 7369a240426..9370be5ba78 100644 --- a/mobile/android/base/GeckoAccessibility.java +++ b/mobile/android/base/GeckoAccessibility.java @@ -120,10 +120,10 @@ public class GeckoAccessibility { private static void sendDirectAccessibilityEvent(int eventType, JSONObject message) { final AccessibilityEvent accEvent = AccessibilityEvent.obtain(eventType); accEvent.setClassName(GeckoAccessibility.class.getName()); - accEvent.setPackageName(GeckoApp.mAppContext.getPackageName()); + accEvent.setPackageName(GeckoAppShell.getContext().getPackageName()); populateEventFromJSON(accEvent, message); AccessibilityManager accessibilityManager = - (AccessibilityManager) GeckoApp.mAppContext.getSystemService(Context.ACCESSIBILITY_SERVICE); + (AccessibilityManager) GeckoAppShell.getContext().getSystemService(Context.ACCESSIBILITY_SERVICE); try { accessibilityManager.sendAccessibilityEvent(accEvent); } catch (IllegalStateException e) { @@ -153,7 +153,7 @@ public class GeckoAccessibility { } else { // In Jelly Bean we populate an AccessibilityNodeInfo with the minimal amount of data to have // it work with TalkBack. - final LayerView view = GeckoApp.mAppContext.getLayerView(); + final LayerView view = GeckoAppShell.getGeckoInterface().getLayerView(); if (view == null) return; @@ -275,7 +275,7 @@ public class GeckoAccessibility { info.setParent(host); info.setSource(host, virtualDescendantId); info.setVisibleToUser(true); - info.setPackageName(GeckoApp.mAppContext.getPackageName()); + info.setPackageName(GeckoAppShell.getContext().getPackageName()); info.setClassName(host.getClass().getName()); info.addAction(AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS); info.addAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS); diff --git a/mobile/android/base/GeckoApp.java b/mobile/android/base/GeckoApp.java index 4b56299620d..118f00eb7a3 100644 --- a/mobile/android/base/GeckoApp.java +++ b/mobile/android/base/GeckoApp.java @@ -41,13 +41,6 @@ import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; -import android.content.pm.ResolveInfo; -import android.content.pm.ServiceInfo; -import android.content.pm.Signature; import android.content.res.Configuration; import android.graphics.Bitmap; import android.graphics.BitmapFactory; @@ -112,10 +105,10 @@ import java.util.regex.Pattern; abstract public class GeckoApp extends GeckoActivity - implements GeckoEventListener, SensorEventListener, LocationListener, + implements GeckoEventListener, SensorEventListener, LocationListener, Tabs.OnTabsChangedListener, GeckoEventResponder, GeckoMenu.Callback, GeckoMenu.MenuPresenter, - TouchEventInterceptor + TouchEventInterceptor, ContextGetter, GeckoAppShell.GeckoInterface { private static final String LOGTAG = "GeckoApp"; @@ -147,7 +140,7 @@ abstract public class GeckoApp protected RelativeLayout mGeckoLayout; public View getView() { return mGeckoLayout; } public SurfaceView cameraView; - public static GeckoApp mAppContext; + private static GeckoApp sAppContext; protected MenuPanel mMenuPanel; protected Menu mMenu; private static GeckoThread sGeckoThread; @@ -204,6 +197,30 @@ abstract public class GeckoApp void focusChrome() { } + public Context getContext() { + return sAppContext; + } + + public Activity getActivity() { + return this; + } + + public LocationListener getLocationListener() { + return this; + } + + public SensorEventListener getSensorEventListener() { + return this; + } + + public SurfaceView getCameraView() { + return cameraView; + } + + public FormAssistPopup getFormAssistPopup() { + return mFormAssistPopup; + } + @Override public void onTabChanged(Tab tab, Tabs.TabEvents msg, Object data) { // When a tab is closed, it is always unselected first. @@ -241,206 +258,6 @@ abstract public class GeckoApp public void refreshChrome() { } - public static final String PLUGIN_ACTION = "android.webkit.PLUGIN"; - - /** - * A plugin that wish to be loaded in the WebView must provide this permission - * in their AndroidManifest.xml. - */ - public static final String PLUGIN_PERMISSION = "android.webkit.permission.PLUGIN"; - - private static final String PLUGIN_SYSTEM_LIB = "/system/lib/plugins/"; - - private static final String PLUGIN_TYPE = "type"; - private static final String TYPE_NATIVE = "native"; - public ArrayList mPackageInfoCache = new ArrayList(); - - // Returns null if plugins are blocked on the device. - String[] getPluginDirectories() { - - // An awful hack to detect Tegra devices. Easiest way to do it without spinning up a EGL context. - boolean isTegra = (new File("/system/lib/hw/gralloc.tegra.so")).exists(); - if (isTegra) { - // disable Flash on Tegra ICS with CM9 and other custom firmware (bug 736421) - File vfile = new File("/proc/version"); - FileReader vreader = null; - try { - if (vfile.canRead()) { - vreader = new FileReader(vfile); - String version = new BufferedReader(vreader).readLine(); - if (version.indexOf("CM9") != -1 || - version.indexOf("cyanogen") != -1 || - version.indexOf("Nova") != -1) - { - Log.w(LOGTAG, "Blocking plugins because of Tegra 2 + unofficial ICS bug (bug 736421)"); - return null; - } - } - } catch (IOException ex) { - // nothing - } finally { - try { - if (vreader != null) { - vreader.close(); - } - } catch (IOException ex) { - // nothing - } - } - } - - ArrayList directories = new ArrayList(); - PackageManager pm = mAppContext.getPackageManager(); - List plugins = pm.queryIntentServices(new Intent(PLUGIN_ACTION), - PackageManager.GET_SERVICES | PackageManager.GET_META_DATA); - - synchronized(mPackageInfoCache) { - - // clear the list of existing packageInfo objects - mPackageInfoCache.clear(); - - - for (ResolveInfo info : plugins) { - - // retrieve the plugin's service information - ServiceInfo serviceInfo = info.serviceInfo; - if (serviceInfo == null) { - Log.w(LOGTAG, "Ignoring bad plugin."); - continue; - } - - // Blacklist HTC's flash lite. - // See bug #704516 - We're not quite sure what Flash Lite does, - // but loading it causes Flash to give errors and fail to draw. - if (serviceInfo.packageName.equals("com.htc.flashliteplugin")) { - Log.w(LOGTAG, "Skipping HTC's flash lite plugin"); - continue; - } - - - // Retrieve information from the plugin's manifest. - PackageInfo pkgInfo; - try { - pkgInfo = pm.getPackageInfo(serviceInfo.packageName, - PackageManager.GET_PERMISSIONS - | PackageManager.GET_SIGNATURES); - } catch (Exception e) { - Log.w(LOGTAG, "Can't find plugin: " + serviceInfo.packageName); - continue; - } - - if (pkgInfo == null) { - Log.w(LOGTAG, "Not loading plugin: " + serviceInfo.packageName + ". Could not load package information."); - continue; - } - - /* - * find the location of the plugin's shared library. The default - * is to assume the app is either a user installed app or an - * updated system app. In both of these cases the library is - * stored in the app's data directory. - */ - String directory = pkgInfo.applicationInfo.dataDir + "/lib"; - final int appFlags = pkgInfo.applicationInfo.flags; - final int updatedSystemFlags = ApplicationInfo.FLAG_SYSTEM | - ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; - - // preloaded system app with no user updates - if ((appFlags & updatedSystemFlags) == ApplicationInfo.FLAG_SYSTEM) { - directory = PLUGIN_SYSTEM_LIB + pkgInfo.packageName; - } - - // check if the plugin has the required permissions - String permissions[] = pkgInfo.requestedPermissions; - if (permissions == null) { - Log.w(LOGTAG, "Not loading plugin: " + serviceInfo.packageName + ". Does not have required permission."); - continue; - } - boolean permissionOk = false; - for (String permit : permissions) { - if (PLUGIN_PERMISSION.equals(permit)) { - permissionOk = true; - break; - } - } - if (!permissionOk) { - Log.w(LOGTAG, "Not loading plugin: " + serviceInfo.packageName + ". Does not have required permission (2)."); - continue; - } - - // check to ensure the plugin is properly signed - Signature signatures[] = pkgInfo.signatures; - if (signatures == null) { - Log.w(LOGTAG, "Not loading plugin: " + serviceInfo.packageName + ". Not signed."); - continue; - } - - // determine the type of plugin from the manifest - if (serviceInfo.metaData == null) { - Log.e(LOGTAG, "The plugin '" + serviceInfo.name + "' has no defined type."); - continue; - } - - String pluginType = serviceInfo.metaData.getString(PLUGIN_TYPE); - if (!TYPE_NATIVE.equals(pluginType)) { - Log.e(LOGTAG, "Unrecognized plugin type: " + pluginType); - continue; - } - - try { - Class cls = getPluginClass(serviceInfo.packageName, serviceInfo.name); - - //TODO implement any requirements of the plugin class here! - boolean classFound = true; - - if (!classFound) { - Log.e(LOGTAG, "The plugin's class' " + serviceInfo.name + "' does not extend the appropriate class."); - continue; - } - - } catch (NameNotFoundException e) { - Log.e(LOGTAG, "Can't find plugin: " + serviceInfo.packageName); - continue; - } catch (ClassNotFoundException e) { - Log.e(LOGTAG, "Can't find plugin's class: " + serviceInfo.name); - continue; - } - - // if all checks have passed then make the plugin available - mPackageInfoCache.add(pkgInfo); - directories.add(directory); - } - } - - return directories.toArray(new String[directories.size()]); - } - - String getPluginPackage(String pluginLib) { - - if (pluginLib == null || pluginLib.length() == 0) { - return null; - } - - synchronized(mPackageInfoCache) { - for (PackageInfo pkgInfo : mPackageInfoCache) { - if (pluginLib.contains(pkgInfo.packageName)) { - return pkgInfo.packageName; - } - } - } - - return null; - } - - Class getPluginClass(String packageName, String className) - throws NameNotFoundException, ClassNotFoundException { - Context pluginContext = mAppContext.createPackageContext(packageName, - Context.CONTEXT_INCLUDE_CODE | - Context.CONTEXT_IGNORE_SECURITY); - ClassLoader pluginCL = pluginContext.getClassLoader(); - return pluginCL.loadClass(className); - } - @Override public void invalidateOptionsMenu() { if (mMenu == null) @@ -464,7 +281,7 @@ abstract public class GeckoApp @Override public MenuInflater getMenuInflater() { if (Build.VERSION.SDK_INT >= 11) - return new GeckoMenuInflater(mAppContext); + return new GeckoMenuInflater(sAppContext); else return super.getMenuInflater(); } @@ -504,7 +321,7 @@ abstract public class GeckoApp public View onCreatePanelView(int featureId) { if (Build.VERSION.SDK_INT >= 11 && featureId == Window.FEATURE_OPTIONS_PANEL) { if (mMenuPanel == null) { - mMenuPanel = new MenuPanel(mAppContext, null); + mMenuPanel = new MenuPanel(this, null); } else { // Prepare the panel everytime before showing the menu. onPreparePanel(featureId, mMenuPanel, mMenu); @@ -523,7 +340,7 @@ abstract public class GeckoApp mMenuPanel = (MenuPanel) onCreatePanelView(featureId); } - GeckoMenu gMenu = new GeckoMenu(mAppContext, null); + GeckoMenu gMenu = new GeckoMenu(this, null); gMenu.setCallback(this); gMenu.setMenuPresenter(this); menu = gMenu; @@ -724,14 +541,15 @@ abstract public class GeckoApp } else if (event.equals("Bookmark:Insert")) { final String url = message.getString("url"); final String title = message.getString("title"); + final Context context = this; ThreadUtils.postToUiThread(new Runnable() { @Override public void run() { - Toast.makeText(GeckoApp.mAppContext, R.string.bookmark_added, Toast.LENGTH_SHORT).show(); + Toast.makeText(context, R.string.bookmark_added, Toast.LENGTH_SHORT).show(); ThreadUtils.postToBackgroundThread(new Runnable() { @Override public void run() { - BrowserDB.addBookmark(GeckoApp.mAppContext.getContentResolver(), title, url); + BrowserDB.addBookmark(GeckoApp.sAppContext.getContentResolver(), title, url); } }); } @@ -906,7 +724,7 @@ abstract public class GeckoApp ThreadUtils.postToUiThread(new Runnable() { @Override public void run() { - Toast.makeText(mAppContext, resId, duration).show(); + Toast.makeText(sAppContext, resId, duration).show(); } }); } @@ -917,9 +735,9 @@ abstract public class GeckoApp public void run() { Toast toast; if (duration.equals("long")) - toast = Toast.makeText(mAppContext, message, Toast.LENGTH_LONG); + toast = Toast.makeText(sAppContext, message, Toast.LENGTH_LONG); else - toast = Toast.makeText(mAppContext, message, Toast.LENGTH_SHORT); + toast = Toast.makeText(sAppContext, message, Toast.LENGTH_SHORT); toast.show(); } }); @@ -953,7 +771,7 @@ abstract public class GeckoApp mFullScreenPluginView = view; } - void addPluginView(final View view, final Rect rect, final boolean isFullScreen) { + public void addPluginView(final View view, final Rect rect, final boolean isFullScreen) { ThreadUtils.postToUiThread(new Runnable() { @Override public void run() { @@ -1010,7 +828,7 @@ abstract public class GeckoApp setFullScreen(false); } - void removePluginView(final View view, final boolean isFullScreen) { + public void removePluginView(final View view, final boolean isFullScreen) { ThreadUtils.postToUiThread(new Runnable() { @Override public void run() { @@ -1031,21 +849,21 @@ abstract public class GeckoApp } private void setImageAsWallpaper(final String aSrc) { - final String progText = mAppContext.getString(R.string.wallpaper_progress); - final String successText = mAppContext.getString(R.string.wallpaper_success); - final String failureText = mAppContext.getString(R.string.wallpaper_fail); + final String progText = sAppContext.getString(R.string.wallpaper_progress); + final String successText = sAppContext.getString(R.string.wallpaper_success); + final String failureText = sAppContext.getString(R.string.wallpaper_fail); final String fileName = aSrc.substring(aSrc.lastIndexOf("/") + 1); - final PendingIntent emptyIntent = PendingIntent.getActivity(mAppContext, 0, new Intent(), 0); - final AlertNotification notification = new AlertNotification(mAppContext, fileName.hashCode(), + final PendingIntent emptyIntent = PendingIntent.getActivity(sAppContext, 0, new Intent(), 0); + final AlertNotification notification = new AlertNotification(sAppContext, fileName.hashCode(), R.drawable.alert_download, fileName, progText, System.currentTimeMillis(), null); - notification.setLatestEventInfo(mAppContext, fileName, progText, emptyIntent ); + notification.setLatestEventInfo(sAppContext, fileName, progText, emptyIntent ); notification.flags |= Notification.FLAG_ONGOING_EVENT; notification.show(); new UiAsyncTask(ThreadUtils.getBackgroundHandler()) { @Override protected Boolean doInBackground(Void... params) { - WallpaperManager mgr = WallpaperManager.getInstance(mAppContext); + WallpaperManager mgr = WallpaperManager.getInstance(sAppContext); if (mgr == null) { return false; } @@ -1163,10 +981,10 @@ abstract public class GeckoApp notification.flags |= Notification.FLAG_AUTO_CANCEL; if(!success) { notification.tickerText = failureText; - notification.setLatestEventInfo(mAppContext, fileName, failureText, emptyIntent); + notification.setLatestEventInfo(sAppContext, fileName, failureText, emptyIntent); } else { notification.tickerText = successText; - notification.setLatestEventInfo(mAppContext, fileName, successText, emptyIntent); + notification.setLatestEventInfo(sAppContext, fileName, successText, emptyIntent); } notification.show(); } @@ -1305,7 +1123,9 @@ abstract public class GeckoApp BrowserDB.initialize(getProfile().getName()); ((GeckoApplication)getApplication()).initialize(); - mAppContext = this; + sAppContext = this; + GeckoAppShell.setContextGetter(this); + GeckoAppShell.setGeckoInterface(this); ThreadUtils.setUiThread(Thread.currentThread(), new Handler()); Tabs.getInstance().attachToActivity(this); @@ -1378,7 +1198,7 @@ abstract public class GeckoApp @Override public void run() { SharedPreferences prefs = - GeckoApp.mAppContext.getSharedPreferences(PREFS_NAME, 0); + GeckoApp.sAppContext.getSharedPreferences(PREFS_NAME, 0); boolean wasOOM = prefs.getBoolean(PREFS_OOM_EXCEPTION, false); boolean wasStopped = prefs.getBoolean(PREFS_WAS_STOPPED, true); @@ -1399,7 +1219,7 @@ abstract public class GeckoApp final String profilePath = getProfile().getDir().getAbsolutePath(); final EventDispatcher dispatcher = GeckoAppShell.getEventDispatcher(); Log.i(LOGTAG, "Creating BrowserHealthRecorder."); - mHealthRecorder = new BrowserHealthRecorder(GeckoApp.mAppContext, profilePath, dispatcher); + mHealthRecorder = new BrowserHealthRecorder(sAppContext, profilePath, dispatcher); } }); @@ -1610,7 +1430,7 @@ abstract public class GeckoApp // Record our launch time for the announcements service // to use in assessing inactivity. - final Context context = GeckoApp.mAppContext; + final Context context = GeckoApp.sAppContext; AnnouncementsBroadcastService.recordLastLaunch(context); // Kick off our background service to fetch product announcements. @@ -1718,7 +1538,7 @@ abstract public class GeckoApp return RESTORE_OOM; } - final SharedPreferences prefs = GeckoApp.mAppContext.getSharedPreferences(PREFS_NAME, 0); + final SharedPreferences prefs = GeckoApp.sAppContext.getSharedPreferences(PREFS_NAME, 0); // We record crashes in the crash reporter. If sessionstore.js // exists, but we didn't flag a crash in the crash reporter, we @@ -1920,7 +1740,7 @@ abstract public class GeckoApp @Override public void run() { SharedPreferences prefs = - GeckoApp.mAppContext.getSharedPreferences(GeckoApp.PREFS_NAME, 0); + GeckoApp.sAppContext.getSharedPreferences(GeckoApp.PREFS_NAME, 0); SharedPreferences.Editor editor = prefs.edit(); editor.putBoolean(GeckoApp.PREFS_WAS_STOPPED, false); editor.commit(); @@ -1948,7 +1768,7 @@ abstract public class GeckoApp @Override public void run() { SharedPreferences prefs = - GeckoApp.mAppContext.getSharedPreferences(GeckoApp.PREFS_NAME, 0); + GeckoApp.sAppContext.getSharedPreferences(GeckoApp.PREFS_NAME, 0); SharedPreferences.Editor editor = prefs.edit(); editor.putBoolean(GeckoApp.PREFS_WAS_STOPPED, true); editor.commit(); @@ -1967,7 +1787,7 @@ abstract public class GeckoApp @Override public void run() { SharedPreferences prefs = - GeckoApp.mAppContext.getSharedPreferences(GeckoApp.PREFS_NAME, 0); + GeckoApp.sAppContext.getSharedPreferences(GeckoApp.PREFS_NAME, 0); SharedPreferences.Editor editor = prefs.edit(); editor.putBoolean(GeckoApp.PREFS_WAS_STOPPED, false); editor.commit(); @@ -2059,7 +1879,7 @@ abstract public class GeckoApp // Get a temporary directory, may return null public static File getTempDirectory() { - File dir = mAppContext.getExternalFilesDir("temp"); + File dir = sAppContext.getExternalFilesDir("temp"); return dir; } @@ -2164,7 +1984,7 @@ abstract public class GeckoApp final File profileDir = getProfile().getDir(); if (profileDir != null) { - final GeckoApp app = GeckoApp.mAppContext; + final GeckoApp app = GeckoApp.sAppContext; ThreadUtils.postToBackgroundThread(new Runnable() { @Override @@ -2187,7 +2007,7 @@ abstract public class GeckoApp final Runnable startCallback = new Runnable() { @Override public void run() { - GeckoApp.mAppContext.runOnUiThread(new Runnable() { + ThreadUtils.postToUiThread(new Runnable() { @Override public void run() { setupScreenHolder[0] = new SetupScreen(app); @@ -2200,7 +2020,7 @@ abstract public class GeckoApp final Runnable stopCallback = new Runnable() { @Override public void run() { - GeckoApp.mAppContext.runOnUiThread(new Runnable() { + ThreadUtils.postToUiThread(new Runnable() { @Override public void run() { SetupScreen screen = setupScreenHolder[0]; @@ -2230,7 +2050,7 @@ abstract public class GeckoApp private void checkMigrateSync() { final File profileDir = getProfile().getDir(); if (!GeckoApp.sIsUsingCustomProfile && profileDir != null) { - final GeckoApp app = GeckoApp.mAppContext; + final GeckoApp app = GeckoApp.sAppContext; ProfileMigrator profileMigrator = new ProfileMigrator(app); if (!profileMigrator.hasSyncMigrated()) { profileMigrator.launchSyncPrefs(); @@ -2238,7 +2058,7 @@ abstract public class GeckoApp } } - PromptService getPromptService() { + public PromptService getPromptService() { return mPromptService; } diff --git a/mobile/android/base/GeckoAppShell.java b/mobile/android/base/GeckoAppShell.java index ecb6258be70..c12ae99d7d4 100644 --- a/mobile/android/base/GeckoAppShell.java +++ b/mobile/android/base/GeckoAppShell.java @@ -16,6 +16,7 @@ import org.mozilla.gecko.util.GeckoEventListener; import org.mozilla.gecko.util.HardwareUtils; import org.mozilla.gecko.util.ThreadUtils; +import android.app.Activity; import android.app.ActivityManager; import android.app.PendingIntent; import android.content.ActivityNotFoundException; @@ -24,8 +25,13 @@ import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; +import android.content.pm.Signature; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Canvas; @@ -39,8 +45,10 @@ import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.hardware.Sensor; import android.hardware.SensorManager; +import android.hardware.SensorEventListener; import android.location.Criteria; import android.location.Location; +import android.location.LocationListener; import android.location.LocationManager; import android.media.MediaScannerConnection; import android.media.MediaScannerConnection.MediaScannerConnectionClient; @@ -61,9 +69,11 @@ import android.util.Log; import android.view.ContextThemeWrapper; import android.view.HapticFeedbackConstants; import android.view.Surface; +import android.view.SurfaceView; import android.view.View; import android.view.inputmethod.InputMethodManager; import android.webkit.MimeTypeMap; +import android.widget.AbsoluteLayout; import android.widget.Toast; import java.io.BufferedReader; @@ -82,6 +92,7 @@ import java.net.ProxySelector; import java.net.URI; import java.net.URL; import java.nio.ByteBuffer; +import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; @@ -186,7 +197,7 @@ public class GeckoAppShell if (e instanceof OutOfMemoryError) { SharedPreferences prefs = - GeckoApp.mAppContext.getSharedPreferences(GeckoApp.PREFS_NAME, 0); + getContext().getSharedPreferences(GeckoApp.PREFS_NAME, 0); SharedPreferences.Editor editor = prefs.edit(); editor.putBoolean(GeckoApp.PREFS_OOM_EXCEPTION, true); editor.commit(); @@ -260,6 +271,16 @@ public class GeckoAppShell } } + private static LayerView sLayerView; + + public static void setLayerView(LayerView lv) { + sLayerView = lv; + } + + public static LayerView getLayerView() { + return sLayerView; + } + public static void runGecko(String apkPath, String args, String url, String type) { Looper.prepare(); @@ -267,7 +288,11 @@ public class GeckoAppShell GeckoAppShell.nativeInit(); // Tell Gecko where the target byte buffer is for rendering - GeckoAppShell.setLayerClient(GeckoApp.mAppContext.getLayerView().getLayerClient()); + if (getGeckoInterface() != null) + sLayerView = getGeckoInterface().getLayerView(); + if (sLayerView != null) + GeckoAppShell.setLayerClient(sLayerView.getLayerClient()); + // First argument is the .apk path String combinedArgs = apkPath + " -greomni " + apkPath; @@ -278,10 +303,10 @@ public class GeckoAppShell if (type != null) combinedArgs += " " + type; - DisplayMetrics metrics = GeckoApp.mAppContext.getResources().getDisplayMetrics(); + DisplayMetrics metrics = getContext().getResources().getDisplayMetrics(); combinedArgs += " -width " + metrics.widthPixels + " -height " + metrics.heightPixels; - GeckoApp.mAppContext.runOnUiThread(new Runnable() { + ThreadUtils.postToUiThread(new Runnable() { @Override public void run() { geckoLoaded(); @@ -427,7 +452,7 @@ public class GeckoAppShell ThreadUtils.postToUiThread(new Runnable() { @Override public void run() { - LocationManager lm = getLocationManager(GeckoApp.mAppContext); + LocationManager lm = getLocationManager(getContext()); if (lm == null) { return; } @@ -435,7 +460,7 @@ public class GeckoAppShell if (enable) { Location lastKnownLocation = getLastKnownLocation(lm); if (lastKnownLocation != null) { - GeckoApp.mAppContext.onLocationChanged(lastKnownLocation); + getGeckoInterface().getLocationListener().onLocationChanged(lastKnownLocation); } Criteria criteria = new Criteria(); @@ -457,9 +482,9 @@ public class GeckoAppShell return; Looper l = Looper.getMainLooper(); - lm.requestLocationUpdates(provider, 100, (float).5, GeckoApp.mAppContext, l); + lm.requestLocationUpdates(provider, 100, (float).5, getGeckoInterface().getLocationListener(), l); } else { - lm.removeUpdates(GeckoApp.mAppContext); + lm.removeUpdates(getGeckoInterface().getLocationListener()); } } }); @@ -483,50 +508,53 @@ public class GeckoAppShell } public static void enableSensor(int aSensortype) { + GeckoInterface gi = getGeckoInterface(); + if (gi == null) + return; SensorManager sm = (SensorManager) - GeckoApp.mAppContext.getSystemService(Context.SENSOR_SERVICE); + getContext().getSystemService(Context.SENSOR_SERVICE); switch(aSensortype) { case GeckoHalDefines.SENSOR_ORIENTATION: if(gOrientationSensor == null) gOrientationSensor = sm.getDefaultSensor(Sensor.TYPE_ORIENTATION); - if (gOrientationSensor != null) - sm.registerListener(GeckoApp.mAppContext, gOrientationSensor, sDefaultSensorHint); + if (gOrientationSensor != null) + sm.registerListener(gi.getSensorEventListener(), gOrientationSensor, sDefaultSensorHint); break; case GeckoHalDefines.SENSOR_ACCELERATION: if(gAccelerometerSensor == null) gAccelerometerSensor = sm.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); if (gAccelerometerSensor != null) - sm.registerListener(GeckoApp.mAppContext, gAccelerometerSensor, sDefaultSensorHint); + sm.registerListener(gi.getSensorEventListener(), gAccelerometerSensor, sDefaultSensorHint); break; case GeckoHalDefines.SENSOR_PROXIMITY: - if(gProximitySensor == null) + if(gProximitySensor == null ) gProximitySensor = sm.getDefaultSensor(Sensor.TYPE_PROXIMITY); if (gProximitySensor != null) - sm.registerListener(GeckoApp.mAppContext, gProximitySensor, SensorManager.SENSOR_DELAY_NORMAL); + sm.registerListener(gi.getSensorEventListener(), gProximitySensor, SensorManager.SENSOR_DELAY_NORMAL); break; case GeckoHalDefines.SENSOR_LIGHT: if(gLightSensor == null) gLightSensor = sm.getDefaultSensor(Sensor.TYPE_LIGHT); if (gLightSensor != null) - sm.registerListener(GeckoApp.mAppContext, gLightSensor, SensorManager.SENSOR_DELAY_NORMAL); + sm.registerListener(gi.getSensorEventListener(), gLightSensor, SensorManager.SENSOR_DELAY_NORMAL); break; case GeckoHalDefines.SENSOR_LINEAR_ACCELERATION: if(gLinearAccelerometerSensor == null) gLinearAccelerometerSensor = sm.getDefaultSensor(10 /* API Level 9 - TYPE_LINEAR_ACCELERATION */); if (gLinearAccelerometerSensor != null) - sm.registerListener(GeckoApp.mAppContext, gLinearAccelerometerSensor, sDefaultSensorHint); + sm.registerListener(gi.getSensorEventListener(), gLinearAccelerometerSensor, sDefaultSensorHint); break; case GeckoHalDefines.SENSOR_GYROSCOPE: if(gGyroscopeSensor == null) gGyroscopeSensor = sm.getDefaultSensor(Sensor.TYPE_GYROSCOPE); if (gGyroscopeSensor != null) - sm.registerListener(GeckoApp.mAppContext, gGyroscopeSensor, sDefaultSensorHint); + sm.registerListener(gi.getSensorEventListener(), gGyroscopeSensor, sDefaultSensorHint); break; default: Log.w(LOGTAG, "Error! Can't enable unknown SENSOR type " + aSensortype); @@ -534,38 +562,42 @@ public class GeckoAppShell } public static void disableSensor(int aSensortype) { + GeckoInterface gi = getGeckoInterface(); + if (gi == null) + return; + SensorManager sm = (SensorManager) - GeckoApp.mAppContext.getSystemService(Context.SENSOR_SERVICE); + getContext().getSystemService(Context.SENSOR_SERVICE); switch (aSensortype) { case GeckoHalDefines.SENSOR_ORIENTATION: if (gOrientationSensor != null) - sm.unregisterListener(GeckoApp.mAppContext, gOrientationSensor); + sm.unregisterListener(gi.getSensorEventListener(), gOrientationSensor); break; case GeckoHalDefines.SENSOR_ACCELERATION: if (gAccelerometerSensor != null) - sm.unregisterListener(GeckoApp.mAppContext, gAccelerometerSensor); + sm.unregisterListener(gi.getSensorEventListener(), gAccelerometerSensor); break; case GeckoHalDefines.SENSOR_PROXIMITY: if (gProximitySensor != null) - sm.unregisterListener(GeckoApp.mAppContext, gProximitySensor); + sm.unregisterListener(gi.getSensorEventListener(), gProximitySensor); break; case GeckoHalDefines.SENSOR_LIGHT: if (gLightSensor != null) - sm.unregisterListener(GeckoApp.mAppContext, gLightSensor); + sm.unregisterListener(gi.getSensorEventListener(), gLightSensor); break; case GeckoHalDefines.SENSOR_LINEAR_ACCELERATION: if (gLinearAccelerometerSensor != null) - sm.unregisterListener(GeckoApp.mAppContext, gLinearAccelerometerSensor); + sm.unregisterListener(gi.getSensorEventListener(), gLinearAccelerometerSensor); break; case GeckoHalDefines.SENSOR_GYROSCOPE: if (gGyroscopeSensor != null) - sm.unregisterListener(GeckoApp.mAppContext, gGyroscopeSensor); + sm.unregisterListener(gi.getSensorEventListener(), gGyroscopeSensor); break; default: Log.w(LOGTAG, "Error! Can't disable unknown SENSOR type " + aSensortype); @@ -573,7 +605,8 @@ public class GeckoAppShell } public static void moveTaskToBack() { - GeckoApp.mAppContext.moveTaskToBack(true); + if (getGeckoInterface() != null) + getGeckoInterface().getActivity().moveTaskToBack(true); } public static void returnIMEQueryResult(String result, int selectionStart, int selectionLength) { @@ -584,10 +617,12 @@ public class GeckoAppShell static void onXreExit() { // The launch state can only be Launched or GeckoRunning at this point GeckoThread.setLaunchState(GeckoThread.LaunchState.GeckoExiting); - if (gRestartScheduled) { - GeckoApp.mAppContext.doRestart(); - } else { - GeckoApp.mAppContext.finish(); + if (getGeckoInterface() != null) { + if (gRestartScheduled) { + getGeckoInterface().doRestart(); + } else { + getGeckoInterface().getActivity().finish(); + } } Log.d(LOGTAG, "Killing via System.exit()"); @@ -599,13 +634,13 @@ public class GeckoAppShell } public static File preInstallWebApp(String aTitle, String aURI, String aUniqueURI) { - int index = WebAppAllocator.getInstance(GeckoApp.mAppContext).findAndAllocateIndex(aUniqueURI, aTitle, (String) null); - GeckoProfile profile = GeckoProfile.get(GeckoApp.mAppContext, "webapp" + index); + int index = WebAppAllocator.getInstance(getContext()).findAndAllocateIndex(aUniqueURI, aTitle, (String) null); + GeckoProfile profile = GeckoProfile.get(getContext(), "webapp" + index); return profile.getDir(); } public static void postInstallWebApp(String aTitle, String aURI, String aUniqueURI, String aIconURL) { - WebAppAllocator allocator = WebAppAllocator.getInstance(GeckoApp.mAppContext); + WebAppAllocator allocator = WebAppAllocator.getInstance(getContext()); int index = allocator.getIndexForApp(aUniqueURI); assert index != -1 && aIconURL != null; allocator.updateAppAllocation(aUniqueURI, index, BitmapUtils.getBitmapFromDataURI(aIconURL)); @@ -615,9 +650,9 @@ public class GeckoAppShell public static Intent getWebAppIntent(String aURI, String aUniqueURI, String aTitle, Bitmap aIcon) { int index; if (aIcon != null && !TextUtils.isEmpty(aTitle)) - index = WebAppAllocator.getInstance(GeckoApp.mAppContext).findAndAllocateIndex(aUniqueURI, aTitle, aIcon); + index = WebAppAllocator.getInstance(getContext()).findAndAllocateIndex(aUniqueURI, aTitle, aIcon); else - index = WebAppAllocator.getInstance(GeckoApp.mAppContext).getIndexForApp(aUniqueURI); + index = WebAppAllocator.getInstance(getContext()).getIndexForApp(aUniqueURI); if (index == -1) return null; @@ -684,7 +719,7 @@ public class GeckoAppShell intent.putExtra("duplicate", false); intent.setAction("com.android.launcher.action.INSTALL_SHORTCUT"); - GeckoApp.mAppContext.sendBroadcast(intent); + getContext().sendBroadcast(intent); } }); } @@ -700,7 +735,7 @@ public class GeckoAppShell // the intent to be launched by the shortcut Intent shortcutIntent; if (aType.equalsIgnoreCase(SHORTCUT_TYPE_WEBAPP)) { - int index = WebAppAllocator.getInstance(GeckoApp.mAppContext).getIndexForApp(aUniqueURI); + int index = WebAppAllocator.getInstance(getContext()).getIndexForApp(aUniqueURI); shortcutIntent = getWebAppIntent(aURI, aUniqueURI, "", null); if (shortcutIntent == null) return; @@ -720,7 +755,7 @@ public class GeckoAppShell intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, aURI); intent.setAction("com.android.launcher.action.UNINSTALL_SHORTCUT"); - GeckoApp.mAppContext.sendBroadcast(intent); + getContext().sendBroadcast(intent); } }); } @@ -732,17 +767,17 @@ public class GeckoAppShell ThreadUtils.postToBackgroundThread(new Runnable() { @Override public void run() { - int index = WebAppAllocator.getInstance(GeckoApp.mAppContext).releaseIndexForApp(uniqueURI); + int index = WebAppAllocator.getInstance(getContext()).releaseIndexForApp(uniqueURI); // if -1, nothing to do; we didn't think it was installed anyway if (index == -1) return; // kill the app if it's running - String targetProcessName = GeckoApp.mAppContext.getPackageName(); + String targetProcessName = getContext().getPackageName(); targetProcessName = targetProcessName + ":" + targetProcessName + ".WebApp" + index; - ActivityManager am = (ActivityManager) GeckoApp.mAppContext.getSystemService(Context.ACTIVITY_SERVICE); + ActivityManager am = (ActivityManager) getContext().getSystemService(Context.ACTIVITY_SERVICE); List procs = am.getRunningAppProcesses(); if (procs != null) { for (ActivityManager.RunningAppProcessInfo proc : procs) { @@ -754,14 +789,14 @@ public class GeckoAppShell } // then nuke the profile - GeckoProfile.removeProfile(GeckoApp.mAppContext, "webapp" + index); + GeckoProfile.removeProfile(getContext(), "webapp" + index); } }); } static public int getPreferredIconSize() { if (android.os.Build.VERSION.SDK_INT >= 11) { - ActivityManager am = (ActivityManager)GeckoApp.mAppContext.getSystemService(Context.ACTIVITY_SERVICE); + ActivityManager am = (ActivityManager)getContext().getSystemService(Context.ACTIVITY_SERVICE); return am.getLauncherLargeIconSize(); } else { switch (getDpi()) { @@ -807,12 +842,12 @@ public class GeckoAppShell } // draw the overlay - Bitmap overlay = BitmapUtils.decodeResource(GeckoApp.mAppContext, R.drawable.home_bg); + Bitmap overlay = BitmapUtils.decodeResource(getContext(), R.drawable.home_bg); canvas.drawBitmap(overlay, null, new Rect(0, 0, size, size), null); // draw the favicon if (aSource == null) - aSource = BitmapUtils.decodeResource(GeckoApp.mAppContext, R.drawable.home_star); + aSource = BitmapUtils.decodeResource(getContext(), R.drawable.home_star); // by default, we scale the icon to this size int sWidth = insetSize / 2; @@ -848,20 +883,20 @@ public class GeckoAppShell // aURL may contain the whole URL or just the protocol Uri uri = aURL.indexOf(':') >= 0 ? Uri.parse(aURL) : new Uri.Builder().scheme(aURL).build(); - Intent intent = getOpenURIIntent(GeckoApp.mAppContext, uri.toString(), "", + Intent intent = getOpenURIIntent(getContext(), uri.toString(), "", TextUtils.isEmpty(aAction) ? Intent.ACTION_VIEW : aAction, ""); return getHandlersForIntent(intent); } static boolean hasHandlersForIntent(Intent intent) { - PackageManager pm = GeckoApp.mAppContext.getPackageManager(); + PackageManager pm = getContext().getPackageManager(); List list = pm.queryIntentActivities(intent, 0); return !list.isEmpty(); } static String[] getHandlersForIntent(Intent intent) { - PackageManager pm = GeckoApp.mAppContext.getPackageManager(); + PackageManager pm = getContext().getPackageManager(); List list = pm.queryIntentActivities(intent, 0); int numAttr = 4; String[] ret = new String[list.size() * numAttr]; @@ -985,14 +1020,14 @@ public class GeckoAppShell } finally { safeStreamClose(os); } - GeckoApp.mAppContext.startActivity(Intent.createChooser(intent, - GeckoApp.mAppContext.getResources().getString(R.string.share_title))); + getContext().startActivity(Intent.createChooser(intent, + getContext().getResources().getString(R.string.share_title))); } // Don't fail silently, tell the user that we weren't able to share the image private static final void showImageShareFailureToast() { - Toast toast = Toast.makeText(GeckoApp.mAppContext, - GeckoApp.mAppContext.getResources().getString(R.string.share_image_failed), + Toast toast = Toast.makeText(getContext(), + getContext().getResources().getString(R.string.share_image_failed), Toast.LENGTH_SHORT); toast.show(); } @@ -1029,7 +1064,7 @@ public class GeckoAppShell String className, String action, String title) { - final Context context = GeckoApp.mAppContext; + final Context context = getContext(); final Intent intent = getOpenURIIntent(context, targetURI, mimeType, action, title); @@ -1184,7 +1219,7 @@ public class GeckoAppShell * on a thread with a looper, so this function requires a looper is * present on the thread. */ private static String getClipboardTextImpl() { - Context context = GeckoApp.mAppContext; + Context context = getContext(); if (android.os.Build.VERSION.SDK_INT >= 11) { android.content.ClipboardManager cm = (android.content.ClipboardManager)context.getSystemService(Context.CLIPBOARD_SERVICE); if (cm.hasPrimaryClip()) { @@ -1238,7 +1273,7 @@ public class GeckoAppShell ThreadUtils.postToBackgroundThread(new Runnable() { @Override public void run() { - Context context = GeckoApp.mAppContext; + Context context = getContext(); if (android.os.Build.VERSION.SDK_INT >= 11) { android.content.ClipboardManager cm = (android.content.ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE); @@ -1270,7 +1305,7 @@ public class GeckoAppShell "- name = '" + aAlertName + "'"); // The intent to launch when the user clicks the expanded notification - String app = GeckoApp.mAppContext.getClass().getName(); + String app = getContext().getClass().getName(); Intent notificationIntent = new Intent(GeckoApp.ACTION_ALERT_CALLBACK); notificationIntent.setClassName(AppConstants.ANDROID_PACKAGE_NAME, app); notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); @@ -1285,7 +1320,7 @@ public class GeckoAppShell .build(); notificationIntent.setData(dataUri); PendingIntent contentIntent = PendingIntent.getActivity( - GeckoApp.mAppContext, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT); + getContext(), 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT); mAlertCookies.put(aAlertName, aAlertCookie); callObserver(aAlertName, "alertshow", aAlertCookie); @@ -1338,29 +1373,34 @@ public class GeckoAppShell public static int getDpi() { if (sDensityDpi == 0) { - sDensityDpi = GeckoApp.mAppContext.getResources().getDisplayMetrics().densityDpi; + sDensityDpi = getContext().getResources().getDisplayMetrics().densityDpi; } return sDensityDpi; } public static void setFullScreen(boolean fullscreen) { - GeckoApp.mAppContext.setFullScreen(fullscreen); + if (getGeckoInterface() != null) + getGeckoInterface().setFullScreen(fullscreen); } public static String showFilePickerForExtensions(String aExtensions) { - return sActivityHelper.showFilePicker(GeckoApp.mAppContext, getMimeTypeFromExtensions(aExtensions)); + if (getGeckoInterface() != null) + return sActivityHelper.showFilePicker(getGeckoInterface().getActivity(), getMimeTypeFromExtensions(aExtensions)); + return ""; } public static String showFilePickerForMimeType(String aMimeType) { - return sActivityHelper.showFilePicker(GeckoApp.mAppContext, aMimeType); + if (getGeckoInterface() != null) + return sActivityHelper.showFilePicker(getGeckoInterface().getActivity(), aMimeType); + return ""; } public static void performHapticFeedback(boolean aIsLongPress) { // Don't perform haptic feedback if a vibration is currently playing, // because the haptic feedback will nuke the vibration. if (!sVibrationMaybePlaying || System.nanoTime() >= sVibrationEndTime) { - LayerView layerView = GeckoApp.mAppContext.getLayerView(); + LayerView layerView = getLayerView(); layerView.performHapticFeedback(aIsLongPress ? HapticFeedbackConstants.LONG_PRESS : HapticFeedbackConstants.VIRTUAL_KEY); @@ -1368,7 +1408,7 @@ public class GeckoAppShell } private static Vibrator vibrator() { - LayerView layerView = GeckoApp.mAppContext.getLayerView(); + LayerView layerView = getLayerView(); return (Vibrator) layerView.getContext().getSystemService(Context.VIBRATOR_SERVICE); } @@ -1400,12 +1440,12 @@ public class GeckoAppShell public static void showInputMethodPicker() { InputMethodManager imm = (InputMethodManager) - GeckoApp.mAppContext.getSystemService(Context.INPUT_METHOD_SERVICE); + getContext().getSystemService(Context.INPUT_METHOD_SERVICE); imm.showInputMethodPicker(); } public static void setKeepScreenOn(final boolean on) { - GeckoApp.mAppContext.runOnUiThread(new Runnable() { + ThreadUtils.postToUiThread(new Runnable() { @Override public void run() { // TODO @@ -1417,7 +1457,7 @@ public class GeckoAppShell ThreadUtils.postToUiThread(new Runnable() { @Override public void run() { - LayerView view = GeckoApp.mAppContext.getLayerView(); + LayerView view = getLayerView(); PanZoomController controller = (view == null ? null : view.getPanZoomController()); if (controller != null) { controller.notifyDefaultActionPrevented(defaultPrevented); @@ -1428,7 +1468,7 @@ public class GeckoAppShell public static boolean isNetworkLinkUp() { ConnectivityManager cm = (ConnectivityManager) - GeckoApp.mAppContext.getSystemService(Context.CONNECTIVITY_SERVICE); + getContext().getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo info = cm.getActiveNetworkInfo(); if (info == null || !info.isConnected()) return false; @@ -1437,7 +1477,7 @@ public class GeckoAppShell public static boolean isNetworkLinkKnown() { ConnectivityManager cm = (ConnectivityManager) - GeckoApp.mAppContext.getSystemService(Context.CONNECTIVITY_SERVICE); + getContext().getSystemService(Context.CONNECTIVITY_SERVICE); if (cm.getActiveNetworkInfo() == null) return false; return true; @@ -1452,8 +1492,8 @@ public class GeckoAppShell //We're not using this, not need to save it (see bug 635342) SharedPreferences settings = - GeckoApp.mAppContext.getPreferences(Activity.MODE_PRIVATE); - settings.edit().putString(GeckoApp.mAppContext.getPackageName() + ".locale", + getContext().getPreferences(Activity.MODE_PRIVATE); + settings.edit().putString(getContext().getPackageName() + ".locale", localeCode).commit(); Locale locale; int index; @@ -1467,7 +1507,7 @@ public class GeckoAppShell } Locale.setDefault(locale); - Resources res = GeckoApp.mAppContext.getBaseContext().getResources(); + Resources res = getContext().getBaseContext().getResources(); Configuration config = res.getConfiguration(); config.locale = locale; res.updateConfiguration(config, res.getDisplayMetrics()); @@ -1494,7 +1534,7 @@ public class GeckoAppShell int[] result = new int[attrsAppearance.length]; final ContextThemeWrapper contextThemeWrapper = - new ContextThemeWrapper(GeckoApp.mAppContext, android.R.style.TextAppearance); + new ContextThemeWrapper(getContext(), android.R.style.TextAppearance); final TypedArray appearance = contextThemeWrapper.getTheme().obtainStyledAttributes(attrsAppearance); @@ -1625,7 +1665,7 @@ public class GeckoAppShell int nameColumn = -1; try { - String filter = GeckoProfile.get(GeckoApp.mAppContext).getDir().toString(); + String filter = GeckoProfile.get(getContext()).getDir().toString(); Log.i(LOGTAG, "[OPENFILE] Filter: " + filter); // run lsof and parse its output @@ -1666,7 +1706,7 @@ public class GeckoAppShell } public static void scanMedia(String aFile, String aMimeType) { - Context context = GeckoApp.mAppContext; + Context context = getContext(); GeckoMediaScannerClient.startScan(context, aFile, aMimeType); } @@ -1678,7 +1718,7 @@ public class GeckoAppShell if (aExt != null && aExt.length() > 1 && aExt.charAt(0) == '.') aExt = aExt.substring(1); - PackageManager pm = GeckoApp.mAppContext.getPackageManager(); + PackageManager pm = getContext().getPackageManager(); Drawable icon = getDrawableForExtension(pm, aExt); if (icon == null) { // Use a generic icon @@ -1726,7 +1766,7 @@ public class GeckoAppShell public static boolean getShowPasswordSetting() { try { int showPassword = - Settings.System.getInt(GeckoApp.mAppContext.getContentResolver(), + Settings.System.getInt(getContext().getContentResolver(), Settings.System.TEXT_SHOW_PASSWORD, 1); return (showPassword > 0); } @@ -1739,18 +1779,221 @@ public class GeckoAppShell int x, int y, int w, int h, boolean isFullScreen) { - GeckoApp.mAppContext.addPluginView(view, new Rect(x, y, x + w, y + h), isFullScreen); + if (getGeckoInterface() != null) + getGeckoInterface().addPluginView(view, new Rect(x, y, x + w, y + h), isFullScreen); } public static void removePluginView(View view, boolean isFullScreen) { - GeckoApp.mAppContext.removePluginView(view, isFullScreen); + if (getGeckoInterface() != null) + getGeckoInterface().removePluginView(view, isFullScreen); + } + + /** + * A plugin that wish to be loaded in the WebView must provide this permission + * in their AndroidManifest.xml. + */ + public static final String PLUGIN_ACTION = "android.webkit.PLUGIN"; + public static final String PLUGIN_PERMISSION = "android.webkit.permission.PLUGIN"; + + private static final String PLUGIN_SYSTEM_LIB = "/system/lib/plugins/"; + + private static final String PLUGIN_TYPE = "type"; + private static final String TYPE_NATIVE = "native"; + static public ArrayList mPackageInfoCache = new ArrayList(); + + // Returns null if plugins are blocked on the device. + static String[] getPluginDirectories() { + + // An awful hack to detect Tegra devices. Easiest way to do it without spinning up a EGL context. + boolean isTegra = (new File("/system/lib/hw/gralloc.tegra.so")).exists(); + if (isTegra) { + // disable Flash on Tegra ICS with CM9 and other custom firmware (bug 736421) + File vfile = new File("/proc/version"); + FileReader vreader = null; + try { + if (vfile.canRead()) { + vreader = new FileReader(vfile); + String version = new BufferedReader(vreader).readLine(); + if (version.indexOf("CM9") != -1 || + version.indexOf("cyanogen") != -1 || + version.indexOf("Nova") != -1) + { + Log.w(LOGTAG, "Blocking plugins because of Tegra 2 + unofficial ICS bug (bug 736421)"); + return null; + } + } + } catch (IOException ex) { + // nothing + } finally { + try { + if (vreader != null) { + vreader.close(); + } + } catch (IOException ex) { + // nothing + } + } + } + + ArrayList directories = new ArrayList(); + PackageManager pm = getContext().getPackageManager(); + List plugins = pm.queryIntentServices(new Intent(PLUGIN_ACTION), + PackageManager.GET_SERVICES | PackageManager.GET_META_DATA); + + synchronized(mPackageInfoCache) { + + // clear the list of existing packageInfo objects + mPackageInfoCache.clear(); + + + for (ResolveInfo info : plugins) { + + // retrieve the plugin's service information + ServiceInfo serviceInfo = info.serviceInfo; + if (serviceInfo == null) { + Log.w(LOGTAG, "Ignoring bad plugin."); + continue; + } + + // Blacklist HTC's flash lite. + // See bug #704516 - We're not quite sure what Flash Lite does, + // but loading it causes Flash to give errors and fail to draw. + if (serviceInfo.packageName.equals("com.htc.flashliteplugin")) { + Log.w(LOGTAG, "Skipping HTC's flash lite plugin"); + continue; + } + + + // Retrieve information from the plugin's manifest. + PackageInfo pkgInfo; + try { + pkgInfo = pm.getPackageInfo(serviceInfo.packageName, + PackageManager.GET_PERMISSIONS + | PackageManager.GET_SIGNATURES); + } catch (Exception e) { + Log.w(LOGTAG, "Can't find plugin: " + serviceInfo.packageName); + continue; + } + + if (pkgInfo == null) { + Log.w(LOGTAG, "Not loading plugin: " + serviceInfo.packageName + ". Could not load package information."); + continue; + } + + /* + * find the location of the plugin's shared library. The default + * is to assume the app is either a user installed app or an + * updated system app. In both of these cases the library is + * stored in the app's data directory. + */ + String directory = pkgInfo.applicationInfo.dataDir + "/lib"; + final int appFlags = pkgInfo.applicationInfo.flags; + final int updatedSystemFlags = ApplicationInfo.FLAG_SYSTEM | + ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; + + // preloaded system app with no user updates + if ((appFlags & updatedSystemFlags) == ApplicationInfo.FLAG_SYSTEM) { + directory = PLUGIN_SYSTEM_LIB + pkgInfo.packageName; + } + + // check if the plugin has the required permissions + String permissions[] = pkgInfo.requestedPermissions; + if (permissions == null) { + Log.w(LOGTAG, "Not loading plugin: " + serviceInfo.packageName + ". Does not have required permission."); + continue; + } + boolean permissionOk = false; + for (String permit : permissions) { + if (PLUGIN_PERMISSION.equals(permit)) { + permissionOk = true; + break; + } + } + if (!permissionOk) { + Log.w(LOGTAG, "Not loading plugin: " + serviceInfo.packageName + ". Does not have required permission (2)."); + continue; + } + + // check to ensure the plugin is properly signed + Signature signatures[] = pkgInfo.signatures; + if (signatures == null) { + Log.w(LOGTAG, "Not loading plugin: " + serviceInfo.packageName + ". Not signed."); + continue; + } + + // determine the type of plugin from the manifest + if (serviceInfo.metaData == null) { + Log.e(LOGTAG, "The plugin '" + serviceInfo.name + "' has no defined type."); + continue; + } + + String pluginType = serviceInfo.metaData.getString(PLUGIN_TYPE); + if (!TYPE_NATIVE.equals(pluginType)) { + Log.e(LOGTAG, "Unrecognized plugin type: " + pluginType); + continue; + } + + try { + Class cls = getPluginClass(serviceInfo.packageName, serviceInfo.name); + + //TODO implement any requirements of the plugin class here! + boolean classFound = true; + + if (!classFound) { + Log.e(LOGTAG, "The plugin's class' " + serviceInfo.name + "' does not extend the appropriate class."); + continue; + } + + } catch (NameNotFoundException e) { + Log.e(LOGTAG, "Can't find plugin: " + serviceInfo.packageName); + continue; + } catch (ClassNotFoundException e) { + Log.e(LOGTAG, "Can't find plugin's class: " + serviceInfo.name); + continue; + } + + // if all checks have passed then make the plugin available + mPackageInfoCache.add(pkgInfo); + directories.add(directory); + } + } + + return directories.toArray(new String[directories.size()]); + } + + static String getPluginPackage(String pluginLib) { + + if (pluginLib == null || pluginLib.length() == 0) { + return null; + } + + synchronized(mPackageInfoCache) { + for (PackageInfo pkgInfo : mPackageInfoCache) { + if (pluginLib.contains(pkgInfo.packageName)) { + return pkgInfo.packageName; + } + } + } + + return null; + } + + static Class getPluginClass(String packageName, String className) + throws NameNotFoundException, ClassNotFoundException { + Context pluginContext = getContext().createPackageContext(packageName, + Context.CONTEXT_INCLUDE_CODE | + Context.CONTEXT_IGNORE_SECURITY); + ClassLoader pluginCL = pluginContext.getClassLoader(); + return pluginCL.loadClass(className); } public static Class loadPluginClass(String className, String libName) { + if (getGeckoInterface() == null) + return null; try { - final String packageName = GeckoApp.mAppContext.getPluginPackage(libName); + final String packageName = getPluginPackage(libName); final int contextFlags = Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY; - final Context pluginContext = GeckoApp.mAppContext.createPackageContext(packageName, contextFlags); + final Context pluginContext = getContext().createPackageContext(packageName, contextFlags); return pluginContext.getClassLoader().loadClass(className); } catch (java.lang.ClassNotFoundException cnfe) { Log.w(LOGTAG, "Couldn't find plugin class " + className, cnfe); @@ -1761,8 +2004,48 @@ public class GeckoAppShell } } + private static ContextGetter sContextGetter; + public static Context getContext() { - return GeckoApp.mAppContext; + return sContextGetter.getContext(); + } + + public static void setContextGetter(ContextGetter cg) { + sContextGetter = cg; + } + + public interface GeckoInterface { + public GeckoProfile getProfile(); + public LayerView getLayerView(); + public PromptService getPromptService(); + public Activity getActivity(); + public String getDefaultUAString(); + public LocationListener getLocationListener(); + public SensorEventListener getSensorEventListener(); + public void doRestart(); + public void setFullScreen(boolean fullscreen); + public void addPluginView(View view, final Rect rect, final boolean isFullScreen); + public void removePluginView(final View view, final boolean isFullScreen); + public void enableCameraView(); + public void disableCameraView(); + public SurfaceView getCameraView(); + public void notifyWakeLockChanged(String topic, String state); + public FormAssistPopup getFormAssistPopup(); + public boolean areTabsShown(); + public AbsoluteLayout getPluginContainer(); + public void notifyCheckUpdateResult(String result); + public boolean hasTabsSideBar(); + public void invalidateOptionsMenu(); + }; + + private static GeckoInterface sGeckoInterface; + + public static GeckoInterface getGeckoInterface() { + return sGeckoInterface; + } + + public static void setGeckoInterface(GeckoInterface aGeckoInterface) { + sGeckoInterface = aGeckoInterface; } public static android.hardware.Camera sCamera = null; @@ -1777,7 +2060,8 @@ public class GeckoAppShell @Override public void run() { try { - GeckoApp.mAppContext.enableCameraView(); + if (getGeckoInterface() != null) + getGeckoInterface().enableCameraView(); } catch (Exception e) {} } }); @@ -1833,7 +2117,8 @@ public class GeckoAppShell } try { - sCamera.setPreviewDisplay(GeckoApp.mAppContext.cameraView.getHolder()); + if (getGeckoInterface() != null) + sCamera.setPreviewDisplay(getGeckoInterface().getCameraView().getHolder()); } catch(IOException e) { Log.w(LOGTAG, "Error setPreviewDisplay:", e); } catch(RuntimeException e) { @@ -1869,7 +2154,8 @@ public class GeckoAppShell @Override public void run() { try { - GeckoApp.mAppContext.disableCameraView(); + if (getGeckoInterface() != null) + getGeckoInterface().disableCameraView(); } catch (Exception e) {} } }); @@ -2013,7 +2299,7 @@ public class GeckoAppShell } public static void viewSizeChanged() { - LayerView v = GeckoApp.mAppContext.getLayerView(); + LayerView v = getLayerView(); if (v != null && v.isIMEEnabled()) { sendEventToGecko(GeckoEvent.createBroadcastEvent( "ScrollTo:FocusedInput", "")); @@ -2192,13 +2478,15 @@ public class GeckoAppShell /* Called by JNI from AndroidBridge */ public static void showFilePickerAsync(String aMimeType, long id) { - if (!sActivityHelper.showFilePicker(GeckoApp.mAppContext, aMimeType, new AsyncResultHandler(id))) { - GeckoAppShell.notifyFilePickerResult("", id); - } + if (getGeckoInterface() != null) + if (!sActivityHelper.showFilePicker(getGeckoInterface().getActivity(), aMimeType, new AsyncResultHandler(id))) { + GeckoAppShell.notifyFilePickerResult("", id); + } } public static void notifyWakeLockChanged(String topic, String state) { - GeckoApp.mAppContext.notifyWakeLockChanged(topic, state); + if (getGeckoInterface() != null) + getGeckoInterface().notifyWakeLockChanged(topic, state); } public static String getGfxInfoData() { @@ -2223,9 +2511,12 @@ public class GeckoAppShell GeckoAppShell.killAnyZombies(); // Then force unlock this profile - GeckoProfile profile = GeckoApp.mAppContext.getProfile(); - File lock = profile.getFile(".parentlock"); - return lock.exists() && lock.delete(); + if (getGeckoInterface() != null) { + GeckoProfile profile = getGeckoInterface().getProfile(); + File lock = profile.getFile(".parentlock"); + return lock.exists() && lock.delete(); + } + return false; } public static String getProxyForURI(String spec, String scheme, String host, int port) { diff --git a/mobile/android/base/GeckoEditable.java b/mobile/android/base/GeckoEditable.java index 11e559b844d..5ec496e82b1 100644 --- a/mobile/android/base/GeckoEditable.java +++ b/mobile/android/base/GeckoEditable.java @@ -346,7 +346,7 @@ final class GeckoEditable Editable.class.getClassLoader(), PROXY_INTERFACES, this); - LayerView v = GeckoApp.mAppContext.getLayerView(); + LayerView v = GeckoAppShell.getLayerView(); mListener = GeckoInputConnection.create(v, this); mIcRunHandler = mIcPostHandler = ThreadUtils.getUiHandler(); @@ -775,7 +775,7 @@ final class GeckoEditable // Set InputConnectionHandler in notifyIMEContext because // GeckoInputConnection.notifyIMEContext calls restartInput() which will invoke // InputConnectionHandler.onCreateInputConnection - LayerView v = GeckoApp.mAppContext.getLayerView(); + LayerView v = GeckoAppShell.getLayerView(); if (v != null) { mListener = GeckoInputConnection.create(v, GeckoEditable.this); v.setInputConnectionHandler((InputConnectionHandler)mListener); diff --git a/mobile/android/base/GeckoEvent.java b/mobile/android/base/GeckoEvent.java index 01947346d51..5cd72d74626 100644 --- a/mobile/android/base/GeckoEvent.java +++ b/mobile/android/base/GeckoEvent.java @@ -311,7 +311,7 @@ public class GeckoEvent { event.mPoints = new Point[1]; PointF geckoPoint = new PointF(pt.x, pt.y); - geckoPoint = GeckoApp.mAppContext.getLayerView().convertViewPointToLayerPoint(geckoPoint); + geckoPoint = GeckoAppShell.getGeckoInterface().getLayerView().convertViewPointToLayerPoint(geckoPoint); if (geckoPoint == null) { // This could happen if Gecko isn't ready yet. @@ -384,7 +384,7 @@ public class GeckoEvent { try { PointF geckoPoint = new PointF(event.getX(eventIndex), event.getY(eventIndex)); if (!keepInViewCoordinates) { - geckoPoint = GeckoApp.mAppContext.getLayerView().convertViewPointToLayerPoint(geckoPoint); + geckoPoint = GeckoAppShell.getLayerView().convertViewPointToLayerPoint(geckoPoint); } mPoints[index] = new Point(Math.round(geckoPoint.x), Math.round(geckoPoint.y)); @@ -415,7 +415,7 @@ public class GeckoEvent { } } else { float size = event.getSize(eventIndex); - Resources resources = GeckoApp.mAppContext.getResources(); + Resources resources = GeckoAppShell.getContext().getResources(); DisplayMetrics displaymetrics = resources.getDisplayMetrics(); size = size*Math.min(displaymetrics.heightPixels, displaymetrics.widthPixels); mPointRadii[index] = new Point((int)size,(int)size); diff --git a/mobile/android/base/GeckoInputConnection.java b/mobile/android/base/GeckoInputConnection.java index 3b276b30412..628ae37a997 100644 --- a/mobile/android/base/GeckoInputConnection.java +++ b/mobile/android/base/GeckoInputConnection.java @@ -327,7 +327,7 @@ class GeckoInputConnection } private static View getView() { - return GeckoApp.mAppContext.getLayerView(); + return GeckoAppShell.getLayerView(); } private static InputMethodManager getInputMethodManager() { @@ -609,8 +609,8 @@ class GeckoInputConnection outAttrs.actionLabel = mIMEActionHint; } - GeckoApp app = GeckoApp.mAppContext; - DisplayMetrics metrics = app.getResources().getDisplayMetrics(); + Context context = GeckoAppShell.getContext(); + DisplayMetrics metrics = context.getResources().getDisplayMetrics(); if (Math.min(metrics.widthPixels, metrics.heightPixels) > INLINE_IME_MIN_DISPLAY_SIZE) { // prevent showing full-screen keyboard only when the screen is tall enough // to show some reasonable amount of the page (see bug 752709) @@ -625,14 +625,14 @@ class GeckoInputConnection } String prevInputMethod = mCurrentInputMethod; - mCurrentInputMethod = InputMethods.getCurrentInputMethod(app); + mCurrentInputMethod = InputMethods.getCurrentInputMethod(context); if (DEBUG) { Log.d(LOGTAG, "IME: CurrentInputMethod=" + mCurrentInputMethod); } // If the user has changed IMEs, then notify input method observers. - if (!mCurrentInputMethod.equals(prevInputMethod)) { - FormAssistPopup popup = app.mFormAssistPopup; + if (!mCurrentInputMethod.equals(prevInputMethod) && GeckoAppShell.getGeckoInterface() != null) { + FormAssistPopup popup = GeckoAppShell.getGeckoInterface().getFormAssistPopup(); if (popup != null) { popup.onInputMethodChanged(mCurrentInputMethod); } diff --git a/mobile/android/base/GeckoPreferences.java b/mobile/android/base/GeckoPreferences.java index a4f89252115..0ed1eabfa0f 100644 --- a/mobile/android/base/GeckoPreferences.java +++ b/mobile/android/base/GeckoPreferences.java @@ -351,9 +351,9 @@ public class GeckoPreferences } else if (PREFS_ANNOUNCEMENTS_ENABLED.equals(prefName)) { // Send a broadcast intent to the product announcements service, either to start or // to stop the repeated background checks. - broadcastAnnouncementsPref(GeckoApp.mAppContext, ((Boolean) newValue).booleanValue()); + broadcastAnnouncementsPref(GeckoAppShell.getContext(), ((Boolean) newValue).booleanValue()); } else if (PREFS_UPDATER_AUTODOWNLOAD.equals(prefName)) { - org.mozilla.gecko.updater.UpdateServiceHelper.registerForUpdates(GeckoApp.mAppContext, (String) newValue); + org.mozilla.gecko.updater.UpdateServiceHelper.registerForUpdates(GeckoAppShell.getContext(), (String) newValue); } else if (PREFS_HEALTHREPORT_UPLOAD_ENABLED.equals(prefName)) { // Healthreport pref only lives in Android. Do not persist to Gecko. return true; @@ -377,7 +377,7 @@ public class GeckoPreferences } private EditText getTextBox(int aHintText) { - EditText input = new EditText(GeckoApp.mAppContext); + EditText input = new EditText(GeckoAppShell.getContext()); int inputtype = InputType.TYPE_CLASS_TEXT; inputtype |= InputType.TYPE_TEXT_VARIATION_PASSWORD | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS; input.setInputType(inputtype); diff --git a/mobile/android/base/GeckoScreenOrientationListener.java b/mobile/android/base/GeckoScreenOrientationListener.java index 2187a231c93..e4ceecad37f 100644 --- a/mobile/android/base/GeckoScreenOrientationListener.java +++ b/mobile/android/base/GeckoScreenOrientationListener.java @@ -13,6 +13,8 @@ import android.view.Surface; import java.util.Arrays; import java.util.List; +import android.app.Activity; + public class GeckoScreenOrientationListener { private static final String LOGTAG = "GeckoScreenOrientationListener"; @@ -51,7 +53,7 @@ public class GeckoScreenOrientationListener { private static final String DEFAULT_ORIENTATION_PREF = "app.orientation.default"; private GeckoScreenOrientationListener() { - mListener = new OrientationEventListenerImpl(GeckoApp.mAppContext); + mListener = new OrientationEventListenerImpl(GeckoAppShell.getContext()); PrefsHelper.getPref(DEFAULT_ORIENTATION_PREF, new PrefsHelper.PrefHandlerBase() { @Override public void prefValue(String pref, String value) { @@ -141,7 +143,11 @@ public class GeckoScreenOrientationListener { } private void updateScreenOrientation() { - int rotation = GeckoApp.mAppContext.getWindowManager().getDefaultDisplay().getRotation(); + Context context = GeckoAppShell.getContext(); + int rotation = mDefaultOrientation; + if (context instanceof Activity) { + rotation = ((Activity)context).getWindowManager().getDefaultDisplay().getRotation(); + } short previousOrientation = mOrientation; if (rotation == Surface.ROTATION_0) { @@ -192,16 +198,18 @@ public class GeckoScreenOrientationListener { Log.e(LOGTAG, "Unexpected value received! (" + aOrientation + ")"); return; } - - GeckoApp.mAppContext.setRequestedOrientation(orientation); + if (GeckoAppShell.getContext() instanceof Activity) + ((Activity)GeckoAppShell.getContext()).setRequestedOrientation(orientation); updateScreenOrientation(); } public void unlockScreenOrientation() { - if (GeckoApp.mAppContext.getRequestedOrientation() == mDefaultOrientation) - return; + if (!(GeckoAppShell.getContext() instanceof Activity)) + return; + if (((Activity)GeckoAppShell.getContext()).getRequestedOrientation() == mDefaultOrientation) + return; - GeckoApp.mAppContext.setRequestedOrientation(mDefaultOrientation); + ((Activity)GeckoAppShell.getContext()).setRequestedOrientation(mDefaultOrientation); updateScreenOrientation(); } } diff --git a/mobile/android/base/GeckoSmsManager.java b/mobile/android/base/GeckoSmsManager.java index 8a30f73a40e..0ab9963effe 100644 --- a/mobile/android/base/GeckoSmsManager.java +++ b/mobile/android/base/GeckoSmsManager.java @@ -362,7 +362,7 @@ public class GeckoSmsManager smsFilter.addAction(GeckoSmsManager.ACTION_SMS_SENT); smsFilter.addAction(GeckoSmsManager.ACTION_SMS_DELIVERED); - GeckoApp.mAppContext.registerReceiver(this, smsFilter); + GeckoAppShell.getContext().registerReceiver(this, smsFilter); } @Override @@ -523,12 +523,12 @@ public class GeckoSmsManager * generated by GetPendingIntentUID(). */ PendingIntent sentPendingIntent = - PendingIntent.getBroadcast(GeckoApp.mAppContext, + PendingIntent.getBroadcast(GeckoAppShell.getContext(), PendingIntentUID.generate(), sentIntent, PendingIntent.FLAG_CANCEL_CURRENT); PendingIntent deliveredPendingIntent = - PendingIntent.getBroadcast(GeckoApp.mAppContext, + PendingIntent.getBroadcast(GeckoAppShell.getContext(), PendingIntentUID.generate(), deliveredIntent, PendingIntent.FLAG_CANCEL_CURRENT); @@ -549,13 +549,13 @@ public class GeckoSmsManager for (int i=0; i(); - Cursor c = BrowserDB.getAllVisitedHistory(GeckoApp.mAppContext.getContentResolver()); + Cursor c = BrowserDB.getAllVisitedHistory(GeckoAppShell.getContext().getContentResolver()); if (c.moveToFirst()) { do { visitedSet.add(c.getString(0)); @@ -120,7 +120,7 @@ class GlobalHistory { if (!canAddURI(uri)) return; - BrowserDB.updateVisitedHistory(GeckoApp.mAppContext.getContentResolver(), uri); + BrowserDB.updateVisitedHistory(GeckoAppShell.getContext().getContentResolver(), uri); addToGeckoOnly(uri); } @@ -128,7 +128,7 @@ class GlobalHistory { if (!canAddURI(uri)) return; - BrowserDB.updateHistoryTitle(GeckoApp.mAppContext.getContentResolver(), uri, title); + BrowserDB.updateHistoryTitle(GeckoAppShell.getContext().getContentResolver(), uri, title); } public void checkUriVisited(final String uri) { diff --git a/mobile/android/base/Makefile.in b/mobile/android/base/Makefile.in index 4c778e9e7d6..722f3d958f5 100644 --- a/mobile/android/base/Makefile.in +++ b/mobile/android/base/Makefile.in @@ -73,6 +73,7 @@ FENNEC_JAVA_FILES = \ CanvasDelegate.java \ CheckableLinearLayout.java \ ClickableWhenDisabledEditText.java \ + ContextGetter.java \ CustomEditText.java \ db/BrowserDB.java \ db/LocalBrowserDB.java \ diff --git a/mobile/android/base/SiteIdentityPopup.java b/mobile/android/base/SiteIdentityPopup.java index fa591357955..c401e87f14a 100644 --- a/mobile/android/base/SiteIdentityPopup.java +++ b/mobile/android/base/SiteIdentityPopup.java @@ -48,9 +48,9 @@ public class SiteIdentityPopup extends PopupWindow { private int mYOffset; private SiteIdentityPopup() { - super(GeckoApp.mAppContext); + super(GeckoAppShell.getContext()); - mResources = GeckoApp.mAppContext.getResources(); + mResources = GeckoAppShell.getContext().getResources(); mYOffset = mResources.getDimensionPixelSize(R.dimen.menu_popup_offset); mInflated = false; setAnimationStyle(R.style.PopupAnimation); @@ -73,7 +73,7 @@ public class SiteIdentityPopup extends PopupWindow { setWindowLayoutMode(HardwareUtils.isTablet() ? LayoutParams.WRAP_CONTENT : LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT); - LayoutInflater inflater = LayoutInflater.from(GeckoApp.mAppContext); + LayoutInflater inflater = LayoutInflater.from(GeckoAppShell.getContext()); RelativeLayout layout = (RelativeLayout) inflater.inflate(R.layout.site_identity_popup, null); setContentView(layout); diff --git a/mobile/android/base/SuggestClient.java b/mobile/android/base/SuggestClient.java index 5955117af89..20c90778ae4 100644 --- a/mobile/android/base/SuggestClient.java +++ b/mobile/android/base/SuggestClient.java @@ -25,7 +25,7 @@ import java.util.ArrayList; */ public class SuggestClient { private static final String LOGTAG = "GeckoSuggestClient"; - private static final String USER_AGENT = GeckoApp.mAppContext.getDefaultUAString(); + private static final String USER_AGENT = GeckoAppShell.getGeckoInterface().getDefaultUAString(); private final Context mContext; private final int mTimeout; diff --git a/mobile/android/base/Tabs.java b/mobile/android/base/Tabs.java index d09c267ac62..af59b448d37 100644 --- a/mobile/android/base/Tabs.java +++ b/mobile/android/base/Tabs.java @@ -15,6 +15,7 @@ import org.json.JSONObject; import android.accounts.Account; import android.accounts.AccountManager; import android.accounts.OnAccountsUpdateListener; +import android.app.Activity; import android.content.ContentResolver; import android.database.ContentObserver; import android.graphics.Color; @@ -63,7 +64,7 @@ public class Tabs implements GeckoEventListener { private static AtomicInteger sTabId = new AtomicInteger(0); private volatile boolean mInitialTabsAdded; - private GeckoApp mActivity; + private Activity mActivity; private ContentObserver mContentObserver; private final Runnable mPersistTabsRunnable = new Runnable() { @@ -99,7 +100,7 @@ public class Tabs implements GeckoEventListener { registerEventListener("DesktopMode:Changed"); } - public synchronized void attachToActivity(GeckoApp activity) { + public synchronized void attachToActivity(Activity activity) { if (mActivity == activity) { return; } @@ -130,7 +131,7 @@ public class Tabs implements GeckoEventListener { // detached; however, we have lifecycle issues with GeckoApp and Tabs that // requires us to keep it around (see // https://bugzilla.mozilla.org/show_bug.cgi?id=844407). - public synchronized void detachFromActivity(GeckoApp activity) { + public synchronized void detachFromActivity(Activity activity) { ThreadUtils.getBackgroundHandler().removeCallbacks(mPersistTabsRunnable); if (mContentObserver != null) { @@ -357,7 +358,7 @@ public class Tabs implements GeckoEventListener { * @return the current GeckoApp instance, or throws if * we aren't correctly initialized. */ - private synchronized GeckoApp getActivity() { + private synchronized android.app.Activity getActivity() { if (mActivity == null) { throw new IllegalStateException("Tabs not initialized with a GeckoApp instance."); } diff --git a/mobile/android/base/TabsPanel.java b/mobile/android/base/TabsPanel.java index 6b8feca6193..2d671ea6ba8 100644 --- a/mobile/android/base/TabsPanel.java +++ b/mobile/android/base/TabsPanel.java @@ -217,7 +217,7 @@ public class TabsPanel extends LinearLayout @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - if (!GeckoApp.mAppContext.hasTabsSideBar()) { + if (!GeckoAppShell.getGeckoInterface().hasTabsSideBar()) { int heightSpec = MeasureSpec.makeMeasureSpec(getTabContainerHeight(TabsListContainer.this), MeasureSpec.EXACTLY); super.onMeasure(widthMeasureSpec, heightSpec); } else { diff --git a/mobile/android/base/ThumbnailHelper.java b/mobile/android/base/ThumbnailHelper.java index 677ab101c2f..56d09829c2e 100644 --- a/mobile/android/base/ThumbnailHelper.java +++ b/mobile/android/base/ThumbnailHelper.java @@ -52,7 +52,7 @@ public final class ThumbnailHelper { private ThumbnailHelper() { mPendingThumbnails = new LinkedList(); - mPendingWidth = new AtomicInteger((int)GeckoApp.mAppContext.getResources().getDimension(R.dimen.tab_thumbnail_width)); + mPendingWidth = new AtomicInteger((int)GeckoAppShell.getContext().getResources().getDimension(R.dimen.tab_thumbnail_width)); mWidth = -1; mHeight = -1; } @@ -66,7 +66,7 @@ public final class ThumbnailHelper { if (tab.getState() == Tab.STATE_DELAYED) { String url = tab.getURL(); if (url != null) { - byte[] thumbnail = BrowserDB.getThumbnailForUrl(GeckoApp.mAppContext.getContentResolver(), url); + byte[] thumbnail = BrowserDB.getThumbnailForUrl(GeckoAppShell.getContext().getContentResolver(), url); if (thumbnail != null) { setTabThumbnail(tab, null, thumbnail); } @@ -198,6 +198,6 @@ public final class ThumbnailHelper { } private boolean shouldUpdateThumbnail(Tab tab) { - return (Tabs.getInstance().isSelectedTab(tab) || GeckoApp.mAppContext.areTabsShown()); + return (Tabs.getInstance().isSelectedTab(tab) || (GeckoAppShell.getGeckoInterface() != null && GeckoAppShell.getGeckoInterface().areTabsShown())); } } diff --git a/mobile/android/base/WebAppAllocator.java b/mobile/android/base/WebAppAllocator.java index 51143ed14e0..98f38974101 100644 --- a/mobile/android/base/WebAppAllocator.java +++ b/mobile/android/base/WebAppAllocator.java @@ -21,7 +21,7 @@ public class WebAppAllocator { protected static GeckoApp sContext = null; protected static WebAppAllocator sInstance = null; public static WebAppAllocator getInstance() { - return getInstance(GeckoApp.mAppContext); + return getInstance(GeckoAppShell.getContext()); } public static synchronized WebAppAllocator getInstance(Context cx) { diff --git a/mobile/android/base/awesomebar/AllPagesTab.java b/mobile/android/base/awesomebar/AllPagesTab.java index 037ad61b322..4dd09a326c2 100644 --- a/mobile/android/base/awesomebar/AllPagesTab.java +++ b/mobile/android/base/awesomebar/AllPagesTab.java @@ -657,7 +657,7 @@ public class AllPagesTab extends AwesomeBarTab implements GeckoEventListener { // should be safe to assume that null means non-private. Tab tab = Tabs.getInstance().getSelectedTab(); if (tab == null || !tab.isPrivate()) - mSuggestClient = new SuggestClient(GeckoApp.mAppContext, suggestTemplate, SUGGESTION_TIMEOUT, SUGGESTION_MAX); + mSuggestClient = new SuggestClient(getView().getContext(), suggestTemplate, SUGGESTION_TIMEOUT, SUGGESTION_MAX); } else { searchEngines.add(new SearchEngine(name, identifier, icon)); } diff --git a/mobile/android/base/gfx/LayerView.java b/mobile/android/base/gfx/LayerView.java index 48440acaca0..a880b1677ac 100644 --- a/mobile/android/base/gfx/LayerView.java +++ b/mobile/android/base/gfx/LayerView.java @@ -6,7 +6,7 @@ package org.mozilla.gecko.gfx; import org.mozilla.gecko.GeckoAccessibility; -import org.mozilla.gecko.GeckoApp; +import org.mozilla.gecko.GeckoAppShell; import org.mozilla.gecko.R; import org.mozilla.gecko.TouchEventInterceptor; import org.mozilla.gecko.ZoomConstraints; @@ -450,7 +450,7 @@ public class LayerView extends FrameLayout { /** This function is invoked by Gecko (compositor thread) via JNI; be careful when modifying signature. */ public static GLController registerCxxCompositor() { try { - LayerView layerView = GeckoApp.mAppContext.getLayerView(); + LayerView layerView = GeckoAppShell.getLayerView(); GLController controller = layerView.getGLController(); controller.compositorCreated(); return controller; diff --git a/mobile/android/base/gfx/PluginLayer.java b/mobile/android/base/gfx/PluginLayer.java index 74f165b7772..2017f70149f 100644 --- a/mobile/android/base/gfx/PluginLayer.java +++ b/mobile/android/base/gfx/PluginLayer.java @@ -4,7 +4,7 @@ package org.mozilla.gecko.gfx; -import org.mozilla.gecko.GeckoApp; +import org.mozilla.gecko.GeckoAppShell; import org.mozilla.gecko.util.FloatUtils; import org.mozilla.gecko.util.ThreadUtils; @@ -39,7 +39,7 @@ public class PluginLayer extends TileLayer { super(new BufferedCairoImage(null, 0, 0, 0), TileLayer.PaintMode.NORMAL); mView = view; - mContainer = GeckoApp.mAppContext.getPluginContainer(); + mContainer = GeckoAppShell.getGeckoInterface().getPluginContainer(); mView.setWillNotDraw(false); if (mView instanceof SurfaceView) { From d8e43fcdc16decd2399be753da372e01752cb1ef Mon Sep 17 00:00:00 2001 From: Jim Blandy Date: Fri, 24 May 2013 10:13:42 -0700 Subject: [PATCH 28/91] Bug 862490: Clarify sharing of _createExtraActors and _appendExtraActors between BrowserRootActor and BrowserTabActor. r=past --- toolkit/devtools/server/actors/webbrowser.js | 106 ++++++++++++++----- toolkit/devtools/server/main.js | 12 ++- 2 files changed, 88 insertions(+), 30 deletions(-) diff --git a/toolkit/devtools/server/actors/webbrowser.js b/toolkit/devtools/server/actors/webbrowser.js index 5645991a754..66beec19b58 100644 --- a/toolkit/devtools/server/actors/webbrowser.js +++ b/toolkit/devtools/server/actors/webbrowser.js @@ -9,6 +9,77 @@ * Browser-specific actors. */ +/** + * Methods shared between BrowserRootActor and BrowserTabActor. + */ + +/** + * Populate |this._extraActors| as specified by |aFactories|, reusing whatever + * actors are already there. Add all actors in the final extra actors table to + * |aPool|. + * + * The root actor and the tab actor use this to instantiate actors that other + * parts of the browser have specified with DebuggerServer.addTabActor antd + * DebuggerServer.addGlobalActor. + * + * @param aFactories + * An object whose own property names are the names of properties to add to + * some reply packet (say, a tab actor grip or the "listTabs" response + * form), and whose own property values are actor constructor functions, as + * documented for addTabActor and addGlobalActor. + * + * @param this + * The BrowserRootActor or BrowserTabActor with which the new actors will + * be associated. It should support whatever API the |aFactories| + * constructor functions might be interested in, as it is passed to them. + * For the sake of CommonCreateExtraActors itself, it should have at least + * the following properties: + * + * - _extraActors + * An object whose own property names are factory table (and packet) + * property names, and whose values are no-argument actor constructors, + * of the sort that one can add to an ActorPool. + * + * - conn + * The DebuggerServerConnection in which the new actors will participate. + * + * - actorID + * The actor's name, for use as the new actors' parentID. + */ +function CommonCreateExtraActors(aFactories, aPool) { + // Walk over global actors added by extensions. + for (let name in aFactories) { + let actor = this._extraActors[name]; + if (!actor) { + actor = aFactories[name].bind(null, this.conn, this); + actor.prototype = aFactories[name].prototype; + actor.parentID = this.actorID; + this._extraActors[name] = actor; + } + aPool.addActor(actor); + } +} + +/** + * Append the extra actors in |this._extraActors|, constructed by a prior call + * to CommonCreateExtraActors, to |aObject|. + * + * @param aObject + * The object to which the extra actors should be added, under the + * property names given in the |aFactories| table passed to + * CommonCreateExtraActors. + * + * @param this + * The BrowserRootActor or BrowserTabActor whose |_extraActors| table we + * should use; see above. + */ +function CommonAppendExtraActors(aObject) { + for (let name in this._extraActors) { + let actor = this._extraActors[name]; + aObject[name] = actor.actorID; + } +} + var windowMediator = Cc["@mozilla.org/appshell/window-mediator;1"] .getService(Ci.nsIWindowMediator); @@ -143,32 +214,9 @@ BrowserRootActor.prototype = { return response; }, - /** - * Adds dynamically-added actors from add-ons to the provided pool. - */ - _createExtraActors: function BRA_createExtraActors(aFactories, aPool) { - // Walk over global actors added by extensions. - for (let name in aFactories) { - let actor = this._extraActors[name]; - if (!actor) { - actor = aFactories[name].bind(null, this.conn, this); - actor.prototype = aFactories[name].prototype; - actor.parentID = this.actorID; - this._extraActors[name] = actor; - } - aPool.addActor(actor); - } - }, - - /** - * Appends the extra actors to the specified object. - */ - _appendExtraActors: function BRA_appendExtraActors(aObject) { - for (let name in this._extraActors) { - let actor = this._extraActors[name]; - aObject[name] = actor.actorID; - } - }, + /* Support for DebuggerServer.addGlobalActor. */ + _createExtraActors: CommonCreateExtraActors, + _appendExtraActors: CommonAppendExtraActors, /** * Watch a window that was visited during onListTabs for @@ -312,8 +360,6 @@ function BrowserTabActor(aConnection, aBrowser, aTabBrowser) // A map of actor names to actor instances provided by extensions. this._extraActors = {}; - this._createExtraActors = BrowserRootActor.prototype._createExtraActors.bind(this); - this._appendExtraActors = BrowserRootActor.prototype._appendExtraActors.bind(this); this._onWindowCreated = this.onWindowCreated.bind(this); } @@ -444,6 +490,10 @@ BrowserTabActor.prototype = { this._tabbrowser = null; }, + /* Support for DebuggerServer.addTabActor. */ + _createExtraActors: CommonCreateExtraActors, + _appendExtraActors: CommonAppendExtraActors, + /** * Does the actual work of attching to a tab. */ diff --git a/toolkit/devtools/server/main.js b/toolkit/devtools/server/main.js index 567586173ff..182460739a3 100644 --- a/toolkit/devtools/server/main.js +++ b/toolkit/devtools/server/main.js @@ -330,7 +330,11 @@ var DebuggerServer = { * 'actor', since that would break the protocol. * * @param aFunction function - * The constructor function for this request type. + * The constructor function for this request type. This expects to be + * called as a constructor (i.e. with 'new'), and passed two + * arguments: the DebuggerServerConnection, and the BrowserTabActor + * with which it will be associated. + * * @param aName string [optional] * The name of the new request type. If this is not present, the * actorPrefix property of the constructor prototype is used. @@ -371,7 +375,11 @@ var DebuggerServer = { * protocol. * * @param aFunction function - * The constructor function for this request type. + * The constructor function for this request type. This expects to be + * called as a constructor (i.e. with 'new'), and passed two + * arguments: the DebuggerServerConnection, and the BrowserRootActor + * with which it will be associated. + * * @param aName string [optional] * The name of the new request type. If this is not present, the * actorPrefix property of the constructor prototype is used. From 39ee36fa364381cfa6a33b950050a312fb28c336 Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Fri, 24 May 2013 13:09:08 -0400 Subject: [PATCH 29/91] Bug 873553 - Part 1: Remove AudioContext::GetRate; r=roc This method is currently unused. --HG-- extra : rebase_source : c6d5a9788e6cb03fc965852e5b97a72e05cfd3e2 --- content/media/webaudio/AudioContext.h | 1 - 1 file changed, 1 deletion(-) diff --git a/content/media/webaudio/AudioContext.h b/content/media/webaudio/AudioContext.h index 552eac64701..5f7958983d5 100644 --- a/content/media/webaudio/AudioContext.h +++ b/content/media/webaudio/AudioContext.h @@ -186,7 +186,6 @@ public: void StartRendering(); IMPL_EVENT_HANDLER(complete) - uint32_t GetRate() const { return IdealAudioRate(); } bool IsOffline() const { return mIsOffline; } MediaStreamGraph* Graph() const; From d0bcd953b7062cbf4ffd48f31bff82dc7ba6ff35 Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Fri, 24 May 2013 13:09:29 -0400 Subject: [PATCH 30/91] Bug 873553 - Part 2: Teach each AudioNodeStream about its sampling rate, and store the value inside AudioContext; r=roc --HG-- extra : rebase_source : beed6a6f965acdff307b7d4861d5f336c224d498 --- content/media/AudioNodeEngine.h | 7 ++-- content/media/AudioNodeStream.cpp | 12 +++--- content/media/AudioNodeStream.h | 8 +++- content/media/MediaStreamGraph.cpp | 53 +++++++++++++++++++------ content/media/MediaStreamGraph.h | 7 +++- content/media/MediaStreamGraphImpl.h | 1 + content/media/webaudio/AudioContext.cpp | 3 +- content/media/webaudio/AudioContext.h | 5 ++- content/media/webaudio/AudioParam.cpp | 5 ++- 9 files changed, 74 insertions(+), 27 deletions(-) diff --git a/content/media/AudioNodeEngine.h b/content/media/AudioNodeEngine.h index 8785c64fd36..a60e7f601a0 100644 --- a/content/media/AudioNodeEngine.h +++ b/content/media/AudioNodeEngine.h @@ -7,13 +7,13 @@ #define MOZILLA_AUDIONODEENGINE_H_ #include "AudioSegment.h" +#include "mozilla/dom/AudioNode.h" #include "mozilla/dom/AudioParam.h" #include "mozilla/Mutex.h" namespace mozilla { namespace dom { -class AudioNode; struct ThreeDPoint; } @@ -154,6 +154,7 @@ public: , mInputCount(aNode ? aNode->NumberOfInputs() : 1) , mOutputCount(aNode ? aNode->NumberOfOutputs() : 0) { + MOZ_ASSERT(NS_IsMainThread()); MOZ_COUNT_CTOR(AudioNodeEngine); } virtual ~AudioNodeEngine() @@ -198,8 +199,8 @@ public: * Produce the next block of audio samples, given input samples aInput * (the mixed data for input 0). * aInput is guaranteed to have float sample format (if it has samples at all) - * and to have been resampled to IdealAudioRate(), and to have exactly - * WEBAUDIO_BLOCK_SIZE samples. + * and to have been resampled to the sampling rate for the stream, and to have + * exactly WEBAUDIO_BLOCK_SIZE samples. * *aFinished is set to false by the caller. If the callee sets it to true, * we'll finish the stream and not call this again. */ diff --git a/content/media/AudioNodeStream.cpp b/content/media/AudioNodeStream.cpp index bfeb7c05992..8a98668d296 100644 --- a/content/media/AudioNodeStream.cpp +++ b/content/media/AudioNodeStream.cpp @@ -15,7 +15,9 @@ namespace mozilla { /** * An AudioNodeStream produces a single audio track with ID - * AUDIO_NODE_STREAM_TRACK_ID. This track has rate IdealAudioRate(). + * AUDIO_NODE_STREAM_TRACK_ID. This track has rate AudioContext::sIdealAudioRate + * for regular audio contexts, and the rate requested by the web content + * for offline audio contexts. * Each chunk in the track is a single block of WEBAUDIO_BLOCK_SIZE samples. */ static const int AUDIO_NODE_STREAM_TRACK_ID = 1; @@ -237,11 +239,11 @@ AudioNodeStream::EnsureTrack() nsAutoPtr segment(new AudioSegment()); for (uint32_t j = 0; j < mListeners.Length(); ++j) { MediaStreamListener* l = mListeners[j]; - l->NotifyQueuedTrackChanges(Graph(), AUDIO_NODE_STREAM_TRACK_ID, IdealAudioRate(), 0, + l->NotifyQueuedTrackChanges(Graph(), AUDIO_NODE_STREAM_TRACK_ID, mSampleRate, 0, MediaStreamListener::TRACK_EVENT_CREATED, *segment); } - track = &mBuffer.AddTrack(AUDIO_NODE_STREAM_TRACK_ID, IdealAudioRate(), 0, segment.forget()); + track = &mBuffer.AddTrack(AUDIO_NODE_STREAM_TRACK_ID, mSampleRate, 0, segment.forget()); } return track; } @@ -437,7 +439,7 @@ AudioNodeStream::ProduceOutput(GraphTime aFrom, GraphTime aTo) AudioSegment tmpSegment; tmpSegment.AppendAndConsumeChunk(©Chunk); l->NotifyQueuedTrackChanges(Graph(), AUDIO_NODE_STREAM_TRACK_ID, - IdealAudioRate(), segment->GetDuration(), 0, + mSampleRate, segment->GetDuration(), 0, tmpSegment); } } @@ -463,7 +465,7 @@ AudioNodeStream::FinishOutput() MediaStreamListener* l = mListeners[j]; AudioSegment emptySegment; l->NotifyQueuedTrackChanges(Graph(), AUDIO_NODE_STREAM_TRACK_ID, - IdealAudioRate(), + mSampleRate, track->GetSegment()->GetDuration(), MediaStreamListener::TRACK_EVENT_ENDED, emptySegment); } diff --git a/content/media/AudioNodeStream.h b/content/media/AudioNodeStream.h index 08cf389151d..4beedc7b0e8 100644 --- a/content/media/AudioNodeStream.h +++ b/content/media/AudioNodeStream.h @@ -47,14 +47,17 @@ public: * Transfers ownership of aEngine to the new AudioNodeStream. */ AudioNodeStream(AudioNodeEngine* aEngine, - MediaStreamGraph::AudioNodeStreamKind aKind) + MediaStreamGraph::AudioNodeStreamKind aKind, + TrackRate aSampleRate) : ProcessedMediaStream(nullptr), mEngine(aEngine), + mSampleRate(aSampleRate), mKind(aKind), mNumberOfInputChannels(2), mMarkAsFinishedAfterThisBlock(false), mAudioParamStream(false) { + MOZ_ASSERT(NS_IsMainThread()); mChannelCountMode = dom::ChannelCountMode::Max; mChannelInterpretation = dom::ChannelInterpretation::Speakers; // AudioNodes are always producing data @@ -108,6 +111,7 @@ public: // Any thread AudioNodeEngine* Engine() { return mEngine; } + TrackRate SampleRate() const { return mSampleRate; } protected: void FinishOutput(); @@ -119,6 +123,8 @@ protected: nsAutoPtr mEngine; // The last block produced by this node. OutputChunks mLastChunks; + // The stream's sampling rate + const TrackRate mSampleRate; // Whether this is an internal or external stream MediaStreamGraph::AudioNodeStreamKind mKind; // The number of input channels that this stream requires. 0 means don't care. diff --git a/content/media/MediaStreamGraph.cpp b/content/media/MediaStreamGraph.cpp index bfc6520b107..5efca39536c 100644 --- a/content/media/MediaStreamGraph.cpp +++ b/content/media/MediaStreamGraph.cpp @@ -909,28 +909,28 @@ MediaStreamGraphImpl::EnsureNextIterationLocked(MonitorAutoLock& aLock) } static GraphTime -RoundUpToAudioBlock(GraphTime aTime) +RoundUpToAudioBlock(TrackRate aSampleRate, GraphTime aTime) { - TrackRate rate = IdealAudioRate(); - int64_t ticksAtIdealRate = (aTime*rate) >> MEDIA_TIME_FRAC_BITS; + int64_t ticksAtIdealaSampleRate = (aTime*aSampleRate) >> MEDIA_TIME_FRAC_BITS; // Round up to nearest block boundary - int64_t blocksAtIdealRate = - (ticksAtIdealRate + (WEBAUDIO_BLOCK_SIZE - 1)) >> + int64_t blocksAtIdealaSampleRate = + (ticksAtIdealaSampleRate + (WEBAUDIO_BLOCK_SIZE - 1)) >> WEBAUDIO_BLOCK_SIZE_BITS; // Round up to nearest MediaTime unit return - ((((blocksAtIdealRate + 1)*WEBAUDIO_BLOCK_SIZE) << MEDIA_TIME_FRAC_BITS) - + rate - 1)/rate; + ((((blocksAtIdealaSampleRate + 1)*WEBAUDIO_BLOCK_SIZE) << MEDIA_TIME_FRAC_BITS) + + aSampleRate - 1)/aSampleRate; } void MediaStreamGraphImpl::ProduceDataForStreamsBlockByBlock(uint32_t aStreamIndex, + TrackRate aSampleRate, GraphTime aFrom, GraphTime aTo) { GraphTime t = aFrom; while (t < aTo) { - GraphTime next = RoundUpToAudioBlock(t + 1); + GraphTime next = RoundUpToAudioBlock(aSampleRate, t + 1); for (uint32_t i = aStreamIndex; i < mStreams.Length(); ++i) { nsRefPtr ps = mStreams[i]->AsProcessedStream(); if (ps) { @@ -982,8 +982,21 @@ MediaStreamGraphImpl::RunThread() UpdateStreamOrder(); + // Find the sampling rate that we need to use for non-realtime graphs. + TrackRate sampleRate = IdealAudioRate(); + if (!mRealtime) { + for (uint32_t i = 0; i < mStreams.Length(); ++i) { + AudioNodeStream* n = mStreams[i]->AsAudioNodeStream(); + if (n) { + // We know that the rest of the streams will run at the same rate. + sampleRate = n->SampleRate(); + break; + } + } + } + GraphTime endBlockingDecisions = - RoundUpToAudioBlock(mCurrentTime + MillisecondsToMediaTime(AUDIO_TARGET_MS)); + RoundUpToAudioBlock(sampleRate, mCurrentTime + MillisecondsToMediaTime(AUDIO_TARGET_MS)); bool ensureNextIteration = false; // Grab pending stream input. @@ -1012,9 +1025,20 @@ MediaStreamGraphImpl::RunThread() if (ps) { AudioNodeStream* n = stream->AsAudioNodeStream(); if (n) { +#ifdef DEBUG + // Verify that the sampling rate for all of the following streams is the same + for (uint32_t j = i + 1; j < mStreams.Length(); ++j) { + AudioNodeStream* nextStream = mStreams[j]->AsAudioNodeStream(); + if (nextStream) { + MOZ_ASSERT(n->SampleRate() == nextStream->SampleRate(), + "All AudioNodeStreams in the graph must have the same sampling rate"); + } + } +#endif // Since an AudioNodeStream is present, go ahead and // produce audio block by block for all the rest of the streams. - ProduceDataForStreamsBlockByBlock(i, prevComputedTime, mStateComputedTime); + ProduceDataForStreamsBlockByBlock(i, n->SampleRate(), prevComputedTime, mStateComputedTime); + ticksProcessed += TimeToTicksRoundDown(n->SampleRate(), mStateComputedTime - prevComputedTime); doneAllProducing = true; } else { ps->ProduceOutput(prevComputedTime, mStateComputedTime); @@ -1042,7 +1066,6 @@ MediaStreamGraphImpl::RunThread() } } if (!mRealtime) { - ticksProcessed += TimeToTicksRoundDown(IdealAudioRate(), mStateComputedTime - prevComputedTime); // Terminate processing if we've produce enough non-realtime ticks. if (ticksProcessed >= mNonRealtimeTicksToProcess) { // Wait indefinitely when we've processed enough non-realtime ticks. @@ -2092,10 +2115,14 @@ MediaStreamGraph::CreateTrackUnionStream(DOMMediaStream* aWrapper) AudioNodeStream* MediaStreamGraph::CreateAudioNodeStream(AudioNodeEngine* aEngine, - AudioNodeStreamKind aKind) + AudioNodeStreamKind aKind, + TrackRate aSampleRate) { MOZ_ASSERT(NS_IsMainThread()); - AudioNodeStream* stream = new AudioNodeStream(aEngine, aKind); + if (!aSampleRate) { + aSampleRate = aEngine->NodeMainThread()->Context()->SampleRate(); + } + AudioNodeStream* stream = new AudioNodeStream(aEngine, aKind, aSampleRate); NS_ADDREF(stream); MediaStreamGraphImpl* graph = static_cast(this); stream->SetGraphImpl(graph); diff --git a/content/media/MediaStreamGraph.h b/content/media/MediaStreamGraph.h index 0568f8eeca3..5ace9b35055 100644 --- a/content/media/MediaStreamGraph.h +++ b/content/media/MediaStreamGraph.h @@ -929,10 +929,13 @@ public: enum AudioNodeStreamKind { INTERNAL_STREAM, EXTERNAL_STREAM }; /** * Create a stream that will process audio for an AudioNode. - * Takes ownership of aEngine. + * Takes ownership of aEngine. aSampleRate is the sampling rate used + * for the stream. If 0 is passed, the sampling rate of the engine's + * node will get used. */ AudioNodeStream* CreateAudioNodeStream(AudioNodeEngine* aEngine, - AudioNodeStreamKind aKind); + AudioNodeStreamKind aKind, + TrackRate aSampleRate = 0); /** * Returns the number of graph updates sent. This can be used to track * whether a given update has been processed by the graph thread and reflected diff --git a/content/media/MediaStreamGraphImpl.h b/content/media/MediaStreamGraphImpl.h index bb594a8a49a..81fe70dcf39 100644 --- a/content/media/MediaStreamGraphImpl.h +++ b/content/media/MediaStreamGraphImpl.h @@ -264,6 +264,7 @@ public: * This is called whenever we have an AudioNodeStream in the graph. */ void ProduceDataForStreamsBlockByBlock(uint32_t aStreamIndex, + TrackRate aSampleRate, GraphTime aFrom, GraphTime aTo); /** diff --git a/content/media/webaudio/AudioContext.cpp b/content/media/webaudio/AudioContext.cpp index 3f5077d235e..79463906bca 100644 --- a/content/media/webaudio/AudioContext.cpp +++ b/content/media/webaudio/AudioContext.cpp @@ -51,7 +51,8 @@ AudioContext::AudioContext(nsPIDOMWindow* aWindow, uint32_t aNumberOfChannels, uint32_t aLength, float aSampleRate) - : mDestination(new AudioDestinationNode(this, aIsOffline, + : mSampleRate(aIsOffline ? aSampleRate : IdealAudioRate()) + , mDestination(new AudioDestinationNode(this, aIsOffline, aNumberOfChannels, aLength, aSampleRate)) , mIsOffline(aIsOffline) diff --git a/content/media/webaudio/AudioContext.h b/content/media/webaudio/AudioContext.h index 5f7958983d5..1bd9a821c8b 100644 --- a/content/media/webaudio/AudioContext.h +++ b/content/media/webaudio/AudioContext.h @@ -105,7 +105,7 @@ public: float SampleRate() const { - return float(IdealAudioRate()); + return mSampleRate; } double CurrentTime() const; @@ -203,6 +203,9 @@ private: friend struct ::mozilla::WebAudioDecodeJob; private: + // Note that it's important for mSampleRate to be initialized before + // mDestination, as mDestination's constructor needs to access it! + const float mSampleRate; nsRefPtr mDestination; nsRefPtr mListener; MediaBufferDecoder mDecoder; diff --git a/content/media/webaudio/AudioParam.cpp b/content/media/webaudio/AudioParam.cpp index 3238a0543d1..303d277af0f 100644 --- a/content/media/webaudio/AudioParam.cpp +++ b/content/media/webaudio/AudioParam.cpp @@ -98,7 +98,10 @@ AudioParam::Stream() } AudioNodeEngine* engine = new AudioNodeEngine(nullptr); - nsRefPtr stream = mNode->Context()->Graph()->CreateAudioNodeStream(engine, MediaStreamGraph::INTERNAL_STREAM); + nsRefPtr stream = + mNode->Context()->Graph()->CreateAudioNodeStream(engine, + MediaStreamGraph::INTERNAL_STREAM, + Node()->Context()->SampleRate()); // Force the input to have only one channel, and make it down-mix using // the speaker rules if needed. From 5ce3257ba41950451964a4d7c1a25f4e8384bc8c Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Fri, 24 May 2013 13:09:51 -0400 Subject: [PATCH 31/91] Bug 873553 - Part 3: Rename AudioBufferSourceNodeEngine::mSampleRate to mBufferSampleRate; r=roc --HG-- extra : rebase_source : 3970da0f20d3d53f17d0ce8244a12de073eeb19a --- content/media/webaudio/AudioBufferSourceNode.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/content/media/webaudio/AudioBufferSourceNode.cpp b/content/media/webaudio/AudioBufferSourceNode.cpp index 212319823f7..935ea1c49a9 100644 --- a/content/media/webaudio/AudioBufferSourceNode.cpp +++ b/content/media/webaudio/AudioBufferSourceNode.cpp @@ -54,7 +54,7 @@ public: mResampler(nullptr), mOffset(0), mDuration(0), mLoopStart(0), mLoopEnd(0), - mSampleRate(0), mPosition(0), mChannels(0), mPlaybackRate(1.0f), + mBufferSampleRate(0), mPosition(0), mChannels(0), mPlaybackRate(1.0f), mDopplerShift(1.0f), mPlaybackRateTimeline(1.0f), mLoop(false) {} @@ -76,7 +76,7 @@ public: // resampler, we can release it. if (mResampler && mPlaybackRateTimeline.HasSimpleValue() && mPlaybackRateTimeline.GetValue() == 1.0 && - mSampleRate == IdealAudioRate()) { + mBufferSampleRate == IdealAudioRate()) { speex_resampler_destroy(mResampler); mResampler = nullptr; } @@ -111,7 +111,7 @@ public: virtual void SetInt32Parameter(uint32_t aIndex, int32_t aParam) { switch (aIndex) { - case AudioBufferSourceNode::SAMPLE_RATE: mSampleRate = aParam; break; + case AudioBufferSourceNode::SAMPLE_RATE: mBufferSampleRate = aParam; break; case AudioBufferSourceNode::OFFSET: mOffset = aParam; break; case AudioBufferSourceNode::DURATION: mDuration = aParam; break; case AudioBufferSourceNode::LOOP: mLoop = !!aParam; break; @@ -135,7 +135,7 @@ public: if (!mResampler) { mChannels = aChannels; - mResampler = speex_resampler_init(mChannels, mSampleRate, + mResampler = speex_resampler_init(mChannels, mBufferSampleRate, ComputeFinalOutSampleRate(), SPEEX_RESAMPLER_QUALITY_DEFAULT, nullptr); @@ -174,7 +174,7 @@ public: } } - // Resamples input data to an output buffer, according to |mSampleRate| and + // Resamples input data to an output buffer, according to |mBufferSampleRate| and // the playbackRate. // The number of frames consumed/produced depends on the amount of space // remaining in both the input and output buffer, and the playback rate (that @@ -186,7 +186,7 @@ public: uint32_t aAvailableInInputBuffer, uint32_t& aFramesRead, uint32_t& aFramesWritten) { - double finalPlaybackRate = static_cast(mSampleRate) / ComputeFinalOutSampleRate(); + double finalPlaybackRate = static_cast(mBufferSampleRate) / ComputeFinalOutSampleRate(); uint32_t availableInOuputBuffer = WEBAUDIO_BLOCK_SIZE - aBufferOffset; uint32_t inputSamples, outputSamples; @@ -318,7 +318,7 @@ public: { return !(mPlaybackRate == 1.0 && mDopplerShift == 1.0 && - mSampleRate == IdealAudioRate()); + mBufferSampleRate == IdealAudioRate()); } void UpdateSampleRateIfNeeded(AudioNodeStream* aStream, uint32_t aChannels) @@ -427,7 +427,7 @@ public: int32_t mDuration; int32_t mLoopStart; int32_t mLoopEnd; - int32_t mSampleRate; + int32_t mBufferSampleRate; uint32_t mPosition; uint32_t mChannels; float mPlaybackRate; From 6c12a5399d1a221d19669c6bb65d76cbc9f89d3b Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Fri, 24 May 2013 13:10:08 -0400 Subject: [PATCH 32/91] Bug 873553 - Part 4: Pass the sampling rate to the engine in SetTimelineParameter; r=roc --HG-- extra : rebase_source : aa574757e9791eafd8c6825328fc4e2f65479f34 --- content/media/AudioNodeEngine.h | 3 ++- content/media/AudioNodeStream.cpp | 8 ++++++-- content/media/webaudio/AudioBufferSourceNode.cpp | 4 +++- content/media/webaudio/BiquadFilterNode.cpp | 4 +++- content/media/webaudio/DelayNode.cpp | 4 +++- content/media/webaudio/DynamicsCompressorNode.cpp | 4 +++- content/media/webaudio/GainNode.cpp | 4 +++- 7 files changed, 23 insertions(+), 8 deletions(-) diff --git a/content/media/AudioNodeEngine.h b/content/media/AudioNodeEngine.h index a60e7f601a0..f9cd8e8a46c 100644 --- a/content/media/AudioNodeEngine.h +++ b/content/media/AudioNodeEngine.h @@ -176,7 +176,8 @@ public: NS_ERROR("Invalid SetInt32Parameter index"); } virtual void SetTimelineParameter(uint32_t aIndex, - const dom::AudioParamTimeline& aValue) + const dom::AudioParamTimeline& aValue, + TrackRate aSampleRate) { NS_ERROR("Invalid SetTimelineParameter index"); } diff --git a/content/media/AudioNodeStream.cpp b/content/media/AudioNodeStream.cpp index 8a98668d296..42c7bae9568 100644 --- a/content/media/AudioNodeStream.cpp +++ b/content/media/AudioNodeStream.cpp @@ -109,13 +109,17 @@ AudioNodeStream::SetTimelineParameter(uint32_t aIndex, public: Message(AudioNodeStream* aStream, uint32_t aIndex, const AudioParamTimeline& aValue) - : ControlMessage(aStream), mValue(aValue), mIndex(aIndex) {} + : ControlMessage(aStream), + mValue(aValue), + mSampleRate(aStream->SampleRate()), + mIndex(aIndex) {} virtual void Run() { static_cast(mStream)->Engine()-> - SetTimelineParameter(mIndex, mValue); + SetTimelineParameter(mIndex, mValue, mSampleRate); } AudioParamTimeline mValue; + TrackRate mSampleRate; uint32_t mIndex; }; GraphImpl()->AppendMessage(new Message(this, aIndex, aValue)); diff --git a/content/media/webaudio/AudioBufferSourceNode.cpp b/content/media/webaudio/AudioBufferSourceNode.cpp index 935ea1c49a9..2826bc9bc7d 100644 --- a/content/media/webaudio/AudioBufferSourceNode.cpp +++ b/content/media/webaudio/AudioBufferSourceNode.cpp @@ -66,7 +66,9 @@ public: } } - virtual void SetTimelineParameter(uint32_t aIndex, const dom::AudioParamTimeline& aValue) + virtual void SetTimelineParameter(uint32_t aIndex, + const dom::AudioParamTimeline& aValue, + TrackRate aSampleRate) MOZ_OVERRIDE { switch (aIndex) { case AudioBufferSourceNode::PLAYBACKRATE: diff --git a/content/media/webaudio/BiquadFilterNode.cpp b/content/media/webaudio/BiquadFilterNode.cpp index 101674ac2bc..2d246bdd8d0 100644 --- a/content/media/webaudio/BiquadFilterNode.cpp +++ b/content/media/webaudio/BiquadFilterNode.cpp @@ -102,7 +102,9 @@ public: NS_ERROR("Bad BiquadFilterNode Int32Parameter"); } } - void SetTimelineParameter(uint32_t aIndex, const AudioParamTimeline& aValue) MOZ_OVERRIDE + void SetTimelineParameter(uint32_t aIndex, + const AudioParamTimeline& aValue, + TrackRate aSampleRate) MOZ_OVERRIDE { MOZ_ASSERT(mSource && mDestination); switch (aIndex) { diff --git a/content/media/webaudio/DelayNode.cpp b/content/media/webaudio/DelayNode.cpp index ba014f18af5..b64d2bedb7e 100644 --- a/content/media/webaudio/DelayNode.cpp +++ b/content/media/webaudio/DelayNode.cpp @@ -84,7 +84,9 @@ public: DELAY, MAX_DELAY }; - void SetTimelineParameter(uint32_t aIndex, const AudioParamTimeline& aValue) MOZ_OVERRIDE + void SetTimelineParameter(uint32_t aIndex, + const AudioParamTimeline& aValue, + TrackRate aSampleRate) MOZ_OVERRIDE { switch (aIndex) { case DELAY: diff --git a/content/media/webaudio/DynamicsCompressorNode.cpp b/content/media/webaudio/DynamicsCompressorNode.cpp index 8a4d937a958..f4348ad21d7 100644 --- a/content/media/webaudio/DynamicsCompressorNode.cpp +++ b/content/media/webaudio/DynamicsCompressorNode.cpp @@ -62,7 +62,9 @@ public: ATTACK, RELEASE }; - void SetTimelineParameter(uint32_t aIndex, const AudioParamTimeline& aValue) MOZ_OVERRIDE + void SetTimelineParameter(uint32_t aIndex, + const AudioParamTimeline& aValue, + TrackRate aSampleRate) MOZ_OVERRIDE { MOZ_ASSERT(mSource && mDestination); switch (aIndex) { diff --git a/content/media/webaudio/GainNode.cpp b/content/media/webaudio/GainNode.cpp index 1aa7fb265c2..dd4319c135b 100644 --- a/content/media/webaudio/GainNode.cpp +++ b/content/media/webaudio/GainNode.cpp @@ -34,7 +34,9 @@ public: enum Parameters { GAIN }; - void SetTimelineParameter(uint32_t aIndex, const AudioParamTimeline& aValue) MOZ_OVERRIDE + void SetTimelineParameter(uint32_t aIndex, + const AudioParamTimeline& aValue, + TrackRate aSampleRate) MOZ_OVERRIDE { switch (aIndex) { case GAIN: From 84490bcf141e3fa5cec8ecc8ea75c9f7e49d877e Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Fri, 24 May 2013 13:13:31 -0400 Subject: [PATCH 33/91] Bug 873553 - Part 5: Port AudioBufferSourceNodeEngine to use the stream's sampling rate; r=roc --HG-- extra : rebase_source : 13d1133fb34d3695eece432047b719b456961610 --- .../media/webaudio/AudioBufferSourceNode.cpp | 45 ++++++++++--------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/content/media/webaudio/AudioBufferSourceNode.cpp b/content/media/webaudio/AudioBufferSourceNode.cpp index 2826bc9bc7d..663a9467868 100644 --- a/content/media/webaudio/AudioBufferSourceNode.cpp +++ b/content/media/webaudio/AudioBufferSourceNode.cpp @@ -78,7 +78,7 @@ public: // resampler, we can release it. if (mResampler && mPlaybackRateTimeline.HasSimpleValue() && mPlaybackRateTimeline.GetValue() == 1.0 && - mBufferSampleRate == IdealAudioRate()) { + mBufferSampleRate == aSampleRate) { speex_resampler_destroy(mResampler); mResampler = nullptr; } @@ -128,7 +128,7 @@ public: mBuffer = aBuffer; } - SpeexResamplerState* Resampler(uint32_t aChannels) + SpeexResamplerState* Resampler(AudioNodeStream* aStream, uint32_t aChannels) { if (aChannels != mChannels && mResampler) { speex_resampler_destroy(mResampler); @@ -138,7 +138,7 @@ public: if (!mResampler) { mChannels = aChannels; mResampler = speex_resampler_init(mChannels, mBufferSampleRate, - ComputeFinalOutSampleRate(), + ComputeFinalOutSampleRate(aStream->SampleRate()), SPEEX_RESAMPLER_QUALITY_DEFAULT, nullptr); } @@ -181,14 +181,16 @@ public: // The number of frames consumed/produced depends on the amount of space // remaining in both the input and output buffer, and the playback rate (that // is, the ratio between the output samplerate and the input samplerate). - void CopyFromInputBufferWithResampling(AudioChunk* aOutput, + void CopyFromInputBufferWithResampling(AudioNodeStream* aStream, + AudioChunk* aOutput, uint32_t aChannels, uintptr_t aSourceOffset, uintptr_t aBufferOffset, uint32_t aAvailableInInputBuffer, uint32_t& aFramesRead, uint32_t& aFramesWritten) { - double finalPlaybackRate = static_cast(mBufferSampleRate) / ComputeFinalOutSampleRate(); + double finalPlaybackRate = + static_cast(mBufferSampleRate) / ComputeFinalOutSampleRate(aStream->SampleRate()); uint32_t availableInOuputBuffer = WEBAUDIO_BLOCK_SIZE - aBufferOffset; uint32_t inputSamples, outputSamples; @@ -201,7 +203,7 @@ public: outputSamples = availableInOuputBuffer; } - SpeexResamplerState* resampler = Resampler(aChannels); + SpeexResamplerState* resampler = Resampler(aStream, aChannels); for (uint32_t i = 0; i < aChannels; ++i) { uint32_t inSamples = inputSamples; @@ -258,7 +260,8 @@ public: * This function knows when it needs to allocate the output buffer, and also * optimizes the case where it can avoid memory allocations. */ - void CopyFromBuffer(AudioChunk* aOutput, + void CopyFromBuffer(AudioNodeStream* aStream, + AudioChunk* aOutput, uint32_t aChannels, uint32_t* aOffsetWithinBlock, TrackTicks* aCurrentPosition, @@ -268,7 +271,7 @@ public: uint32_t numFrames = std::min(std::min(WEBAUDIO_BLOCK_SIZE - *aOffsetWithinBlock, aBufferMax - aBufferOffset), uint32_t(mStop - *aCurrentPosition)); - if (numFrames == WEBAUDIO_BLOCK_SIZE && !ShouldResample()) { + if (numFrames == WEBAUDIO_BLOCK_SIZE && !ShouldResample(aStream->SampleRate())) { BorrowFromInputBuffer(aOutput, aChannels, aBufferOffset); *aOffsetWithinBlock += numFrames; *aCurrentPosition += numFrames; @@ -278,7 +281,7 @@ public: MOZ_ASSERT(*aOffsetWithinBlock == 0); AllocateAudioBlock(aChannels, aOutput); } - if (!ShouldResample()) { + if (!ShouldResample(aStream->SampleRate())) { CopyFromInputBuffer(aOutput, aChannels, aBufferOffset, *aOffsetWithinBlock, numFrames); *aOffsetWithinBlock += numFrames; *aCurrentPosition += numFrames; @@ -288,7 +291,7 @@ public: availableInInputBuffer = aBufferMax - aBufferOffset; - CopyFromInputBufferWithResampling(aOutput, aChannels, aBufferOffset, *aOffsetWithinBlock, availableInInputBuffer, framesRead, framesWritten); + CopyFromInputBufferWithResampling(aStream, aOutput, aChannels, aBufferOffset, *aOffsetWithinBlock, availableInInputBuffer, framesRead, framesWritten); *aOffsetWithinBlock += framesWritten; *aCurrentPosition += framesRead; mPosition += framesRead; @@ -304,7 +307,7 @@ public: return mStart + mPosition; } - uint32_t ComputeFinalOutSampleRate() + uint32_t ComputeFinalOutSampleRate(TrackRate aStreamSampleRate) { if (mPlaybackRate <= 0 || mPlaybackRate != mPlaybackRate) { mPlaybackRate = 1.0f; @@ -312,15 +315,15 @@ public: if (mDopplerShift <= 0 || mDopplerShift != mDopplerShift) { mDopplerShift = 1.0f; } - return WebAudioUtils::TruncateFloatToInt(IdealAudioRate() / + return WebAudioUtils::TruncateFloatToInt(aStreamSampleRate / (mPlaybackRate * mDopplerShift)); } - bool ShouldResample() const + bool ShouldResample(TrackRate aStreamSampleRate) const { return !(mPlaybackRate == 1.0 && mDopplerShift == 1.0 && - mBufferSampleRate == IdealAudioRate()); + mBufferSampleRate == aStreamSampleRate); } void UpdateSampleRateIfNeeded(AudioNodeStream* aStream, uint32_t aChannels) @@ -333,16 +336,16 @@ public: // Make sure the playback rate and the doppler shift are something // our resampler can work with. - if (ComputeFinalOutSampleRate() == 0) { + if (ComputeFinalOutSampleRate(aStream->SampleRate()) == 0) { mPlaybackRate = 1.0; mDopplerShift = 1.0; } uint32_t currentOutSampleRate, currentInSampleRate; - if (ShouldResample()) { - SpeexResamplerState* resampler = Resampler(aChannels); + if (ShouldResample(aStream->SampleRate())) { + SpeexResamplerState* resampler = Resampler(aStream, aChannels); speex_resampler_get_rate(resampler, ¤tInSampleRate, ¤tOutSampleRate); - uint32_t finalSampleRate = ComputeFinalOutSampleRate(); + uint32_t finalSampleRate = ComputeFinalOutSampleRate(aStream->SampleRate()); if (currentOutSampleRate != finalSampleRate) { speex_resampler_set_rate(resampler, currentInSampleRate, finalSampleRate); speex_resampler_skip_zeros(mResampler); @@ -384,14 +387,14 @@ public: TrackTicks t = currentPosition - mStart; if (mLoop) { if (mOffset + t < mLoopEnd) { - CopyFromBuffer(aOutput, channels, &written, ¤tPosition, mOffset + t, mLoopEnd); + CopyFromBuffer(aStream, aOutput, channels, &written, ¤tPosition, mOffset + t, mLoopEnd); } else { uint32_t offsetInLoop = (mOffset + t - mLoopEnd) % (mLoopEnd - mLoopStart); - CopyFromBuffer(aOutput, channels, &written, ¤tPosition, mLoopStart + offsetInLoop, mLoopEnd); + CopyFromBuffer(aStream, aOutput, channels, &written, ¤tPosition, mLoopStart + offsetInLoop, mLoopEnd); } } else { if (mOffset + t < mDuration) { - CopyFromBuffer(aOutput, channels, &written, ¤tPosition, mOffset + t, mDuration); + CopyFromBuffer(aStream, aOutput, channels, &written, ¤tPosition, mOffset + t, mDuration); } else { FillWithZeroes(aOutput, channels, &written, ¤tPosition, TRACK_TICKS_MAX); } From e3a8d368e26ede198d82b5620a0c22db50743219 Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Fri, 24 May 2013 13:10:24 -0400 Subject: [PATCH 34/91] Bug 873553 - Part 6: Port BiquadFilterNode to use the stream's sampling rate; r=roc --HG-- extra : rebase_source : 3522c7cb9ea0469f6ac9607ea0408b078f56f09e --- content/media/webaudio/BiquadFilterNode.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/content/media/webaudio/BiquadFilterNode.cpp b/content/media/webaudio/BiquadFilterNode.cpp index 2d246bdd8d0..641e9c9d118 100644 --- a/content/media/webaudio/BiquadFilterNode.cpp +++ b/content/media/webaudio/BiquadFilterNode.cpp @@ -24,13 +24,14 @@ NS_IMPL_ADDREF_INHERITED(BiquadFilterNode, AudioNode) NS_IMPL_RELEASE_INHERITED(BiquadFilterNode, AudioNode) void SetParamsOnBiquad(WebCore::Biquad& aBiquad, + float aSampleRate, BiquadFilterType aType, double aFrequency, double aQ, double aGain, double aDetune) { - const double nyquist = IdealAudioRate() * 0.5; + const double nyquist = aSampleRate * 0.5; double normalizedFrequency = aFrequency / nyquist; if (aDetune) { @@ -153,7 +154,7 @@ public: double detune = mDetune.GetValueAtTime(pos); for (uint32_t i = 0; i < numberOfChannels; ++i) { - SetParamsOnBiquad(mBiquads[i], mType, freq, q, gain, detune); + SetParamsOnBiquad(mBiquads[i], aStream->SampleRate(), mType, freq, q, gain, detune); mBiquads[i].process(static_cast(aInput.mChannelData[i]), static_cast(const_cast(aOutput->mChannelData[i])), @@ -215,7 +216,7 @@ BiquadFilterNode::GetFrequencyResponse(const Float32Array& aFrequencyHz, nsAutoArrayPtr frequencies(new float[length]); float* frequencyHz = aFrequencyHz.Data(); - const double nyquist = IdealAudioRate() * 0.5; + const double nyquist = Context()->SampleRate() * 0.5; // Normalize the frequencies for (uint32_t i = 0; i < length; ++i) { @@ -230,7 +231,7 @@ BiquadFilterNode::GetFrequencyResponse(const Float32Array& aFrequencyHz, double detune = mDetune->GetValueAtTime(currentTime); WebCore::Biquad biquad; - SetParamsOnBiquad(biquad, mType, freq, q, gain, detune); + SetParamsOnBiquad(biquad, Context()->SampleRate(), mType, freq, q, gain, detune); biquad.getFrequencyResponse(int(length), frequencies, aMagResponse.Data(), aPhaseResponse.Data()); } From 33f9fbcce5e8935c3e8b15176a30c19a50613778 Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Fri, 24 May 2013 13:10:44 -0400 Subject: [PATCH 35/91] Bug 873553 - Part 7: Port DelayNode to use the stream's sampling rate; r=roc --HG-- extra : rebase_source : db35f08edab8a54c6c60a0ead2d7afed59d2c360 --- content/media/webaudio/DelayNode.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/content/media/webaudio/DelayNode.cpp b/content/media/webaudio/DelayNode.cpp index b64d2bedb7e..4816626c97c 100644 --- a/content/media/webaudio/DelayNode.cpp +++ b/content/media/webaudio/DelayNode.cpp @@ -107,7 +107,7 @@ public: } } - bool EnsureBuffer(uint32_t aNumberOfChannels) + bool EnsureBuffer(uint32_t aNumberOfChannels, TrackRate aSampleRate) { if (aNumberOfChannels == 0) { return false; @@ -116,7 +116,7 @@ public: if (!mBuffer.SetLength(aNumberOfChannels)) { return false; } - const int32_t numFrames = NS_lround(mMaxDelay) * IdealAudioRate(); + const int32_t numFrames = NS_lround(mMaxDelay) * aSampleRate; for (uint32_t channel = 0; channel < aNumberOfChannels; ++channel) { if (!mBuffer[channel].SetLength(numFrames)) { return false; @@ -146,7 +146,7 @@ public: if (!mBuffer.IsEmpty() && mLeftOverData == INT32_MIN && aStream->AllInputsFinished()) { - mLeftOverData = static_cast(mCurrentDelayTime * IdealAudioRate()) - WEBAUDIO_BLOCK_SIZE; + mLeftOverData = static_cast(mCurrentDelayTime * aStream->SampleRate()) - WEBAUDIO_BLOCK_SIZE; if (mLeftOverData > 0) { nsRefPtr refchanged = @@ -165,7 +165,7 @@ public: } } - if (!EnsureBuffer(numChannels)) { + if (!EnsureBuffer(numChannels, aStream->SampleRate())) { aOutput->SetNull(0); return; } @@ -175,7 +175,7 @@ public: double delayTime = 0; float computedDelay[WEBAUDIO_BLOCK_SIZE]; // Use a smoothing range of 20ms - const double smoothingRate = WebAudioUtils::ComputeSmoothingRate(0.02, IdealAudioRate()); + const double smoothingRate = WebAudioUtils::ComputeSmoothingRate(0.02, aStream->SampleRate()); if (mDelay.HasSimpleValue()) { delayTime = std::max(0.0, std::min(mMaxDelay, double(mDelay.GetValue()))); @@ -219,7 +219,7 @@ public: // from currentDelayTime seconds in the past. We also interpolate the two input // frames in case the read position does not match an integer index. double readPosition = writeIndex + bufferLength - - (currentDelayTime * IdealAudioRate()); + (currentDelayTime * aStream->SampleRate()); if (readPosition >= bufferLength) { readPosition -= bufferLength; } From 32907f2bc25b9941b7a0dbd5f771ea36ce7fdc2b Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Fri, 24 May 2013 13:10:59 -0400 Subject: [PATCH 36/91] Bug 873553 - Part 8: Port DynamicsCompressorNode to use the stream's sampling rate; r=roc --HG-- extra : rebase_source : ebf2989b778a03f873eea83ed5c885360981e2ed --- content/media/webaudio/DynamicsCompressorNode.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/content/media/webaudio/DynamicsCompressorNode.cpp b/content/media/webaudio/DynamicsCompressorNode.cpp index f4348ad21d7..668bf0fc056 100644 --- a/content/media/webaudio/DynamicsCompressorNode.cpp +++ b/content/media/webaudio/DynamicsCompressorNode.cpp @@ -46,7 +46,7 @@ public: , mRatio(12.f) , mAttack(0.003f) , mRelease(0.25f) - , mCompressor(new DynamicsCompressor(IdealAudioRate(), 2)) + , mCompressor(new DynamicsCompressor(mDestination->SampleRate(), 2)) { } @@ -107,7 +107,7 @@ public: const uint32_t channelCount = aInput.mChannelData.Length(); if (mCompressor->numberOfChannels() != channelCount) { // Create a new compressor object with a new channel count - mCompressor = new WebCore::DynamicsCompressor(IdealAudioRate(), + mCompressor = new WebCore::DynamicsCompressor(aStream->SampleRate(), aInput.mChannelData.Length()); } From c74b3d338f5cfa9a99269e1863e706b77969d43f Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Fri, 24 May 2013 13:11:18 -0400 Subject: [PATCH 37/91] Bug 873553 - Part 9: Port WebAudioUtils to use the stream's sampling rate; r=roc --HG-- extra : rebase_source : 18e01ed739bb505f7825c3d79e00c8552a70fad0 --- content/media/webaudio/WebAudioUtils.cpp | 14 +++++++++----- content/media/webaudio/WebAudioUtils.h | 2 +- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/content/media/webaudio/WebAudioUtils.cpp b/content/media/webaudio/WebAudioUtils.cpp index e210121d6e4..fef2f04e21d 100644 --- a/content/media/webaudio/WebAudioUtils.cpp +++ b/content/media/webaudio/WebAudioUtils.cpp @@ -20,24 +20,26 @@ struct ConvertTimeToTickHelper { ConvertTimeToTickHelper* This = static_cast (aClosure); if (This->mSourceStream) { + MOZ_ASSERT(This->mSourceStream->SampleRate() == This->mDestinationStream->SampleRate()); return WebAudioUtils::ConvertDestinationStreamTimeToSourceStreamTime( aTime, This->mSourceStream, This->mDestinationStream); } else { StreamTime streamTime = This->mDestinationStream->GetCurrentPosition(); - return TimeToTicksRoundUp(IdealAudioRate(), streamTime + SecondsToMediaTime(aTime)); + TrackRate sampleRate = This->mDestinationStream->SampleRate(); + return TimeToTicksRoundUp(sampleRate, streamTime + SecondsToMediaTime(aTime)); } } }; TrackTicks WebAudioUtils::ConvertDestinationStreamTimeToSourceStreamTime(double aTime, - MediaStream* aSource, + AudioNodeStream* aSource, MediaStream* aDestination) { StreamTime streamTime = std::max(0, SecondsToMediaTime(aTime)); GraphTime graphTime = aDestination->StreamTimeToGraphTime(streamTime); StreamTime thisStreamTime = aSource->GraphTimeToStreamTimeOptimistic(graphTime); - TrackTicks ticks = TimeToTicksRoundUp(IdealAudioRate(), thisStreamTime); + TrackTicks ticks = TimeToTicksRoundUp(aSource->SampleRate(), thisStreamTime); return ticks; } @@ -46,7 +48,8 @@ WebAudioUtils::StreamPositionToDestinationTime(TrackTicks aSourcePosition, AudioNodeStream* aSource, AudioNodeStream* aDestination) { - StreamTime sourceTime = TicksToTimeRoundDown(IdealAudioRate(), aSourcePosition); + MOZ_ASSERT(aSource->SampleRate() == aDestination->SampleRate()); + StreamTime sourceTime = TicksToTimeRoundDown(aSource->SampleRate(), aSourcePosition); GraphTime graphTime = aSource->StreamTimeToGraphTime(sourceTime); StreamTime destinationTime = aDestination->GraphTimeToStreamTimeOptimistic(graphTime); return MediaTimeToSeconds(destinationTime); @@ -57,10 +60,11 @@ WebAudioUtils::ConvertAudioParamToTicks(AudioParamTimeline& aParam, AudioNodeStream* aSource, AudioNodeStream* aDest) { + MOZ_ASSERT(!aSource || aSource->SampleRate() == aDest->SampleRate()); ConvertTimeToTickHelper ctth; ctth.mSourceStream = aSource; ctth.mDestinationStream = aDest; - aParam.ConvertEventTimesToTicks(ConvertTimeToTickHelper::Convert, &ctth, IdealAudioRate()); + aParam.ConvertEventTimesToTicks(ConvertTimeToTickHelper::Convert, &ctth, aDest->SampleRate()); } } diff --git a/content/media/webaudio/WebAudioUtils.h b/content/media/webaudio/WebAudioUtils.h index 75fe3562c14..76c0a2c7d2a 100644 --- a/content/media/webaudio/WebAudioUtils.h +++ b/content/media/webaudio/WebAudioUtils.h @@ -47,7 +47,7 @@ struct WebAudioUtils { */ static TrackTicks ConvertDestinationStreamTimeToSourceStreamTime(double aTime, - MediaStream* aSource, + AudioNodeStream* aSource, MediaStream* aDestination); /** From 6d703a83b0e9bf3db99fa5039319222e55795615 Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Fri, 24 May 2013 13:11:32 -0400 Subject: [PATCH 38/91] Bug 873553 - Part 10: Allow creating OfflineAudioContexts with arbitrary sampling rates; r=roc --HG-- extra : rebase_source : 99758cbe05b83d34734e4df11f30ebbbd12811df --- content/media/webaudio/AudioContext.cpp | 4 ++-- .../media/webaudio/test/test_OfflineAudioContext.html | 9 ++++++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/content/media/webaudio/AudioContext.cpp b/content/media/webaudio/AudioContext.cpp index 79463906bca..474ff4d83ad 100644 --- a/content/media/webaudio/AudioContext.cpp +++ b/content/media/webaudio/AudioContext.cpp @@ -108,8 +108,8 @@ AudioContext::Constructor(const GlobalObject& aGlobal, return nullptr; } - if (aSampleRate != IdealAudioRate()) { - // TODO: Add support for running OfflineAudioContext at other sampling rates + if (aSampleRate <= 0.0f) { + // The DOM binding protects us against infinity and NaN aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); return nullptr; } diff --git a/content/media/webaudio/test/test_OfflineAudioContext.html b/content/media/webaudio/test/test_OfflineAudioContext.html index 5eebfc2a658..e6c723506ff 100644 --- a/content/media/webaudio/test/test_OfflineAudioContext.html +++ b/content/media/webaudio/test/test_OfflineAudioContext.html @@ -13,7 +13,7 @@ SimpleTest.waitForExplicitFinish(); addLoadEvent(function() { SpecialPowers.setBoolPref("media.webaudio.enabled", true); - var ctx = new OfflineAudioContext(2, 100, (new AudioContext()).sampleRate); + var ctx = new OfflineAudioContext(2, 100, 22050); ok(ctx instanceof EventTarget, "OfflineAudioContexts must be EventTargets"); var buf = ctx.createBuffer(2, 100, ctx.sampleRate); @@ -23,6 +23,13 @@ addLoadEvent(function() { } } + expectException(function() { + new OfflineAudioContext(2, 100, 0); + }, DOMException.SYNTAX_ERR); + expectException(function() { + new OfflineAudioContext(2, 100, -1); + }, DOMException.SYNTAX_ERR); + var src = ctx.createBufferSource(); src.buffer = buf; src.start(0); From ec8cc4a0845c6de451468247155d4b1cb941d7e3 Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Fri, 24 May 2013 13:11:53 -0400 Subject: [PATCH 39/91] Bug 873553 - Part 11: Run the Web Audio tests with two OfflineAudioContexts, one at 48KHz and one at 44.1KHz; r=roc --HG-- extra : rebase_source : b3847be5b05a9dc5e0d3233fadd30c92a6f07292 --- content/media/webaudio/test/webaudio.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/content/media/webaudio/test/webaudio.js b/content/media/webaudio/test/webaudio.js index 9cc017b049f..26c2fd0d73b 100644 --- a/content/media/webaudio/test/webaudio.js +++ b/content/media/webaudio/test/webaudio.js @@ -156,7 +156,7 @@ function runTest() runTestOnContext(context, callback, testOutput); } - function testOnOfflineContext(callback) { + function testOnOfflineContext(callback, sampleRate) { function testOutput(nodeToInspect, expectedBuffers, callback) { nodeToInspect.connect(context.destination); context.oncomplete = function(e) { @@ -180,12 +180,14 @@ function runTest() }; context.startRendering(); } - var context = new OfflineAudioContext(gTest.numberOfChannels, testLength, 48000); + var context = new OfflineAudioContext(gTest.numberOfChannels, testLength, sampleRate); runTestOnContext(context, callback, testOutput); } testOnNormalContext(function() { - testOnOfflineContext(done); + testOnOfflineContext(function() { + testOnOfflineContext(done, 44100); + }, 48000); }); }); } From 764fa52c0e70728609a8360d90ebd65e47436516 Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Fri, 24 May 2013 13:19:47 -0400 Subject: [PATCH 40/91] Bug 875596 - Properly handle AudioParam SetCurve automation events with a 0 duration; r=roc --HG-- rename : content/media/webaudio/test/test_audioParamSetCurveAtTime.html => content/media/webaudio/test/test_audioParamSetCurveAtTimeZeroDuration.html --- content/media/AudioEventTimeline.h | 4 ++ content/media/test/crashtests/875596.html | 12 ++++ content/media/test/crashtests/crashtests.list | 1 + content/media/webaudio/test/Makefile.in | 1 + ..._audioParamSetCurveAtTimeZeroDuration.html | 57 +++++++++++++++++++ 5 files changed, 75 insertions(+) create mode 100644 content/media/test/crashtests/875596.html create mode 100644 content/media/webaudio/test/test_audioParamSetCurveAtTimeZeroDuration.html diff --git a/content/media/AudioEventTimeline.h b/content/media/AudioEventTimeline.h index baa783ca9f7..fe2fec5b1f8 100644 --- a/content/media/AudioEventTimeline.h +++ b/content/media/AudioEventTimeline.h @@ -393,6 +393,10 @@ public: static float ExtractValueFromCurve(double startTime, float* aCurve, uint32_t aCurveLength, double duration, double t) { + if (t >= startTime + duration) { + // After the duration, return the last curve value + return aCurve[aCurveLength - 1]; + } double ratio = (t - startTime) / duration; MOZ_ASSERT(ratio >= 0.0, "Ratio can never be negative here"); if (ratio >= 1.0) { diff --git a/content/media/test/crashtests/875596.html b/content/media/test/crashtests/875596.html new file mode 100644 index 00000000000..b71abcde0fe --- /dev/null +++ b/content/media/test/crashtests/875596.html @@ -0,0 +1,12 @@ + \ No newline at end of file diff --git a/content/media/test/crashtests/crashtests.list b/content/media/test/crashtests/crashtests.list index 281f257de76..4a393b474a1 100644 --- a/content/media/test/crashtests/crashtests.list +++ b/content/media/test/crashtests/crashtests.list @@ -21,3 +21,4 @@ load 874934.html load 874952.html load 875144.html load 875402.html +load 875596.html diff --git a/content/media/webaudio/test/Makefile.in b/content/media/webaudio/test/Makefile.in index d69403832ca..19acdda4b40 100644 --- a/content/media/webaudio/test/Makefile.in +++ b/content/media/webaudio/test/Makefile.in @@ -31,6 +31,7 @@ MOCHITEST_FILES := \ test_audioParamExponentialRamp.html \ test_audioParamLinearRamp.html \ test_audioParamSetCurveAtTime.html \ + test_audioParamSetCurveAtTimeZeroDuration.html \ test_audioParamSetTargetAtTime.html \ test_audioParamTimelineDestinationOffset.html \ test_audioBufferSourceNode.html \ diff --git a/content/media/webaudio/test/test_audioParamSetCurveAtTimeZeroDuration.html b/content/media/webaudio/test/test_audioParamSetCurveAtTimeZeroDuration.html new file mode 100644 index 00000000000..174c15c6fe3 --- /dev/null +++ b/content/media/webaudio/test/test_audioParamSetCurveAtTimeZeroDuration.html @@ -0,0 +1,57 @@ + + + + Test AudioParam.linearRampToValue + + + + + +
+
+
+ + From e664cd9a1ebcb004f1fc7dc293bb6cc8c96cd4f3 Mon Sep 17 00:00:00 2001 From: Olli Pettay Date: Fri, 24 May 2013 20:22:20 +0300 Subject: [PATCH 41/91] Bug 875545 - Make Telephony.h/cpp to use EventListenerAdded/Removed, r=bz --HG-- rename : content/media/webaudio/test/test_audioParamSetCurveAtTimeZeroDuration.html => content/media/webaudio/test/test_audioParamSetCurveAtTime.html rename : mobile/android/base/Prompt.java => mobile/android/base/PromptService.java extra : rebase_source : fcd1f9bbecc8f9025ea0f27274d0f31d745bcf39 --- content/events/src/nsEventListenerManager.cpp | 6 + dom/base/nsIJSEventListener.h | 6 + dom/telephony/Telephony.cpp | 164 +----------------- dom/telephony/Telephony.h | 12 +- 4 files changed, 19 insertions(+), 169 deletions(-) diff --git a/content/events/src/nsEventListenerManager.cpp b/content/events/src/nsEventListenerManager.cpp index 78ff7d6e53f..06b7febebd9 100644 --- a/content/events/src/nsEventListenerManager.cpp +++ b/content/events/src/nsEventListenerManager.cpp @@ -586,7 +586,13 @@ nsEventListenerManager::SetEventHandlerInternal(nsIScriptContext *aContext, MOZ_ASSERT(scriptListener, "How can we have an event handler with no nsIJSEventListener?"); + bool same = scriptListener->GetHandler() == aHandler; + // Possibly the same listener, but update still the context and scope. scriptListener->SetHandler(aHandler, aContext, aScopeObject); + if (mTarget && !same) { + mTarget->EventListenerRemoved(aName); + mTarget->EventListenerAdded(aName); + } } if (NS_SUCCEEDED(rv) && ls) { diff --git a/dom/base/nsIJSEventListener.h b/dom/base/nsIJSEventListener.h index 8dc55340cda..ed804ecb9a7 100644 --- a/dom/base/nsIJSEventListener.h +++ b/dom/base/nsIJSEventListener.h @@ -136,6 +136,12 @@ public: mBits = 0; } + bool operator==(const nsEventHandler& aOther) const + { + return + Ptr() && aOther.Ptr() && + Ptr()->CallbackPreserveColor() == aOther.Ptr()->CallbackPreserveColor(); + } private: void operator=(const nsEventHandler&) MOZ_DELETE; diff --git a/dom/telephony/Telephony.cpp b/dom/telephony/Telephony.cpp index ea1c4de0abc..e80db5cb5ed 100644 --- a/dom/telephony/Telephony.cpp +++ b/dom/telephony/Telephony.cpp @@ -388,175 +388,19 @@ Telephony::StopTone() } NS_IMPL_EVENT_HANDLER(Telephony, incoming) +NS_IMPL_EVENT_HANDLER(Telephony, callschanged) -NS_IMETHODIMP -Telephony::GetOncallschanged(JSContext* aCx, JS::Value* aValue) -{ - GetEventHandler(nsGkAtoms::oncallschanged, aCx, aValue); - return NS_OK; -} - -NS_IMETHODIMP -Telephony::SetOncallschanged(JSContext* aCx, const JS::Value& aValue) -{ - JS::Value value; - GetEventHandler(nsGkAtoms::oncallschanged, aCx, &value); - if (aValue == value) { - // The event handler is being set to itself. - return NS_OK; - } - - nsresult rv = SetEventHandler(nsGkAtoms::oncallschanged, aCx, aValue); - if (NS_FAILED(rv)) { - return rv; - } - - // Fire oncallschanged on the next tick if the calls array is ready. - EnqueueEnumerationAck(); - - return NS_OK; -} - -// nsIDOMEventTarget - -NS_IMETHODIMP -Telephony::AddEventListener(const nsAString& aType, - nsIDOMEventListener* aListener, bool aUseCapture, - bool aWantsUntrusted, uint8_t aArgc) -{ - nsresult rv = nsDOMEventTargetHelper::AddEventListener(aType, aListener, - aUseCapture, - aWantsUntrusted, - aArgc); - NS_ENSURE_SUCCESS(rv, rv); - - if (aType.EqualsLiteral("callschanged")) { - // Fire oncallschanged on the next tick if the calls array is ready. - EnqueueEnumerationAck(); - } - - return NS_OK; -} +// EventTarget void -Telephony::AddEventListener(const nsAString& aType, - nsIDOMEventListener* aListener, bool aUseCapture, - const Nullable& aWantsUntrusted, - ErrorResult& aRv) +Telephony::EventListenerAdded(nsIAtom* aType) { - nsDOMEventTargetHelper::AddEventListener(aType, aListener, aUseCapture, - aWantsUntrusted, aRv); - if (aRv.Failed()) { - return; - } - - if (aType.EqualsLiteral("callschanged")) { + if (aType == nsGkAtoms::oncallschanged) { // Fire oncallschanged on the next tick if the calls array is ready. EnqueueEnumerationAck(); } } -NS_IMETHODIMP -Telephony::AddSystemEventListener(const nsAString& aType, - nsIDOMEventListener* aListener, - bool aUseCapture, bool aWantsUntrusted, - uint8_t aArgc) -{ - nsresult rv = nsDOMEventTargetHelper::AddSystemEventListener(aType, aListener, - aUseCapture, - aWantsUntrusted, - aArgc); - NS_ENSURE_SUCCESS(rv, rv); - - if (aType.EqualsLiteral("callschanged")) { - // Fire oncallschanged on the next tick if the calls array is ready. - EnqueueEnumerationAck(); - } - - return NS_OK; -} - -NS_IMETHODIMP -Telephony::RemoveEventListener(const nsAString& aType, - nsIDOMEventListener* aListener, - bool aUseCapture) -{ - return nsDOMEventTargetHelper::RemoveEventListener(aType, aListener, false); -} - -NS_IMETHODIMP -Telephony::RemoveSystemEventListener(const nsAString& aType, - nsIDOMEventListener* aListener, - bool aUseCapture) -{ - return nsDOMEventTargetHelper::RemoveSystemEventListener(aType, aListener, - aUseCapture); -} - -NS_IMETHODIMP -Telephony::DispatchEvent(nsIDOMEvent* aEvt, bool* aRetval) -{ - return nsDOMEventTargetHelper::DispatchEvent(aEvt, aRetval); -} - -EventTarget* -Telephony::GetTargetForDOMEvent() -{ - return nsDOMEventTargetHelper::GetTargetForDOMEvent(); -} - -EventTarget* -Telephony::GetTargetForEventTargetChain() -{ - return nsDOMEventTargetHelper::GetTargetForEventTargetChain(); -} - -nsresult -Telephony::PreHandleEvent(nsEventChainPreVisitor& aVisitor) -{ - return nsDOMEventTargetHelper::PreHandleEvent(aVisitor); -} - -nsresult -Telephony::WillHandleEvent(nsEventChainPostVisitor& aVisitor) -{ - return nsDOMEventTargetHelper::WillHandleEvent(aVisitor); -} - -nsresult -Telephony::PostHandleEvent(nsEventChainPostVisitor& aVisitor) -{ - return nsDOMEventTargetHelper::PostHandleEvent(aVisitor); -} - -nsresult -Telephony::DispatchDOMEvent(nsEvent* aEvent, nsIDOMEvent* aDOMEvent, - nsPresContext* aPresContext, - nsEventStatus* aEventStatus) -{ - return nsDOMEventTargetHelper::DispatchDOMEvent(aEvent, aDOMEvent, - aPresContext, - aEventStatus); -} - -nsEventListenerManager* -Telephony::GetListenerManager(bool aMayCreate) -{ - return nsDOMEventTargetHelper::GetListenerManager(aMayCreate); -} - -nsIScriptContext* -Telephony::GetContextForEventHandlers(nsresult* aRv) -{ - return nsDOMEventTargetHelper::GetContextForEventHandlers(aRv); -} - -JSContext* -Telephony::GetJSContextForEventHandlers() -{ - return nsDOMEventTargetHelper::GetJSContextForEventHandlers(); -} - // nsITelephonyListener NS_IMETHODIMP diff --git a/dom/telephony/Telephony.h b/dom/telephony/Telephony.h index 9a8ad72d75d..73ab7f164c8 100644 --- a/dom/telephony/Telephony.h +++ b/dom/telephony/Telephony.h @@ -50,19 +50,11 @@ public: NS_DECL_ISUPPORTS_INHERITED NS_DECL_NSIDOMTELEPHONY NS_DECL_NSITELEPHONYLISTENER - - NS_DECL_NSIDOMEVENTTARGET + NS_REALLY_FORWARD_NSIDOMEVENTTARGET(nsDOMEventTargetHelper) NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED( Telephony, nsDOMEventTargetHelper) - using nsDOMEventTargetHelper::RemoveEventListener; - virtual void AddEventListener(const nsAString& aType, - nsIDOMEventListener* aListener, - bool aUseCapture, - const mozilla::dom::Nullable& aWantsUntrusted, - mozilla::ErrorResult& aRv) MOZ_OVERRIDE; - static already_AddRefed Create(nsPIDOMWindow* aOwner, nsITelephonyProvider* aProvider); @@ -96,6 +88,8 @@ public: return mProvider; } + virtual void EventListenerAdded(nsIAtom* aType) MOZ_OVERRIDE; + private: Telephony(); ~Telephony(); From cdd51f042b128521e034b0d7ddc16d624817be01 Mon Sep 17 00:00:00 2001 From: Jonathan Watt Date: Fri, 24 May 2013 18:31:33 +0100 Subject: [PATCH 42/91] Bug 875037 - Fix lots of bugs in nsStyleSVG/nsSVGStyleReset::CalcDifference so that they return the correct hints. r=roc --- layout/style/nsStyleStruct.cpp | 78 +++++++++++++++++++++++----------- 1 file changed, 53 insertions(+), 25 deletions(-) diff --git a/layout/style/nsStyleStruct.cpp b/layout/style/nsStyleStruct.cpp index b9eef3d2107..f24c9897877 100644 --- a/layout/style/nsStyleStruct.cpp +++ b/layout/style/nsStyleStruct.cpp @@ -900,17 +900,15 @@ nsChangeHint nsStyleSVG::CalcDifference(const nsStyleSVG& aOther) const { nsChangeHint hint = nsChangeHint(0); - if (mTextRendering != aOther.mTextRendering) { - NS_UpdateHint(hint, nsChangeHint_RepaintFrame); - // May be needed for non-svg frames - NS_UpdateHint(hint, nsChangeHint_AllReflowHints); - } - if (!EqualURIs(mMarkerEnd, aOther.mMarkerEnd) || !EqualURIs(mMarkerMid, aOther.mMarkerMid) || !EqualURIs(mMarkerStart, aOther.mMarkerStart)) { - NS_UpdateHint(hint, nsChangeHint_RepaintFrame); + // Markers currently contribute to nsSVGPathGeometryFrame::mRect, + // so we need a reflow as well as a repaint. No intrinsic sizes need + // to change, so nsChangeHint_NeedReflow is sufficient. NS_UpdateHint(hint, nsChangeHint_UpdateEffects); + NS_UpdateHint(hint, nsChangeHint_NeedReflow); + NS_UpdateHint(hint, nsChangeHint_RepaintFrame); return hint; } @@ -921,17 +919,32 @@ nsChangeHint nsStyleSVG::CalcDifference(const nsStyleSVG& aOther) const PaintURIChanged(mStroke, aOther.mStroke)) { NS_UpdateHint(hint, nsChangeHint_UpdateEffects); } - // Nothing more to do, below we can only set "repaint" + } + + // Stroke currently contributes to nsSVGPathGeometryFrame::mRect, so + // we need a reflow here. No intrinsic sizes need to change, so + // nsChangeHint_NeedReflow is sufficient. + // Note that stroke-dashoffset does not affect nsSVGPathGeometryFrame::mRect. + // text-anchor and text-rendering changes also require a reflow since they + // change frames' rects. + if (mStrokeWidth != aOther.mStrokeWidth || + mStrokeMiterlimit != aOther.mStrokeMiterlimit || + mStrokeLinecap != aOther.mStrokeLinecap || + mStrokeLinejoin != aOther.mStrokeLinejoin || + mTextAnchor != aOther.mTextAnchor || + mTextRendering != aOther.mTextRendering) { + NS_UpdateHint(hint, nsChangeHint_NeedReflow); + NS_UpdateHint(hint, nsChangeHint_RepaintFrame); return hint; } + if (hint & nsChangeHint_RepaintFrame) { + return hint; // we don't add anything else below + } + if ( mStrokeDashoffset != aOther.mStrokeDashoffset || - mStrokeWidth != aOther.mStrokeWidth || - mFillOpacity != aOther.mFillOpacity || - mStrokeMiterlimit != aOther.mStrokeMiterlimit || mStrokeOpacity != aOther.mStrokeOpacity || - mClipRule != aOther.mClipRule || mColorInterpolation != aOther.mColorInterpolation || mColorInterpolationFilters != aOther.mColorInterpolationFilters || @@ -940,9 +953,6 @@ nsChangeHint nsStyleSVG::CalcDifference(const nsStyleSVG& aOther) const mPaintOrder != aOther.mPaintOrder || mShapeRendering != aOther.mShapeRendering || mStrokeDasharrayLength != aOther.mStrokeDasharrayLength || - mStrokeLinecap != aOther.mStrokeLinecap || - mStrokeLinejoin != aOther.mStrokeLinejoin || - mTextAnchor != aOther.mTextAnchor || mFillOpacitySource != aOther.mFillOpacitySource || mStrokeOpacitySource != aOther.mStrokeOpacitySource || mStrokeDasharrayFromObject != aOther.mStrokeDasharrayFromObject || @@ -1010,18 +1020,28 @@ nsChangeHint nsStyleSVGReset::CalcDifference(const nsStyleSVGReset& aOther) cons !EqualURIs(mFilter, aOther.mFilter) || !EqualURIs(mMask, aOther.mMask)) { NS_UpdateHint(hint, nsChangeHint_UpdateEffects); - NS_UpdateHint(hint, nsChangeHint_AllReflowHints); NS_UpdateHint(hint, nsChangeHint_RepaintFrame); - } else if (mDominantBaseline != aOther.mDominantBaseline) { + } + + if (mDominantBaseline != aOther.mDominantBaseline) { + // XXXjwatt: why NS_STYLE_HINT_REFLOW? Isn't that excessive? NS_UpdateHint(hint, NS_STYLE_HINT_REFLOW); + } else if (mVectorEffect != aOther.mVectorEffect) { + // Stroke currently affects nsSVGPathGeometryFrame::mRect, and + // vector-effect affect stroke. As a result we need to reflow if + // vector-effect changes in order to have nsSVGPathGeometryFrame:: + // ReflowSVG called to update its mRect. No intrinsic sizes need + // to change so nsChangeHint_NeedReflow is sufficient. + NS_UpdateHint(hint, nsChangeHint_NeedReflow); + NS_UpdateHint(hint, nsChangeHint_RepaintFrame); } else if (mStopColor != aOther.mStopColor || mFloodColor != aOther.mFloodColor || mLightingColor != aOther.mLightingColor || mStopOpacity != aOther.mStopOpacity || mFloodOpacity != aOther.mFloodOpacity || - mVectorEffect != aOther.mVectorEffect || - mMaskType != aOther.mMaskType) + mMaskType != aOther.mMaskType) { NS_UpdateHint(hint, nsChangeHint_RepaintFrame); + } return hint; } @@ -2296,12 +2316,20 @@ nsChangeHint nsStyleVisibility::CalcDifference(const nsStyleVisibility& aOther) if (mDirection != aOther.mDirection) { NS_UpdateHint(hint, nsChangeHint_ReconstructFrame); - } else if (mVisible != aOther.mVisible) { - if ((NS_STYLE_VISIBILITY_COLLAPSE == mVisible) || - (NS_STYLE_VISIBILITY_COLLAPSE == aOther.mVisible)) { - NS_UpdateHint(hint, NS_STYLE_HINT_REFLOW); - } else { - NS_UpdateHint(hint, NS_STYLE_HINT_VISUAL); + } else { + if (mVisible != aOther.mVisible) { + if ((NS_STYLE_VISIBILITY_COLLAPSE == mVisible) || + (NS_STYLE_VISIBILITY_COLLAPSE == aOther.mVisible)) { + NS_UpdateHint(hint, NS_STYLE_HINT_REFLOW); + } else { + NS_UpdateHint(hint, NS_STYLE_HINT_VISUAL); + } + } + if (mPointerEvents != aOther.mPointerEvents) { + // nsSVGPathGeometryFrame's mRect depends on stroke _and_ on the value + // of pointer-events. See nsSVGPathGeometryFrame::ReflowSVG's use of + // GetHitTestFlags. (Only a reflow, no visual change.) + NS_UpdateHint(hint, nsChangeHint_NeedReflow); } } return hint; From 5373250846383894d7cf3f0b7fcb0d96d81ff8b2 Mon Sep 17 00:00:00 2001 From: Jonathan Watt Date: Fri, 24 May 2013 18:32:33 +0100 Subject: [PATCH 43/91] Bug 854765 - Get rid of the DidSetStyleContext overrides in SVG frame code to avoid some major perf hits. r=roc --- layout/svg/SVGFEContainerFrame.cpp | 9 --------- layout/svg/SVGFEImageFrame.cpp | 9 --------- layout/svg/SVGFELeafFrame.cpp | 9 --------- layout/svg/nsSVGForeignObjectFrame.cpp | 17 ----------------- layout/svg/nsSVGForeignObjectFrame.h | 2 -- layout/svg/nsSVGGradientFrame.cpp | 7 ------- layout/svg/nsSVGGradientFrame.h | 2 -- layout/svg/nsSVGMaskFrame.cpp | 7 ------- layout/svg/nsSVGMaskFrame.h | 2 -- layout/svg/nsSVGPathGeometryFrame.cpp | 15 --------------- layout/svg/nsSVGPathGeometryFrame.h | 2 -- layout/svg/nsSVGPatternFrame.cpp | 7 ------- layout/svg/nsSVGPatternFrame.h | 2 -- layout/svg/nsSVGStopFrame.cpp | 9 --------- 14 files changed, 99 deletions(-) diff --git a/layout/svg/SVGFEContainerFrame.cpp b/layout/svg/SVGFEContainerFrame.cpp index 512d1d25c96..3e866622e3d 100644 --- a/layout/svg/SVGFEContainerFrame.cpp +++ b/layout/svg/SVGFEContainerFrame.cpp @@ -44,8 +44,6 @@ public: } #endif - virtual void DidSetStyleContext(nsStyleContext* aOldStyleContext); - #ifdef DEBUG virtual void Init(nsIContent* aContent, nsIFrame* aParent, @@ -76,13 +74,6 @@ NS_NewSVGFEContainerFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) NS_IMPL_FRAMEARENA_HELPERS(SVGFEContainerFrame) -/* virtual */ void -SVGFEContainerFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext) -{ - SVGFEContainerFrameBase::DidSetStyleContext(aOldStyleContext); - nsSVGEffects::InvalidateRenderingObservers(this); -} - #ifdef DEBUG void SVGFEContainerFrame::Init(nsIContent* aContent, diff --git a/layout/svg/SVGFEImageFrame.cpp b/layout/svg/SVGFEImageFrame.cpp index 8f89ee8c619..dd769ff9d7c 100644 --- a/layout/svg/SVGFEImageFrame.cpp +++ b/layout/svg/SVGFEImageFrame.cpp @@ -48,8 +48,6 @@ public: } #endif - virtual void DidSetStyleContext(nsStyleContext* aOldStyleContext); - /** * Get the "type" of the frame * @@ -75,13 +73,6 @@ NS_NewSVGFEImageFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) NS_IMPL_FRAMEARENA_HELPERS(SVGFEImageFrame) -/* virtual */ void -SVGFEImageFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext) -{ - SVGFEImageFrameBase::DidSetStyleContext(aOldStyleContext); - nsSVGEffects::InvalidateRenderingObservers(this); -} - /* virtual */ void SVGFEImageFrame::DestroyFrom(nsIFrame* aDestructRoot) { diff --git a/layout/svg/SVGFELeafFrame.cpp b/layout/svg/SVGFELeafFrame.cpp index f93fb946084..4f578f27dd6 100644 --- a/layout/svg/SVGFELeafFrame.cpp +++ b/layout/svg/SVGFELeafFrame.cpp @@ -47,8 +47,6 @@ public: } #endif - virtual void DidSetStyleContext(nsStyleContext* aOldStyleContext); - /** * Get the "type" of the frame * @@ -74,13 +72,6 @@ NS_NewSVGFELeafFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) NS_IMPL_FRAMEARENA_HELPERS(SVGFELeafFrame) -/* virtual */ void -SVGFELeafFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext) -{ - SVGFELeafFrameBase::DidSetStyleContext(aOldStyleContext); - nsSVGEffects::InvalidateRenderingObservers(this); -} - #ifdef DEBUG void SVGFELeafFrame::Init(nsIContent* aContent, diff --git a/layout/svg/nsSVGForeignObjectFrame.cpp b/layout/svg/nsSVGForeignObjectFrame.cpp index 98688874657..c9ac187acb9 100644 --- a/layout/svg/nsSVGForeignObjectFrame.cpp +++ b/layout/svg/nsSVGForeignObjectFrame.cpp @@ -118,23 +118,6 @@ nsSVGForeignObjectFrame::AttributeChanged(int32_t aNameSpaceID, return NS_OK; } -/* virtual */ void -nsSVGForeignObjectFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext) -{ - nsSVGForeignObjectFrameBase::DidSetStyleContext(aOldStyleContext); - - // No need to invalidate before first reflow - that will happen elsewhere. - // Moreover we haven't been initialised properly yet so we may not have the - // right state bits. - if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW)) { - // XXXperf: probably only need a bounds update if 'font-size' changed and - // we have em unit width/height. Or, once we map 'transform' into style, - // if some transform property changed. - nsSVGEffects::InvalidateRenderingObservers(this); - nsSVGUtils::ScheduleReflowSVG(this); - } -} - NS_IMETHODIMP nsSVGForeignObjectFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, diff --git a/layout/svg/nsSVGForeignObjectFrame.h b/layout/svg/nsSVGForeignObjectFrame.h index c460e887248..04696cfd22b 100644 --- a/layout/svg/nsSVGForeignObjectFrame.h +++ b/layout/svg/nsSVGForeignObjectFrame.h @@ -39,8 +39,6 @@ public: nsIAtom* aAttribute, int32_t aModType); - virtual void DidSetStyleContext(nsStyleContext* aOldStyleContext) MOZ_OVERRIDE; - virtual nsIFrame* GetContentInsertionFrame() { return GetFirstPrincipalChild()->GetContentInsertionFrame(); } diff --git a/layout/svg/nsSVGGradientFrame.cpp b/layout/svg/nsSVGGradientFrame.cpp index be6fea879e9..c89fd4acdf3 100644 --- a/layout/svg/nsSVGGradientFrame.cpp +++ b/layout/svg/nsSVGGradientFrame.cpp @@ -57,13 +57,6 @@ NS_IMPL_FRAMEARENA_HELPERS(nsSVGGradientFrame) //---------------------------------------------------------------------- // nsIFrame methods: -/* virtual */ void -nsSVGGradientFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext) -{ - nsSVGEffects::InvalidateDirectRenderingObservers(this); - nsSVGGradientFrameBase::DidSetStyleContext(aOldStyleContext); -} - NS_IMETHODIMP nsSVGGradientFrame::AttributeChanged(int32_t aNameSpaceID, nsIAtom* aAttribute, diff --git a/layout/svg/nsSVGGradientFrame.h b/layout/svg/nsSVGGradientFrame.h index a31d9adcbba..203d7fd1d6c 100644 --- a/layout/svg/nsSVGGradientFrame.h +++ b/layout/svg/nsSVGGradientFrame.h @@ -53,8 +53,6 @@ public: const gfxRect *aOverrideBounds); // nsIFrame interface: - virtual void DidSetStyleContext(nsStyleContext* aOldStyleContext); - NS_IMETHOD AttributeChanged(int32_t aNameSpaceID, nsIAtom* aAttribute, int32_t aModType); diff --git a/layout/svg/nsSVGMaskFrame.cpp b/layout/svg/nsSVGMaskFrame.cpp index 90c39f27a51..0dcbb952409 100644 --- a/layout/svg/nsSVGMaskFrame.cpp +++ b/layout/svg/nsSVGMaskFrame.cpp @@ -131,13 +131,6 @@ nsSVGMaskFrame::ComputeMaskAlpha(nsRenderingContext *aContext, return retval.forget(); } -/* virtual */ void -nsSVGMaskFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext) -{ - nsSVGEffects::InvalidateDirectRenderingObservers(this); - nsSVGMaskFrameBase::DidSetStyleContext(aOldStyleContext); -} - NS_IMETHODIMP nsSVGMaskFrame::AttributeChanged(int32_t aNameSpaceID, nsIAtom* aAttribute, diff --git a/layout/svg/nsSVGMaskFrame.h b/layout/svg/nsSVGMaskFrame.h index ad078a83836..53d103b83c0 100644 --- a/layout/svg/nsSVGMaskFrame.h +++ b/layout/svg/nsSVGMaskFrame.h @@ -37,8 +37,6 @@ public: const gfxMatrix &aMatrix, float aOpacity = 1.0f); - virtual void DidSetStyleContext(nsStyleContext* aOldStyleContext); - NS_IMETHOD AttributeChanged(int32_t aNameSpaceID, nsIAtom* aAttribute, int32_t aModType); diff --git a/layout/svg/nsSVGPathGeometryFrame.cpp b/layout/svg/nsSVGPathGeometryFrame.cpp index 7d746db38d3..5eba955662b 100644 --- a/layout/svg/nsSVGPathGeometryFrame.cpp +++ b/layout/svg/nsSVGPathGeometryFrame.cpp @@ -120,21 +120,6 @@ nsSVGPathGeometryFrame::AttributeChanged(int32_t aNameSpaceID, return NS_OK; } -/* virtual */ void -nsSVGPathGeometryFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext) -{ - nsSVGPathGeometryFrameBase::DidSetStyleContext(aOldStyleContext); - - // XXX: we'd like to use the style_hint mechanism and the - // ContentStateChanged/AttributeChanged functions for style changes - // to get slightly finer granularity, but unfortunately the - // style_hints don't map very well onto svg. Here seems to be the - // best place to deal with style changes: - - nsSVGEffects::InvalidateRenderingObservers(this); - nsSVGUtils::ScheduleReflowSVG(this); -} - nsIAtom * nsSVGPathGeometryFrame::GetType() const { diff --git a/layout/svg/nsSVGPathGeometryFrame.h b/layout/svg/nsSVGPathGeometryFrame.h index 8e29cb3f88e..c7618f2b366 100644 --- a/layout/svg/nsSVGPathGeometryFrame.h +++ b/layout/svg/nsSVGPathGeometryFrame.h @@ -56,8 +56,6 @@ public: nsIAtom* aAttribute, int32_t aModType); - virtual void DidSetStyleContext(nsStyleContext* aOldStyleContext); - /** * Get the "type" of the frame * diff --git a/layout/svg/nsSVGPatternFrame.cpp b/layout/svg/nsSVGPatternFrame.cpp index 7ac54f881ba..207a1c516f0 100644 --- a/layout/svg/nsSVGPatternFrame.cpp +++ b/layout/svg/nsSVGPatternFrame.cpp @@ -62,13 +62,6 @@ NS_IMPL_FRAMEARENA_HELPERS(nsSVGPatternFrame) //---------------------------------------------------------------------- // nsIFrame methods: -/* virtual */ void -nsSVGPatternFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext) -{ - nsSVGEffects::InvalidateDirectRenderingObservers(this); - nsSVGPatternFrameBase::DidSetStyleContext(aOldStyleContext); -} - NS_IMETHODIMP nsSVGPatternFrame::AttributeChanged(int32_t aNameSpaceID, nsIAtom* aAttribute, diff --git a/layout/svg/nsSVGPatternFrame.h b/layout/svg/nsSVGPatternFrame.h index 3d35110480c..a1b276b750b 100644 --- a/layout/svg/nsSVGPatternFrame.h +++ b/layout/svg/nsSVGPatternFrame.h @@ -52,8 +52,6 @@ public: virtual gfxMatrix GetCanvasTM(uint32_t aFor); // nsIFrame interface: - virtual void DidSetStyleContext(nsStyleContext* aOldStyleContext); - NS_IMETHOD AttributeChanged(int32_t aNameSpaceID, nsIAtom* aAttribute, int32_t aModType); diff --git a/layout/svg/nsSVGStopFrame.cpp b/layout/svg/nsSVGStopFrame.cpp index 10073eee941..815fb8b92ce 100644 --- a/layout/svg/nsSVGStopFrame.cpp +++ b/layout/svg/nsSVGStopFrame.cpp @@ -40,8 +40,6 @@ public: const nsRect& aDirtyRect, const nsDisplayListSet& aLists) MOZ_OVERRIDE {} - virtual void DidSetStyleContext(nsStyleContext* aOldStyleContext); - NS_IMETHOD AttributeChanged(int32_t aNameSpaceID, nsIAtom* aAttribute, int32_t aModType); @@ -86,13 +84,6 @@ nsSVGStopFrame::Init(nsIContent* aContent, } #endif /* DEBUG */ -/* virtual */ void -nsSVGStopFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext) -{ - nsSVGStopFrameBase::DidSetStyleContext(aOldStyleContext); - nsSVGEffects::InvalidateRenderingObservers(this); -} - nsIAtom * nsSVGStopFrame::GetType() const { From 7f0fcf5b4967ad4923f9aaaac1ed8892b61a3e17 Mon Sep 17 00:00:00 2001 From: Justin Lebar Date: Fri, 24 May 2013 13:10:47 -0400 Subject: [PATCH 44/91] Bug 873799 - Add an explicit barrier before __sync_lock_test_and_set in mfbt/Atomics.h r=froydnj --HG-- extra : rebase_source : 64d23f1df2fef8719413800ebb0afe6f51d1e3af --- mfbt/Atomics.h | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/mfbt/Atomics.h b/mfbt/Atomics.h index 776c6738aac..a47bd1f2622 100644 --- a/mfbt/Atomics.h +++ b/mfbt/Atomics.h @@ -312,12 +312,10 @@ namespace detail { * the header and so will be handled above. We provide a version of * atomics using the __sync_* intrinsics to support older versions of GCC. * - * All __sync_* intrinsics that we use below act as full memory - * barriers, for both compiler and hardware reordering, with one notable - * exception: __sync_lock_test_and_set. This intrinsic is not a full - * barrier, but only an acquire barrier. In practice, this has turned - * out to not matter very much, and will become less important as newer - * compilers are used. + * All __sync_* intrinsics that we use below act as full memory barriers, for + * both compiler and hardware reordering, except for __sync_lock_test_and_set, + * which is a only an acquire barrier. When we call __sync_lock_test_and_set, + * we add a barrier above it as appropriate. */ template struct Barrier; @@ -371,6 +369,12 @@ struct IntrinsicMemoryOps Barrier::afterStore(); } static T exchange(T& ptr, T val) { + // __sync_lock_test_and_set is only an acquire barrier; loads and stores + // can't be moved up from after to before it, but they can be moved down + // from before to after it. We may want a stricter ordering, so we need + // an explicit barrier. + + Barrier::beforeStore(); return __sync_lock_test_and_set(&ptr, val); } }; From 20471613d31a3e2765b3bfc51a269b9d158e28d2 Mon Sep 17 00:00:00 2001 From: Justin Lebar Date: Fri, 24 May 2013 13:10:47 -0400 Subject: [PATCH 45/91] Bug 873800 - Add compare-and-exchange to mfbt/Atomics.h. r=froydnj --HG-- extra : rebase_source : c9e455ed249e6b3cd0f532d240abfb602e2e88a8 --- mfbt/Atomics.h | 71 ++++++++++++++++++++++++++++++++------ mfbt/tests/TestAtomics.cpp | 19 ++++++++++ 2 files changed, 80 insertions(+), 10 deletions(-) diff --git a/mfbt/Atomics.h b/mfbt/Atomics.h index a47bd1f2622..d79af6c6952 100644 --- a/mfbt/Atomics.h +++ b/mfbt/Atomics.h @@ -214,6 +214,9 @@ struct IntrinsicMemoryOps : public IntrinsicBase static T exchange(typename Base::ValueType& ptr, T val) { return ptr.exchange(val, Base::OrderedOp::AtomicRMWOrder); } + static bool compareExchange(typename Base::ValueType& ptr, T oldVal, T newVal) { + return ptr.compare_exchange_strong(oldVal, newVal, Base::OrderedOp::AtomicRMWOrder); + } }; template @@ -377,6 +380,9 @@ struct IntrinsicMemoryOps Barrier::beforeStore(); return __sync_lock_test_and_set(&ptr, val); } + static bool compareExchange(T& ptr, T oldVal, T newVal) { + return __sync_bool_compare_and_swap(&ptr, oldVal, newVal); + } }; template @@ -463,6 +469,7 @@ long __cdecl _InterlockedOr(long volatile* dst, long value); long __cdecl _InterlockedXor(long volatile* dst, long value); long __cdecl _InterlockedAnd(long volatile* dst, long value); long __cdecl _InterlockedExchange(long volatile *dst, long value); +long __cdecl _InterlockedCompareExchange(long volatile *dst, long newVal, long oldVal); } # pragma intrinsic(_InterlockedExchangeAdd) @@ -470,6 +477,7 @@ long __cdecl _InterlockedExchange(long volatile *dst, long value); # pragma intrinsic(_InterlockedXor) # pragma intrinsic(_InterlockedAnd) # pragma intrinsic(_InterlockedExchange) +# pragma intrinsic(_InterlockedCompareExchange) namespace mozilla { namespace detail { @@ -484,10 +492,8 @@ namespace detail { # endif /* - * This template should define seven functions and |Type|, the datatype upon - * which the functions operate. These five functions perform the obvious - * operation on the value contained in |*ptr| combined with |val| and return - * the value previously stored in |*ptr| + * The PrimitiveIntrinsics template should define |Type|, the datatype of size + * DataSize upon which we operate, and the following eight functions. * * static Type add(Type* ptr, Type val); * static Type sub(Type* ptr, Type val); @@ -495,17 +501,33 @@ namespace detail { * static Type xor_(Type* ptr, Type val); * static Type and_(Type* ptr, Type val); * - * This function atomically stores |val| into |*ptr| and must provide a - * full memory fence after the store to prevent compiler and hardware - * instruction reordering. It should also act as a compiler barrier - * to prevent reads and writes from moving to after the store. + * These functions perform the obvious operation on the value contained in + * |*ptr| combined with |val| and return the value previously stored in + * |*ptr|. * * static void store(Type* ptr, Type val); * - * This function atomically stores |val| into |*ptr| and returns the - * previous contents of *ptr; + * This function atomically stores |val| into |*ptr| and must provide a full + * memory fence after the store to prevent compiler and hardware instruction + * reordering. It should also act as a compiler barrier to prevent reads and + * writes from moving to after the store. * * static Type exchange(Type* ptr, Type val); + * + * This function atomically stores |val| into |*ptr| and returns the previous + * contents of *ptr; + * + * static bool compareExchange(Type* ptr, Type oldVal, Type newVal); + * + * This function atomically performs the following operation: + * + * if (*ptr == oldVal) { + * *ptr = newVal; + * return true; + * } else { + * return false; + * } + * */ template struct PrimitiveIntrinsics; @@ -539,6 +561,9 @@ struct PrimitiveIntrinsics<4> static Type exchange(Type* ptr, Type val) { return _InterlockedExchange(ptr, val); } + static bool compareExchange(Type* ptr, Type oldVal, Type newVal) { + return _InterlockedCompareExchange(ptr, newVal, oldVal) == oldVal; + } }; # if defined(_M_X64) @@ -554,6 +579,9 @@ long long __cdecl _InterlockedAnd64(long long volatile* dst, long long value); long long __cdecl _InterlockedExchange64(long long volatile* dst, long long value); +long long __cdecl _InterlockedCompareExchange64(long long volatile* dst, + long long newVal, + long long oldVal); } # pragma intrinsic(_InterlockedExchangeAdd64) @@ -561,6 +589,7 @@ long long __cdecl _InterlockedExchange64(long long volatile* dst, # pragma intrinsic(_InterlockedXor64) # pragma intrinsic(_InterlockedAnd64) # pragma intrinsic(_InterlockedExchange64) +# pragma intrinsic(_InterlockedCompareExchange64) template <> struct PrimitiveIntrinsics<8> @@ -591,6 +620,9 @@ struct PrimitiveIntrinsics<8> static Type exchange(Type* ptr, Type val) { return _InterlockedExchange64(ptr, val); } + static bool compareExchange(T* ptr, T oldVal, T newVal) { + return _InterlockedCompareExchange64(ptr, newVal, oldVal) == oldVal; + } }; # endif @@ -683,6 +715,11 @@ struct IntrinsicMemoryOps : public IntrinsicBase Cast::toPrimType(val)); return Cast::fromPrimType(oldval); } + static bool compareExchange(ValueType& ptr, ValueType oldVal, ValueType newVal) { + return Primitives::compareExchange(reinterpret_cast(&ptr), + Cast::toPrimType(oldVal), + Cast::toPrimType(newVal)); + } }; template @@ -792,6 +829,20 @@ class AtomicBase T exchange(T aValue) { return Intrinsics::exchange(mValue, aValue); } + /** + * Performs an atomic compare-and-swap operation and returns true if it + * succeeded. This is equivalent to atomically doing + * + * if (mValue == aOldValue) { + * mValue = aNewValue; + * return true; + * } else { + * return false; + * } + */ + bool compareExchange(T aOldValue, T aNewValue) { + return Intrinsics::compareExchange(mValue, aOldValue, aNewValue); + } private: template diff --git a/mfbt/tests/TestAtomics.cpp b/mfbt/tests/TestAtomics.cpp index fc5156a5174..3e058936d92 100644 --- a/mfbt/tests/TestAtomics.cpp +++ b/mfbt/tests/TestAtomics.cpp @@ -68,6 +68,16 @@ TestTypeWithOrdering() result = atomic.exchange(42); MOZ_ASSERT(atomic == T(42), "Atomic exchange did not work"); MOZ_ASSERT(result == T(30), "Atomic exchange returned the wrong value"); + + // Test CAS. + atomic = T(1); + DebugOnly boolResult = atomic.compareExchange(0, 2); + MOZ_ASSERT(!boolResult, "CAS should have returned false."); + MOZ_ASSERT(atomic == T(1), "CAS shouldn't have done anything."); + + boolResult = atomic.compareExchange(1, 42); + MOZ_ASSERT(boolResult, "CAS should have succeeded."); + MOZ_ASSERT(atomic == T(42), "CAS should have changed atomic's value."); } template @@ -107,6 +117,15 @@ TestPointerWithOrdering() result = atomic.exchange(array1); MOZ_ASSERT(atomic == array1, "Atomic exchange did not work"); MOZ_ASSERT(result == array1 + 2, "Atomic exchange returned the wrong value"); + + atomic = array1; + DebugOnly boolResult = atomic.compareExchange(array1 + 1, array1 + 2); + MOZ_ASSERT(!boolResult, "CAS should have returned false."); + MOZ_ASSERT(atomic == array1, "CAS shouldn't have done anything."); + + boolResult = atomic.compareExchange(array1, array1 + 3); + MOZ_ASSERT(boolResult, "CAS should have succeeded."); + MOZ_ASSERT(atomic == array1 + 3, "CAS should have changed atomic's value."); } template From fe4ce4616483d5018eb2441592599c8a87160259 Mon Sep 17 00:00:00 2001 From: Justin Lebar Date: Fri, 24 May 2013 13:10:47 -0400 Subject: [PATCH 46/91] Bug 832609 - Add mozilla::OffTheBooksMutex, which is just like mozilla::Mutex, except it's not leak-checked. r=khuey --HG-- extra : rebase_source : 2c06334a5db4c8b57c9a3480a83e543dd98a9b20 --- xpcom/glue/BlockingResourceBase.cpp | 6 +-- xpcom/glue/Mutex.h | 62 +++++++++++++++++++---------- 2 files changed, 45 insertions(+), 23 deletions(-) diff --git a/xpcom/glue/BlockingResourceBase.cpp b/xpcom/glue/BlockingResourceBase.cpp index 07024bbbd9a..b08ff23f5fe 100644 --- a/xpcom/glue/BlockingResourceBase.cpp +++ b/xpcom/glue/BlockingResourceBase.cpp @@ -218,9 +218,9 @@ BlockingResourceBase::PrintCycle(const DDT::ResourceAcquisitionArray* aCycle, // -// Debug implementation of Mutex +// Debug implementation of (OffTheBooks)Mutex void -Mutex::Lock() +OffTheBooksMutex::Lock() { CallStack callContext = CallStack(); @@ -230,7 +230,7 @@ Mutex::Lock() } void -Mutex::Unlock() +OffTheBooksMutex::Unlock() { Release(); // protected by mLock PRStatus status = PR_Unlock(mLock); diff --git a/xpcom/glue/Mutex.h b/xpcom/glue/Mutex.h index c395fc349f5..2edd53c59cb 100644 --- a/xpcom/glue/Mutex.h +++ b/xpcom/glue/Mutex.h @@ -20,48 +20,44 @@ // locked and unlocked // - MutexAutoUnlock, complementary sibling to MutexAutoLock // -// Using MutexAutoLock/MutexAutoUnlock is MUCH preferred to making bare -// calls to Mutex.Lock and Unlock. +// - OffTheBooksMutex, a non-recursive mutex that doesn't do leak checking +// - OffTheBooksMutexAuto{Lock,Unlock} - Like MutexAuto{Lock,Unlock}, but for +// an OffTheBooksMutex. +// +// Using MutexAutoLock/MutexAutoUnlock etc. is MUCH preferred to making bare +// calls to Lock and Unlock. // namespace mozilla { - /** - * Mutex - * When possible, use MutexAutoLock/MutexAutoUnlock to lock/unlock this - * mutex within a scope, instead of calling Lock/Unlock directly. - **/ - -class NS_COM_GLUE Mutex : BlockingResourceBase + * OffTheBooksMutex is identical to Mutex, except that OffTheBooksMutex doesn't + * include leak checking. Sometimes you want to intentionally "leak" a mutex + * until shutdown; in these cases, OffTheBooksMutex is for you. + */ +class NS_COM_GLUE OffTheBooksMutex : BlockingResourceBase { public: /** - * Mutex * @param name A name which can reference this lock * @returns If failure, nullptr * If success, a valid Mutex* which must be destroyed * by Mutex::DestroyMutex() **/ - Mutex(const char* name) : + OffTheBooksMutex(const char* name) : BlockingResourceBase(name, eMutex) { - MOZ_COUNT_CTOR(Mutex); mLock = PR_NewLock(); if (!mLock) NS_RUNTIMEABORT("Can't allocate mozilla::Mutex"); } - /** - * ~Mutex - **/ - ~Mutex() + ~OffTheBooksMutex() { NS_ASSERTION(mLock, "improperly constructed Lock or double free"); // NSPR does consistency checks for us PR_DestroyLock(mLock); mLock = 0; - MOZ_COUNT_DTOR(Mutex); } #ifndef DEBUG @@ -116,15 +112,39 @@ public: #endif // ifndef DEBUG private: - Mutex(); - Mutex(const Mutex&); - Mutex& operator=(const Mutex&); + OffTheBooksMutex(); + OffTheBooksMutex(const OffTheBooksMutex&); + OffTheBooksMutex& operator=(const OffTheBooksMutex&); PRLock* mLock; friend class CondVar; }; +/** + * Mutex + * When possible, use MutexAutoLock/MutexAutoUnlock to lock/unlock this + * mutex within a scope, instead of calling Lock/Unlock directly. + */ +class NS_COM_GLUE Mutex : public OffTheBooksMutex +{ +public: + Mutex(const char* name) + : OffTheBooksMutex(name) + { + MOZ_COUNT_CTOR(Mutex); + } + + ~Mutex() + { + MOZ_COUNT_DTOR(Mutex); + } + +private: + Mutex(); + Mutex(const Mutex&); + Mutex& operator=(const Mutex&); +}; /** * MutexAutoLock @@ -169,6 +189,7 @@ private: }; typedef BaseAutoLock MutexAutoLock; +typedef BaseAutoLock OffTheBooksMutexAutoLock; /** * MutexAutoUnlock @@ -206,6 +227,7 @@ private: }; typedef BaseAutoUnlock MutexAutoUnlock; +typedef BaseAutoUnlock OffTheBooksMutexAutoUnlock; } // namespace mozilla From 6dd6b1436d8960459267398af0b945983980baaa Mon Sep 17 00:00:00 2001 From: Justin Lebar Date: Fri, 24 May 2013 13:10:47 -0400 Subject: [PATCH 47/91] Bug 873801 - Add StaticMutex, which is like StaticAutoPtr, except it's lazily initialized in a threadsafe manner. r=khuey --HG-- extra : rebase_source : aaa9be27be5dfd448f8877073a44d4b29b02d70f --- xpcom/base/StaticMutex.h | 83 ++++++++++++++++++++++++++++++++++++++++ xpcom/base/moz.build | 1 + 2 files changed, 84 insertions(+) create mode 100644 xpcom/base/StaticMutex.h diff --git a/xpcom/base/StaticMutex.h b/xpcom/base/StaticMutex.h new file mode 100644 index 00000000000..29c1c1a0d74 --- /dev/null +++ b/xpcom/base/StaticMutex.h @@ -0,0 +1,83 @@ +#ifndef mozilla_StaticMutex_h +#define mozilla_StaticMutex_h + +#include "mozilla/Atomics.h" +#include "mozilla/Mutex.h" + +namespace mozilla { + +/** + * StaticMutex is a Mutex that can (and in fact, must) be used as a + * global/static variable. + * + * The main reason to use StaticMutex as opposed to + * StaticAutoPtr is that we instantiate the StaticMutex in a + * thread-safe manner the first time it's used. + * + * The same caveats that apply to StaticAutoPtr apply to StaticMutex. In + * particular, do not use StaticMutex as a stack variable or a class instance + * variable, because this class relies on the fact that global variablies are + * initialized to 0 in order to initialize mMutex. It is only safe to use + * StaticMutex as a global or static variable. + */ +class StaticMutex +{ +public: + // In debug builds, check that mMutex is initialized for us as we expect by + // the compiler. In non-debug builds, don't declare a constructor so that + // the compiler can see that the constructor is trivial. +#ifdef DEBUG + StaticMutex() + { + MOZ_ASSERT(!mMutex); + } +#endif + + void Lock() + { + Mutex()->Lock(); + } + + void Unlock() + { + Mutex()->Unlock(); + } + +private: + OffTheBooksMutex* Mutex() + { + if (mMutex) { + return mMutex; + } + + OffTheBooksMutex* mutex = new OffTheBooksMutex("StaticMutex"); + if (!mMutex.compareExchange(nullptr, mutex)) { + delete mutex; + } + + return mMutex; + } + + Atomic mMutex; + + + // Disallow copy constructor, but only in debug mode. We only define + // a default constructor in debug mode (see above); if we declared + // this constructor always, the compiler wouldn't generate a trivial + // default constructor for us in non-debug mode. +#ifdef DEBUG + StaticMutex(StaticMutex &other); +#endif + + // Disallow these operators. + StaticMutex& operator=(StaticMutex* rhs); + static void* operator new(size_t) CPP_THROW_NEW; + static void operator delete(void*); +}; + +typedef BaseAutoLock StaticMutexAutoLock; +typedef BaseAutoUnlock StaticMutexAutoUnlock; + +} // namespace mozilla + +#endif diff --git a/xpcom/base/moz.build b/xpcom/base/moz.build index dbd4defb0fd..f3a0b42058e 100644 --- a/xpcom/base/moz.build +++ b/xpcom/base/moz.build @@ -64,6 +64,7 @@ EXPORTS.mozilla += [ 'ClearOnShutdown.h', 'MapsMemoryReporter.h', 'StackWalk.h', + 'StaticMutex.h', 'StaticPtr.h', 'VisualEventTracer.h', 'nsMemoryInfoDumper.h', From 72301329cf17fc1329ede9f2eeed68138bcf9741 Mon Sep 17 00:00:00 2001 From: Justin Lebar Date: Fri, 24 May 2013 13:10:48 -0400 Subject: [PATCH 48/91] Bug 832609 - Make memory reporter for blobs threadsafe. r=khuey --HG-- extra : rebase_source : 4dc2387ae3867699085139545ff2b57104b1f72a --- content/base/public/nsDOMFile.h | 16 +++++++++++++++- content/base/src/nsDOMFile.cpp | 5 +++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/content/base/public/nsDOMFile.h b/content/base/public/nsDOMFile.h index 367f33747ea..19c7b4f2422 100644 --- a/content/base/public/nsDOMFile.h +++ b/content/base/public/nsDOMFile.h @@ -22,6 +22,7 @@ #include "mozilla/GuardObjects.h" #include "mozilla/LinkedList.h" #include "mozilla/StandardInteger.h" +#include "mozilla/StaticMutex.h" #include "mozilla/StaticPtr.h" #include "mozilla/dom/DOMError.h" #include "mozilla/dom/indexedDB/FileInfo.h" @@ -343,6 +344,10 @@ protected: bool mStoredFile; }; +/** + * This class may be used off the main thread, and in particular, its + * constructor and destructor may not run on the same thread. Be careful! + */ class nsDOMMemoryFile : public nsDOMFile { public: @@ -394,6 +399,8 @@ protected: : mData(aMemoryBuffer) , mLength(aLength) { + mozilla::StaticMutexAutoLock lock(sDataOwnerMutex); + if (!sDataOwners) { sDataOwners = new mozilla::LinkedList(); EnsureMemoryReporterRegistered(); @@ -402,6 +409,8 @@ protected: } ~DataOwner() { + mozilla::StaticMutexAutoLock lock(sDataOwnerMutex); + remove(); if (sDataOwners->isEmpty()) { // Free the linked list if it's empty. @@ -413,8 +422,13 @@ protected: static void EnsureMemoryReporterRegistered(); - static bool sMemoryReporterRegistered; + // sDataOwners and sMemoryReporterRegistered may only be accessed while + // holding sDataOwnerMutex! You also must hold the mutex while touching + // elements of the linked list that DataOwner inherits from. + static mozilla::StaticMutex sDataOwnerMutex; static mozilla::StaticAutoPtr > sDataOwners; + static bool sMemoryReporterRegistered; + void* mData; uint64_t mLength; }; diff --git a/content/base/src/nsDOMFile.cpp b/content/base/src/nsDOMFile.cpp index c1618849a6b..583dbc2edcf 100644 --- a/content/base/src/nsDOMFile.cpp +++ b/content/base/src/nsDOMFile.cpp @@ -611,6 +611,9 @@ nsDOMMemoryFile::GetInternalStream(nsIInputStream **aStream) return DataOwnerAdapter::Create(mDataOwner, mStart, mLength, aStream); } +/* static */ StaticMutex +nsDOMMemoryFile::DataOwner::sDataOwnerMutex; + /* static */ StaticAutoPtr > nsDOMMemoryFile::DataOwner::sDataOwners; @@ -635,6 +638,8 @@ class nsDOMMemoryFileDataOwnerMemoryReporter MOZ_FINAL { typedef nsDOMMemoryFile::DataOwner DataOwner; + StaticMutexAutoLock lock(DataOwner::sDataOwnerMutex); + if (!DataOwner::sDataOwners) { return NS_OK; } From 8d8ccd383d017a8a06a4cf9bc659e698691e9caa Mon Sep 17 00:00:00 2001 From: Shane Tully Date: Fri, 24 May 2013 10:56:45 -0700 Subject: [PATCH 49/91] Bug 874641 - Fix warning: xpcom/base/nsUUIDGenerator.cpp [-Wsign-compare]. r=bsmedberg --- xpcom/base/nsUUIDGenerator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xpcom/base/nsUUIDGenerator.cpp b/xpcom/base/nsUUIDGenerator.cpp index 5f07cb4f1e9..872037285c8 100644 --- a/xpcom/base/nsUUIDGenerator.cpp +++ b/xpcom/base/nsUUIDGenerator.cpp @@ -124,7 +124,7 @@ nsUUIDGenerator::GenerateUUIDInPlace(nsID* id) while (bytesLeft > 0) { #ifdef ANDROID long rval = arc4random(); - const int mRBytes = 4; + const size_t mRBytes = 4; #else long rval = random(); #endif From 869557dcf743f11775aed93b874c3349a94073e5 Mon Sep 17 00:00:00 2001 From: Eitan Isaacson Date: Fri, 24 May 2013 14:13:50 -0400 Subject: [PATCH 50/91] Bug 869280 - Add modalRoot to pivot API. rs=surkov --- accessible/public/nsIAccessiblePivot.idl | 7 +- accessible/src/base/nsAccessiblePivot.cpp | 78 ++++++++++++++++------- accessible/src/base/nsAccessiblePivot.h | 31 ++++++--- 3 files changed, 81 insertions(+), 35 deletions(-) diff --git a/accessible/public/nsIAccessiblePivot.idl b/accessible/public/nsIAccessiblePivot.idl index 84cfcdc8cea..813d7dd6f7c 100644 --- a/accessible/public/nsIAccessiblePivot.idl +++ b/accessible/public/nsIAccessiblePivot.idl @@ -20,7 +20,7 @@ interface nsIAccessiblePivotObserver; * provides traversal methods to move the pivot to next/prev state that complies * to a given rule. */ -[scriptable, uuid(15ff23de-879e-47ea-b536-6532466108c5)] +[scriptable, uuid(c2938033-e240-4fe5-9cb6-e7ad649ccd10)] interface nsIAccessiblePivot : nsISupports { const TextBoundaryType CHAR_BOUNDARY = 0; @@ -46,6 +46,11 @@ interface nsIAccessiblePivot : nsISupports */ readonly attribute nsIAccessible root; + /** + * The temporary modal root to which traversal is limited to. + */ + attribute nsIAccessible modalRoot; + /** * The start offset of the text range the pivot points at, otherwise -1. */ diff --git a/accessible/src/base/nsAccessiblePivot.cpp b/accessible/src/base/nsAccessiblePivot.cpp index 254a68d7ddb..5b2317e2715 100644 --- a/accessible/src/base/nsAccessiblePivot.cpp +++ b/accessible/src/base/nsAccessiblePivot.cpp @@ -6,7 +6,6 @@ #include "nsAccessiblePivot.h" -#include "Accessible-inl.h" #include "DocAccessible.h" #include "HyperTextAccessible.h" #include "nsAccUtils.h" @@ -45,7 +44,7 @@ private: // nsAccessiblePivot nsAccessiblePivot::nsAccessiblePivot(Accessible* aRoot) : - mRoot(aRoot), mPosition(nullptr), + mRoot(aRoot), mModalRoot(nullptr), mPosition(nullptr), mStartOffset(-1), mEndOffset(-1) { NS_ASSERTION(aRoot, "A root accessible is required"); @@ -95,7 +94,7 @@ nsAccessiblePivot::SetPosition(nsIAccessible* aPosition) if (aPosition) { secondPosition = do_QueryObject(aPosition); - if (!secondPosition || !IsRootDescendant(secondPosition)) + if (!secondPosition || !IsDescendantOf(secondPosition, GetActiveRoot())) return NS_ERROR_INVALID_ARG; } @@ -109,6 +108,32 @@ nsAccessiblePivot::SetPosition(nsIAccessible* aPosition) return NS_OK; } +NS_IMETHODIMP +nsAccessiblePivot::GetModalRoot(nsIAccessible** aModalRoot) +{ + NS_ENSURE_ARG_POINTER(aModalRoot); + + NS_IF_ADDREF(*aModalRoot = mModalRoot); + + return NS_OK; +} + +NS_IMETHODIMP +nsAccessiblePivot::SetModalRoot(nsIAccessible* aModalRoot) +{ + nsRefPtr modalRoot; + + if (aModalRoot) { + modalRoot = do_QueryObject(aModalRoot); + if (!modalRoot || !IsDescendantOf(modalRoot, mRoot)) + return NS_ERROR_INVALID_ARG; + } + + mModalRoot.swap(modalRoot); + + return NS_OK; +} + NS_IMETHODIMP nsAccessiblePivot::GetStartOffset(int32_t* aStartOffset) { @@ -146,7 +171,7 @@ nsAccessiblePivot::SetTextRange(nsIAccessibleText* aTextAccessible, return NS_ERROR_INVALID_ARG; HyperTextAccessible* newPosition = acc->AsHyperText(); - if (!newPosition || !IsRootDescendant(newPosition)) + if (!newPosition || !IsDescendantOf(newPosition, GetActiveRoot())) return NS_ERROR_INVALID_ARG; // Make sure the given offsets don't exceed the character count. @@ -180,9 +205,10 @@ nsAccessiblePivot::MoveNext(nsIAccessibleTraversalRule* aRule, *aResult = false; + Accessible* root = GetActiveRoot(); nsRefPtr anchor = (aArgc > 0) ? do_QueryObject(aAnchor) : mPosition; - if (anchor && (anchor->IsDefunct() || !IsRootDescendant(anchor))) + if (anchor && (anchor->IsDefunct() || !IsDescendantOf(anchor, root))) return NS_ERROR_NOT_IN_TREE; nsresult rv = NS_OK; @@ -207,9 +233,10 @@ nsAccessiblePivot::MovePrevious(nsIAccessibleTraversalRule* aRule, *aResult = false; + Accessible* root = GetActiveRoot(); nsRefPtr anchor = (aArgc > 0) ? do_QueryObject(aAnchor) : mPosition; - if (anchor && (anchor->IsDefunct() || !IsRootDescendant(anchor))) + if (anchor && (anchor->IsDefunct() || !IsDescendantOf(anchor, root))) return NS_ERROR_NOT_IN_TREE; nsresult rv = NS_OK; @@ -229,11 +256,11 @@ nsAccessiblePivot::MoveFirst(nsIAccessibleTraversalRule* aRule, bool* aResult) NS_ENSURE_ARG(aResult); NS_ENSURE_ARG(aRule); - if (mRoot && mRoot->IsDefunct()) - return NS_ERROR_NOT_IN_TREE; + Accessible* root = GetActiveRoot(); + NS_ENSURE_TRUE(root && !root->IsDefunct(), NS_ERROR_NOT_IN_TREE); nsresult rv = NS_OK; - Accessible* accessible = SearchForward(mRoot, aRule, true, &rv); + Accessible* accessible = SearchForward(root, aRule, true, &rv); NS_ENSURE_SUCCESS(rv, rv); if (accessible) @@ -243,20 +270,21 @@ nsAccessiblePivot::MoveFirst(nsIAccessibleTraversalRule* aRule, bool* aResult) } NS_IMETHODIMP -nsAccessiblePivot::MoveLast(nsIAccessibleTraversalRule* aRule, bool* aResult) +nsAccessiblePivot::MoveLast(nsIAccessibleTraversalRule* aRule, + bool* aResult) { NS_ENSURE_ARG(aResult); NS_ENSURE_ARG(aRule); - if (mRoot && mRoot->IsDefunct()) - return NS_ERROR_NOT_IN_TREE; + Accessible* root = GetActiveRoot(); + NS_ENSURE_TRUE(root && !root->IsDefunct(), NS_ERROR_NOT_IN_TREE); *aResult = false; nsresult rv = NS_OK; - Accessible* lastAccessible = mRoot; + Accessible* lastAccessible = root; Accessible* accessible = nullptr; - // First got to the last accessible in pre-order + // First go to the last accessible in pre-order while (lastAccessible->HasChildren()) lastAccessible = lastAccessible->LastChild(); @@ -302,13 +330,13 @@ nsAccessiblePivot::MoveToPoint(nsIAccessibleTraversalRule* aRule, *aResult = false; - if (mRoot && mRoot->IsDefunct()) - return NS_ERROR_NOT_IN_TREE; + Accessible* root = GetActiveRoot(); + NS_ENSURE_TRUE(root && !root->IsDefunct(), NS_ERROR_NOT_IN_TREE); RuleCache cache(aRule); Accessible* match = nullptr; - Accessible* child = mRoot->ChildAtPoint(aX, aY, Accessible::eDeepestChild); - while (child && mRoot != child) { + Accessible* child = root->ChildAtPoint(aX, aY, Accessible::eDeepestChild); + while (child && root != child) { uint16_t filtered = nsIAccessibleTraversalRule::FILTER_IGNORE; nsresult rv = cache.ApplyFilter(child, &filtered); NS_ENSURE_SUCCESS(rv, rv); @@ -360,15 +388,15 @@ nsAccessiblePivot::RemoveObserver(nsIAccessiblePivotObserver* aObserver) // Private utility methods bool -nsAccessiblePivot::IsRootDescendant(Accessible* aAccessible) +nsAccessiblePivot::IsDescendantOf(Accessible* aAccessible, Accessible* aAncestor) { - if (!mRoot || mRoot->IsDefunct()) + if (!aAncestor || aAncestor->IsDefunct()) return false; // XXX Optimize with IsInDocument() when appropriate. Blocked by bug 759875. Accessible* accessible = aAccessible; do { - if (accessible == mRoot) + if (accessible == aAncestor) return true; } while ((accessible = accessible->Parent())); @@ -411,7 +439,8 @@ nsAccessiblePivot::SearchBackward(Accessible* aAccessible, return accessible; } - while (accessible != mRoot) { + Accessible* root = GetActiveRoot(); + while (accessible != root) { Accessible* parent = accessible->Parent(); int32_t idxInParent = accessible->IndexInParent(); while (idxInParent > 0) { @@ -457,7 +486,8 @@ nsAccessiblePivot::SearchForward(Accessible* aAccessible, *aResult = NS_OK; // Initial position could be not set, in that case begin search from root. - Accessible* accessible = (!aAccessible) ? mRoot.get() : aAccessible; + Accessible* root = GetActiveRoot(); + Accessible* accessible = (!aAccessible) ? root : aAccessible; RuleCache cache(aRule); @@ -482,7 +512,7 @@ nsAccessiblePivot::SearchForward(Accessible* aAccessible, Accessible* sibling = nullptr; Accessible* temp = accessible; do { - if (temp == mRoot) + if (temp == root) break; sibling = temp->NextSibling(); diff --git a/accessible/src/base/nsAccessiblePivot.h b/accessible/src/base/nsAccessiblePivot.h index ced47d85983..4d6be159fdf 100644 --- a/accessible/src/base/nsAccessiblePivot.h +++ b/accessible/src/base/nsAccessiblePivot.h @@ -9,19 +9,12 @@ #include "nsIAccessiblePivot.h" +#include "Accessible-inl.h" #include "nsAutoPtr.h" #include "nsTObserverArray.h" #include "nsCycleCollectionParticipant.h" #include "mozilla/Attributes.h" -namespace mozilla { -namespace a11y { - -class Accessible; - -} // namespace a11y -} // namespace mozilla - class nsIAccessibleTraversalRule; /** @@ -58,9 +51,9 @@ private: PivotMoveReason aReason); /* - * Check to see that the given accessible is in the pivot's subtree. + * Check to see that the given accessible is a descendant of given ancestor */ - bool IsRootDescendant(Accessible* aAccessible); + bool IsDescendantOf(Accessible* aAccessible, Accessible* aAncestor); /* @@ -79,6 +72,19 @@ private: bool aSearchCurrent, nsresult* aResult); + /* + * Get the effective root for this pivot, either the true root or modal root. + */ + Accessible* GetActiveRoot() const + { + if (mModalRoot) { + NS_ENSURE_FALSE(mModalRoot->IsDefunct(), mRoot); + return mModalRoot; + } + + return mRoot; + } + /* * Update the pivot, and notify observers. Return true if it moved. */ @@ -89,6 +95,11 @@ private: */ nsRefPtr mRoot; + /* + * The temporary modal root accessible. + */ + nsRefPtr mModalRoot; + /* * The current pivot position. */ From 45546a3a666e2be8a831fa9f14fd2663e97c0d0c Mon Sep 17 00:00:00 2001 From: Eitan Isaacson Date: Fri, 24 May 2013 14:13:50 -0400 Subject: [PATCH 51/91] Bug 869280 - tests for modalroot. r=davidb --- accessible/tests/mochitest/pivot.js | 56 +++++++++++++++++-- .../mochitest/pivot/test_virtualcursor.html | 13 ++++- 2 files changed, 61 insertions(+), 8 deletions(-) diff --git a/accessible/tests/mochitest/pivot.js b/accessible/tests/mochitest/pivot.js index 47b9cb0e34a..0ffc7273efc 100644 --- a/accessible/tests/mochitest/pivot.js +++ b/accessible/tests/mochitest/pivot.js @@ -9,6 +9,7 @@ const FILTER_IGNORE = nsIAccessibleTraversalRule.FILTER_IGNORE; const FILTER_IGNORE_SUBTREE = nsIAccessibleTraversalRule.FILTER_IGNORE_SUBTREE; const NS_ERROR_NOT_IN_TREE = 0x80780026; +const NS_ERROR_INVALID_ARG = 0x80070057; //////////////////////////////////////////////////////////////////////////////// // Traversal rules @@ -260,20 +261,60 @@ function moveVCCoordInvoker(aDocAcc, aX, aY, aIgnoreNoMatch, } } +/** + * Change the pivot modalRoot + * + * @param aDocAcc [in] document that manages the virtual cursor + * @param aModalRootAcc [in] accessible of the modal root, or null + * @param aExpectedResult [in] error result expected. 0 if expecting success + */ +function setModalRootInvoker(aDocAcc, aModalRootAcc, aExpectedResult) +{ + this.invoke = function setModalRootInvoker_invoke() + { + var errorResult = 0; + try { + aDocAcc.virtualCursor.modalRoot = aModalRootAcc; + } catch (x) { + SimpleTest.ok( + x.result, "Unexpected exception when changing modal root: " + x); + errorResult = x.result; + } + + SimpleTest.is(errorResult, aExpectedResult, + "Did not get expected result when changing modalRoot"); + }; + + this.getID = function setModalRootInvoker_getID() + { + return "Set modalRoot to " + prettyName(aModalRootAcc); + }; + + this.eventSeq = []; + this.unexpectedEventSeq = [ + new invokerChecker(EVENT_VIRTUALCURSOR_CHANGED, aDocAcc) + ]; +} + /** * Add invokers to a queue to test a rule and an expected sequence of element ids * or accessible names for that rule in the given document. * - * @param aQueue [in] event queue in which to push invoker sequence. - * @param aDocAcc [in] the managing document of the virtual cursor we are testing - * @param aRule [in] the traversal rule to use in the invokers - * @param aSequence [in] a sequence of accessible names or element ids to expect with - * the given rule in the given document + * @param aQueue [in] event queue in which to push invoker sequence. + * @param aDocAcc [in] the managing document of the virtual cursor we are + * testing + * @param aRule [in] the traversal rule to use in the invokers + * @param aModalRoot [in] a modal root to use in this traversal sequence + * @param aSequence [in] a sequence of accessible names or element ids to expect + * with the given rule in the given document */ -function queueTraversalSequence(aQueue, aDocAcc, aRule, aSequence) +function queueTraversalSequence(aQueue, aDocAcc, aRule, aModalRoot, aSequence) { aDocAcc.virtualCursor.position = null; + // Add modal root (if any) + aQueue.push(new setModalRootInvoker(aDocAcc, aModalRoot, 0)); + for (var i = 0; i < aSequence.length; i++) { var invoker = new setVCPosInvoker(aDocAcc, "moveNext", aRule, aSequence[i]); @@ -302,6 +343,9 @@ function queueTraversalSequence(aQueue, aDocAcc, aRule, aSequence) // No previous more matches for given rule, expect no virtual cursor changes. aQueue.push(new setVCPosInvoker(aDocAcc, "movePrevious", aRule, false)); + + // Remove modal root (if any). + aQueue.push(new setModalRootInvoker(aDocAcc, null, 0)); } /** diff --git a/accessible/tests/mochitest/pivot/test_virtualcursor.html b/accessible/tests/mochitest/pivot/test_virtualcursor.html index 9c3af5d6dd0..195ae17ff38 100644 --- a/accessible/tests/mochitest/pivot/test_virtualcursor.html +++ b/accessible/tests/mochitest/pivot/test_virtualcursor.html @@ -45,11 +45,11 @@ closeBrowserWindow(); } - queueTraversalSequence(gQueue, docAcc, HeadersTraversalRule, + queueTraversalSequence(gQueue, docAcc, HeadersTraversalRule, null, ['heading-1-1', 'heading-2-1', 'heading-2-2']); queueTraversalSequence( - gQueue, docAcc, ObjectTraversalRule, + gQueue, docAcc, ObjectTraversalRule, null, ['Main Title', 'First Section Title', 'Lorem ipsum ', 'dolor', ' sit amet. Integer vitae urna leo, id ', 'semper', ' nulla. ', 'Second Section Title', @@ -86,6 +86,15 @@ gQueue.push(new moveVCCoordInvoker(docAcc, x - 1, y - 1, false, HeadersTraversalRule, null)); + queueTraversalSequence( + gQueue, docAcc, ObjectTraversalRule, + getAccessible(doc.getElementById('paragraph-1')), + ['Lorem ipsum ', 'dolor', ' sit amet. Integer vitae urna leo, id ', + 'semper', ' nulla. ']); + + gQueue.push(new setModalRootInvoker(docAcc, docAcc.parent, + NS_ERROR_INVALID_ARG)); + gQueue.invoke(); } From cf64724ee8c0f53344591aa10629e9c982fed186 Mon Sep 17 00:00:00 2001 From: Eitan Isaacson Date: Fri, 24 May 2013 14:13:51 -0400 Subject: [PATCH 52/91] Bug 775621 - Add traversal flag for aria-hidden. r=surkov --- accessible/public/nsIAccessiblePivot.idl | 3 ++- accessible/src/base/nsAccessiblePivot.cpp | 10 ++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/accessible/public/nsIAccessiblePivot.idl b/accessible/public/nsIAccessiblePivot.idl index 813d7dd6f7c..8982b25a9fa 100644 --- a/accessible/public/nsIAccessiblePivot.idl +++ b/accessible/public/nsIAccessiblePivot.idl @@ -186,7 +186,7 @@ interface nsIAccessiblePivotObserver : nsISupports in PivotMoveReason aReason); }; -[scriptable, uuid(307d98b6-dba9-49cf-ba17-ef8b053044eb)] +[scriptable, uuid(366fe92b-44c9-4769-ae40-7c2a075d3b16)] interface nsIAccessibleTraversalRule : nsISupports { /* Ignore this accessible object */ @@ -200,6 +200,7 @@ interface nsIAccessibleTraversalRule : nsISupports const unsigned long PREFILTER_INVISIBLE = 0x00000001; const unsigned long PREFILTER_OFFSCREEN = 0x00000002; const unsigned long PREFILTER_NOT_FOCUSABLE = 0x00000004; + const unsigned long PREFILTER_ARIA_HIDDEN = 0x00000008; /** * Pre-filter bitfield to filter out obviously ignorable nodes and lighten diff --git a/accessible/src/base/nsAccessiblePivot.cpp b/accessible/src/base/nsAccessiblePivot.cpp index 5b2317e2715..19861fa76a9 100644 --- a/accessible/src/base/nsAccessiblePivot.cpp +++ b/accessible/src/base/nsAccessiblePivot.cpp @@ -579,6 +579,16 @@ RuleCache::ApplyFilter(Accessible* aAccessible, uint16_t* aResult) if ((nsIAccessibleTraversalRule::PREFILTER_NOT_FOCUSABLE & mPreFilter) && !(state & states::FOCUSABLE)) return NS_OK; + + if (nsIAccessibleTraversalRule::PREFILTER_ARIA_HIDDEN & mPreFilter) { + nsIContent* content = aAccessible->GetContent(); + if (nsAccUtils::HasDefinedARIAToken(content, nsGkAtoms::aria_hidden) && + !content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::aria_hidden, + nsGkAtoms::_false, eCaseMatters)) { + *aResult |= nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE; + return NS_OK; + } + } } if (mAcceptRolesLength > 0) { From 633e18be5196aa7c9e1b408fbe3829787637bc35 Mon Sep 17 00:00:00 2001 From: Eitan Isaacson Date: Fri, 24 May 2013 14:13:51 -0400 Subject: [PATCH 53/91] Bug 775621 - Introduce aria-hidden tests for pivot API. r=surkov --- accessible/tests/mochitest/pivot.js | 3 ++- .../tests/mochitest/pivot/doc_virtualcursor.html | 12 ++++++++---- .../tests/mochitest/pivot/test_virtualcursor.html | 2 +- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/accessible/tests/mochitest/pivot.js b/accessible/tests/mochitest/pivot.js index 0ffc7273efc..8bb2f42a544 100644 --- a/accessible/tests/mochitest/pivot.js +++ b/accessible/tests/mochitest/pivot.js @@ -4,6 +4,7 @@ Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); // Constants const PREFILTER_INVISIBLE = nsIAccessibleTraversalRule.PREFILTER_INVISIBLE; +const PREFILTER_ARIA_HIDDEN = nsIAccessibleTraversalRule.PREFILTER_ARIA_HIDDEN; const FILTER_MATCH = nsIAccessibleTraversalRule.FILTER_MATCH; const FILTER_IGNORE = nsIAccessibleTraversalRule.FILTER_IGNORE; const FILTER_IGNORE_SUBTREE = nsIAccessibleTraversalRule.FILTER_IGNORE_SUBTREE; @@ -46,7 +47,7 @@ var ObjectTraversalRule = return 0; }, - preFilter: PREFILTER_INVISIBLE, + preFilter: PREFILTER_INVISIBLE | PREFILTER_ARIA_HIDDEN, match: function(aAccessible) { diff --git a/accessible/tests/mochitest/pivot/doc_virtualcursor.html b/accessible/tests/mochitest/pivot/doc_virtualcursor.html index 113efe2a63e..658dc001d64 100644 --- a/accessible/tests/mochitest/pivot/doc_virtualcursor.html +++ b/accessible/tests/mochitest/pivot/doc_virtualcursor.html @@ -6,19 +6,23 @@

Main Title

-

First Section Title

+

Lorem ipsum dolor sit amet. Integer vitae urna leo, id semper nulla.

-

Second Section Title

-

+

Second Section Title

+

Sed accumsan luctus lacus, vitae mollis arcu tristique vulputate.

+
Hide me
-
-

- Based on the MochiKit unit tests. -

-
-

Status

-

Passed: 0

-

Failed: 0

-

Todo: 0

-
-
-
- Currently Executing: _ -
-
-
- -
-
- -
- - - - - - - - - - - - - - - - -
PassedFailedTodoTest Files
000
-
- - - - -
-
-
- - diff --git a/testing/mochitest/runtests.py b/testing/mochitest/runtests.py index 6be00a87f22..7c979b60485 100644 --- a/testing/mochitest/runtests.py +++ b/testing/mochitest/runtests.py @@ -499,7 +499,6 @@ class Mochitest(object): # Path to the test script on the server TEST_PATH = "tests" CHROME_PATH = "redirect.html" - PLAIN_LOOP_PATH = "plain-loop.html" urlOpts = [] runSSLTunnel = True vmwareHelper = None @@ -529,7 +528,7 @@ class Mochitest(object): testHost = "http://mochi.test:8888" testURL = ("/").join([testHost, self.TEST_PATH, options.testPath]) if os.path.isfile(os.path.join(self.oldcwd, os.path.dirname(__file__), self.TEST_PATH, options.testPath)) and options.repeat > 0: - testURL = ("/").join([testHost, self.PLAIN_LOOP_PATH]) + testURL = ("/").join([testHost, self.TEST_PATH, os.path.dirname(options.testPath)]) if options.chrome or options.a11y: testURL = ("/").join([testHost, self.CHROME_PATH]) elif options.browserChrome: diff --git a/testing/mochitest/server.js b/testing/mochitest/server.js index ef6c80a70ef..c935a7b8c2b 100644 --- a/testing/mochitest/server.js +++ b/testing/mochitest/server.js @@ -583,8 +583,14 @@ function testListing(metadata, response) true); var table_class = metadata.queryString.indexOf("hideResultsTable=1") > -1 ? "invisible": ""; + let testname = (metadata.queryString.indexOf("testname=") > -1) + ? metadata.queryString.match(/testname=([^&]+)/)[1] + : ""; + dumpn("count: " + count); - var tests = jsonArrayOfTestFiles(links); + var tests = testname + ? "['/" + testname + "']" + : jsonArrayOfTestFiles(links); response.write( HTML( HEAD( diff --git a/testing/mochitest/tests/SimpleTest/TestRunner.js b/testing/mochitest/tests/SimpleTest/TestRunner.js index 4aa46907362..5cd25a32434 100644 --- a/testing/mochitest/tests/SimpleTest/TestRunner.js +++ b/testing/mochitest/tests/SimpleTest/TestRunner.js @@ -73,6 +73,7 @@ var TestRunner = {}; TestRunner.logEnabled = false; TestRunner._currentTest = 0; TestRunner._lastTestFinished = -1; +TestRunner._loopIsRestarting = false; TestRunner.currentTestURL = ""; TestRunner.originalTestURL = ""; TestRunner._urls = []; @@ -148,7 +149,7 @@ TestRunner.requestLongerTimeout = function(factor) { * This is used to loop tests **/ TestRunner.repeat = 0; -TestRunner._currentLoop = 0; +TestRunner._currentLoop = 1; TestRunner.expectAssertions = function(min, max) { if (typeof(max) == "undefined") { @@ -301,39 +302,6 @@ TestRunner.resetTests = function(listURLs) { TestRunner.runNextTest(); } -/* - * Used to run a single test in a loop and update the UI with the results - */ -TestRunner.loopTest = function(testPath) { - //must set the following line so that TestHarness.updateUI finds the right div to update - document.getElementById("current-test-path").innerHTML = testPath; - var numLoops = TestRunner.repeat; - var completed = 0; // keep track of how many tests have finished - - // function to kick off the test and to check when the test is complete - function checkComplete() { - var testWindow = window.open(testPath, 'test window'); // kick off the test or find the active window - if (testWindow.document.readyState == "complete") { - // the test is complete -> mark as complete - TestRunner.currentTestURL = testPath; - TestRunner.updateUI(testWindow.SimpleTest._tests); - testWindow.close(); - if (TestRunner.repeat == completed && TestRunner.onComplete) { - TestRunner.onComplete(); - } - completed++; - } - else { - // wait and check later - setTimeout(checkComplete, 1000); - } - } - while (numLoops >= 0) { - checkComplete(); - numLoops--; - } -} - /** * Run the next test. If no test remains, calls onComplete(). **/ @@ -389,9 +357,10 @@ TestRunner.runNextTest = function() { TestRunner.onComplete(); } - if (TestRunner._currentLoop < TestRunner.repeat) { + if (TestRunner._currentLoop <= TestRunner.repeat) { TestRunner._currentLoop++; TestRunner.resetTests(TestRunner._urls); + TestRunner._loopIsRestarting = true; } else { // Loops are finished if (TestRunner.logEnabled) { @@ -416,7 +385,8 @@ TestRunner.expectChildProcessCrash = function() { TestRunner.testFinished = function(tests) { // Prevent a test from calling finish() multiple times before we // have a chance to unload it. - if (TestRunner._currentTest == TestRunner._lastTestFinished) { + if (TestRunner._currentTest == TestRunner._lastTestFinished && + !TestRunner._loopIsRestarting) { TestRunner.error("TEST-UNEXPECTED-FAIL | " + TestRunner.currentTestURL + " | called finish() multiple times"); @@ -424,6 +394,7 @@ TestRunner.testFinished = function(tests) { return; } TestRunner._lastTestFinished = TestRunner._currentTest; + TestRunner._loopIsRestarting = false; function cleanUpCrashDumpFiles() { if (!SpecialPowers.removeExpectedCrashDumpFiles(TestRunner._expectingProcessCrash)) { From 83f43a4cc074d547649d802c861190feeddb279f Mon Sep 17 00:00:00 2001 From: Felipe Gomes Date: Fri, 24 May 2013 16:03:50 -0300 Subject: [PATCH 57/91] Bug 875463 - Implement --run-until-failure option for mochitests. r=jmaher --- testing/mochitest/browser-test.js | 21 ++++++++++----- testing/mochitest/mach_commands.py | 26 ++++++++++++------- testing/mochitest/runtests.py | 15 +++++++++++ .../mochitest/tests/SimpleTest/TestRunner.js | 5 +++- testing/mochitest/tests/SimpleTest/setup.js | 6 ++++- 5 files changed, 56 insertions(+), 17 deletions(-) diff --git a/testing/mochitest/browser-test.js b/testing/mochitest/browser-test.js index a4350e4e386..59600a159fe 100644 --- a/testing/mochitest/browser-test.js +++ b/testing/mochitest/browser-test.js @@ -79,6 +79,7 @@ Tester.prototype = { SimpleTest: {}, repeat: 0, + runUntilFailure: false, checker: null, currentTestIndex: -1, lastStartTime: null, @@ -104,7 +105,13 @@ Tester.prototype = { //if testOnLoad was not called, then gConfig is not defined if (!gConfig) gConfig = readConfig(); - this.repeat = gConfig.repeat; + + if (gConfig.runUntilFailure) + this.runUntilFailure = true; + + if (gConfig.repeat) + this.repeat = gConfig.repeat; + this.dumper.dump("*** Start BrowserChrome Test Results ***\n"); Services.console.registerListener(this); Services.obs.addObserver(this, "chrome-document-global-created", false); @@ -169,6 +176,13 @@ Tester.prototype = { }, finish: function Tester_finish(aSkipSummary) { + var passCount = this.tests.reduce(function(a, f) a + f.passCount, 0); + var failCount = this.tests.reduce(function(a, f) a + f.failCount, 0); + var todoCount = this.tests.reduce(function(a, f) a + f.todoCount, 0); + + if (failCount > 0 && this.runUntilFailure) + this.repeat = 0; + if (this.repeat > 0) { --this.repeat; this.currentTestIndex = -1; @@ -183,11 +197,6 @@ Tester.prototype = { if (this.tests.length) { this.dumper.dump("Browser Chrome Test Summary\n"); - function sum(a,b) a+b; - var passCount = this.tests.map(function (f) f.passCount).reduce(sum); - var failCount = this.tests.map(function (f) f.failCount).reduce(sum); - var todoCount = this.tests.map(function (f) f.todoCount).reduce(sum); - this.dumper.dump("\tPassed: " + passCount + "\n" + "\tFailed: " + failCount + "\n" + "\tTodo: " + todoCount + "\n"); diff --git a/testing/mochitest/mach_commands.py b/testing/mochitest/mach_commands.py index edc89bbc490..4328c78bf85 100644 --- a/testing/mochitest/mach_commands.py +++ b/testing/mochitest/mach_commands.py @@ -38,7 +38,7 @@ class MochitestRunner(MozbuildObject): """ def run_mochitest_test(self, suite=None, test_file=None, debugger=None, shuffle=False, keep_open=False, rerun_failures=False, no_autorun=False, - repeat=0, slow=False): + repeat=0, run_until_failure=False, slow=False): """Runs a mochitest. test_file is a path to a test file. It can be a relative path from the @@ -125,16 +125,12 @@ class MochitestRunner(MozbuildObject): else: raise Exception('None or unrecognized mochitest suite type.') - options = opts.verifyOptions(options, runner) - - if options is None: - raise Exception('mochitest option validator failed.') - options.autorun = not no_autorun options.closeWhenDone = not keep_open options.shuffle = shuffle options.consoleLevel = 'INFO' options.repeat = repeat + options.runUntilFailure = run_until_failure options.runSlower = slow options.testingModulesDir = os.path.join(tests_dir, 'modules') options.extraProfileFiles.append(os.path.join(self.distdir, 'plugins')) @@ -142,9 +138,6 @@ class MochitestRunner(MozbuildObject): options.failureFile = failure_file_path - automation.setServerInfo(options.webServer, options.httpPort, - options.sslPort, options.webSocketPort) - if test_path: test_root = runner.getTestRoot(options) test_root_file = mozpack.path.join(mochitest_dir, test_root, test_path) @@ -162,6 +155,15 @@ class MochitestRunner(MozbuildObject): if debugger: options.debugger = debugger + options = opts.verifyOptions(options, runner) + + if options is None: + raise Exception('mochitest option validator failed.') + + automation.setServerInfo(options.webServer, options.httpPort, + options.sslPort, options.webSocketPort) + + # We need this to enable colorization of output. self.log_manager.enable_unstructured() @@ -225,6 +227,12 @@ def MochitestCommand(func): help='Repeat the test the given number of times.') func = repeat(func) + runUntilFailure = CommandArgument("--run-until-failure", action='store_true', + help='Run a test repeatedly and stops on the first time the test fails. ' \ + 'Only available when running a single test. Default cap is 30 runs, ' \ + 'which can be overwritten with the --repeat parameter.') + func = runUntilFailure(func) + slow = CommandArgument('--slow', action='store_true', help='Delay execution between tests.') func = slow(func) diff --git a/testing/mochitest/runtests.py b/testing/mochitest/runtests.py index 7c979b60485..23fcccd535e 100644 --- a/testing/mochitest/runtests.py +++ b/testing/mochitest/runtests.py @@ -226,6 +226,13 @@ class MochitestOptions(optparse.OptionParser): help = "repeats the test or set of tests the given number of times, ie: repeat=1 will run the test twice.") defaults["repeat"] = 0 + self.add_option("--run-until-failure", + action = "store_true", dest="runUntilFailure", + help = "Run a test repeatedly and stops on the first time the test fails. " + "Only available when running a single test. Default cap is 30 runs, " + "which can be overwritten with the --repeat parameter.") + defaults["runUntilFailure"] = False + self.add_option("--run-only-tests", action = "store", type="string", dest = "runOnlyTests", help = "JSON list of tests that we only want to run, cannot be specified with --exclude-tests. [DEPRECATED- please use --test-manifest]") @@ -377,6 +384,12 @@ See for details on the logg self.error("%s not found, cannot launch immersive tests." % mochitest.immersiveHelperPath) + if options.runUntilFailure: + if not os.path.isfile(os.path.join(mochitest.oldcwd, os.path.dirname(__file__), mochitest.getTestRoot(options), options.testPath)): + self.error("--run-until-failure can only be used together with --test-path specifying a single test.") + if not options.repeat: + options.repeat = 29 + return options @@ -658,6 +671,8 @@ class Mochitest(object): self.urlOpts.append("shuffle=1") if "MOZ_HIDE_RESULTS_TABLE" in env and env["MOZ_HIDE_RESULTS_TABLE"] == "1": self.urlOpts.append("hideResultsTable=1") + if options.runUntilFailure: + self.urlOpts.append("runUntilFailure=1") if options.repeat: self.urlOpts.append("repeat=%d" % options.repeat) if os.path.isfile(os.path.join(self.oldcwd, os.path.dirname(__file__), self.TEST_PATH, options.testPath)) and options.repeat > 0: diff --git a/testing/mochitest/tests/SimpleTest/TestRunner.js b/testing/mochitest/tests/SimpleTest/TestRunner.js index 5cd25a32434..3934e0f4ca6 100644 --- a/testing/mochitest/tests/SimpleTest/TestRunner.js +++ b/testing/mochitest/tests/SimpleTest/TestRunner.js @@ -357,7 +357,10 @@ TestRunner.runNextTest = function() { TestRunner.onComplete(); } - if (TestRunner._currentLoop <= TestRunner.repeat) { + var failCount = parseInt($("fail-count").innerHTML); + var stopLooping = failCount > 0 && TestRunner.runUntilFailure; + + if (TestRunner._currentLoop <= TestRunner.repeat && !stopLooping) { TestRunner._currentLoop++; TestRunner.resetTests(TestRunner._urls); TestRunner._loopIsRestarting = true; diff --git a/testing/mochitest/tests/SimpleTest/setup.js b/testing/mochitest/tests/SimpleTest/setup.js index d364d6c6d79..5e31390debf 100644 --- a/testing/mochitest/tests/SimpleTest/setup.js +++ b/testing/mochitest/tests/SimpleTest/setup.js @@ -76,11 +76,15 @@ if (params.timeout) { var fileLevel = params.fileLevel || null; var consoleLevel = params.consoleLevel || null; -// loop tells us how many times to run the tests +// repeat tells us how many times to repeat the tests if (params.repeat) { TestRunner.repeat = params.repeat; } +if (params.runUntilFailure) { + TestRunner.runUntilFailure = true; +} + // closeWhenDone tells us to close the browser when complete if (params.closeWhenDone) { TestRunner.onComplete = SpecialPowers.quit; From 25b0df75e66f8f72fa9af6bc96f09919d62d7581 Mon Sep 17 00:00:00 2001 From: "Dan Minor (dminor@mozilla.com)" Date: Fri, 24 May 2013 10:39:24 -0400 Subject: [PATCH 58/91] Bug 875737 - Add androidx86.json to mochitests. r=jmaher DONTBUILD --- testing/mochitest/Makefile.in | 1 + testing/mochitest/androidx86.json | 412 ++++++++++++++++++++++++++++++ 2 files changed, 413 insertions(+) create mode 100644 testing/mochitest/androidx86.json diff --git a/testing/mochitest/Makefile.in b/testing/mochitest/Makefile.in index e291d11850c..06bd7baf6c5 100644 --- a/testing/mochitest/Makefile.in +++ b/testing/mochitest/Makefile.in @@ -60,6 +60,7 @@ _SERV_FILES = \ $(topsrcdir)/netwerk/test/httpserver/httpd.js \ pywebsocket_wrapper.py \ android.json \ + androidx86.json \ b2g.json \ root-ev-tester.crl \ intermediate-ev-tester.crl \ diff --git a/testing/mochitest/androidx86.json b/testing/mochitest/androidx86.json new file mode 100644 index 00000000000..829ecaeb4da --- /dev/null +++ b/testing/mochitest/androidx86.json @@ -0,0 +1,412 @@ +{ +"runtests": {}, +"excludetests": { + "content/base/test/test_bug590812.html": "bug 687032", + "content/base/test/test_CSP.html": "TIMED_OUT", + "content/base/test/test_CSP_frameancestors.html": "", + "content/base/test/test_CSP_inlinescript.html": "", + "content/base/test/test_CrossSiteXHR.html": "", + "content/base/test/test_CrossSiteXHR_cache.html": "", + "content/base/test/test_CrossSiteXHR_origin.html": "", + "content/base/test/test_bug166235.html": "", + "content/base/test/test_bug338583.html": "", + "content/base/test/test_bug466080.html": "", + "content/base/test/test_bug503481.html": "TIMED_OUT", + "content/base/test/test_bug503481b.html": "TIMED_OUT", + "content/base/test/test_bug505783.html": "TIMED_OUT", + "content/base/test/test_copypaste.html": "", + "content/base/test/test_csp_redirects.html": "TIMED_OUT", + "content/base/test/test_fileapi_slice.html": "bug 775227", + "content/base/test/test_mozfiledataurl.html": "TIMED_OUT", + "content/base/test/test_mixed_content_blocker.html": "TIMED_OUT, SSL_REQUIRED", + "content/base/test/test_mixed_content_blocker_bug803225.html": "TIMED_OUT, SSL_REQUIRED", + "content/base/test/test_mixed_content_blocker_frameNavigation.html": "TIMED_OUT, SSL_REQUIRED", + "content/base/test/test_mutationobservers.html": "", + "content/base/test/test_plugin_freezing.html": "CLICK_TO_PLAY", + "content/base/test/test_object.html": "", + "content/base/test/test_range_bounds.html": "", + "content/base/test/test_reentrant_flush.html": "RANDOM", + "content/base/test/test_sync_xhr_timer.xhtml": "RANDOM", + "content/base/test/test_websocket.html": "", + "content/base/test/test_websocket_basic.html": "", + "content/base/test/test_websocket_hello.html": "", + "content/base/test/test_x-frame-options.html": "", + "content/base/test/test_xhr_abort_after_load.html": "", + "content/base/test/test_xhr_forbidden_headers.html": "", + "content/base/test/test_xhr_progressevents.html": "", + "content/base/test/websocket_hybi/test_receive-arraybuffer.html": "", + "content/base/test/websocket_hybi/test_receive-blob.html": "", + "content/base/test/websocket_hybi/test_send-arraybuffer.html": "", + "content/base/test/websocket_hybi/test_send-blob.html": "", + "content/canvas/test/webgl": "bug 865443- seperate suite", + "content/events/test/test_bug409604.html": "TIMED_OUT", + "content/events/test/test_bug426082.html": "", + "content/events/test/test_bug457672.html": "CRASH_DUMP, RANDOM", + "content/events/test/test_bug502818.html": "CRASH_DUMP, RANDOM", + "content/events/test/test_bug508479.html": "CRASH_DUMP, RANDOM", + "content/events/test/test_bug508906.html": "CRASH_DUMP, RANDOM", + "content/events/test/test_bug517851.htm": "CRASH_DUMP, RANDOM", + "content/events/test/test_bug534833.html": "CRASH_DUMP, RANDOM", + "content/events/test/test_bug545268.html": "CRASH_DUMP, RANDOM", + "content/events/test/test_bug547996-1.html": "CRASH_DUMP, RANDOM", + "content/events/test/test_bug547996-2.xhtml": "CRASH_DUMP, RANDOM", + "content/events/test/test_bug556493.html": "CRASH_DUMP, RANDOM", + "content/events/test/test_bug574663.html": "CRASH_DUMP, RANDOM", + "content/events/test/test_bug603008.html": "", + "content/events/test/test_bug605242.html": "CRASH_DUMP, RANDOM", + "content/events/test/test_bug607464.html": "CRASH_DUMP, RANDOM", + "content/events/test/test_bug613634.html": "CRASH_DUMP, RANDOM", + "content/events/test/test_bug624127.html": "CRASH_DUMP, RANDOM", + "content/events/test/test_bug635465.html": "CRASH_DUMP, RANDOM", + "content/events/test/test_bug648573.html": "CRASH_DUMP, RANDOM", + "content/events/test/test_bug650493.html": "CRASH_DUMP, RANDOM", + "content/events/test/test_bug656379-1.html": "TIMED_OUT", + "content/events/test/test_bug656379-2.html": "CRASH_DUMP, RANDOM", + "content/events/test/test_bug656954.html": "CRASH_DUMP, RANDOM", + "content/events/test/test_bug659350.html": "CRASH_DUMP, RANDOM", + "content/events/test/test_bug662678.html": "CRASH_DUMP, RANDOM", + "content/events/test/test_bug667612.html": "CRASH_DUMP, RANDOM", + "content/events/test/test_bug667919-1.html": "CRASH_DUMP, RANDOM", + "content/events/test/test_bug667919-2.html": "CRASH_DUMP, RANDOM", + "content/events/test/test_bug689564.html": "CRASH_DUMP, RANDOM", + "content/events/test/test_bug698929.html": "CRASH_DUMP, RANDOM", + "content/events/test/test_bug741666.html": "", + "content/events/test/test_clickevent_on_input.html": "CRASH_DUMP, RANDOM", + "content/events/test/test_dom_keyboard_event.html": "CRASH_DUMP, RANDOM", + "content/events/test/test_dom_mouse_event.html": "CRASH_DUMP, RANDOM", + "content/events/test/test_draggableprop.html": "CRASH_DUMP, RANDOM", + "content/events/test/test_dragstart.html": "TIMED_OUT", + "content/events/test/test_eventctors.html": "CRASH_DUMP, RANDOM", + "content/events/test": "CRASH_DUMP, RANDOM", + "content/html/content/test/test_bug209275.xhtml": "TIMED_OUT", + "content/html/content/test/test_bug481335.xhtml": "TIMED_OUT", + "content/html/content/test/test_bug514856.html": "", + "content/html/content/test/test_bug557087-2.html": "TIMED_OUT", + "content/html/content/test/test_bug612730.html": "", + "content/html/content/test/test_bug615833.html": "TIMED_OUT", + "content/html/content/test/test_formSubmission.html": "TIMED_OUT", + "content/html/content/test/test_formSubmission2.html": "", + "content/html/content/test/test_fullscreen-api.html": "TIMED_OUT", + "content/html/content/test/test_iframe_sandbox_plugins.html": "", + "content/html/content/test/test_object_plugin_nav.html": "TIMED_OUT", + "content/html/document/test/test_bug199692.html": "bug 811644", + "content/html/document/test/test_bug369370.html": "", + "content/html/document/test/test_bug391777.html": "", + "content/html/document/test/test_bug445004.html": "", + "content/html/document/test/test_bug446483.html": "", + "content/html/document/test/test_bug741266.html": "", + "content/media/test/test_autoplay_contentEditable.html": "", + "content/media/test/test_buffered.html": "", + "content/media/test/test_bug448534.html": "", + "content/media/test/test_bug463162.xhtml": "", + "content/media/test/test_bug686137.html": "TIMED_OUT", + "content/media/test/test_contentDuration1.html": "TIMED_OUT", + "content/media/test/test_contentDuration2.html": "TIMED_OUT", + "content/media/test/test_contentDuration3.html": "TIMED_OUT", + "content/media/test/test_contentDuration4.html": "TIMED_OUT", + "content/media/test/test_contentDuration5.html": "TIMED_OUT", + "content/media/test/test_contentDuration6.html": "TIMED_OUT", + "content/media/test/test_contentDuration7.html": "", + "content/media/test/test_decoder_disable.html": "", + "content/media/test/test_fragment_noplay.html": "", + "content/media/test/test_fragment_play.html": "", + "content/media/test/test_framebuffer.html": "", + "content/media/test/test_media_selection.html": "", + "content/media/test/test_playback.html": "", + "content/media/test/test_seekLies.html": "TIMED_OUT", + "content/media/test/test_seekable2.html": "", + "content/media/test/test_too_many_elements.html": "bug 775227", + "content/media/test/test_wave_data_s16.html": "TIMED_OUT", + "content/media/test/test_wave_data_u8.html": "TIMED_OUT", + "content/media/webaudio/test/test_currentTime.html": "bug 868116", + "content/media/webaudio/test/test_delayNode.html": "bug 865642", + "content/media/webaudio/test/test_delayNodeWithGain.html": "bug 865642", + "content/media/webaudio/test/test_gainNode.html": "bug 865642", + "content/media/webaudio/test/test_scriptProcessorNode.html": "bug 865642", + "content/media/webaudio/test/test_scriptProcessorNodeChannelCount.html": "bug 865642", + "content/media/webspeech/synth/ipc/test/test_ipc.html": "bug 857673", + "content/media/webspeech/recognition/test/test_nested_eventloop.html": "", + "content/smil/test/test_smilRepeatTiming.xhtml": "TIMED_OUT", + "content/smil/test/test_smilExtDoc.xhtml": "", + "content/xul/content/test/test_bug486990.xul": "TIMED_OUT", + "docshell/test/navigation/test_bug13871.html": "RANDOM", + "docshell/test/navigation/test_bug430723.html": "TIMED_OUT", + "docshell/test/navigation/test_popup-navigates-children.html": "bug 783589", + "docshell/test/navigation/test_sessionhistory.html": "RANDOM", + "docshell/test/navigation/test_bug344861.html": "", + "docshell/test/test_bug94514.html": "TIMED_OUT", + "docshell/test/test_bug413310.html": "", + "docshell/test/test_bug590573.html": "bug 823022", + "docshell/test/test_bug598895.html": "", + "docshell/test/test_bug637644.html": "", + "docshell/test/test_bug668513.html": "RANDOM", + "docshell/test/test_framedhistoryframes.html": "bug 784321", + "dom/browser-element/mochitest/test_browserElement_oop_SecurityChange.html": "TIMED_OUT, bug 766586", + "dom/browser-element/mochitest/test_browserElement_inproc_AppFramePermission.html": "", + "dom/browser-element/mochitest/test_browserElement_inproc_AppWindowNamespace.html": "TIMED_OUT, bug 783509", + "dom/browser-element/mochitest/test_browserElement_inproc_SecurityChange.html": "TIMED_OUT, bug 766586", + "dom/browser-element/mochitest/test_browserElement_inproc_CloseApp.html": "FAILS, bug 796982", + "dom/devicestorage": "bug 781789 & bug 782275", + "dom/imptests/editing/conformancetest/test_event.html": "", + "dom/imptests/editing/conformancetest/test_runtest.html": "", + "dom/imptests/editing/selecttest/test_addRange.html": "bug 775227", + "dom/imptests/html/webgl": "WebGL", + "dom/imptests/webapps/DOMCore/tests/approved/test_Range-cloneContents.html": "bug 775227", + "dom/imptests/webapps/DOMCore/tests/approved/test_Range-compareBoundaryPoints.html": "bug 775227", + "dom/imptests/webapps/DOMCore/tests/approved/test_Range-deleteContents.html": "bug 775227", + "dom/imptests/webapps/DOMCore/tests/approved/test_Range-extractContents.html": "bug 775227", + "dom/imptests/webapps/DOMCore/tests/approved/test_Range-insertNode.html": "bug 775227", + "dom/imptests/webapps/DOMCore/tests/approved/test_Range-mutations.html": "bug 775227", + "dom/imptests/webapps/DOMCore/tests/approved/test_Range-set.html": "bug 775227", + "dom/imptests/webapps/DOMCore/tests/approved/test_Range-surroundContents.html": "bug 775227", + "dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_storage_local_key.html": "bug 775227", + "dom/indexedDB/ipc/test_ipc.html": "bug 783513", + "dom/indexedDB/test/test_third_party.html": "TIMED_OUT", + "dom/indexedDB/test/test_event_propagation.html": "TIMED_OUT, bug 780855", + "dom/indexedDB/test/test_app_isolation_inproc.html": "TIMED_OUT", + "dom/indexedDB/test/test_app_isolation_oop.html": "TIMED_OUT", + "dom/indexedDB/test/test_webapp_clearBrowserData_inproc_inproc.html": "No test app installed", + "dom/indexedDB/test/test_webapp_clearBrowserData_inproc_oop.html": "No test app installed", + "dom/indexedDB/test/test_webapp_clearBrowserData_oop_inproc.html": "No test app installed", + "dom/network/tests/test_network_basics.html": "", + "dom/permission/tests/test_permission_basics.html": "", + "dom/mobilemessage/tests/test_sms_basics.html": "", + "dom/tests/mochitest/ajax/jquery/test_jQuery.html": "bug 775227", + "dom/tests/mochitest/ajax/offline/test_simpleManifest.html": "TIMED_OUT", + "dom/tests/mochitest/ajax/offline/test_updatingManifest.html": "TIMED_OUT", + "dom/tests/mochitest/ajax/offline/test_xhtmlManifest.xhtml": "TIMED_OUT", + "dom/tests/mochitest/ajax/prototype/test_Prototype.html": "", + "dom/tests/mochitest/ajax/scriptaculous/test_Scriptaculous.html": "", + "dom/tests/mochitest/browser-frame/test_browserFrame1.html": "TIMED_OUT", + "dom/tests/mochitest/bugs/test_bug260264.html": "", + "dom/tests/mochitest/bugs/test_bug291653.html": "TIMED_OUT", + "dom/tests/mochitest/bugs/test_bug369306.html": "TIMED_OUT", + "dom/tests/mochitest/bugs/test_bug406375.html": "", + "dom/tests/mochitest/bugs/test_bug414291.html": "", + "dom/tests/mochitest/bugs/test_bug427744.html": "", + "dom/tests/mochitest/bugs/test_bug437361.html": "", + "dom/tests/mochitest/bugs/test_bug479143.html": "", + "dom/tests/mochitest/bugs/test_bug504862.html": "RANDOM", + "dom/tests/mochitest/bugs/test_bug597809.html": "", + "dom/tests/mochitest/bugs/test_bug61098.html": "", + "dom/tests/mochitest/bugs/test_bug641552.html": "", + "dom/tests/mochitest/bugs/test_devicemotion_multiple_listeners.html": "bug 775227", + "dom/tests/mochitest/bugs/test_resize_move_windows.html": "Windows can't change size and position on Android", + "dom/tests/mochitest/bugs/test_sizetocontent_clamp.html": "Windows can't change size on Android", + "dom/tests/mochitest/bugs/test_window_bar.html": "", + "dom/tests/mochitest/general/test_497898.html": "", + "dom/tests/mochitest/general/test_focusrings.xul": "TIMED_OUT", + "dom/tests/mochitest/general/test_vibrator.html": "CRASH_SUTAGENT", + "dom/tests/mochitest/geolocation/test_allowCurrent.html": "TIMED_OUT", + "dom/tests/mochitest/geolocation/test_allowWatch.html": "TIMED_OUT", + "dom/tests/mochitest/geolocation/test_cachedPosition.html": "TIMED_OUT", + "dom/tests/mochitest/geolocation/test_clearWatch.html": "TIMED_OUT", + "dom/tests/mochitest/geolocation/test_manyCurrentConcurrent.html": "TIMED_OUT", + "dom/tests/mochitest/geolocation/test_manyCurrentSerial.html": "TIMED_OUT", + "dom/tests/mochitest/geolocation/test_manyWatchConcurrent.html": "TIMED_OUT", + "dom/tests/mochitest/geolocation/test_manyWatchSerial.html": "TIMED_OUT", + "dom/tests/mochitest/geolocation/test_mozsettings.html": "mozSettings is undefined", + "dom/tests/mochitest/geolocation/test_mozsettingsWatch.html": "mozSettings is undefined", + "dom/tests/mochitest/geolocation/test_shutdown.html": "TIMED_OUT", + "dom/tests/mochitest/geolocation/test_windowClose.html": "TIMED_OUT", + "dom/tests/mochitest/geolocation/test_worseAccuracyDoesNotBlockCallback.html": "TIMED_OUT", + "dom/tests/mochitest/localstorage/test_bug624047.html": "TIMED_OUT", + "dom/tests/mochitest/localstorage/test_localStorageOriginsSchemaDiffs.html": "TIMED_OUT", + "dom/tests/mochitest/localstorage/test_localStorageOriginsEquals.html": "", + "dom/tests/mochitest/localstorage/test_localStorageQuota.html": "TIMED_OUT", + "dom/tests/mochitest/localstorage/test_localStorageQuotaSessionOnly.html": "TIMED_OUT", + "dom/tests/mochitest/localstorage/test_localStorageQuotaSessionOnly2.html": "TIMED_OUT", + "dom/tests/mochitest/localstorage/test_localStorageReplace.html": "", + "dom/tests/mochitest/localstorage/test_removeOwnersAPI.html": "TIMED_OUT", + "dom/tests/mochitest/localstorage/test_removeOwnersAPISessionOnly.html": "TIMED_OUT", + "dom/tests/mochitest/localstorage/test_appIsolation.html": "bug 793211", + "dom/tests/mochitest/pointerlock/test_pointerlock-api.html": "TIMED_OUT", + "dom/tests/mochitest/sessionstorage/test_sessionStorageClone.html": "", + "dom/tests/mochitest/sessionstorage/test_sessionStorageHttpHttps.html": "TIMED_OUT", + "dom/tests/mochitest/webapps/test_bug_779982.html": "Bug 793211", + "dom/tests/mochitest/whatwg/test_bug500328.html": "TIMED_OUT", + "dom/workers/test/test_xhr_timeout.html": "bug 798220", + "editor/composer/test/test_bug389350.html": "", + "editor/libeditor/base/tests/test_bug408231.html": "", + "editor/libeditor/base/tests/test_bug586662.html": "", + "editor/libeditor/html/tests/test_bug372345.html": "", + "editor/libeditor/html/tests/test_bug410986.html": "", + "editor/libeditor/html/tests/test_bug432225.html": "", + "editor/libeditor/html/tests/test_bug478725.html": "", + "editor/libeditor/html/tests/test_bug480972.html": "", + "editor/libeditor/html/tests/test_bug484181.html": "", + "editor/libeditor/html/tests/test_bug520189.html": "", + "editor/libeditor/html/tests/test_bug525389.html": "", + "editor/libeditor/html/tests/test_bug549262.html": "", + "editor/libeditor/html/tests/test_bug599322.html": "", + "editor/libeditor/html/tests/test_bug611182.html": "", + "editor/libeditor/html/tests/test_bug620906.html": "TIMED_OUT", + "editor/libeditor/html/tests/test_bug674770-1.html": "", + "editor/libeditor/html/tests/test_bug674770-2.html": "", + "editor/libeditor/html/tests/test_spellcheck_pref.html": "", + "editor/libeditor/text/tests/test_bug527935.html": "", + "editor/libeditor/text/tests/test_bug569988.html": "TIMED_OUT", + "editor/libeditor/text/tests/test_bug596333.html": "", + "editor/libeditor/text/tests/test_bug600570.html": "", + "editor/libeditor/text/tests/test_bug604532.html": "", + "editor/libeditor/text/tests/test_bug629172.html": "", + "editor/libeditor/text/tests/test_texteditor_keyevent_handling.html": "", + "embedding/test/test_bug449141.html": "", + "embedding/test/test_window_open_units.html": "", + "intl/uconv/tests/test_long_doc.html": "bug 775227", + "js/jsd/test/test_bug507448.html": "TIMED_OUT", + "js/jsd/test/test_bug617870-callhooks.html": "TIMED_OUT", + "layout/base/tests/test_bug332655-1.html": "", + "layout/base/tests/test_bug603550.html": "TIMED_OUT", + "layout/base/tests/test_bug629838.html": "", + "layout/base/tests/test_flush_on_paint.html": "", + "layout/base/tests/test_mozPaintCount.html": "", + "layout/base/tests/test_reftests_with_caret.html": "", + "layout/forms/test/test_bug348236.html": "", + "layout/forms/test/test_bug378670.html": "TIMED_OUT", + "layout/forms/test/test_bug446663.html": "", + "layout/forms/test/test_bug478219.xhtml": "", + "layout/forms/test/test_bug564115.html": "TIMED_OUT", + "layout/forms/test/test_bug571352.html": "TIMED_OUT", + "layout/forms/test/test_bug572649.html": "TIMED_OUT", + "layout/forms/test/test_bug644542.html": "TIMED_OUT", + "layout/forms/test/test_bug672810.html": "", + "layout/forms/test/test_textarea_resize.html": "", + "layout/forms/test/test_listcontrol_search.html": "select elements don't use an in-page popup on Android", + "layout/generic/test/test_bug496275.html": "CRASH_DUMP", + "layout/generic/test/test_bug503813.html": "CRASH_DUMP", + "layout/generic/test/test_bug514732.html": "CRASH_DUMP", + "layout/generic/test/test_invalidate_during_plugin_paint.html": "", + "layout/generic/test/test_plugin_mouse_coords.html": "", + "layout/generic": "CRASH_DUMP, RANDOM, ONLY IN CHUNK 10", + "layout/style/test/test_animations.html": "", + "layout/style/test/test_bug379440.html": "", + "layout/style/test/test_compute_data_with_start_struct.html": "", + "layout/style/test/test_css_cross_domain.html": "bug 536603", + "layout/style/test/test_inherit_computation.html": "", + "layout/style/test/test_initial_computation.html": "", + "layout/style/test/test_selectors.html": "bug 775227", + "layout/style/test/test_transitions_per_property.html": "bug 775227", + "layout/style/test/test_value_cloning.html": "bug 775227", + "layout/style/test/test_value_computation.html": "", + "layout/style/test/test_visited_image_loading.html": "TIMED_OUT", + "layout/style/test/test_visited_image_loading_empty.html": "TIMED_OUT", + "layout/style/test/test_visited_lying.html": "", + "layout/style/test/test_visited_pref.html": "TIMED_OUT", + "layout/style/test/test_visited_reftests.html": "TIMED_OUT", + "layout/xul/base/test/test_bug511075.html": "bug 798806", + "parser/htmlparser/tests/mochitest/test_html5_tree_construction.html": "TIMED_OUT", + "parser/htmlparser/tests/mochitest/test_html5_tree_construction_part2.html": "TIMED_OUT", + "robocop": "TIMED_OUT", + "security/manager/ssl/tests/mochitest/bugs/test_bug480509.html": "", + "security/manager/ssl/tests/mochitest/bugs/test_bug483440.html": "", + "security/manager/ssl/tests/mochitest/bugs/test_bug484111.html": "", + "security/manager/ssl/tests/mochitest/bugs/test_ev_validation.html": "TIMED_OUT", + "security/manager/ssl/tests/mochitest/mixedcontent": "TIMED_OUT", + "security/manager/ssl/tests/mochitest/stricttransportsecurity/test_stricttransportsecurity.html": "TIMED_OUT", + "security/manager/ssl/tests/mochitest/stricttransportsecurity/test_sts_privatebrowsing.html": "TIMED_OUT", + "toolkit/components/alerts/test/test_alerts.html": "", + "toolkit/components/passwordmgr/test/test_basic_form_autocomplete.html": "", + "toolkit/components/passwordmgr/test/test_master_password.html": "TIMED_OUT", + "toolkit/components/passwordmgr/test/test_maxforms_1.html": "TIMED_OUT", + "toolkit/components/passwordmgr/test/test_maxforms_2.html": "TIMED_OUT", + "toolkit/components/passwordmgr/test/test_maxforms_3.html": "TIMED_OUT", + "toolkit/components/passwordmgr/test/test_bug_627616.html": "TIMED_OUT", + "toolkit/components/passwordmgr/test/test_master_password_cleanup.html": "", + "toolkit/components/passwordmgr/test/test_notifications.html": "", + "toolkit/components/passwordmgr/test/test_notifications_popup.html": "", + "toolkit/components/passwordmgr/test/test_privbrowsing_perwindowpb.html": "", + "toolkit/components/passwordmgr/test/test_prompt.html": "TIMED_OUT", + "toolkit/components/passwordmgr/test/test_prompt_async.html": "TIMED_OUT", + "toolkit/components/passwordmgr/test/test_xhr.html": "TIMED_OUT", + "toolkit/components/passwordmgr/test/test_xml_load.html": "TIMED_OUT", + "toolkit/components/places/tests/test_bug_411966.html": "RANDOM", + "toolkit/components/prompts/test/test_bug620145.html": "TIMED_OUT", + "toolkit/components/prompts/test/test_modal_prompts.html": "TIMED_OUT", + "toolkit/components/prompts/test/test_modal_select.html": "TIMED_OUT", + "toolkit/components/satchel/test/test_bug_511615.html": "", + "toolkit/components/satchel/test/test_bug_787624.html": "", + "toolkit/components/satchel/test/test_form_autocomplete.html": "TIMED_OUT", + "toolkit/components/satchel/test/test_form_autocomplete_with_list.html": "", + "toolkit/components/satchel/test/test_form_submission.html": "", + "toolkit/components/satchel/test/test_form_submission_cap.html": "", + "toolkit/components/satchel/test/test_form_submission_cap2.html": "", + "toolkit/content/tests/widgets/test_audiocontrols_dimensions.html": "", + "toolkit/content/tests/widgets/test_contextmenu_nested.xul": "", + "toolkit/content/tests/widgets/test_menubar.xul": "W/SharedBufferStack(21799): waitForCondition(LockCondition) timed out (identity=34, status=0). CPU may be pegged. trying again", + "toolkit/content/tests/widgets/test_videocontrols.html": "TIMED_OUT", + "uriloader/exthandler/tests/mochitest/test_handlerApps.xhtml": "", + "widget/tests/test_bug760802.html": "", + "dom/tests/mochitest/ajax/offline": "SLOW_DIRECTORY", + "layout/base/tests": "SLOW_DIRECTORY", + "Harness_sanity/test_bug816847.html": "No test app installed", + "content/media/webaudio/test/test_audioBufferSourceNodeLoopStartEndSame.html": "", + "content/media/webspeech/recognition/test/test_audio_capture_error.html": "", + "docshell/test/navigation/test_bug13871.html": "", + "layout/forms/test/test_bug231389.html": "", + "docshell/test/navigation/test_bug278916.html": "", + "docshell/test/navigation/test_bug279495.html": "", + "layout/forms/test/test_bug287446.html": "", + "dom/tests/mochitest/bugs/test_bug291653.html": "", + "docshell/test/navigation/test_bug344861.html": "", + "layout/forms/test/test_bug345267.html": "", + "dom/tests/mochitest/bugs/test_bug369306.html": "", + "docshell/test/navigation/test_bug430624.html": "", + "docshell/test/navigation/test_bug430723.html": "", + "content/xul/content/test/test_bug486990.xul": "", + "layout/forms/test/test_bug564115.html": "", + "layout/forms/test/test_bug571352.html": "", + "layout/forms/test/test_bug572406.html": "", + "layout/forms/test/test_bug572649.html": "", + "layout/forms/test/test_bug595310.html": "", + "dom/tests/mochitest/bugs/test_bug61098.html": "", + "content/html/content/test/test_bug615833.html": "", + "layout/forms/test/test_bug620936.html": "", + "layout/forms/test/test_bug644542.html": "", + "content/html/content/test/test_fullscreen-api.html": "", + "dom/devicestorage/ipc/test_ipc.html": "", + "layout/base/tests/test_remote_frame.html": "", + "dom/tests/mochitest/bugs/test_resize_move_windows.html": "", + "layout/base/tests/test_scroll_event_ordering.html": "", + "layout/base/tests/test_scroll_selection_into_view.html": "", + "content/media/test/test_streams_element_capture.html": "", + "content/events/test/test_wheel_default_action.html": "TIMED OUT", + "content/html/content/test/forms/test_formaction_attribute.html": "TIMED OUT", + "js/xpconnect/tests/mochitest/test_bug865260.html": "CRASH", + "content/media/webaudio/test/test_bug867089.html": "CRASH", + "content/base/test/test_bug560780.html": "Unexpected result", + "content/base/test/test_bug588990.html": "Unexpected result", + "content/base/test/test_bug704063.html": "Unexpected result", + "content/canvas/test/test_canvas.html": "Unexpected result", + "content/html/content/test/forms/test_input_attributes_reflection.html": "Unexpected result", + "docshell/test/navigation/test_not-opener.html": "Unexpected result", + "dom/contacts/tests/test_contacts_basics.html": "Unexpected result", + "dom/contacts/tests/test_contacts_international.html": "Unexpected result", + "dom/imptests/webapps/DOMCore/tests/approved/test_interfaces.html": "Unexpected result", + "dom/imptests/webapps/XMLHttpRequest/tests/submissions/Ms2ger/test_interfaces.html": "Unexpected result", + "dom/imptests/webapps/XMLHttpRequest/tests/submissions/Ms2ger/test_interfaces.html": "Unexpected result", + "dom/tests/mochitest/bugs/test_bug857555.html": "Unexpected result", + "gfx/tests/test_acceleration.html": "Unexpected result", + "js/xpconnect/tests/mochitest/test_asmjs.html": "Unexpected result", + "js/xpconnect/tests/mochitest/test_bug829872.html": "Unexpected result", + "layout/style/test/test_media_queries.html": "Unexpected result", + "layout/style/test/test_transitions_events.html": "Unexpected result", + "Harness_sanity/test_sanityEventUtils.html": "Unexpected result", + "content/html/content/test/test_video_wakelock.html": "Test timed out", + "content/media/test/test_a4_tone.html": "Unexpected result", + "content/media/test/test_can_play_type_mpeg.html": "Unexpected result", + "content/media/test/test_playback_rate.html": "Test timed out", + "content/media/webaudio/test/test_mediaDecoding.html": "Unexpected result", + "content/canvas/test/crossorigin/test_video_crossorigin.html": "Unexpected result", + "/dom/media/tests/mochitest/test_peerConnection_bug825703.html": "Unexpected Result", + "/dom/media/tests/mochitest/test_peerConnection_bug825703.html": "Unexpected Result", + "/dom/media/tests/mochitest/test_peerConnection_bug825703.html": "Unexpected Result", + "/dom/media/tests/mochitest/test_peerConnection_bug825703.html": "Unexpected Result", + "/dom/media/tests/mochitest/test_peerConnection_errorCallbacks.html": "Unexpected Result", + "/dom/media/tests/mochitest/test_peerConnection_throwInCallbacks.html": "Unexpected Result", + "/dom/tests/mochitest/general/test_interfaces.html": "Unexpected Result", + "/dom/tests/mochitest/general/test_interfaces.html": "Unexpected Result", + "/dom/tests/mochitest/general/test_interfaces.html": "Unexpected Result", + "/dom/tests/mochitest/general/test_showModalDialog.html": "Unexpected Result" + } +} From 0ddc1881c185d8bb95fcec5114b2720fef9e3bb5 Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Fri, 24 May 2013 15:28:28 -0400 Subject: [PATCH 59/91] Backed out changeset dc96b4762676 (bug 875402) because of crashtest leaks --- content/media/MediaStreamGraph.cpp | 7 +++--- content/media/test/crashtests/875402.html | 24 ------------------- content/media/test/crashtests/crashtests.list | 1 - 3 files changed, 3 insertions(+), 29 deletions(-) delete mode 100644 content/media/test/crashtests/875402.html diff --git a/content/media/MediaStreamGraph.cpp b/content/media/MediaStreamGraph.cpp index 5efca39536c..b73d06ebbb3 100644 --- a/content/media/MediaStreamGraph.cpp +++ b/content/media/MediaStreamGraph.cpp @@ -1908,6 +1908,9 @@ MediaInputPort::Destroy() { Run(); } + // This does not need to be strongly referenced; the graph is holding + // a strong reference to the port, which we will remove. This will be the + // last message for the port. MediaInputPort* mPort; }; GraphImpl()->AppendMessage(new Message(this)); @@ -1949,10 +1952,6 @@ ProcessedMediaStream::AllocateInputPort(MediaStream* aStream, uint32_t aFlags, // The graph holds its reference implicitly mPort.forget(); } - virtual void RunDuringShutdown() - { - Run(); - } nsRefPtr mPort; }; nsRefPtr port = new MediaInputPort(aStream, this, aFlags, diff --git a/content/media/test/crashtests/875402.html b/content/media/test/crashtests/875402.html deleted file mode 100644 index d641bc2b58e..00000000000 --- a/content/media/test/crashtests/875402.html +++ /dev/null @@ -1,24 +0,0 @@ - \ No newline at end of file diff --git a/content/media/test/crashtests/crashtests.list b/content/media/test/crashtests/crashtests.list index 4a393b474a1..061d45921ac 100644 --- a/content/media/test/crashtests/crashtests.list +++ b/content/media/test/crashtests/crashtests.list @@ -20,5 +20,4 @@ load 874915.html load 874934.html load 874952.html load 875144.html -load 875402.html load 875596.html From 84bb4e09a924a45101500fb3c1a8a2cc812f3c1d Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Fri, 24 May 2013 15:44:26 -0400 Subject: [PATCH 60/91] Bug 868742 - Use scroll(Left|Top)Max to detect scrollable elements. r=mfinkle --- mobile/android/chrome/content/browser.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index f2fb9f70337..b40f690fd9c 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -4388,8 +4388,7 @@ var BrowserEventHandler = { * - It's a select element showing multiple rows */ if (checkElem) { - if (((elem.scrollHeight > elem.clientHeight) || - (elem.scrollWidth > elem.clientWidth)) && + if ((elem.scrollTopMax > 0 || elem.scrollLeftMax > 0) && (this._hasScrollableOverflow(elem) || elem.mozMatchesSelector("html, body, textarea")) || (elem instanceof HTMLSelectElement && (elem.size > 1 || elem.multiple))) { @@ -4420,10 +4419,10 @@ var BrowserEventHandler = { _elementCanScroll: function(elem, x, y) { let scrollX = (x < 0 && elem.scrollLeft > 0) - || (x > 0 && elem.scrollLeft < (elem.scrollWidth - elem.clientWidth)); + || (x > 0 && elem.scrollLeft < elem.scrollLeftMax); let scrollY = (y < 0 && elem.scrollTop > 0) - || (y > 0 && elem.scrollTop < (elem.scrollHeight - elem.clientHeight)); + || (y > 0 && elem.scrollTop < elem.scrollTopMax); return scrollX || scrollY; } From 67f4978f9d26ead221359e79fc07a7b65bd83778 Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Fri, 24 May 2013 16:02:48 -0400 Subject: [PATCH 61/91] Backed out changeset e3e70237a47a (bug 854765) because of mochitest-1 assertions --- layout/svg/SVGFEContainerFrame.cpp | 9 +++++++++ layout/svg/SVGFEImageFrame.cpp | 9 +++++++++ layout/svg/SVGFELeafFrame.cpp | 9 +++++++++ layout/svg/nsSVGForeignObjectFrame.cpp | 17 +++++++++++++++++ layout/svg/nsSVGForeignObjectFrame.h | 2 ++ layout/svg/nsSVGGradientFrame.cpp | 7 +++++++ layout/svg/nsSVGGradientFrame.h | 2 ++ layout/svg/nsSVGMaskFrame.cpp | 7 +++++++ layout/svg/nsSVGMaskFrame.h | 2 ++ layout/svg/nsSVGPathGeometryFrame.cpp | 15 +++++++++++++++ layout/svg/nsSVGPathGeometryFrame.h | 2 ++ layout/svg/nsSVGPatternFrame.cpp | 7 +++++++ layout/svg/nsSVGPatternFrame.h | 2 ++ layout/svg/nsSVGStopFrame.cpp | 9 +++++++++ 14 files changed, 99 insertions(+) diff --git a/layout/svg/SVGFEContainerFrame.cpp b/layout/svg/SVGFEContainerFrame.cpp index 3e866622e3d..512d1d25c96 100644 --- a/layout/svg/SVGFEContainerFrame.cpp +++ b/layout/svg/SVGFEContainerFrame.cpp @@ -44,6 +44,8 @@ public: } #endif + virtual void DidSetStyleContext(nsStyleContext* aOldStyleContext); + #ifdef DEBUG virtual void Init(nsIContent* aContent, nsIFrame* aParent, @@ -74,6 +76,13 @@ NS_NewSVGFEContainerFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) NS_IMPL_FRAMEARENA_HELPERS(SVGFEContainerFrame) +/* virtual */ void +SVGFEContainerFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext) +{ + SVGFEContainerFrameBase::DidSetStyleContext(aOldStyleContext); + nsSVGEffects::InvalidateRenderingObservers(this); +} + #ifdef DEBUG void SVGFEContainerFrame::Init(nsIContent* aContent, diff --git a/layout/svg/SVGFEImageFrame.cpp b/layout/svg/SVGFEImageFrame.cpp index dd769ff9d7c..8f89ee8c619 100644 --- a/layout/svg/SVGFEImageFrame.cpp +++ b/layout/svg/SVGFEImageFrame.cpp @@ -48,6 +48,8 @@ public: } #endif + virtual void DidSetStyleContext(nsStyleContext* aOldStyleContext); + /** * Get the "type" of the frame * @@ -73,6 +75,13 @@ NS_NewSVGFEImageFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) NS_IMPL_FRAMEARENA_HELPERS(SVGFEImageFrame) +/* virtual */ void +SVGFEImageFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext) +{ + SVGFEImageFrameBase::DidSetStyleContext(aOldStyleContext); + nsSVGEffects::InvalidateRenderingObservers(this); +} + /* virtual */ void SVGFEImageFrame::DestroyFrom(nsIFrame* aDestructRoot) { diff --git a/layout/svg/SVGFELeafFrame.cpp b/layout/svg/SVGFELeafFrame.cpp index 4f578f27dd6..f93fb946084 100644 --- a/layout/svg/SVGFELeafFrame.cpp +++ b/layout/svg/SVGFELeafFrame.cpp @@ -47,6 +47,8 @@ public: } #endif + virtual void DidSetStyleContext(nsStyleContext* aOldStyleContext); + /** * Get the "type" of the frame * @@ -72,6 +74,13 @@ NS_NewSVGFELeafFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) NS_IMPL_FRAMEARENA_HELPERS(SVGFELeafFrame) +/* virtual */ void +SVGFELeafFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext) +{ + SVGFELeafFrameBase::DidSetStyleContext(aOldStyleContext); + nsSVGEffects::InvalidateRenderingObservers(this); +} + #ifdef DEBUG void SVGFELeafFrame::Init(nsIContent* aContent, diff --git a/layout/svg/nsSVGForeignObjectFrame.cpp b/layout/svg/nsSVGForeignObjectFrame.cpp index c9ac187acb9..98688874657 100644 --- a/layout/svg/nsSVGForeignObjectFrame.cpp +++ b/layout/svg/nsSVGForeignObjectFrame.cpp @@ -118,6 +118,23 @@ nsSVGForeignObjectFrame::AttributeChanged(int32_t aNameSpaceID, return NS_OK; } +/* virtual */ void +nsSVGForeignObjectFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext) +{ + nsSVGForeignObjectFrameBase::DidSetStyleContext(aOldStyleContext); + + // No need to invalidate before first reflow - that will happen elsewhere. + // Moreover we haven't been initialised properly yet so we may not have the + // right state bits. + if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW)) { + // XXXperf: probably only need a bounds update if 'font-size' changed and + // we have em unit width/height. Or, once we map 'transform' into style, + // if some transform property changed. + nsSVGEffects::InvalidateRenderingObservers(this); + nsSVGUtils::ScheduleReflowSVG(this); + } +} + NS_IMETHODIMP nsSVGForeignObjectFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, diff --git a/layout/svg/nsSVGForeignObjectFrame.h b/layout/svg/nsSVGForeignObjectFrame.h index 04696cfd22b..c460e887248 100644 --- a/layout/svg/nsSVGForeignObjectFrame.h +++ b/layout/svg/nsSVGForeignObjectFrame.h @@ -39,6 +39,8 @@ public: nsIAtom* aAttribute, int32_t aModType); + virtual void DidSetStyleContext(nsStyleContext* aOldStyleContext) MOZ_OVERRIDE; + virtual nsIFrame* GetContentInsertionFrame() { return GetFirstPrincipalChild()->GetContentInsertionFrame(); } diff --git a/layout/svg/nsSVGGradientFrame.cpp b/layout/svg/nsSVGGradientFrame.cpp index c89fd4acdf3..be6fea879e9 100644 --- a/layout/svg/nsSVGGradientFrame.cpp +++ b/layout/svg/nsSVGGradientFrame.cpp @@ -57,6 +57,13 @@ NS_IMPL_FRAMEARENA_HELPERS(nsSVGGradientFrame) //---------------------------------------------------------------------- // nsIFrame methods: +/* virtual */ void +nsSVGGradientFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext) +{ + nsSVGEffects::InvalidateDirectRenderingObservers(this); + nsSVGGradientFrameBase::DidSetStyleContext(aOldStyleContext); +} + NS_IMETHODIMP nsSVGGradientFrame::AttributeChanged(int32_t aNameSpaceID, nsIAtom* aAttribute, diff --git a/layout/svg/nsSVGGradientFrame.h b/layout/svg/nsSVGGradientFrame.h index 203d7fd1d6c..a31d9adcbba 100644 --- a/layout/svg/nsSVGGradientFrame.h +++ b/layout/svg/nsSVGGradientFrame.h @@ -53,6 +53,8 @@ public: const gfxRect *aOverrideBounds); // nsIFrame interface: + virtual void DidSetStyleContext(nsStyleContext* aOldStyleContext); + NS_IMETHOD AttributeChanged(int32_t aNameSpaceID, nsIAtom* aAttribute, int32_t aModType); diff --git a/layout/svg/nsSVGMaskFrame.cpp b/layout/svg/nsSVGMaskFrame.cpp index 0dcbb952409..90c39f27a51 100644 --- a/layout/svg/nsSVGMaskFrame.cpp +++ b/layout/svg/nsSVGMaskFrame.cpp @@ -131,6 +131,13 @@ nsSVGMaskFrame::ComputeMaskAlpha(nsRenderingContext *aContext, return retval.forget(); } +/* virtual */ void +nsSVGMaskFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext) +{ + nsSVGEffects::InvalidateDirectRenderingObservers(this); + nsSVGMaskFrameBase::DidSetStyleContext(aOldStyleContext); +} + NS_IMETHODIMP nsSVGMaskFrame::AttributeChanged(int32_t aNameSpaceID, nsIAtom* aAttribute, diff --git a/layout/svg/nsSVGMaskFrame.h b/layout/svg/nsSVGMaskFrame.h index 53d103b83c0..ad078a83836 100644 --- a/layout/svg/nsSVGMaskFrame.h +++ b/layout/svg/nsSVGMaskFrame.h @@ -37,6 +37,8 @@ public: const gfxMatrix &aMatrix, float aOpacity = 1.0f); + virtual void DidSetStyleContext(nsStyleContext* aOldStyleContext); + NS_IMETHOD AttributeChanged(int32_t aNameSpaceID, nsIAtom* aAttribute, int32_t aModType); diff --git a/layout/svg/nsSVGPathGeometryFrame.cpp b/layout/svg/nsSVGPathGeometryFrame.cpp index 5eba955662b..7d746db38d3 100644 --- a/layout/svg/nsSVGPathGeometryFrame.cpp +++ b/layout/svg/nsSVGPathGeometryFrame.cpp @@ -120,6 +120,21 @@ nsSVGPathGeometryFrame::AttributeChanged(int32_t aNameSpaceID, return NS_OK; } +/* virtual */ void +nsSVGPathGeometryFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext) +{ + nsSVGPathGeometryFrameBase::DidSetStyleContext(aOldStyleContext); + + // XXX: we'd like to use the style_hint mechanism and the + // ContentStateChanged/AttributeChanged functions for style changes + // to get slightly finer granularity, but unfortunately the + // style_hints don't map very well onto svg. Here seems to be the + // best place to deal with style changes: + + nsSVGEffects::InvalidateRenderingObservers(this); + nsSVGUtils::ScheduleReflowSVG(this); +} + nsIAtom * nsSVGPathGeometryFrame::GetType() const { diff --git a/layout/svg/nsSVGPathGeometryFrame.h b/layout/svg/nsSVGPathGeometryFrame.h index c7618f2b366..8e29cb3f88e 100644 --- a/layout/svg/nsSVGPathGeometryFrame.h +++ b/layout/svg/nsSVGPathGeometryFrame.h @@ -56,6 +56,8 @@ public: nsIAtom* aAttribute, int32_t aModType); + virtual void DidSetStyleContext(nsStyleContext* aOldStyleContext); + /** * Get the "type" of the frame * diff --git a/layout/svg/nsSVGPatternFrame.cpp b/layout/svg/nsSVGPatternFrame.cpp index 207a1c516f0..7ac54f881ba 100644 --- a/layout/svg/nsSVGPatternFrame.cpp +++ b/layout/svg/nsSVGPatternFrame.cpp @@ -62,6 +62,13 @@ NS_IMPL_FRAMEARENA_HELPERS(nsSVGPatternFrame) //---------------------------------------------------------------------- // nsIFrame methods: +/* virtual */ void +nsSVGPatternFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext) +{ + nsSVGEffects::InvalidateDirectRenderingObservers(this); + nsSVGPatternFrameBase::DidSetStyleContext(aOldStyleContext); +} + NS_IMETHODIMP nsSVGPatternFrame::AttributeChanged(int32_t aNameSpaceID, nsIAtom* aAttribute, diff --git a/layout/svg/nsSVGPatternFrame.h b/layout/svg/nsSVGPatternFrame.h index a1b276b750b..3d35110480c 100644 --- a/layout/svg/nsSVGPatternFrame.h +++ b/layout/svg/nsSVGPatternFrame.h @@ -52,6 +52,8 @@ public: virtual gfxMatrix GetCanvasTM(uint32_t aFor); // nsIFrame interface: + virtual void DidSetStyleContext(nsStyleContext* aOldStyleContext); + NS_IMETHOD AttributeChanged(int32_t aNameSpaceID, nsIAtom* aAttribute, int32_t aModType); diff --git a/layout/svg/nsSVGStopFrame.cpp b/layout/svg/nsSVGStopFrame.cpp index 815fb8b92ce..10073eee941 100644 --- a/layout/svg/nsSVGStopFrame.cpp +++ b/layout/svg/nsSVGStopFrame.cpp @@ -40,6 +40,8 @@ public: const nsRect& aDirtyRect, const nsDisplayListSet& aLists) MOZ_OVERRIDE {} + virtual void DidSetStyleContext(nsStyleContext* aOldStyleContext); + NS_IMETHOD AttributeChanged(int32_t aNameSpaceID, nsIAtom* aAttribute, int32_t aModType); @@ -84,6 +86,13 @@ nsSVGStopFrame::Init(nsIContent* aContent, } #endif /* DEBUG */ +/* virtual */ void +nsSVGStopFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext) +{ + nsSVGStopFrameBase::DidSetStyleContext(aOldStyleContext); + nsSVGEffects::InvalidateRenderingObservers(this); +} + nsIAtom * nsSVGStopFrame::GetType() const { From 5dc60f0aa8f32852bbd8c29b23a9dc5e3556e5d5 Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Fri, 24 May 2013 16:03:03 -0400 Subject: [PATCH 62/91] Backed out changeset 831c1928fba9 (bug 875037) for mochitest-1 assertions on a CLOSED TREE --- layout/style/nsStyleStruct.cpp | 78 +++++++++++----------------------- 1 file changed, 25 insertions(+), 53 deletions(-) diff --git a/layout/style/nsStyleStruct.cpp b/layout/style/nsStyleStruct.cpp index f24c9897877..b9eef3d2107 100644 --- a/layout/style/nsStyleStruct.cpp +++ b/layout/style/nsStyleStruct.cpp @@ -900,15 +900,17 @@ nsChangeHint nsStyleSVG::CalcDifference(const nsStyleSVG& aOther) const { nsChangeHint hint = nsChangeHint(0); + if (mTextRendering != aOther.mTextRendering) { + NS_UpdateHint(hint, nsChangeHint_RepaintFrame); + // May be needed for non-svg frames + NS_UpdateHint(hint, nsChangeHint_AllReflowHints); + } + if (!EqualURIs(mMarkerEnd, aOther.mMarkerEnd) || !EqualURIs(mMarkerMid, aOther.mMarkerMid) || !EqualURIs(mMarkerStart, aOther.mMarkerStart)) { - // Markers currently contribute to nsSVGPathGeometryFrame::mRect, - // so we need a reflow as well as a repaint. No intrinsic sizes need - // to change, so nsChangeHint_NeedReflow is sufficient. - NS_UpdateHint(hint, nsChangeHint_UpdateEffects); - NS_UpdateHint(hint, nsChangeHint_NeedReflow); NS_UpdateHint(hint, nsChangeHint_RepaintFrame); + NS_UpdateHint(hint, nsChangeHint_UpdateEffects); return hint; } @@ -919,32 +921,17 @@ nsChangeHint nsStyleSVG::CalcDifference(const nsStyleSVG& aOther) const PaintURIChanged(mStroke, aOther.mStroke)) { NS_UpdateHint(hint, nsChangeHint_UpdateEffects); } - } - - // Stroke currently contributes to nsSVGPathGeometryFrame::mRect, so - // we need a reflow here. No intrinsic sizes need to change, so - // nsChangeHint_NeedReflow is sufficient. - // Note that stroke-dashoffset does not affect nsSVGPathGeometryFrame::mRect. - // text-anchor and text-rendering changes also require a reflow since they - // change frames' rects. - if (mStrokeWidth != aOther.mStrokeWidth || - mStrokeMiterlimit != aOther.mStrokeMiterlimit || - mStrokeLinecap != aOther.mStrokeLinecap || - mStrokeLinejoin != aOther.mStrokeLinejoin || - mTextAnchor != aOther.mTextAnchor || - mTextRendering != aOther.mTextRendering) { - NS_UpdateHint(hint, nsChangeHint_NeedReflow); - NS_UpdateHint(hint, nsChangeHint_RepaintFrame); + // Nothing more to do, below we can only set "repaint" return hint; } - if (hint & nsChangeHint_RepaintFrame) { - return hint; // we don't add anything else below - } - if ( mStrokeDashoffset != aOther.mStrokeDashoffset || + mStrokeWidth != aOther.mStrokeWidth || + mFillOpacity != aOther.mFillOpacity || + mStrokeMiterlimit != aOther.mStrokeMiterlimit || mStrokeOpacity != aOther.mStrokeOpacity || + mClipRule != aOther.mClipRule || mColorInterpolation != aOther.mColorInterpolation || mColorInterpolationFilters != aOther.mColorInterpolationFilters || @@ -953,6 +940,9 @@ nsChangeHint nsStyleSVG::CalcDifference(const nsStyleSVG& aOther) const mPaintOrder != aOther.mPaintOrder || mShapeRendering != aOther.mShapeRendering || mStrokeDasharrayLength != aOther.mStrokeDasharrayLength || + mStrokeLinecap != aOther.mStrokeLinecap || + mStrokeLinejoin != aOther.mStrokeLinejoin || + mTextAnchor != aOther.mTextAnchor || mFillOpacitySource != aOther.mFillOpacitySource || mStrokeOpacitySource != aOther.mStrokeOpacitySource || mStrokeDasharrayFromObject != aOther.mStrokeDasharrayFromObject || @@ -1020,28 +1010,18 @@ nsChangeHint nsStyleSVGReset::CalcDifference(const nsStyleSVGReset& aOther) cons !EqualURIs(mFilter, aOther.mFilter) || !EqualURIs(mMask, aOther.mMask)) { NS_UpdateHint(hint, nsChangeHint_UpdateEffects); + NS_UpdateHint(hint, nsChangeHint_AllReflowHints); NS_UpdateHint(hint, nsChangeHint_RepaintFrame); - } - - if (mDominantBaseline != aOther.mDominantBaseline) { - // XXXjwatt: why NS_STYLE_HINT_REFLOW? Isn't that excessive? + } else if (mDominantBaseline != aOther.mDominantBaseline) { NS_UpdateHint(hint, NS_STYLE_HINT_REFLOW); - } else if (mVectorEffect != aOther.mVectorEffect) { - // Stroke currently affects nsSVGPathGeometryFrame::mRect, and - // vector-effect affect stroke. As a result we need to reflow if - // vector-effect changes in order to have nsSVGPathGeometryFrame:: - // ReflowSVG called to update its mRect. No intrinsic sizes need - // to change so nsChangeHint_NeedReflow is sufficient. - NS_UpdateHint(hint, nsChangeHint_NeedReflow); - NS_UpdateHint(hint, nsChangeHint_RepaintFrame); } else if (mStopColor != aOther.mStopColor || mFloodColor != aOther.mFloodColor || mLightingColor != aOther.mLightingColor || mStopOpacity != aOther.mStopOpacity || mFloodOpacity != aOther.mFloodOpacity || - mMaskType != aOther.mMaskType) { + mVectorEffect != aOther.mVectorEffect || + mMaskType != aOther.mMaskType) NS_UpdateHint(hint, nsChangeHint_RepaintFrame); - } return hint; } @@ -2316,20 +2296,12 @@ nsChangeHint nsStyleVisibility::CalcDifference(const nsStyleVisibility& aOther) if (mDirection != aOther.mDirection) { NS_UpdateHint(hint, nsChangeHint_ReconstructFrame); - } else { - if (mVisible != aOther.mVisible) { - if ((NS_STYLE_VISIBILITY_COLLAPSE == mVisible) || - (NS_STYLE_VISIBILITY_COLLAPSE == aOther.mVisible)) { - NS_UpdateHint(hint, NS_STYLE_HINT_REFLOW); - } else { - NS_UpdateHint(hint, NS_STYLE_HINT_VISUAL); - } - } - if (mPointerEvents != aOther.mPointerEvents) { - // nsSVGPathGeometryFrame's mRect depends on stroke _and_ on the value - // of pointer-events. See nsSVGPathGeometryFrame::ReflowSVG's use of - // GetHitTestFlags. (Only a reflow, no visual change.) - NS_UpdateHint(hint, nsChangeHint_NeedReflow); + } else if (mVisible != aOther.mVisible) { + if ((NS_STYLE_VISIBILITY_COLLAPSE == mVisible) || + (NS_STYLE_VISIBILITY_COLLAPSE == aOther.mVisible)) { + NS_UpdateHint(hint, NS_STYLE_HINT_REFLOW); + } else { + NS_UpdateHint(hint, NS_STYLE_HINT_VISUAL); } } return hint; From a3a9600c07f3deec9479fd34c5d5c7200737fbfe Mon Sep 17 00:00:00 2001 From: Mark Capella Date: Fri, 24 May 2013 16:31:53 -0400 Subject: [PATCH 63/91] Bug 827445 - Updater continues to notify about updates --- mobile/android/base/UpdateService.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/mobile/android/base/UpdateService.java b/mobile/android/base/UpdateService.java index b90a64b49ee..1bba0c56b5b 100644 --- a/mobile/android/base/UpdateService.java +++ b/mobile/android/base/UpdateService.java @@ -115,9 +115,11 @@ public class UpdateService extends IntentService { registerForUpdates(false); } else if (UpdateServiceHelper.ACTION_CHECK_FOR_UPDATE.equals(intent.getAction())) { + Log.i(LOGTAG, "XYZZY Coming from Point 1"); startUpdate(intent.getIntExtra(UpdateServiceHelper.EXTRA_UPDATE_FLAGS_NAME, 0)); } else if (UpdateServiceHelper.ACTION_DOWNLOAD_UPDATE.equals(intent.getAction())) { // We always want to do the download here + Log.i(LOGTAG, "XYZZY Coming from Point 2"); startUpdate(UpdateServiceHelper.FLAG_FORCE_DOWNLOAD); } else if (UpdateServiceHelper.ACTION_APPLY_UPDATE.equals(intent.getAction())) { applyUpdate(intent.getStringExtra(UpdateServiceHelper.EXTRA_PACKAGE_PATH_NAME)); @@ -158,6 +160,7 @@ public class UpdateService extends IntentService { // We've either never attempted an update, or we are passed the desired // time. Start an update now. Log.i(LOGTAG, "no update has ever been attempted, checking now"); + Log.i(LOGTAG, "XYZZY Coming from Point 3"); startUpdate(0); return; } @@ -201,6 +204,7 @@ public class UpdateService extends IntentService { int connectionType = netInfo.getType(); int autoDownloadPolicy = getAutoDownloadPolicy(); + Log.i(LOGTAG, "XYZZY Checking AutoDownloadPolicy " + Integer.toString(autoDownloadPolicy)); /** @@ -210,6 +214,9 @@ public class UpdateService extends IntentService { * - The preference is set to 'always' * - The preference is set to 'wifi' and we are actually using wifi (or regular ethernet) */ + + Log.i(LOGTAG, "XYZZY " + Integer.toString(flags)); + boolean shouldStartDownload = hasFlag(flags, UpdateServiceHelper.FLAG_FORCE_DOWNLOAD) || autoDownloadPolicy == UpdateServiceHelper.AUTODOWNLOAD_ENABLED || (autoDownloadPolicy == UpdateServiceHelper.AUTODOWNLOAD_WIFI && @@ -560,9 +567,11 @@ public class UpdateService extends IntentService { } private void setAutoDownloadPolicy(int policy) { + Log.i(LOGTAG, "XYZZY Setting AutoDownloadPolicy " + Integer.toString(policy)); SharedPreferences.Editor editor = mPrefs.edit(); editor.putInt(KEY_AUTODOWNLOAD_POLICY, policy); editor.commit(); + Log.i(LOGTAG, "XYZZY Verifying AutoDownloadPolicy " + Integer.toString(getAutoDownloadPolicy())); } private void saveUpdateInfo(UpdateInfo info, File downloaded) { From 95247ad2dafb38486174e22c7de8e62bc722da1a Mon Sep 17 00:00:00 2001 From: Ed Morley Date: Fri, 24 May 2013 21:41:17 +0100 Subject: [PATCH 64/91] Backed out changeset d3f38905e7a0 (bug 827445) since pushed by mistake on a CLOSED TREE --- mobile/android/base/UpdateService.java | 9 --------- 1 file changed, 9 deletions(-) diff --git a/mobile/android/base/UpdateService.java b/mobile/android/base/UpdateService.java index 1bba0c56b5b..b90a64b49ee 100644 --- a/mobile/android/base/UpdateService.java +++ b/mobile/android/base/UpdateService.java @@ -115,11 +115,9 @@ public class UpdateService extends IntentService { registerForUpdates(false); } else if (UpdateServiceHelper.ACTION_CHECK_FOR_UPDATE.equals(intent.getAction())) { - Log.i(LOGTAG, "XYZZY Coming from Point 1"); startUpdate(intent.getIntExtra(UpdateServiceHelper.EXTRA_UPDATE_FLAGS_NAME, 0)); } else if (UpdateServiceHelper.ACTION_DOWNLOAD_UPDATE.equals(intent.getAction())) { // We always want to do the download here - Log.i(LOGTAG, "XYZZY Coming from Point 2"); startUpdate(UpdateServiceHelper.FLAG_FORCE_DOWNLOAD); } else if (UpdateServiceHelper.ACTION_APPLY_UPDATE.equals(intent.getAction())) { applyUpdate(intent.getStringExtra(UpdateServiceHelper.EXTRA_PACKAGE_PATH_NAME)); @@ -160,7 +158,6 @@ public class UpdateService extends IntentService { // We've either never attempted an update, or we are passed the desired // time. Start an update now. Log.i(LOGTAG, "no update has ever been attempted, checking now"); - Log.i(LOGTAG, "XYZZY Coming from Point 3"); startUpdate(0); return; } @@ -204,7 +201,6 @@ public class UpdateService extends IntentService { int connectionType = netInfo.getType(); int autoDownloadPolicy = getAutoDownloadPolicy(); - Log.i(LOGTAG, "XYZZY Checking AutoDownloadPolicy " + Integer.toString(autoDownloadPolicy)); /** @@ -214,9 +210,6 @@ public class UpdateService extends IntentService { * - The preference is set to 'always' * - The preference is set to 'wifi' and we are actually using wifi (or regular ethernet) */ - - Log.i(LOGTAG, "XYZZY " + Integer.toString(flags)); - boolean shouldStartDownload = hasFlag(flags, UpdateServiceHelper.FLAG_FORCE_DOWNLOAD) || autoDownloadPolicy == UpdateServiceHelper.AUTODOWNLOAD_ENABLED || (autoDownloadPolicy == UpdateServiceHelper.AUTODOWNLOAD_WIFI && @@ -567,11 +560,9 @@ public class UpdateService extends IntentService { } private void setAutoDownloadPolicy(int policy) { - Log.i(LOGTAG, "XYZZY Setting AutoDownloadPolicy " + Integer.toString(policy)); SharedPreferences.Editor editor = mPrefs.edit(); editor.putInt(KEY_AUTODOWNLOAD_POLICY, policy); editor.commit(); - Log.i(LOGTAG, "XYZZY Verifying AutoDownloadPolicy " + Integer.toString(getAutoDownloadPolicy())); } private void saveUpdateInfo(UpdateInfo info, File downloaded) { From 64764733081f8dfc23746d0bcdac53bdbdf46b1b Mon Sep 17 00:00:00 2001 From: Jeff Walden Date: Tue, 21 May 2013 17:42:34 -0700 Subject: [PATCH 65/91] Bug 873718 - Add a second argument to MakeConstructible, a value to be set as .prototype on the function. This ensures that .prototype is always an object, and that |new|ing the function will never invoke user-defined code trying to access the .prototype (and possibly hitting such a property along the prototype chain). r=till --HG-- extra : rebase_source : 039448500590360f99e24350fdaa8da4c17f9487 --- js/src/builtin/Utilities.js | 5 ++--- ...uct-primitive-Function.prototype.prototype.js | 5 +++++ ...ible-function-inherited-prototype-property.js | 3 +++ js/src/vm/SelfHosting.cpp | 16 ++++++++++++++-- 4 files changed, 24 insertions(+), 5 deletions(-) create mode 100644 js/src/jit-test/tests/basic/construct-primitive-Function.prototype.prototype.js create mode 100644 js/src/jit-test/tests/self-hosting/makeconstructible-function-inherited-prototype-property.js diff --git a/js/src/builtin/Utilities.js b/js/src/builtin/Utilities.js index f5abb38faca..9a65cb1d7df 100644 --- a/js/src/builtin/Utilities.js +++ b/js/src/builtin/Utilities.js @@ -80,9 +80,8 @@ function List() {} ListProto.push = std_Array_push; ListProto.slice = std_Array_slice; ListProto.sort = std_Array_sort; - List.prototype = ListProto; + MakeConstructible(List, ListProto); } -MakeConstructible(List); /********** Record specification type **********/ @@ -92,7 +91,7 @@ MakeConstructible(List); function Record() { return std_Object_create(null); } -MakeConstructible(Record); +MakeConstructible(Record, {}); /********** Abstract operations defined in ECMAScript Language Specification **********/ diff --git a/js/src/jit-test/tests/basic/construct-primitive-Function.prototype.prototype.js b/js/src/jit-test/tests/basic/construct-primitive-Function.prototype.prototype.js new file mode 100644 index 00000000000..0b83325cfe8 --- /dev/null +++ b/js/src/jit-test/tests/basic/construct-primitive-Function.prototype.prototype.js @@ -0,0 +1,5 @@ +Object.defineProperty(Function.prototype, "prototype", { + get: function() { throw 17; }, + set: function() { throw 42; } +}); +this.hasOwnProperty("Intl"); diff --git a/js/src/jit-test/tests/self-hosting/makeconstructible-function-inherited-prototype-property.js b/js/src/jit-test/tests/self-hosting/makeconstructible-function-inherited-prototype-property.js new file mode 100644 index 00000000000..ff4d0c000a8 --- /dev/null +++ b/js/src/jit-test/tests/self-hosting/makeconstructible-function-inherited-prototype-property.js @@ -0,0 +1,3 @@ +var proxy = new Proxy({ get: function() { throw 42; } }, {}); +Function.prototype.__proto__ = proxy; +this.hasOwnProperty("Intl"); diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp index 326f070529e..395c741c178 100644 --- a/js/src/vm/SelfHosting.cpp +++ b/js/src/vm/SelfHosting.cpp @@ -140,10 +140,22 @@ static JSBool intrinsic_MakeConstructible(JSContext *cx, unsigned argc, Value *vp) { CallArgs args = CallArgsFromVp(argc, vp); - JS_ASSERT(args.length() >= 1); + JS_ASSERT(args.length() == 2); JS_ASSERT(args[0].isObject()); JS_ASSERT(args[0].toObject().isFunction()); - args[0].toObject().toFunction()->setIsSelfHostedConstructor(); + JS_ASSERT(args[1].isObject()); + + // Normal .prototype properties aren't enumerable. But for this to clone + // correctly, it must be enumerable. + RootedObject ctor(cx, &args[0].toObject()); + if (!JSObject::defineProperty(cx, ctor, cx->names().classPrototype, args.handleAt(1), + JS_PropertyStub, JS_StrictPropertyStub, + JSPROP_READONLY | JSPROP_ENUMERATE | JSPROP_PERMANENT)) + { + return false; + } + + ctor->toFunction()->setIsSelfHostedConstructor(); args.rval().setUndefined(); return true; } From 6d7a94e1120a4caa1ec346aa72a407839054b1fd Mon Sep 17 00:00:00 2001 From: Jeff Walden Date: Thu, 23 May 2013 14:37:23 -0700 Subject: [PATCH 66/91] Bug 816033 - Convert a jstest for hash table init with a really huge number to a jsapi-test, to avoid mega-tests taking forever and a day to run. Also add operation callbacks to the JSON.stringify replacer-is-array loop, and cap the initial size of the HashSet used to store property names extracted from the replacer array. (It'll grow naturally if extra elements are added.) r=luke --HG-- extra : rebase_source : ec150c815c0c7003508a103edd45ec5f570603f3 --- js/src/jsapi-tests/Makefile.in | 1 + js/src/jsapi-tests/testHashTableInit.cpp | 27 +++++++++++++++++++ js/src/json.cpp | 10 ++++++- .../JSON/stringify-large-replacer-array.js | 26 ++++++++++++++++++ .../JSON-string-replacer-overflow.js | 25 ----------------- 5 files changed, 63 insertions(+), 26 deletions(-) create mode 100644 js/src/jsapi-tests/testHashTableInit.cpp create mode 100644 js/src/tests/ecma_5/JSON/stringify-large-replacer-array.js delete mode 100644 js/src/tests/ecma_5/extensions/JSON-string-replacer-overflow.js diff --git a/js/src/jsapi-tests/Makefile.in b/js/src/jsapi-tests/Makefile.in index 66b690a5133..16783bfccd8 100644 --- a/js/src/jsapi-tests/Makefile.in +++ b/js/src/jsapi-tests/Makefile.in @@ -40,6 +40,7 @@ CPPSRCS = \ testGCOutOfMemory.cpp \ testGetPropertyDefault.cpp \ testHashTable.cpp \ + testHashTableInit.cpp \ testIndexToString.cpp \ testIntString.cpp \ testIntTypesABI.cpp \ diff --git a/js/src/jsapi-tests/testHashTableInit.cpp b/js/src/jsapi-tests/testHashTableInit.cpp new file mode 100644 index 00000000000..75060697104 --- /dev/null +++ b/js/src/jsapi-tests/testHashTableInit.cpp @@ -0,0 +1,27 @@ +/* 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 "tests.h" + +#include "js/HashTable.h" + +typedef js::HashSet, js::SystemAllocPolicy> IntSet; + +static const uint32_t MaxAllowedHashInit = 1 << 23; + +BEGIN_TEST(testHashInitAlmostTooHuge) +{ + IntSet smallEnough; + CHECK(smallEnough.init(MaxAllowedHashInit)); + return true; +} +END_TEST(testHashInitAlmostTooHuge) + +BEGIN_TEST(testHashInitTooHuge) +{ + IntSet tooBig; + CHECK(!tooBig.init(MaxAllowedHashInit + 1)); + return true; +} +END_TEST(testHashInitTooHuge) diff --git a/js/src/json.cpp b/js/src/json.cpp index 340f1ff2fd1..36f02bac821 100644 --- a/js/src/json.cpp +++ b/js/src/json.cpp @@ -621,8 +621,13 @@ js_Stringify(JSContext *cx, MutableHandleValue vp, JSObject *replacer_, Value sp if (replacer->isArray() && !replacer->isIndexed()) len = Min(len, replacer->getDenseInitializedLength()); + // Cap the initial size to a moderately small value. This avoids + // ridiculous over-allocation if an array with bogusly-huge length + // is passed in. If we end up having to add elements past this + // size, the set will naturally resize to accommodate them. + const uint32_t MaxInitialSize = 1024; HashSet idSet(cx); - if (!idSet.init(len)) + if (!idSet.init(Min(len, MaxInitialSize))) return false; /* Step 4b(iii). */ @@ -631,6 +636,9 @@ js_Stringify(JSContext *cx, MutableHandleValue vp, JSObject *replacer_, Value sp /* Step 4b(iv). */ RootedValue v(cx); for (; i < len; i++) { + if (!JS_CHECK_OPERATION_LIMIT(cx)) + return false; + /* Step 4b(iv)(2). */ if (!JSObject::getElement(cx, replacer, replacer, i, &v)) return false; diff --git a/js/src/tests/ecma_5/JSON/stringify-large-replacer-array.js b/js/src/tests/ecma_5/JSON/stringify-large-replacer-array.js new file mode 100644 index 00000000000..28180b4bf16 --- /dev/null +++ b/js/src/tests/ecma_5/JSON/stringify-large-replacer-array.js @@ -0,0 +1,26 @@ +// Any copyright is dedicated to the Public Domain. +// http://creativecommons.org/licenses/publicdomain/ + +var gTestfile = 'stringify-large-replacer-array.js'; +//----------------------------------------------------------------------------- +var BUGNUMBER = 816033; +var summary = "JSON.stringify with a large replacer array"; + +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + +var replacer = []; +for (var i = 0; i < 4096; i++) + replacer.push(i); + +assertEq(JSON.stringify({ "foopy": "FAIL", "4093": 17 }, replacer), '{"4093":17}'); + +/******************************************************************************/ + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("Tests complete"); diff --git a/js/src/tests/ecma_5/extensions/JSON-string-replacer-overflow.js b/js/src/tests/ecma_5/extensions/JSON-string-replacer-overflow.js deleted file mode 100644 index 05dadff20e8..00000000000 --- a/js/src/tests/ecma_5/extensions/JSON-string-replacer-overflow.js +++ /dev/null @@ -1,25 +0,0 @@ -// |reftest| skip-if(!xulRuntime.shell&&xulRuntime.OS=="WINNT") -- intermittent failures bug 816033 -// Any copyright is dedicated to the Public Domain. -// http://creativecommons.org/licenses/publicdomain/ - -var r1 = [0, 1, 2, 3]; -Object.defineProperty(r1, (1 << 23) - 1, {}); -JSON.stringify({ 0: 0, 1: 1, 2: 2, 3: 3 }, r1) - -var r2 = [0, 1, 2, 3]; -Object.defineProperty(r2, (1 << 30), {}); -try -{ - JSON.stringify({ 0: 0, 1: 1, 2: 2, 3: 3 }, r2) -} -catch (e) -{ - assertEq(""+e, "InternalError: allocation size overflow"); -} - -/******************************************************************************/ - -if (typeof reportCompare === "function") - reportCompare(true, true); - -print("Tests complete"); From 5e157a3641fd1fe95b3141315beeff1df8954876 Mon Sep 17 00:00:00 2001 From: Masatoshi Kimura Date: Sat, 25 May 2013 07:55:18 +0900 Subject: [PATCH 67/91] Bug 875155 - Make bug 809674 test self-contained. r=bholley --- js/xpconnect/tests/components/js/Makefile.in | 1 + .../tests/components/js/xpctest.manifest | 3 ++ .../tests/components/js/xpctest_bug809674.js | 19 ++++++++++ js/xpconnect/tests/idl/moz.build | 1 + js/xpconnect/tests/idl/xpctest_bug809674.idl | 18 ++++++++++ js/xpconnect/tests/mochitest/Makefile.in | 1 - .../tests/mochitest/test_bug809674.html | 35 ------------------- js/xpconnect/tests/unit/test_bug809674.js | 30 ++++++++++++++++ js/xpconnect/tests/unit/xpcshell.ini | 1 + 9 files changed, 73 insertions(+), 36 deletions(-) create mode 100644 js/xpconnect/tests/components/js/xpctest_bug809674.js create mode 100644 js/xpconnect/tests/idl/xpctest_bug809674.idl delete mode 100644 js/xpconnect/tests/mochitest/test_bug809674.html create mode 100644 js/xpconnect/tests/unit/test_bug809674.js diff --git a/js/xpconnect/tests/components/js/Makefile.in b/js/xpconnect/tests/components/js/Makefile.in index 8345db0115c..eb407d5684e 100644 --- a/js/xpconnect/tests/components/js/Makefile.in +++ b/js/xpconnect/tests/components/js/Makefile.in @@ -17,6 +17,7 @@ NO_DIST_INSTALL = 1 JS_FILES = \ xpctest_attributes.js \ + xpctest_bug809674.js \ xpctest_interfaces.js \ xpctest_params.js \ $(NULL) diff --git a/js/xpconnect/tests/components/js/xpctest.manifest b/js/xpconnect/tests/components/js/xpctest.manifest index 576113971a2..10ad72e6bb3 100644 --- a/js/xpconnect/tests/components/js/xpctest.manifest +++ b/js/xpconnect/tests/components/js/xpctest.manifest @@ -4,6 +4,9 @@ contract @mozilla.org/js/xpc/test/js/ObjectReadWrite;1 {8ff41d9c-66e9-4453-924a- component {916c4247-253d-4ed0-a425-adfedf53ecc8} xpctest_attributes.js contract @mozilla.org/js/xpc/test/js/ObjectReadOnly;1 {916c4247-253d-4ed0-a425-adfedf53ecc8} +component {2df46559-da21-49bf-b863-0d7b7bbcbc73} xpctest_bug809674.js +contract @mozilla.org/js/xpc/test/js/Bug809674;1 {2df46559-da21-49bf-b863-0d7b7bbcbc73} + component {e3b86f4e-49c0-487c-a2b0-3a986720a044} xpctest_params.js contract @mozilla.org/js/xpc/test/js/Params;1 {e3b86f4e-49c0-487c-a2b0-3a986720a044} diff --git a/js/xpconnect/tests/components/js/xpctest_bug809674.js b/js/xpconnect/tests/components/js/xpctest_bug809674.js new file mode 100644 index 00000000000..4f018c12803 --- /dev/null +++ b/js/xpconnect/tests/components/js/xpctest_bug809674.js @@ -0,0 +1,19 @@ +/* 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/. */ +Components.utils.import("resource:///modules/XPCOMUtils.jsm"); + +function TestBug809674() {} +TestBug809674.prototype = { + + /* Boilerplate */ + QueryInterface: XPCOMUtils.generateQI([Components.interfaces["nsIXPCTestBug809674"]]), + contractID: "@mozilla.org/js/xpc/test/js/Bug809674;1", + classID: Components.ID("{2df46559-da21-49bf-b863-0d7b7bbcbc73}"), + + /* nsIXPCTestBug809674 */ + jsvalProperty: {}, +}; + + +this.NSGetFactory = XPCOMUtils.generateNSGetFactory([TestBug809674]); diff --git a/js/xpconnect/tests/idl/moz.build b/js/xpconnect/tests/idl/moz.build index b6ad7a72f4d..e5ff90df2fd 100644 --- a/js/xpconnect/tests/idl/moz.build +++ b/js/xpconnect/tests/idl/moz.build @@ -6,6 +6,7 @@ XPIDL_SOURCES += [ 'xpctest_attributes.idl', + 'xpctest_bug809674.idl', 'xpctest_interfaces.idl', 'xpctest_params.idl', ] diff --git a/js/xpconnect/tests/idl/xpctest_bug809674.idl b/js/xpconnect/tests/idl/xpctest_bug809674.idl new file mode 100644 index 00000000000..c276f027fbd --- /dev/null +++ b/js/xpconnect/tests/idl/xpctest_bug809674.idl @@ -0,0 +1,18 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISupports.idl" +/* + * Test interface for https://bugzilla.mozilla.org/show_bug.cgi?id=809674 . + * + * This test makes sure that accessing an attribute marked with + * [implicit_jscontext] will throw without crashing. + */ + +[scriptable, uuid(2df46559-da21-49bf-b863-0d7b7bbcbc73)] +interface nsIXPCTestBug809674 : nsISupports { + [implicit_jscontext] attribute jsval jsvalProperty; +}; diff --git a/js/xpconnect/tests/mochitest/Makefile.in b/js/xpconnect/tests/mochitest/Makefile.in index 3bee09fe3f6..cfa3b9a91f2 100644 --- a/js/xpconnect/tests/mochitest/Makefile.in +++ b/js/xpconnect/tests/mochitest/Makefile.in @@ -91,7 +91,6 @@ MOCHITEST_FILES = chrome_wrappers_helper.html \ file_bug802557.html \ test_bug803730.html \ test_bug809547.html \ - test_bug809674.html \ test_bug829872.html \ test_bug862380.html \ test_bug865260.html \ diff --git a/js/xpconnect/tests/mochitest/test_bug809674.html b/js/xpconnect/tests/mochitest/test_bug809674.html deleted file mode 100644 index 0d385ce795e..00000000000 --- a/js/xpconnect/tests/mochitest/test_bug809674.html +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - Test for Bug 809674 - - - - -Mozilla Bug 809674 -

- -
-
-
- - diff --git a/js/xpconnect/tests/unit/test_bug809674.js b/js/xpconnect/tests/unit/test_bug809674.js new file mode 100644 index 00000000000..1a498dec208 --- /dev/null +++ b/js/xpconnect/tests/unit/test_bug809674.js @@ -0,0 +1,30 @@ +/* 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/. */ + +const Cc = Components.classes; +const Ci = Components.interfaces; + +function run_test() { + + // Load the component manifest. + Components.manager.autoRegister(do_get_file('../components/js/xpctest.manifest')); + + // Test for each component. + test_property_throws("@mozilla.org/js/xpc/test/js/Bug809674;1"); +} + +function test_property_throws(contractid) { + + // Instantiate the object. + var o = Cc[contractid].createInstance(Ci["nsIXPCTestBug809674"]); + + // Test the initial values. + try { + o.jsvalProperty; + do_check_true(false, "Should have thrown"); + } catch (e) { + do_check_true(/implicit_jscontext/.test(e), "Should throw a helpful error"); + } + +} diff --git a/js/xpconnect/tests/unit/xpcshell.ini b/js/xpconnect/tests/unit/xpcshell.ini index 471fbdc82aa..154a71f31fa 100644 --- a/js/xpconnect/tests/unit/xpcshell.ini +++ b/js/xpconnect/tests/unit/xpcshell.ini @@ -15,6 +15,7 @@ tail = [test_bug780370.js] [test_bug805807.js] [test_bug809652.js] +[test_bug809674.js] [test_bug813901.js] [test_bug845201.js] [test_bug845862.js] From e7bdd24f89f9b123754ea1f65dcbbe6253b93186 Mon Sep 17 00:00:00 2001 From: Masatoshi Kimura Date: Sat, 25 May 2013 07:55:19 +0900 Subject: [PATCH 68/91] Bug 875155 - Convert TimeEvent to WebIDL. r=smaug --- content/smil/moz.build | 1 + content/smil/nsDOMTimeEvent.cpp | 5 +---- content/smil/nsDOMTimeEvent.h | 29 ++++++++++++++++++++++++++--- dom/base/nsDOMClassInfo.cpp | 9 --------- dom/base/nsDOMClassInfoClasses.h | 2 -- dom/bindings/Bindings.conf | 6 +++++- dom/webidl/TimeEvent.webidl | 22 ++++++++++++++++++++++ dom/webidl/WebIDL.mk | 1 + 8 files changed, 56 insertions(+), 19 deletions(-) create mode 100644 dom/webidl/TimeEvent.webidl diff --git a/content/smil/moz.build b/content/smil/moz.build index a43c940fb46..17134595b18 100644 --- a/content/smil/moz.build +++ b/content/smil/moz.build @@ -9,6 +9,7 @@ TEST_TOOL_DIRS += ['test'] MODULE = 'content' EXPORTS += [ + 'nsDOMTimeEvent.h', 'nsISMILAttr.h', 'nsISMILType.h', 'nsSMILAnimationController.h', diff --git a/content/smil/nsDOMTimeEvent.cpp b/content/smil/nsDOMTimeEvent.cpp index 752402b65d0..5d2ee583231 100644 --- a/content/smil/nsDOMTimeEvent.cpp +++ b/content/smil/nsDOMTimeEvent.cpp @@ -7,13 +7,13 @@ #include "nsGUIEvent.h" #include "nsPresContext.h" #include "nsIInterfaceRequestorUtils.h" -#include "nsDOMClassInfoID.h" nsDOMTimeEvent::nsDOMTimeEvent(mozilla::dom::EventTarget* aOwner, nsPresContext* aPresContext, nsEvent* aEvent) : nsDOMEvent(aOwner, aPresContext, aEvent ? aEvent : new nsUIEvent(false, 0, 0)), mDetail(0) { + SetIsDOMBinding(); if (aEvent) { mEventIsInternal = false; } else { @@ -51,11 +51,8 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_ADDREF_INHERITED(nsDOMTimeEvent, nsDOMEvent) NS_IMPL_RELEASE_INHERITED(nsDOMTimeEvent, nsDOMEvent) -DOMCI_DATA(TimeEvent, nsDOMTimeEvent) - NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsDOMTimeEvent) NS_INTERFACE_MAP_ENTRY(nsIDOMTimeEvent) - NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(TimeEvent) NS_INTERFACE_MAP_END_INHERITING(nsDOMEvent) NS_IMETHODIMP diff --git a/content/smil/nsDOMTimeEvent.h b/content/smil/nsDOMTimeEvent.h index 0a6850f5b2a..80fb63c114f 100644 --- a/content/smil/nsDOMTimeEvent.h +++ b/content/smil/nsDOMTimeEvent.h @@ -8,14 +8,15 @@ #include "nsIDOMTimeEvent.h" #include "nsDOMEvent.h" +#include "mozilla/dom/TimeEventBinding.h" -class nsDOMTimeEvent : public nsDOMEvent, - public nsIDOMTimeEvent +class nsDOMTimeEvent MOZ_FINAL : public nsDOMEvent, + public nsIDOMTimeEvent { public: nsDOMTimeEvent(mozilla::dom::EventTarget* aOwner, nsPresContext* aPresContext, nsEvent* aEvent); - + // nsISupports interface: NS_DECL_ISUPPORTS_INHERITED NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsDOMTimeEvent, nsDOMEvent) @@ -26,6 +27,28 @@ public: // Forward to base class NS_FORWARD_TO_NSDOMEVENT + virtual JSObject* WrapObject(JSContext* aCx, + JS::Handle aScope) MOZ_OVERRIDE + { + return mozilla::dom::TimeEventBinding::Wrap(aCx, aScope, this); + } + + int32_t Detail() const + { + return mDetail; + } + + nsIDOMWindow* GetView() const + { + return mView; + } + + void InitTimeEvent(const nsAString& aType, nsIDOMWindow* aView, + int32_t aDetail, mozilla::ErrorResult& aRv) + { + aRv = InitTimeEvent(aType, aView, aDetail); + } + private: nsCOMPtr mView; int32_t mDetail; diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp index af3c7c1b555..e7c9611567a 100644 --- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -193,7 +193,6 @@ #include "nsIDOMSVGAnimatedInteger.h" #include "nsIDOMSVGAnimatedNumber.h" #include "nsIDOMSVGAnimatedString.h" -#include "nsIDOMTimeEvent.h" #include "nsIDOMSVGLength.h" #include "nsIDOMSVGNumber.h" @@ -619,9 +618,6 @@ static nsDOMClassInfoData sClassInfoData[] = { NS_DEFINE_CLASSINFO_DATA(CSSSupportsRule, nsDOMGenericSH, DOM_DEFAULT_SCRIPTABLE_FLAGS) - NS_DEFINE_CLASSINFO_DATA(TimeEvent, nsEventSH, - DOM_DEFAULT_SCRIPTABLE_FLAGS) - // other SVG classes NS_DEFINE_CLASSINFO_DATA(SVGAnimatedEnumeration, nsDOMGenericSH, DOM_DEFAULT_SCRIPTABLE_FLAGS) @@ -1719,11 +1715,6 @@ nsDOMClassInfo::Init() // The SVG document - DOM_CLASSINFO_MAP_BEGIN(TimeEvent, nsIDOMTimeEvent) - DOM_CLASSINFO_MAP_ENTRY(nsIDOMTimeEvent) - DOM_CLASSINFO_EVENT_MAP_ENTRIES - DOM_CLASSINFO_MAP_END - // other SVG classes DOM_CLASSINFO_MAP_BEGIN(SVGAnimatedEnumeration, nsIDOMSVGAnimatedEnumeration) DOM_CLASSINFO_MAP_ENTRY(nsIDOMSVGAnimatedEnumeration) diff --git a/dom/base/nsDOMClassInfoClasses.h b/dom/base/nsDOMClassInfoClasses.h index 0869bff9bc4..50b00fec7b7 100644 --- a/dom/base/nsDOMClassInfoClasses.h +++ b/dom/base/nsDOMClassInfoClasses.h @@ -84,8 +84,6 @@ DOMCI_CLASS(TreeColumn) DOMCI_CLASS(CSSMozDocumentRule) DOMCI_CLASS(CSSSupportsRule) -DOMCI_CLASS(TimeEvent) - // other SVG classes DOMCI_CLASS(SVGAnimatedEnumeration) DOMCI_CLASS(SVGAnimatedInteger) diff --git a/dom/bindings/Bindings.conf b/dom/bindings/Bindings.conf index d6d7d594768..e35ac1af11a 100644 --- a/dom/bindings/Bindings.conf +++ b/dom/bindings/Bindings.conf @@ -1047,7 +1047,11 @@ DOMInterfaces = { 'nativeOwnership': 'owned', }, - 'TimeRanges': { +'TimeEvent': { + 'nativeType': 'nsDOMTimeEvent', +}, + +'TimeRanges': { 'wrapperCache': False }, diff --git a/dom/webidl/TimeEvent.webidl b/dom/webidl/TimeEvent.webidl new file mode 100644 index 00000000000..20cd9c982dd --- /dev/null +++ b/dom/webidl/TimeEvent.webidl @@ -0,0 +1,22 @@ +/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * For more information on this interface please see + * http://dev.w3.org/2006/webapi/DOM-Level-3-Events/html/DOM3-Events.html + * + * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C + * liability, trademark and document use rules apply. + */ + +interface WindowProxy; + +interface TimeEvent : Event +{ + readonly attribute long detail; + readonly attribute WindowProxy? view; + void initTimeEvent(DOMString aType, + WindowProxy? aView, + long aDetail); +}; diff --git a/dom/webidl/WebIDL.mk b/dom/webidl/WebIDL.mk index 6550ab4b735..a0dd51277bb 100644 --- a/dom/webidl/WebIDL.mk +++ b/dom/webidl/WebIDL.mk @@ -320,6 +320,7 @@ webidl_files = \ TextTrackCue.webidl \ TextTrackCueList.webidl \ TextTrackList.webidl \ + TimeEvent.webidl \ TimeRanges.webidl \ Touch.webidl \ TouchEvent.webidl \ From bcb5e4a8b69ba3562267b9a7c37b78317a72449d Mon Sep 17 00:00:00 2001 From: Masatoshi Kimura Date: Sat, 25 May 2013 07:55:20 +0900 Subject: [PATCH 69/91] Bug 726933 - Add a deprecation warning to getPreventDefault. r=smaug --- content/base/public/nsDeprecatedOperationList.h | 1 + content/events/src/nsDOMEvent.cpp | 11 +++++++++++ content/events/src/nsDOMEvent.h | 5 +---- dom/locales/en-US/chrome/dom/dom.properties | 2 ++ 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/content/base/public/nsDeprecatedOperationList.h b/content/base/public/nsDeprecatedOperationList.h index 2d7f673fce5..92f361f5110 100644 --- a/content/base/public/nsDeprecatedOperationList.h +++ b/content/base/public/nsDeprecatedOperationList.h @@ -30,3 +30,4 @@ DEPRECATED_OPERATION(PrefixedVisibilityAPI) DEPRECATED_OPERATION(NodeIteratorDetach) DEPRECATED_OPERATION(MozAudioData) DEPRECATED_OPERATION(LenientThis) +DEPRECATED_OPERATION(GetPreventDefault) diff --git a/content/events/src/nsDOMEvent.cpp b/content/events/src/nsDOMEvent.cpp index 4ffc32485f2..1c819e3d2ca 100644 --- a/content/events/src/nsDOMEvent.cpp +++ b/content/events/src/nsDOMEvent.cpp @@ -1220,6 +1220,17 @@ const char* nsDOMEvent::GetEventName(uint32_t aEventType) return nullptr; } +bool +nsDOMEvent::GetPreventDefault() const +{ + if (mOwner) { + if (nsIDocument* doc = mOwner->GetExtantDoc()) { + doc->WarnOnceAbout(nsIDocument::eGetPreventDefault); + } + } + return DefaultPrevented(); +} + NS_IMETHODIMP nsDOMEvent::GetPreventDefault(bool* aReturn) { diff --git a/content/events/src/nsDOMEvent.h b/content/events/src/nsDOMEvent.h index 26b51b26801..c525dfa0e17 100644 --- a/content/events/src/nsDOMEvent.h +++ b/content/events/src/nsDOMEvent.h @@ -189,10 +189,7 @@ public: mozilla::dom::EventTarget* GetOriginalTarget() const; mozilla::dom::EventTarget* GetExplicitOriginalTarget() const; - bool GetPreventDefault() const - { - return DefaultPrevented(); - } + bool GetPreventDefault() const; protected: diff --git a/dom/locales/en-US/chrome/dom/dom.properties b/dom/locales/en-US/chrome/dom/dom.properties index 0329f36f51e..3409fe84a74 100644 --- a/dom/locales/en-US/chrome/dom/dom.properties +++ b/dom/locales/en-US/chrome/dom/dom.properties @@ -130,3 +130,5 @@ MozAudioDataWarning=The Mozilla Audio Data API is deprecated. Please use the We LenientThisWarning=Ignoring get or set of property that has [LenientThis] because the "this" object is incorrect. # LOCALIZATION NOTE: Do not translate "nsIDOMWindowUtils", "getWindowWithOuterId", or "nsIWindowMediator" GetWindowWithOuterIdWarning=Use of nsIDOMWindowUtils.getOuterWindowWithId() is deprecated. Instead, use the nsIWindowMediator method of the same name. +# LOCALIZATION NOTE: Do not translate "getPreventDefault" or "defaultPrevented". +GetPreventDefaultWarning=Use of getPreventDefault() is deprecated. Use defaultPrevented instead. From 7fc032e09d20e09460a0b31398b02c5fb54eebfb Mon Sep 17 00:00:00 2001 From: Mark Capella Date: Fri, 24 May 2013 19:10:33 -0400 Subject: [PATCH 70/91] Bug 862445 - Adjust reader mode content layout for serif fonts, r=lucasr, f=margaret --- .../android/chrome/content/aboutReader.html | 3 ++- mobile/android/chrome/content/aboutReader.js | 5 ++-- mobile/android/themes/core/aboutReader.css | 25 ++++++++++++++++--- 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/mobile/android/chrome/content/aboutReader.html b/mobile/android/chrome/content/aboutReader.html index b5aedccf788..13d0394fd2d 100644 --- a/mobile/android/chrome/content/aboutReader.html +++ b/mobile/android/chrome/content/aboutReader.html @@ -10,7 +10,8 @@
-
+ +

diff --git a/mobile/android/chrome/content/aboutReader.js b/mobile/android/chrome/content/aboutReader.js index a3cf22a3b3e..57b94c0d971 100644 --- a/mobile/android/chrome/content/aboutReader.js +++ b/mobile/android/chrome/content/aboutReader.js @@ -515,10 +515,9 @@ AboutReader.prototype = { this._article = article; + this._domainElement.href = article.url; let articleUri = Services.io.newURI(article.url, null, null); - let domain = articleUri.host; - - this._domainElement.innerHTML = domain; + this._domainElement.innerHTML = articleUri.host; this._creditsElement.innerHTML = article.byline; diff --git a/mobile/android/themes/core/aboutReader.css b/mobile/android/themes/core/aboutReader.css index e59eba4961e..80315382ddc 100644 --- a/mobile/android/themes/core/aboutReader.css +++ b/mobile/android/themes/core/aboutReader.css @@ -44,13 +44,25 @@ body { display: none; } -.header > .domain { +.domain, +.credits { + font-family: "Open Sans", sans-serif; +} + +.domain { margin-top: 10px; padding-bottom: 10px; - border-bottom: 2px solid; + color: #00acff !important; + text-decoration: none; +} + +.domain-border { + border-bottom: 1.5px solid #777777; + width: 50%; } .header > h1 { + font-size: 2.625em; font-weight: 300; line-height: 1.1em; width: 100%; @@ -60,6 +72,10 @@ body { padding: 0px; } +.serif > .header > h1 { + font-weight: 700; +} + .header > .credits { padding: 0px; margin: 0px; @@ -163,14 +179,14 @@ body { .light > .content a:visited, .light > .content a:hover, .light > .content a:active { - color: #ee6700 !important; + color: #00acff !important; } .dark > .content a, .dark > .content a:visited, .dark > .content a:hover, .dark > .content a:active { - color: #ff9400 !important; + color: #00acff !important; } .content * { @@ -218,6 +234,7 @@ body { .content .caption, .content .wp-caption-text, .content figcaption { + font-family: "Open Sans", sans-serif; margin: 0px !important; padding-top: 4px !important; } From b4874d1944f3e7a15b8256601dbdbae8ab540f4f Mon Sep 17 00:00:00 2001 From: Jeff Walden Date: Fri, 24 May 2013 16:15:46 -0700 Subject: [PATCH 71/91] Followup to bug 816033, remove a test. This was removed locally, but somehow the removal ended up in a different, not-yet-pushed patch. Sorry! :-( --HG-- extra : rebase_source : 1f487c932c5d8ebffabe8c08da12a1f9916e221b --- js/src/jit-test/tests/auto-regress/bug678362.js | 8 -------- 1 file changed, 8 deletions(-) delete mode 100644 js/src/jit-test/tests/auto-regress/bug678362.js diff --git a/js/src/jit-test/tests/auto-regress/bug678362.js b/js/src/jit-test/tests/auto-regress/bug678362.js deleted file mode 100644 index 4919c3fe258..00000000000 --- a/js/src/jit-test/tests/auto-regress/bug678362.js +++ /dev/null @@ -1,8 +0,0 @@ -// |jit-test| error:InternalError - -// Binary: cache/js-dbg-64-4b4b359e77e4-linux -// Flags: -// -var replacer = [0, 1, 2, 3]; -Object.defineProperty(replacer, 3.e7, {}); -JSON.stringify({ 0: 0, 1: 1, 2: 2, 3: 3 }, replacer) From c6d1c5449506fa3810b6bf459642d16d836be8ce Mon Sep 17 00:00:00 2001 From: "Nicolas B. Pierron" Date: Fri, 24 May 2013 14:58:08 -0700 Subject: [PATCH 72/91] Bug 774006 - IonMonkey: Implement SetElementIC for integer indexes. r=h4writer --- js/src/ion/CodeGenerator.cpp | 53 +++++++++ js/src/ion/CodeGenerator.h | 3 + js/src/ion/IonBuilder.cpp | 18 ++++ js/src/ion/IonCaches.cpp | 142 +++++++++++++++++++++++++ js/src/ion/IonCaches.h | 66 +++++++++++- js/src/ion/LIR-Common.h | 52 +++++++++ js/src/ion/LOpcodes.h | 2 + js/src/ion/Lowering.cpp | 27 +++++ js/src/ion/Lowering.h | 1 + js/src/ion/MIR.h | 68 +++++++++--- js/src/ion/MOpcodes.h | 1 + js/src/ion/ParallelArrayAnalysis.cpp | 1 + js/src/jit-test/tests/ion/bug774006.js | 43 ++++++++ 13 files changed, 462 insertions(+), 15 deletions(-) create mode 100644 js/src/jit-test/tests/ion/bug774006.js diff --git a/js/src/ion/CodeGenerator.cpp b/js/src/ion/CodeGenerator.cpp index 2e41e25794e..9fc3d84c49b 100644 --- a/js/src/ion/CodeGenerator.cpp +++ b/js/src/ion/CodeGenerator.cpp @@ -5671,6 +5671,59 @@ CodeGenerator::visitGetElementIC(OutOfLineUpdateCache *ool, GetElementIC *ic) return true; } +bool +CodeGenerator::visitSetElementCacheV(LSetElementCacheV *ins) +{ + Register obj = ToRegister(ins->object()); + Register temp = ToRegister(ins->temp()); + ValueOperand index = ToValue(ins, LSetElementCacheV::Index); + ConstantOrRegister value = TypedOrValueRegister(ToValue(ins, LSetElementCacheV::Value)); + + SetElementIC cache(obj, temp, index, value, ins->mir()->strict()); + + return addCache(ins, allocateCache(cache)); +} + +bool +CodeGenerator::visitSetElementCacheT(LSetElementCacheT *ins) +{ + Register obj = ToRegister(ins->object()); + Register temp = ToRegister(ins->temp()); + ValueOperand index = ToValue(ins, LSetElementCacheT::Index); + ConstantOrRegister value; + const LAllocation *tmp = ins->value(); + if (tmp->isConstant()) + value = *tmp->toConstant(); + else + value = TypedOrValueRegister(ins->mir()->value()->type(), ToAnyRegister(tmp)); + + SetElementIC cache(obj, temp, index, value, ins->mir()->strict()); + + return addCache(ins, allocateCache(cache)); +} + +typedef bool (*SetElementICFn)(JSContext *, size_t, HandleObject, HandleValue, HandleValue); +const VMFunction SetElementIC::UpdateInfo = + FunctionInfo(SetElementIC::update); + +bool +CodeGenerator::visitSetElementIC(OutOfLineUpdateCache *ool, SetElementIC *ic) +{ + LInstruction *lir = ool->lir(); + saveLive(lir); + + pushArg(ic->value()); + pushArg(ic->index()); + pushArg(ic->object()); + pushArg(Imm32(ool->getCacheIndex())); + if (!callVM(SetElementIC::UpdateInfo, lir)) + return false; + restoreLive(lir); + + masm.jump(ool->rejoin()); + return true; +} + bool CodeGenerator::visitBindNameCache(LBindNameCache *ins) { diff --git a/js/src/ion/CodeGenerator.h b/js/src/ion/CodeGenerator.h index d07136c602a..7fa8e61f10f 100644 --- a/js/src/ion/CodeGenerator.h +++ b/js/src/ion/CodeGenerator.h @@ -269,6 +269,8 @@ class CodeGenerator : public CodeGeneratorSpecific bool visitGetPropertyCacheT(LGetPropertyCacheT *ins); bool visitGetElementCacheV(LGetElementCacheV *ins); bool visitGetElementCacheT(LGetElementCacheT *ins); + bool visitSetElementCacheV(LSetElementCacheV *ins); + bool visitSetElementCacheT(LSetElementCacheT *ins); bool visitBindNameCache(LBindNameCache *ins); bool visitCallSetProperty(LInstruction *ins); bool visitSetPropertyCacheV(LSetPropertyCacheV *ins); @@ -280,6 +282,7 @@ class CodeGenerator : public CodeGeneratorSpecific bool visitParallelGetPropertyIC(OutOfLineUpdateCache *ool, ParallelGetPropertyIC *ic); bool visitSetPropertyIC(OutOfLineUpdateCache *ool, SetPropertyIC *ic); bool visitGetElementIC(OutOfLineUpdateCache *ool, GetElementIC *ic); + bool visitSetElementIC(OutOfLineUpdateCache *ool, SetElementIC *ic); bool visitBindNameIC(OutOfLineUpdateCache *ool, BindNameIC *ic); bool visitNameIC(OutOfLineUpdateCache *ool, NameIC *ic); bool visitCallsiteCloneIC(OutOfLineUpdateCache *ool, CallsiteCloneIC *ic); diff --git a/js/src/ion/IonBuilder.cpp b/js/src/ion/IonBuilder.cpp index 29f119a9417..fa2bfb1f5c5 100644 --- a/js/src/ion/IonBuilder.cpp +++ b/js/src/ion/IonBuilder.cpp @@ -6590,6 +6590,24 @@ IonBuilder::jsop_setelem() if (script()->argumentsHasVarBinding() && object->mightBeType(MIRType_Magic)) return abort("Type is not definitely lazy arguments."); + // Check if only objects are manipulated valid index, and generate a SetElementCache. + do { + if (!object->mightBeType(MIRType_Object)) + break; + + if (!index->mightBeType(MIRType_Int32) && + !index->mightBeType(MIRType_String)) + { + break; + } + + MInstruction *ins = MSetElementCache::New(object, index, value, script()->strict); + current->add(ins); + current->push(value); + + return resumeAfter(ins); + } while (false); + MInstruction *ins = MCallSetElement::New(object, index, value); current->add(ins); current->push(value); diff --git a/js/src/ion/IonCaches.cpp b/js/src/ion/IonCaches.cpp index 04cf4a57fc7..0b3a4ab3ae5 100644 --- a/js/src/ion/IonCaches.cpp +++ b/js/src/ion/IonCaches.cpp @@ -1543,6 +1543,8 @@ GetPropertyIC::reset() RepatchIonCache::reset(); hasArrayLengthStub_ = false; hasTypedArrayLengthStub_ = false; + hasStrictArgumentsLengthStub_ = false; + hasNormalArgumentsLengthStub_ = false; } void @@ -2557,6 +2559,146 @@ GetElementIC::update(JSContext *cx, size_t cacheIndex, HandleObject obj, void GetElementIC::reset() +{ + RepatchIonCache::reset(); + hasDenseStub_ = false; + hasStrictArgumentsStub_ = false; + hasNormalArgumentsStub_ = false; +} + +static bool +IsElementSetInlineable(HandleObject obj, HandleValue index) +{ + if (!obj->isArray()) + return false; + + if (obj->watched()) + return false; + + if (!index.isInt32()) + return false; + + return true; +} + +bool +SetElementIC::attachDenseElement(JSContext *cx, IonScript *ion, JSObject *obj, const Value &idval) +{ + JS_ASSERT(obj->isNative()); + JS_ASSERT(idval.isInt32()); + + Label failures; + Label outOfBounds; // index >= capacity || index > initialized length + + MacroAssembler masm(cx); + RepatchStubAppender attacher(*this); + + // Guard object is a dense array. + RootedObject globalObj(cx, &script->global()); + RootedShape shape(cx, obj->lastProperty()); + if (!shape) + return false; + masm.branchTestObjShape(Assembler::NotEqual, object(), shape, &failures); + + // Ensure the index is an int32 value. + ValueOperand indexVal = index(); + masm.branchTestInt32(Assembler::NotEqual, indexVal, &failures); + + // Unbox the index. + Register scratch = temp(); + Register index = masm.extractInt32(indexVal, scratch); + Register elements = scratch; + + { + // If needed, push the object register to store the element pointer. + if (scratch == index) { + masm.push(object()); + elements = object(); + } + + // Load obj->elements. + masm.loadPtr(Address(object(), JSObject::offsetOfElements()), elements); + + // Compute the location of the element. + BaseIndex target(elements, index, TimesEight); + + // Guard that we can increase the initialized length. + Address capacity(elements, ObjectElements::offsetOfCapacity()); + masm.branch32(Assembler::BelowOrEqual, capacity, index, &outOfBounds); + + // Guard on the initialized length. + Address initLength(elements, ObjectElements::offsetOfInitializedLength()); + masm.branch32(Assembler::Below, initLength, index, &outOfBounds); + + // if (initLength == index) + Label markElem, storeElem; + masm.branch32(Assembler::NotEqual, initLength, index, &markElem); + { + // Increase initialize length. + Int32Key newLength(index); + masm.bumpKey(&newLength, 1); + masm.storeKey(newLength, initLength); + + // Increase length if needed. + Label bumpedLength; + Address length(elements, ObjectElements::offsetOfLength()); + masm.branch32(Assembler::AboveOrEqual, length, index, &bumpedLength); + masm.storeKey(newLength, length); + masm.bind(&bumpedLength); + + // Restore the index. + masm.bumpKey(&newLength, -1); + masm.jump(&storeElem); + } + // else + { + // Mark old element. + masm.bind(&markElem); + if (cx->zone()->needsBarrier()) + masm.callPreBarrier(target, MIRType_Value); + } + + // Store the value. + masm.bind(&storeElem); + masm.storeConstantOrRegister(value(), target); + + if (elements == object()) + masm.pop(object()); + } + attacher.jumpRejoin(masm); + + // All failures flow to here. + { + masm.bind(&outOfBounds); + if (elements == object()) + masm.pop(object()); + } + masm.bind(&failures); + attacher.jumpNextStub(masm); + + setHasDenseStub(); + return linkAndAttachStub(cx, masm, attacher, ion, "dense array"); +} + +bool +SetElementIC::update(JSContext *cx, size_t cacheIndex, HandleObject obj, + HandleValue idval, HandleValue value) +{ + IonScript *ion = GetTopIonJSScript(cx)->ionScript(); + SetElementIC &cache = ion->getCache(cacheIndex).toSetElement(); + + if (cache.canAttachStub() && !cache.hasDenseStub() && IsElementSetInlineable(obj, idval)) { + if (!cache.attachDenseElement(cx, ion, obj, idval)) + return false; + } + + if (!SetObjectElement(cx, obj, idval, value, cache.strict())) + return false; + return true; +} + +void +SetElementIC::reset() { RepatchIonCache::reset(); hasDenseStub_ = false; diff --git a/js/src/ion/IonCaches.h b/js/src/ion/IonCaches.h index 2dc141529c0..586b3ffb5b6 100644 --- a/js/src/ion/IonCaches.h +++ b/js/src/ion/IonCaches.h @@ -22,6 +22,7 @@ namespace ion { _(GetProperty) \ _(SetProperty) \ _(GetElement) \ + _(SetElement) \ _(BindName) \ _(Name) \ _(CallsiteClone) \ @@ -510,7 +511,9 @@ class GetPropertyIC : public RepatchIonCache output_(output), allowGetters_(allowGetters), hasArrayLengthStub_(false), - hasTypedArrayLengthStub_(false) + hasTypedArrayLengthStub_(false), + hasStrictArgumentsLengthStub_(false), + hasNormalArgumentsLengthStub_(false) { } @@ -630,6 +633,8 @@ class GetElementIC : public RepatchIonCache output_(output), monitoredResult_(monitoredResult), hasDenseStub_(false), + hasStrictArgumentsStub_(false), + hasNormalArgumentsStub_(false), failedUpdates_(0) { } @@ -682,6 +687,65 @@ class GetElementIC : public RepatchIonCache } }; +class SetElementIC : public RepatchIonCache +{ + protected: + Register object_; + Register temp_; + ValueOperand index_; + ConstantOrRegister value_; + bool strict_; + + bool hasDenseStub_ : 1; + + public: + SetElementIC(Register object, Register temp, + ValueOperand index, ConstantOrRegister value, + bool strict) + : object_(object), + temp_(temp), + index_(index), + value_(value), + strict_(strict), + hasDenseStub_(false) + { + } + + CACHE_HEADER(SetElement) + + void reset(); + + Register object() const { + return object_; + } + Register temp() const { + return temp_; + } + ValueOperand index() const { + return index_; + } + ConstantOrRegister value() const { + return value_; + } + bool strict() const { + return strict_; + } + + bool hasDenseStub() const { + return hasDenseStub_; + } + void setHasDenseStub() { + JS_ASSERT(!hasDenseStub()); + hasDenseStub_ = true; + } + + bool attachDenseElement(JSContext *cx, IonScript *ion, JSObject *obj, const Value &idval); + + static bool + update(JSContext *cx, size_t cacheIndex, HandleObject obj, HandleValue idval, + HandleValue value); +}; + class BindNameIC : public RepatchIonCache { protected: diff --git a/js/src/ion/LIR-Common.h b/js/src/ion/LIR-Common.h index 018f01ac6af..5ee2aadee5e 100644 --- a/js/src/ion/LIR-Common.h +++ b/js/src/ion/LIR-Common.h @@ -3969,6 +3969,58 @@ class LSetPropertyCacheT : public LInstructionHelper<0, 2, 1> } }; +class LSetElementCacheV : public LInstructionHelper<0, 1 + 2 * BOX_PIECES, 1> +{ + public: + LIR_HEADER(SetElementCacheV); + + static const size_t Index = 1; + static const size_t Value = 1 + BOX_PIECES; + + LSetElementCacheV(const LAllocation &object, const LDefinition &temp) { + setOperand(0, object); + setTemp(0, temp); + } + const MSetElementCache *mir() const { + return mir_->toSetElementCache(); + } + + const LAllocation *object() { + return getOperand(0); + } + const LDefinition *temp() { + return getTemp(0); + } +}; + +class LSetElementCacheT : public LInstructionHelper<0, 2 + BOX_PIECES, 1> +{ + public: + LIR_HEADER(SetElementCacheT); + + static const size_t Index = 2; + + LSetElementCacheT(const LAllocation &object, const LAllocation &value, + const LDefinition &temp) { + setOperand(0, object); + setOperand(1, value); + setTemp(0, temp); + } + const MSetElementCache *mir() const { + return mir_->toSetElementCache(); + } + + const LAllocation *object() { + return getOperand(0); + } + const LAllocation *value() { + return getOperand(1); + } + const LDefinition *temp() { + return getTemp(0); + } +}; + class LCallIteratorStart : public LCallInstructionHelper<1, 1, 0> { public: diff --git a/js/src/ion/LOpcodes.h b/js/src/ion/LOpcodes.h index b0d59de7bd1..72115c438e6 100644 --- a/js/src/ion/LOpcodes.h +++ b/js/src/ion/LOpcodes.h @@ -192,6 +192,8 @@ _(CallDeleteProperty) \ _(SetPropertyCacheV) \ _(SetPropertyCacheT) \ + _(SetElementCacheV) \ + _(SetElementCacheT) \ _(SetPropertyPolymorphicV) \ _(SetPropertyPolymorphicT) \ _(CallIteratorStart) \ diff --git a/js/src/ion/Lowering.cpp b/js/src/ion/Lowering.cpp index 6e2cd04c891..47529435029 100644 --- a/js/src/ion/Lowering.cpp +++ b/js/src/ion/Lowering.cpp @@ -2392,6 +2392,33 @@ LIRGenerator::visitSetPropertyCache(MSetPropertyCache *ins) return assignSafepoint(lir, ins); } +bool +LIRGenerator::visitSetElementCache(MSetElementCache *ins) +{ + JS_ASSERT(ins->object()->type() == MIRType_Object); + JS_ASSERT(ins->index()->type() == MIRType_Value); + + LInstruction *lir; + if (ins->value()->type() == MIRType_Value) { + lir = new LSetElementCacheV(useRegister(ins->object()), temp()); + + if (!useBox(lir, LSetElementCacheV::Index, ins->index())) + return false; + if (!useBox(lir, LSetElementCacheV::Value, ins->value())) + return false; + } else { + lir = new LSetElementCacheT( + useRegister(ins->object()), + useRegisterOrConstant(ins->value()), + temp()); + + if (!useBox(lir, LSetElementCacheT::Index, ins->index())) + return false; + } + + return add(lir, ins) && assignSafepoint(lir, ins); +} + bool LIRGenerator::visitCallSetElement(MCallSetElement *ins) { diff --git a/js/src/ion/Lowering.h b/js/src/ion/Lowering.h index 4964c4794b0..c73164a3b6e 100644 --- a/js/src/ion/Lowering.h +++ b/js/src/ion/Lowering.h @@ -211,6 +211,7 @@ class LIRGenerator : public LIRGeneratorSpecific bool visitCallSetElement(MCallSetElement *ins); bool visitCallInitElementArray(MCallInitElementArray *ins); bool visitSetPropertyCache(MSetPropertyCache *ins); + bool visitSetElementCache(MSetElementCache *ins); bool visitCallSetProperty(MCallSetProperty *ins); bool visitIteratorStart(MIteratorStart *ins); bool visitIteratorNext(MIteratorNext *ins); diff --git a/js/src/ion/MIR.h b/js/src/ion/MIR.h index 856f2984c72..548277de632 100644 --- a/js/src/ion/MIR.h +++ b/js/src/ion/MIR.h @@ -6343,6 +6343,27 @@ class MSetPropertyInstruction : public MBinaryInstruction } }; +class MSetElementInstruction + : public MTernaryInstruction +{ + protected: + MSetElementInstruction(MDefinition *object, MDefinition *index, MDefinition *value) + : MTernaryInstruction(object, index, value) + { + } + + public: + MDefinition *object() const { + return getOperand(0); + } + MDefinition *index() const { + return getOperand(1); + } + MDefinition *value() const { + return getOperand(2); + } +}; + class MDeleteProperty : public MUnaryInstruction, public BoxInputsPolicy @@ -6418,6 +6439,35 @@ class MSetPropertyCache } }; +class MSetElementCache + : public MSetElementInstruction, + public MixPolicy, BoxPolicy<1> > +{ + bool strict_; + + MSetElementCache(MDefinition *obj, MDefinition *index, MDefinition *value, bool strict) + : MSetElementInstruction(obj, index, value), + strict_(strict) + { + } + + public: + INSTRUCTION_HEADER(SetElementCache); + + static MSetElementCache *New(MDefinition *obj, MDefinition *index, MDefinition *value, + bool strict) { + return new MSetElementCache(obj, index, value, strict); + } + + bool strict() const { + return strict_; + } + + TypePolicy *typePolicy() { + return this; + } +}; + class MCallGetProperty : public MUnaryInstruction, public BoxInputsPolicy @@ -6485,13 +6535,12 @@ class MCallGetElement }; class MCallSetElement - : public MAryInstruction<3>, + : public MSetElementInstruction, public CallSetElementPolicy { - MCallSetElement(MDefinition *object, MDefinition *index, MDefinition *value) { - setOperand(0, object); - setOperand(1, index); - setOperand(2, value); + MCallSetElement(MDefinition *object, MDefinition *index, MDefinition *value) + : MSetElementInstruction(object, index, value) + { } public: @@ -6504,15 +6553,6 @@ class MCallSetElement TypePolicy *typePolicy() { return this; } - MDefinition *object() const { - return getOperand(0); - } - MDefinition *index() const { - return getOperand(1); - } - MDefinition *value() const { - return getOperand(2); - } }; class MCallInitElementArray diff --git a/js/src/ion/MOpcodes.h b/js/src/ion/MOpcodes.h index f2e9d95b140..be558af0cb9 100644 --- a/js/src/ion/MOpcodes.h +++ b/js/src/ion/MOpcodes.h @@ -107,6 +107,7 @@ namespace ion { _(GetPropertyPolymorphic) \ _(SetPropertyPolymorphic) \ _(GetElementCache) \ + _(SetElementCache) \ _(BindNameCache) \ _(GuardShape) \ _(GuardObjectType) \ diff --git a/js/src/ion/ParallelArrayAnalysis.cpp b/js/src/ion/ParallelArrayAnalysis.cpp index 1e4f849cda7..b3700c8f9a8 100644 --- a/js/src/ion/ParallelArrayAnalysis.cpp +++ b/js/src/ion/ParallelArrayAnalysis.cpp @@ -195,6 +195,7 @@ class ParallelArrayVisitor : public MInstructionVisitor SAFE_OP(GetPropertyPolymorphic) UNSAFE_OP(SetPropertyPolymorphic) UNSAFE_OP(GetElementCache) + UNSAFE_OP(SetElementCache) UNSAFE_OP(BindNameCache) SAFE_OP(GuardShape) SAFE_OP(GuardObjectType) diff --git a/js/src/jit-test/tests/ion/bug774006.js b/js/src/jit-test/tests/ion/bug774006.js new file mode 100644 index 00000000000..6037628ab89 --- /dev/null +++ b/js/src/jit-test/tests/ion/bug774006.js @@ -0,0 +1,43 @@ + +// Test IonMonkey SetElementIC when ran with --ion-eager. + +function setelem(o, i, v) { + o[i] = v; +} + +var arr = new Array(); +var obj = {}; + +setelem(arr, "prop0", 2); +setelem(arr, 0, 2); // invalidate +setelem(arr, 1, 1); // recompile with setElemIC + +setelem(arr, 0, 0); // set known element. +setelem(arr, 2, 2); // push last element. +setelem(arr, 4, 4); // test out-of-bounds. +setelem(arr, "prop0", 0); +setelem(arr, "prop1", 1); + +setelem(obj, "prop0", 2); +setelem(obj, 0, 2); +setelem(obj, 1, 1); + +setelem(obj, 0, 0); +setelem(obj, 2, 2); +setelem(obj, 4, 4); +setelem(obj, "prop0", 0); +setelem(obj, "prop1", 1); + +assertEq(arr.prop0, 0); +assertEq(arr.prop1, 1); +assertEq(arr[0], 0); +assertEq(arr[1], 1); +assertEq(arr[2], 2); +assertEq(arr[4], 4); + +assertEq(obj.prop0, 0); +assertEq(obj.prop1, 1); +assertEq(obj[0], 0); +assertEq(obj[1], 1); +assertEq(obj[2], 2); +assertEq(obj[4], 4); From a121b6f9dc636395a5c4edb1efd3276be93f1f87 Mon Sep 17 00:00:00 2001 From: Masatoshi Kimura Date: Sat, 25 May 2013 08:41:19 +0900 Subject: [PATCH 73/91] Backed out changeset db13e1fbc642 (bug 691151) because some in-tree callers are still using the function --- content/base/public/nsDeprecatedOperationList.h | 1 - content/events/src/nsDOMEvent.cpp | 11 ----------- content/events/src/nsDOMEvent.h | 5 ++++- dom/locales/en-US/chrome/dom/dom.properties | 2 -- 4 files changed, 4 insertions(+), 15 deletions(-) diff --git a/content/base/public/nsDeprecatedOperationList.h b/content/base/public/nsDeprecatedOperationList.h index 92f361f5110..2d7f673fce5 100644 --- a/content/base/public/nsDeprecatedOperationList.h +++ b/content/base/public/nsDeprecatedOperationList.h @@ -30,4 +30,3 @@ DEPRECATED_OPERATION(PrefixedVisibilityAPI) DEPRECATED_OPERATION(NodeIteratorDetach) DEPRECATED_OPERATION(MozAudioData) DEPRECATED_OPERATION(LenientThis) -DEPRECATED_OPERATION(GetPreventDefault) diff --git a/content/events/src/nsDOMEvent.cpp b/content/events/src/nsDOMEvent.cpp index 1c819e3d2ca..4ffc32485f2 100644 --- a/content/events/src/nsDOMEvent.cpp +++ b/content/events/src/nsDOMEvent.cpp @@ -1220,17 +1220,6 @@ const char* nsDOMEvent::GetEventName(uint32_t aEventType) return nullptr; } -bool -nsDOMEvent::GetPreventDefault() const -{ - if (mOwner) { - if (nsIDocument* doc = mOwner->GetExtantDoc()) { - doc->WarnOnceAbout(nsIDocument::eGetPreventDefault); - } - } - return DefaultPrevented(); -} - NS_IMETHODIMP nsDOMEvent::GetPreventDefault(bool* aReturn) { diff --git a/content/events/src/nsDOMEvent.h b/content/events/src/nsDOMEvent.h index c525dfa0e17..26b51b26801 100644 --- a/content/events/src/nsDOMEvent.h +++ b/content/events/src/nsDOMEvent.h @@ -189,7 +189,10 @@ public: mozilla::dom::EventTarget* GetOriginalTarget() const; mozilla::dom::EventTarget* GetExplicitOriginalTarget() const; - bool GetPreventDefault() const; + bool GetPreventDefault() const + { + return DefaultPrevented(); + } protected: diff --git a/dom/locales/en-US/chrome/dom/dom.properties b/dom/locales/en-US/chrome/dom/dom.properties index 3409fe84a74..0329f36f51e 100644 --- a/dom/locales/en-US/chrome/dom/dom.properties +++ b/dom/locales/en-US/chrome/dom/dom.properties @@ -130,5 +130,3 @@ MozAudioDataWarning=The Mozilla Audio Data API is deprecated. Please use the We LenientThisWarning=Ignoring get or set of property that has [LenientThis] because the "this" object is incorrect. # LOCALIZATION NOTE: Do not translate "nsIDOMWindowUtils", "getWindowWithOuterId", or "nsIWindowMediator" GetWindowWithOuterIdWarning=Use of nsIDOMWindowUtils.getOuterWindowWithId() is deprecated. Instead, use the nsIWindowMediator method of the same name. -# LOCALIZATION NOTE: Do not translate "getPreventDefault" or "defaultPrevented". -GetPreventDefaultWarning=Use of getPreventDefault() is deprecated. Use defaultPrevented instead. From b4bb75b22e6f89c92bda15c6a561ae7c4809937b Mon Sep 17 00:00:00 2001 From: Shu-yu Guo Date: Fri, 24 May 2013 16:55:54 -0700 Subject: [PATCH 74/91] Bug 875742. (r=djvj) --- js/src/ion/IonBuilder.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/js/src/ion/IonBuilder.cpp b/js/src/ion/IonBuilder.cpp index fa2bfb1f5c5..cae32dcc242 100644 --- a/js/src/ion/IonBuilder.cpp +++ b/js/src/ion/IonBuilder.cpp @@ -6962,13 +6962,13 @@ IonBuilder::jsop_rest() // We know the exact number of arguments the callee pushed. unsigned numActuals = inlinedArguments_.length(); unsigned numFormals = info().nargs() - 1; - unsigned numRest = numActuals - numFormals; + unsigned numRest = numActuals > numFormals ? numActuals - numFormals : 0; JSObject *templateObject = getNewArrayTemplateObject(numRest); MNewArray *array = new MNewArray(numRest, templateObject, MNewArray::NewArray_Allocating); current->add(array); - if (numFormals >= numActuals) { + if (numActuals <= numFormals) { current->push(array); return true; } From 6c5ae9572f6f25f3fe3763451e3fb8f133e7e538 Mon Sep 17 00:00:00 2001 From: Shu-yu Guo Date: Fri, 24 May 2013 16:55:54 -0700 Subject: [PATCH 75/91] Bug 875748. (r=djvj) --- js/src/ion/IonBuilder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/ion/IonBuilder.cpp b/js/src/ion/IonBuilder.cpp index cae32dcc242..db6a622bf6f 100644 --- a/js/src/ion/IonBuilder.cpp +++ b/js/src/ion/IonBuilder.cpp @@ -6980,7 +6980,7 @@ IonBuilder::jsop_rest() // checking here. MConstant *index; for (unsigned i = numFormals; i < numActuals; i++) { - index = MConstant::New(Int32Value(i)); + index = MConstant::New(Int32Value(i - numFormals)); current->add(index); MStoreElement *store = MStoreElement::New(elements, index, inlinedArguments_[i], /* needsHoleCheck = */ false); From 1ae006d3bfc13f86508d1b95f40aa124ec76bada Mon Sep 17 00:00:00 2001 From: Eddy Bruel Date: Fri, 24 May 2013 17:00:08 -0700 Subject: [PATCH 76/91] Bug 637572: Implement Debugger.Source.prototype.text; r=jimb --- .../jit-test/tests/debug/Source-surfaces.js | 13 ++++ js/src/jit-test/tests/debug/Source-text-01.js | 25 ++++++++ js/src/jit-test/tests/debug/Source-text-02.js | 18 ++++++ js/src/vm/Debugger.cpp | 59 ++++++++++++++++++- 4 files changed, 113 insertions(+), 2 deletions(-) create mode 100644 js/src/jit-test/tests/debug/Source-surfaces.js create mode 100644 js/src/jit-test/tests/debug/Source-text-01.js create mode 100644 js/src/jit-test/tests/debug/Source-text-02.js diff --git a/js/src/jit-test/tests/debug/Source-surfaces.js b/js/src/jit-test/tests/debug/Source-surfaces.js new file mode 100644 index 00000000000..8683d916a8d --- /dev/null +++ b/js/src/jit-test/tests/debug/Source-surfaces.js @@ -0,0 +1,13 @@ +// Debugger.Source.prototype + +load(libdir + 'asserts.js'); + +assertThrowsInstanceOf(function () { + Debugger.Source.prototype.text.call(42) +}, TypeError); +assertThrowsInstanceOf(function () { + Debugger.Source.prototype.text.call({}) +}, TypeError); +assertThrowsInstanceOf(function () { + Debugger.Source.prototype.text.call(Debugger.Source.prototype) +}, TypeError); diff --git a/js/src/jit-test/tests/debug/Source-text-01.js b/js/src/jit-test/tests/debug/Source-text-01.js new file mode 100644 index 00000000000..ce37d9dd376 --- /dev/null +++ b/js/src/jit-test/tests/debug/Source-text-01.js @@ -0,0 +1,25 @@ +/* + * Script.prototype.source should be a string. Moreover, it should be the + * same string for each child script within the same debugger. + */ +let g = newGlobal('new-compartment'); +let dbg = new Debugger(g); + +var count = 0; +dbg.onNewScript = function (script) { + var text = script.source.text; + assertEq(typeof text, "string"); + function traverse(script) { + ++count; + script.getChildScripts().forEach(function (script) { + assertEq(script.source.text, text); + traverse(script); + }); + }; + traverse(script); +} + +g.eval("2 * 3"); +g.eval("function f() {}"); +g.eval("function f() { function g() {} }"); +assertEq(count, 6); diff --git a/js/src/jit-test/tests/debug/Source-text-02.js b/js/src/jit-test/tests/debug/Source-text-02.js new file mode 100644 index 00000000000..86a4342863d --- /dev/null +++ b/js/src/jit-test/tests/debug/Source-text-02.js @@ -0,0 +1,18 @@ +// Source.prototype.text should be a string +let g = newGlobal('new-compartment'); +let dbg = new Debugger(g); + +var count = 0; +dbg.onNewScript = function (script) { + ++count; + if (count % 2 == 0) + assertEq(script.source.text, text); +} + +g.eval("eval('" + (text = "") + "')"); +g.eval("eval('" + (text = "2 * 3") + "')"); +g.eval("new Function('" + (text = "") + "')"); +g.eval("new Function('" + (text = "2 * 3") + "')"); +evaluate("", { global: g }); +evaluate("2 * 3", { global: g }); +assertEq(count, 10); diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp index ed44b67ceed..d75787361ad 100644 --- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -3506,6 +3506,13 @@ static const JSFunctionSpec DebuggerScript_methods[] = { /*** Debugger.Source *****************************************************************************/ +static inline ScriptSourceObject * +GetSourceReferent(JSObject *obj) +{ + JS_ASSERT(obj->getClass() == &DebuggerSource_class); + return static_cast(obj->getPrivate()); +} + static void DebuggerSource_trace(JSTracer *trc, JSObject *obj) { @@ -3513,7 +3520,7 @@ DebuggerSource_trace(JSTracer *trc, JSObject *obj) * There is a barrier on private pointers, so the Unbarriered marking * is okay. */ - if (JSObject *referent = (JSObject *) obj->getPrivate()) { + if (JSObject *referent = GetSourceReferent(obj)) { MarkCrossCompartmentObjectUnbarriered(trc, obj, &referent, "Debugger.Source referent"); obj->setPrivateUnbarriered(referent); } @@ -3573,7 +3580,7 @@ Debugger::wrapSource(JSContext *cx, JS::HandleScriptSource source) } } - JS_ASSERT((JSObject *) p->value->getPrivate() == source); + JS_ASSERT(GetSourceReferent(p->value) == source); return p->value; } @@ -3584,7 +3591,55 @@ DebuggerSource_construct(JSContext *cx, unsigned argc, Value *vp) return false; } +static JSObject * +DebuggerSource_checkThis(JSContext *cx, const CallArgs &args, const char *fnname) +{ + if (!args.thisv().isObject()) { + ReportObjectRequired(cx); + return NULL; + } + + JSObject *thisobj = &args.thisv().toObject(); + if (thisobj->getClass() != &DebuggerSource_class) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INCOMPATIBLE_PROTO, + "Debugger.Source", fnname, thisobj->getClass()->name); + return NULL; + } + + if (!GetSourceReferent(thisobj)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INCOMPATIBLE_PROTO, + "Debugger.Frame", fnname, "prototype object"); + return NULL; + } + + return thisobj; +} + +#define THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, fnname, args, obj, sourceObject) \ + CallArgs args = CallArgsFromVp(argc, vp); \ + RootedObject obj(cx, DebuggerSource_checkThis(cx, args, fnname)); \ + if (!obj) \ + return false; \ + JS::RootedScriptSource sourceObject(cx, GetSourceReferent(obj)); \ + if (!sourceObject) \ + return false; + +static JSBool +DebuggerSource_getText(JSContext *cx, unsigned argc, Value *vp) +{ + THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, "(get text)", args, obj, sourceObject); + + ScriptSource *ss = sourceObject->source(); + JSString *str = ss->substring(cx, 0, ss->length()); + if (!str) + return false; + + args.rval().setString(str); + return true; +} + static const JSPropertySpec DebuggerSource_properties[] = { + JS_PSG("text", DebuggerSource_getText, 0), JS_PS_END }; From b5eb4cb307a126fd31c2e9aaf68a0e487d7b87c8 Mon Sep 17 00:00:00 2001 From: Joey Armstrong Date: Fri, 24 May 2013 17:21:14 -0700 Subject: [PATCH 77/91] bug 870406: move to moz.build (config batch #1) r=mshal --- intl/uconv/tools/Makefile.in | 4 +-- intl/uconv/tools/moz.build | 3 +++ modules/libmar/tool/Makefile.in | 2 +- modules/libmar/tool/moz.build | 6 +++++ netwerk/dns/Makefile.in | 2 +- netwerk/dns/moz.build | 5 ++++ netwerk/sctp/src/Makefile.in | 2 +- netwerk/sctp/src/moz.build | 27 +++++++++++++++++++ netwerk/srtp/src/Makefile.in | 2 +- netwerk/srtp/src/moz.build | 26 ++++++++++++++++++ .../google-breakpad/src/common/Makefile.in | 2 +- .../google-breakpad/src/common/moz.build | 3 +++ tools/codesighs/Makefile.in | 6 ++--- tools/codesighs/moz.build | 14 ++++++++++ 14 files changed, 94 insertions(+), 10 deletions(-) diff --git a/intl/uconv/tools/Makefile.in b/intl/uconv/tools/Makefile.in index 2afda789c23..a3d6a134c37 100644 --- a/intl/uconv/tools/Makefile.in +++ b/intl/uconv/tools/Makefile.in @@ -10,11 +10,11 @@ VPATH = @srcdir@ include $(DEPTH)/config/autoconf.mk -CSRCS = \ +DISABLED_CSRCS = \ umaptable.c \ $(NULL) -SIMPLE_PROGRAMS = $(CSRCS:.c=$(BIN_SUFFIX)) +SIMPLE_PROGRAMS = $(DISABLED_CSRCS:.c=$(BIN_SUFFIX)) include $(topsrcdir)/config/rules.mk diff --git a/intl/uconv/tools/moz.build b/intl/uconv/tools/moz.build index 895d11993cf..2009d5f4555 100644 --- a/intl/uconv/tools/moz.build +++ b/intl/uconv/tools/moz.build @@ -4,3 +4,6 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. +CSRCS += [ + 'umaptable.c', +] diff --git a/modules/libmar/tool/Makefile.in b/modules/libmar/tool/Makefile.in index 03aa096ddc3..af8df0080dc 100644 --- a/modules/libmar/tool/Makefile.in +++ b/modules/libmar/tool/Makefile.in @@ -43,7 +43,7 @@ HOST_CFLAGS += \ HOST_CSRCS = \ mar.c \ $(NULL) -CSRCS = $(HOST_CSRCS) +DISABLED_CSRCS = $(HOST_CSRCS) HOST_LIBS = $(DIST)/host/lib/$(LIB_PREFIX)hostmar.$(LIB_SUFFIX) LIBS = $(DEPTH)/modules/libmar/src/$(LIB_PREFIX)mar.$(LIB_SUFFIX) diff --git a/modules/libmar/tool/moz.build b/modules/libmar/tool/moz.build index ab0bd2f8f31..be56ae70cdf 100644 --- a/modules/libmar/tool/moz.build +++ b/modules/libmar/tool/moz.build @@ -8,3 +8,9 @@ MODULE = 'mar' if CONFIG['MOZ_ENABLE_SIGNMAR']: PROGRAM = 'signmar' + +# bug 875549: Temporarly break dep on HOST_CSRCS= so CSRCS can convert. +host_csrcs = [ + 'mar.c', +] +CSRCS += host_csrcs diff --git a/netwerk/dns/Makefile.in b/netwerk/dns/Makefile.in index d544c75f1a2..bd18d50d618 100644 --- a/netwerk/dns/Makefile.in +++ b/netwerk/dns/Makefile.in @@ -23,7 +23,7 @@ CPPSRCS = \ nsEffectiveTLDService.cpp \ $(NULL) -CSRCS = \ +DISABLED_CSRCS = \ race.c \ nameprep.c \ punycode.c \ diff --git a/netwerk/dns/moz.build b/netwerk/dns/moz.build index 1e6f97b2f95..c9ea457d52e 100644 --- a/netwerk/dns/moz.build +++ b/netwerk/dns/moz.build @@ -21,3 +21,8 @@ EXPORTS.mozilla.net += [ 'DNS.h', ] +CSRCS += [ + 'nameprep.c', + 'punycode.c', + 'race.c', +] diff --git a/netwerk/sctp/src/Makefile.in b/netwerk/sctp/src/Makefile.in index eb8660007b8..140b5c7913e 100644 --- a/netwerk/sctp/src/Makefile.in +++ b/netwerk/sctp/src/Makefile.in @@ -25,7 +25,7 @@ CPPSRCS = \ $(NULL) endif -CSRCS = \ +DISABLED_CSRCS = \ user_environment.c \ user_mbuf.c \ user_recv_thread.c \ diff --git a/netwerk/sctp/src/moz.build b/netwerk/sctp/src/moz.build index d9ba7adbea3..44ade75917e 100644 --- a/netwerk/sctp/src/moz.build +++ b/netwerk/sctp/src/moz.build @@ -12,3 +12,30 @@ EXPORTS.mozilla.net += [ 'usrsctp.h', ] +CSRCS += [ + 'sctp6_usrreq.c', + 'sctp_asconf.c', + 'sctp_auth.c', + 'sctp_bsd_addr.c', + 'sctp_callout.c', + 'sctp_cc_functions.c', + 'sctp_crc32.c', + 'sctp_hashdriver.c', + 'sctp_indata.c', + 'sctp_input.c', + 'sctp_output.c', + 'sctp_pcb.c', + 'sctp_peeloff.c', + 'sctp_sha1.c', + 'sctp_ss_functions.c', + 'sctp_sysctl.c', + 'sctp_timer.c', + 'sctp_userspace.c', + 'sctp_usrreq.c', + 'sctputil.c', + 'user_environment.c', + 'user_mbuf.c', + 'user_recv_thread.c', + 'user_sctp_timer_iterate.c', + 'user_socket.c', +] diff --git a/netwerk/srtp/src/Makefile.in b/netwerk/srtp/src/Makefile.in index 36ae4c835aa..b72b774e046 100644 --- a/netwerk/srtp/src/Makefile.in +++ b/netwerk/srtp/src/Makefile.in @@ -24,7 +24,7 @@ LIBRARY_NAME = nksrtp_s LIBXUL_LIBRARY = 1 FORCE_STATIC_LIB = 1 -CSRCS := \ +DISABLED_CSRCS := \ ekt.c \ srtp.c \ aes.c \ diff --git a/netwerk/srtp/src/moz.build b/netwerk/srtp/src/moz.build index 6d85fe42b5d..e3062f9c670 100644 --- a/netwerk/srtp/src/moz.build +++ b/netwerk/srtp/src/moz.build @@ -8,3 +8,29 @@ XPIDL_MODULE = 'necko_srtp' MODULE = 'necko' +CSRCS += [ + 'aes.c', + 'aes_cbc.c', + 'aes_icm.c', + 'alloc.c', + 'auth.c', + 'cipher.c', + 'crypto_kernel.c', + 'ctr_prng.c', + 'datatypes.c', + 'ekt.c', + 'err.c', + 'gf2_8.c', + 'hmac.c', + 'key.c', + 'null_auth.c', + 'null_cipher.c', + 'prng.c', + 'rand_source.c', + 'rdb.c', + 'rdbx.c', + 'sha1.c', + 'srtp.c', + 'stat.c', + 'ut_sim.c', +] diff --git a/toolkit/crashreporter/google-breakpad/src/common/Makefile.in b/toolkit/crashreporter/google-breakpad/src/common/Makefile.in index 579272e39a5..adb9030c31a 100644 --- a/toolkit/crashreporter/google-breakpad/src/common/Makefile.in +++ b/toolkit/crashreporter/google-breakpad/src/common/Makefile.in @@ -67,7 +67,7 @@ CMMSRCS += \ mac/dump_syms.mm endif -CSRCS = \ +DISABLED_CSRCS = \ convert_UTF.c \ $(NULL) diff --git a/toolkit/crashreporter/google-breakpad/src/common/moz.build b/toolkit/crashreporter/google-breakpad/src/common/moz.build index 07f1f7c2048..23d05ba0671 100644 --- a/toolkit/crashreporter/google-breakpad/src/common/moz.build +++ b/toolkit/crashreporter/google-breakpad/src/common/moz.build @@ -9,3 +9,6 @@ if CONFIG['OS_ARCH'] in ('Darwin', 'Linux'): MODULE = 'breakpad_common' +CSRCS += [ + 'convert_UTF.c', +] diff --git a/tools/codesighs/Makefile.in b/tools/codesighs/Makefile.in index 66dd3bfe7fd..ba7128e66d4 100644 --- a/tools/codesighs/Makefile.in +++ b/tools/codesighs/Makefile.in @@ -11,18 +11,18 @@ VPATH = @srcdir@ include $(DEPTH)/config/autoconf.mk -CSRCS += \ +DISABLED_CSRCS += \ codesighs.c \ maptsvdifftool.c \ $(NULL) ifeq ($(OS_ARCH),WINNT) -CSRCS += \ +DISABLED_CSRCS += \ msmap2tsv.c \ msdump2symdb.c \ $(NULL) else -CSRCS += \ +DISABLED_CSRCS += \ nm2tsv.c \ $(NULL) endif diff --git a/tools/codesighs/moz.build b/tools/codesighs/moz.build index 895d11993cf..7eab91da2a7 100644 --- a/tools/codesighs/moz.build +++ b/tools/codesighs/moz.build @@ -4,3 +4,17 @@ # 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/. +CSRCS += [ + 'codesighs.c', + 'maptsvdifftool.c', +] + +if CONFIG['OS_ARCH'] == 'WINNT': + CSRCS += [ + 'msdump2symdb.c', + 'msmap2tsv.c', + ] +else: + CSRCS += [ + 'nm2tsv.c', + ] From 13c03fac440c52b20be3bc7bf0e204dacfa4b090 Mon Sep 17 00:00:00 2001 From: Daniel Holbert Date: Tue, 21 May 2013 12:15:57 -0400 Subject: [PATCH 78/91] Bug 874418: Assert that placeholders are reflowed before their out-of-flows. r=bz --- layout/generic/nsPlaceholderFrame.cpp | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/layout/generic/nsPlaceholderFrame.cpp b/layout/generic/nsPlaceholderFrame.cpp index 50c8ee2cda5..9deaca59130 100644 --- a/layout/generic/nsPlaceholderFrame.cpp +++ b/layout/generic/nsPlaceholderFrame.cpp @@ -97,6 +97,33 @@ nsPlaceholderFrame::Reflow(nsPresContext* aPresContext, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { +#ifdef DEBUG + // We should be getting reflowed before our out-of-flow. + // If this is our first reflow, and our out-of-flow has already received its + // first reflow (before us), complain. + if ((GetStateBits() & NS_FRAME_FIRST_REFLOW) && + !(mOutOfFlowFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW)) { + + // Unfortunately, this can currently happen when the placeholder is in a + // later continuation than its out-of-flow (as is the case in some unit + // tests). So for now, in that case, we'll warn instead of asserting. + bool isInContinuation = false; + nsIFrame* ancestor = this; + while ((ancestor = ancestor->GetParent())) { + if (ancestor->GetPrevContinuation()) { + isInContinuation = true; + break; + } + } + + if (isInContinuation) { + NS_WARNING("Out-of-flow frame got reflowed before its placeholder"); + } else { + NS_ERROR("Out-of-flow frame got reflowed before its placeholder"); + } + } +#endif + DO_GLOBAL_REFLOW_COUNT("nsPlaceholderFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); aDesiredSize.width = 0; From 4207b31a0e42721ec1d4a253289967fd22366139 Mon Sep 17 00:00:00 2001 From: Phil Ringnalda Date: Fri, 24 May 2013 19:00:38 -0700 Subject: [PATCH 79/91] Back out affd9b74be00 (bug 874418) for unexpected crashtest assertions CLOSED TREE --- layout/generic/nsPlaceholderFrame.cpp | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/layout/generic/nsPlaceholderFrame.cpp b/layout/generic/nsPlaceholderFrame.cpp index 9deaca59130..50c8ee2cda5 100644 --- a/layout/generic/nsPlaceholderFrame.cpp +++ b/layout/generic/nsPlaceholderFrame.cpp @@ -97,33 +97,6 @@ nsPlaceholderFrame::Reflow(nsPresContext* aPresContext, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { -#ifdef DEBUG - // We should be getting reflowed before our out-of-flow. - // If this is our first reflow, and our out-of-flow has already received its - // first reflow (before us), complain. - if ((GetStateBits() & NS_FRAME_FIRST_REFLOW) && - !(mOutOfFlowFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW)) { - - // Unfortunately, this can currently happen when the placeholder is in a - // later continuation than its out-of-flow (as is the case in some unit - // tests). So for now, in that case, we'll warn instead of asserting. - bool isInContinuation = false; - nsIFrame* ancestor = this; - while ((ancestor = ancestor->GetParent())) { - if (ancestor->GetPrevContinuation()) { - isInContinuation = true; - break; - } - } - - if (isInContinuation) { - NS_WARNING("Out-of-flow frame got reflowed before its placeholder"); - } else { - NS_ERROR("Out-of-flow frame got reflowed before its placeholder"); - } - } -#endif - DO_GLOBAL_REFLOW_COUNT("nsPlaceholderFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); aDesiredSize.width = 0; From e1dd3b207710ad3b63ff6fdda40139cf3bee5613 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Fri, 24 May 2013 22:38:09 -0400 Subject: [PATCH 80/91] Bug 875939. Make passing a Rooted or Handle to a function taking |const T&| not be a gc hazard. r=terrence --- js/public/RootingAPI.h | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/js/public/RootingAPI.h b/js/public/RootingAPI.h index 5437c0f642f..d9b89719d06 100644 --- a/js/public/RootingAPI.h +++ b/js/public/RootingAPI.h @@ -231,9 +231,13 @@ class MOZ_STACK_CLASS Handle : public js::HandleBase typename mozilla::EnableIf::value, int>::Type dummy = 0); const T *address() const { return ptr; } - T get() const { return *ptr; } + const T& get() const { return *ptr; } - operator T() const { return get(); } + /* + * Return a reference so passing a Handle to something that + * takes a |const T&| is not a GC hazard. + */ + operator const T&() const { return get(); } T operator->() const { return get(); } bool operator!=(const T &other) { return *ptr != other; } @@ -465,7 +469,11 @@ class MOZ_STACK_CLASS Rooted : public js::RootedBase Rooted *previous() { return prev; } #endif - operator T() const { return ptr; } + /* + * Important: Return a reference here so passing a Rooted to + * something that takes a |const T&| is not a GC hazard. + */ + operator const T&() const { return ptr; } T operator->() const { return ptr; } T *address() { return &ptr; } const T *address() const { return &ptr; } From 124b358485b6889f1af750f0d35892a96dbc09d4 Mon Sep 17 00:00:00 2001 From: Ethan Hugg Date: Tue, 21 May 2013 07:49:50 -0700 Subject: [PATCH 81/91] Bug 873888 - Wait for construction of tracks before returning onAddStream r=jesup --- .../signaling/src/media/VcmSIPCCBinding.cpp | 55 +++++++++++++++++++ .../src/peerconnection/PeerConnectionImpl.cpp | 35 ++++++++++++ .../peerconnection/PeerConnectionMedia.cpp | 19 +++++++ .../src/peerconnection/PeerConnectionMedia.h | 7 ++- .../signaling/src/sipcc/core/gsm/gsm_sdp.c | 22 ++++++++ .../webrtc/signaling/src/sipcc/include/vcm.h | 17 ++++++ .../webrtc/signaling/test/FakeMediaStreams.h | 7 ++- 7 files changed, 158 insertions(+), 4 deletions(-) diff --git a/media/webrtc/signaling/src/media/VcmSIPCCBinding.cpp b/media/webrtc/signaling/src/media/VcmSIPCCBinding.cpp index b9f5d9e812f..38970db61b8 100644 --- a/media/webrtc/signaling/src/media/VcmSIPCCBinding.cpp +++ b/media/webrtc/signaling/src/media/VcmSIPCCBinding.cpp @@ -966,6 +966,61 @@ short vcmCreateRemoteStream(cc_mcapid_t mcap_id, return ret; } +/* + * Add remote stream hint + * + * We are sending track information up to PeerConnection before + * the tracks exist so it knows when the stream is fully constructed. + * + * @param[in] peerconnection + * @param[in] pc_stream_id + * @param[in] is_video + * + * Returns: zero(0) for success; otherwise, ERROR for failure + */ +static short vcmAddRemoteStreamHint_m( + const char *peerconnection, + int pc_stream_id, + cc_boolean is_video) { + nsresult res; + + sipcc::PeerConnectionWrapper pc(peerconnection); + ENSURE_PC(pc, VCM_ERROR); + + res = pc.impl()->media()->AddRemoteStreamHint(pc_stream_id, + is_video ? TRUE : FALSE); + if (NS_FAILED(res)) { + return VCM_ERROR; + } + + CSFLogDebug( logTag, "%s: added remote stream hint %u with index %d", + __FUNCTION__, is_video, pc_stream_id); + + return 0; +} + +/* + * Add remote stream hint + * + * This is a thunk to vcmAddRemoteStreamHint_m + * + * Returns: zero(0) for success; otherwise, ERROR for failure + */ +short vcmAddRemoteStreamHint( + const char *peerconnection, + int pc_stream_id, + cc_boolean is_video) { + short ret = 0; + + mozilla::SyncRunnable::DispatchToThread(VcmSIPCCBinding::getMainThread(), + WrapRunnableNMRet(&vcmAddRemoteStreamHint_m, + peerconnection, + pc_stream_id, + is_video, + &ret)); + + return ret; +} /* * Get DTLS key data diff --git a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp index 4337f1d8db1..da25068e289 100644 --- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp @@ -48,6 +48,7 @@ #include "MediaStreamList.h" #include "nsIScriptGlobalObject.h" #include "jsapi.h" +#include "DOMMediaStream.h" #endif #ifndef USE_FAKE_MEDIA_STREAMS @@ -142,6 +143,33 @@ public: ~PeerConnectionObserverDispatch(){} +#ifdef MOZILLA_INTERNAL_API + class TracksAvailableCallback : public DOMMediaStream::OnTracksAvailableCallback + { + public: + TracksAvailableCallback(DOMMediaStream::TrackTypeHints aTrackTypeHints, + nsCOMPtr aObserver) + : DOMMediaStream::OnTracksAvailableCallback(aTrackTypeHints), + mObserver(aObserver) {} + + virtual void NotifyTracksAvailable(DOMMediaStream* aStream) MOZ_OVERRIDE + { + MOZ_ASSERT(NS_IsMainThread()); + + // Start currentTime from the point where this stream was successfully + // returned. + aStream->SetLogicalStreamStartTime(aStream->GetStream()->GetCurrentTime()); + + CSFLogInfo(logTag, "Returning success for OnAddStream()"); + // We are running on main thread here so we shouldn't have a race + // on this callback + mObserver->OnAddStream(aStream); + } + + nsCOMPtr mObserver; + }; +#endif + NS_IMETHOD Run() { CSFLogInfo(logTag, "PeerConnectionObserverDispatch processing " @@ -230,7 +258,14 @@ public: if (!stream) { CSFLogError(logTag, "%s: GetMediaStream returned NULL", __FUNCTION__); } else { +#ifdef MOZILLA_INTERNAL_API + TracksAvailableCallback* tracksAvailableCallback = + new TracksAvailableCallback(mRemoteStream->mTrackTypeHints, mObserver); + + stream->OnTracksAvailable(tracksAvailableCallback); +#else mObserver->OnAddStream(stream); +#endif } break; } diff --git a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp index 16930b6fd68..0016765fa1a 100644 --- a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp @@ -393,6 +393,25 @@ PeerConnectionMedia::AddRemoteStream(nsRefPtr aInfo, return NS_OK; } +nsresult +PeerConnectionMedia::AddRemoteStreamHint(int aIndex, bool aIsVideo) +{ + if (aIndex >= mRemoteSourceStreams.Length()) { + return NS_ERROR_ILLEGAL_VALUE; + } + + RemoteSourceStreamInfo *pInfo = mRemoteSourceStreams.ElementAt(aIndex); + MOZ_ASSERT(pInfo); + + if (aIsVideo) { + pInfo->mTrackTypeHints |= DOMMediaStream::HINT_CONTENTS_VIDEO; + } else { + pInfo->mTrackTypeHints |= DOMMediaStream::HINT_CONTENTS_AUDIO; + } + + return NS_OK; +} + void PeerConnectionMedia::IceGatheringCompleted(NrIceCtx *aCtx) diff --git a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h index cb8e2de726d..805ac087cbf 100644 --- a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h @@ -207,7 +207,8 @@ RemoteSourceStreamInfo(already_AddRefed aMediaStream, PeerConnectionMedia *aParent) : mMediaStream(aMediaStream), mPipelines(), - mParent(aParent) { + mParent(aParent), + mTrackTypeHints(0) { MOZ_ASSERT(mMediaStream); } @@ -221,6 +222,9 @@ RemoteSourceStreamInfo(already_AddRefed aMediaStream, void DetachMedia_m(); NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RemoteSourceStreamInfo) + +public: + DOMMediaStream::TrackTypeHints mTrackTypeHints; private: nsRefPtr mMediaStream; std::map > mPipelines; @@ -271,6 +275,7 @@ class PeerConnectionMedia : public sigslot::has_slots<> { // Add a remote stream. Returns the index in index nsresult AddRemoteStream(nsRefPtr aInfo, int *aIndex); + nsresult AddRemoteStreamHint(int aIndex, bool aIsVideo); const nsCOMPtr& GetMainThread() const { return mMainThread; } const nsCOMPtr& GetSTSThread() const { return mSTSThread; } diff --git a/media/webrtc/signaling/src/sipcc/core/gsm/gsm_sdp.c b/media/webrtc/signaling/src/sipcc/core/gsm/gsm_sdp.c index 7f32848b258..f133920f81c 100644 --- a/media/webrtc/signaling/src/sipcc/core/gsm/gsm_sdp.c +++ b/media/webrtc/signaling/src/sipcc/core/gsm/gsm_sdp.c @@ -25,6 +25,8 @@ #include "plstr.h" #include "sdp_private.h" +static const char* logTag = "gsm_sdp"; + //TODO Need to place this in a portable location #define MULTICAST_START_ADDRESS 0xe1000000 #define MULTICAST_END_ADDRESS 0xefffffff @@ -6717,6 +6719,8 @@ static boolean gsmsdp_add_remote_track(uint16_t idx, uint16_t track, fsmdef_dcb_t *dcb_p, fsmdef_media_t *media) { cc_media_remote_track_table_t *stream; + int vcm_ret; + PR_ASSERT(idx < CC_MAX_STREAMS); if (idx >= CC_MAX_STREAMS) return FALSE; @@ -6737,6 +6741,24 @@ static boolean gsmsdp_add_remote_track(uint16_t idx, uint16_t track, ++stream->num_tracks; + if (media->type == SDP_MEDIA_VIDEO) { + vcm_ret = vcmAddRemoteStreamHint(dcb_p->peerconnection, idx, TRUE); + } else if (media->type == SDP_MEDIA_AUDIO) { + vcm_ret = vcmAddRemoteStreamHint(dcb_p->peerconnection, idx, FALSE); + } else { + // No other track types should be valid here + MOZ_ASSERT(FALSE); + // Not setting a hint for this track type will simply cause the + // onaddstream callback not to wait for the track to be ready. + vcm_ret = 0; + } + + if (vcm_ret) { + CSFLogError(logTag, "%s: vcmAddRemoteStreamHint returned error: %d", + __FUNCTION__, vcm_ret); + return FALSE; + } + return TRUE; } diff --git a/media/webrtc/signaling/src/sipcc/include/vcm.h b/media/webrtc/signaling/src/sipcc/include/vcm.h index 8c4ae59c48d..79cf8038b12 100755 --- a/media/webrtc/signaling/src/sipcc/include/vcm.h +++ b/media/webrtc/signaling/src/sipcc/include/vcm.h @@ -500,6 +500,23 @@ short vcmCreateRemoteStream( const char *peerconnection, int *pc_stream_id); +/* + * Add remote stream hint + * + * We are sending track information up to PeerConnection before + * the tracks exist so it knows when the stream is fully constructed. + * + * @param[in] peerconnection + * @param[in] pc_stream_id + * @param[in] is_video + * + * Returns: zero(0) for success; otherwise, ERROR for failure + */ +short vcmAddRemoteStreamHint( + const char *peerconnection, + int pc_stream_id, + cc_boolean is_video); + /*! * Release the allocated port * @param[in] mcap_id - media capability id (0 is audio) diff --git a/media/webrtc/signaling/test/FakeMediaStreams.h b/media/webrtc/signaling/test/FakeMediaStreams.h index 8f1ed8b6292..7ecf9726ba5 100644 --- a/media/webrtc/signaling/test/FakeMediaStreams.h +++ b/media/webrtc/signaling/test/FakeMediaStreams.h @@ -129,7 +129,7 @@ class Fake_SourceMediaStream : public Fake_MediaStream { for(int i=0; i Date: Sat, 25 May 2013 08:59:48 +0100 Subject: [PATCH 82/91] Bug 869156 - Show dynamic toolbar is visible if page shrinks. r=kats --- mobile/android/base/BrowserApp.java | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/mobile/android/base/BrowserApp.java b/mobile/android/base/BrowserApp.java index b07b143d093..b890d0203fd 100644 --- a/mobile/android/base/BrowserApp.java +++ b/mobile/android/base/BrowserApp.java @@ -128,6 +128,10 @@ abstract public class BrowserApp extends GeckoApp // Stored value of the toolbar height, so we know when it's changed. private int mToolbarHeight = 0; + // Stored value of whether the last metrics change allowed for toolbar + // scrolling. + private boolean mDynamicToolbarCanScroll = false; + private Integer mPrefObserverId; // Tag for the AboutHome fragment. The fragment is automatically attached @@ -712,6 +716,23 @@ abstract public class BrowserApp extends GeckoApp return; } + // If the page has shrunk so that the toolbar no longer scrolls, make + // sure the toolbar is visible. + if (aMetrics.getPageHeight() < aMetrics.getHeight()) { + if (mDynamicToolbarCanScroll) { + mDynamicToolbarCanScroll = false; + if (!mBrowserToolbar.isVisible()) { + ThreadUtils.postToUiThread(new Runnable() { + public void run() { + mLayerView.getLayerMarginsAnimator().showMargins(false); + } + }); + } + } + } else { + mDynamicToolbarCanScroll = true; + } + final View toolbarLayout = mBrowserToolbar.getLayout(); final int marginTop = Math.round(aMetrics.marginTop); ThreadUtils.postToUiThread(new Runnable() { @@ -727,8 +748,12 @@ abstract public class BrowserApp extends GeckoApp return; } + // Make sure the toolbar is fully hidden or fully shown when the user + // lifts their finger. If the page is shorter than the viewport, the + // toolbar is always shown. ImmutableViewportMetrics metrics = mLayerView.getViewportMetrics(); - if (metrics.marginTop >= mToolbarHeight / 2) { + if (metrics.getPageHeight() < metrics.getHeight() + || metrics.marginTop >= mToolbarHeight / 2) { mLayerView.getLayerMarginsAnimator().showMargins(false); } else { mLayerView.getLayerMarginsAnimator().hideMargins(false); From c44e28a47c1b4622252b86172369e29488efddfe Mon Sep 17 00:00:00 2001 From: Jonathan Watt Date: Fri, 24 May 2013 18:31:33 +0100 Subject: [PATCH 83/91] Bug 875037 - Fix lots of bugs in nsStyleSVG/nsSVGStyleReset::CalcDifference so that they return the correct hints. r=roc --- layout/style/nsStyleStruct.cpp | 82 +++++++++++++++++++++++----------- layout/style/nsStyleStruct.h | 2 +- 2 files changed, 58 insertions(+), 26 deletions(-) diff --git a/layout/style/nsStyleStruct.cpp b/layout/style/nsStyleStruct.cpp index b9eef3d2107..1614f9b020d 100644 --- a/layout/style/nsStyleStruct.cpp +++ b/layout/style/nsStyleStruct.cpp @@ -900,17 +900,16 @@ nsChangeHint nsStyleSVG::CalcDifference(const nsStyleSVG& aOther) const { nsChangeHint hint = nsChangeHint(0); - if (mTextRendering != aOther.mTextRendering) { - NS_UpdateHint(hint, nsChangeHint_RepaintFrame); - // May be needed for non-svg frames - NS_UpdateHint(hint, nsChangeHint_AllReflowHints); - } - if (!EqualURIs(mMarkerEnd, aOther.mMarkerEnd) || !EqualURIs(mMarkerMid, aOther.mMarkerMid) || !EqualURIs(mMarkerStart, aOther.mMarkerStart)) { - NS_UpdateHint(hint, nsChangeHint_RepaintFrame); + // Markers currently contribute to nsSVGPathGeometryFrame::mRect, + // so we need a reflow as well as a repaint. No intrinsic sizes need + // to change, so nsChangeHint_NeedReflow is sufficient. NS_UpdateHint(hint, nsChangeHint_UpdateEffects); + NS_UpdateHint(hint, nsChangeHint_NeedReflow); + NS_UpdateHint(hint, nsChangeHint_NeedDirtyReflow); // XXX remove me: bug 876085 + NS_UpdateHint(hint, nsChangeHint_RepaintFrame); return hint; } @@ -921,17 +920,33 @@ nsChangeHint nsStyleSVG::CalcDifference(const nsStyleSVG& aOther) const PaintURIChanged(mStroke, aOther.mStroke)) { NS_UpdateHint(hint, nsChangeHint_UpdateEffects); } - // Nothing more to do, below we can only set "repaint" + } + + // Stroke currently contributes to nsSVGPathGeometryFrame::mRect, so + // we need a reflow here. No intrinsic sizes need to change, so + // nsChangeHint_NeedReflow is sufficient. + // Note that stroke-dashoffset does not affect nsSVGPathGeometryFrame::mRect. + // text-anchor and text-rendering changes also require a reflow since they + // change frames' rects. + if (mStrokeWidth != aOther.mStrokeWidth || + mStrokeMiterlimit != aOther.mStrokeMiterlimit || + mStrokeLinecap != aOther.mStrokeLinecap || + mStrokeLinejoin != aOther.mStrokeLinejoin || + mTextAnchor != aOther.mTextAnchor || + mTextRendering != aOther.mTextRendering) { + NS_UpdateHint(hint, nsChangeHint_NeedReflow); + NS_UpdateHint(hint, nsChangeHint_NeedDirtyReflow); // XXX remove me: bug 876085 + NS_UpdateHint(hint, nsChangeHint_RepaintFrame); return hint; } + if (hint & nsChangeHint_RepaintFrame) { + return hint; // we don't add anything else below + } + if ( mStrokeDashoffset != aOther.mStrokeDashoffset || - mStrokeWidth != aOther.mStrokeWidth || - mFillOpacity != aOther.mFillOpacity || - mStrokeMiterlimit != aOther.mStrokeMiterlimit || mStrokeOpacity != aOther.mStrokeOpacity || - mClipRule != aOther.mClipRule || mColorInterpolation != aOther.mColorInterpolation || mColorInterpolationFilters != aOther.mColorInterpolationFilters || @@ -940,9 +955,6 @@ nsChangeHint nsStyleSVG::CalcDifference(const nsStyleSVG& aOther) const mPaintOrder != aOther.mPaintOrder || mShapeRendering != aOther.mShapeRendering || mStrokeDasharrayLength != aOther.mStrokeDasharrayLength || - mStrokeLinecap != aOther.mStrokeLinecap || - mStrokeLinejoin != aOther.mStrokeLinejoin || - mTextAnchor != aOther.mTextAnchor || mFillOpacitySource != aOther.mFillOpacitySource || mStrokeOpacitySource != aOther.mStrokeOpacitySource || mStrokeDasharrayFromObject != aOther.mStrokeDasharrayFromObject || @@ -1010,18 +1022,29 @@ nsChangeHint nsStyleSVGReset::CalcDifference(const nsStyleSVGReset& aOther) cons !EqualURIs(mFilter, aOther.mFilter) || !EqualURIs(mMask, aOther.mMask)) { NS_UpdateHint(hint, nsChangeHint_UpdateEffects); - NS_UpdateHint(hint, nsChangeHint_AllReflowHints); NS_UpdateHint(hint, nsChangeHint_RepaintFrame); - } else if (mDominantBaseline != aOther.mDominantBaseline) { + } + + if (mDominantBaseline != aOther.mDominantBaseline) { + // XXXjwatt: why NS_STYLE_HINT_REFLOW? Isn't that excessive? NS_UpdateHint(hint, NS_STYLE_HINT_REFLOW); + } else if (mVectorEffect != aOther.mVectorEffect) { + // Stroke currently affects nsSVGPathGeometryFrame::mRect, and + // vector-effect affect stroke. As a result we need to reflow if + // vector-effect changes in order to have nsSVGPathGeometryFrame:: + // ReflowSVG called to update its mRect. No intrinsic sizes need + // to change so nsChangeHint_NeedReflow is sufficient. + NS_UpdateHint(hint, nsChangeHint_NeedReflow); + NS_UpdateHint(hint, nsChangeHint_NeedDirtyReflow); // XXX remove me: bug 876085 + NS_UpdateHint(hint, nsChangeHint_RepaintFrame); } else if (mStopColor != aOther.mStopColor || mFloodColor != aOther.mFloodColor || mLightingColor != aOther.mLightingColor || mStopOpacity != aOther.mStopOpacity || mFloodOpacity != aOther.mFloodOpacity || - mVectorEffect != aOther.mVectorEffect || - mMaskType != aOther.mMaskType) + mMaskType != aOther.mMaskType) { NS_UpdateHint(hint, nsChangeHint_RepaintFrame); + } return hint; } @@ -2296,12 +2319,21 @@ nsChangeHint nsStyleVisibility::CalcDifference(const nsStyleVisibility& aOther) if (mDirection != aOther.mDirection) { NS_UpdateHint(hint, nsChangeHint_ReconstructFrame); - } else if (mVisible != aOther.mVisible) { - if ((NS_STYLE_VISIBILITY_COLLAPSE == mVisible) || - (NS_STYLE_VISIBILITY_COLLAPSE == aOther.mVisible)) { - NS_UpdateHint(hint, NS_STYLE_HINT_REFLOW); - } else { - NS_UpdateHint(hint, NS_STYLE_HINT_VISUAL); + } else { + if (mVisible != aOther.mVisible) { + if ((NS_STYLE_VISIBILITY_COLLAPSE == mVisible) || + (NS_STYLE_VISIBILITY_COLLAPSE == aOther.mVisible)) { + NS_UpdateHint(hint, NS_STYLE_HINT_REFLOW); + } else { + NS_UpdateHint(hint, NS_STYLE_HINT_VISUAL); + } + } + if (mPointerEvents != aOther.mPointerEvents) { + // nsSVGPathGeometryFrame's mRect depends on stroke _and_ on the value + // of pointer-events. See nsSVGPathGeometryFrame::ReflowSVG's use of + // GetHitTestFlags. (Only a reflow, no visual change.) + NS_UpdateHint(hint, nsChangeHint_NeedReflow); + NS_UpdateHint(hint, nsChangeHint_NeedDirtyReflow); // XXX remove me: bug 876085 } } return hint; diff --git a/layout/style/nsStyleStruct.h b/layout/style/nsStyleStruct.h index feb26725561..e94d867270d 100644 --- a/layout/style/nsStyleStruct.h +++ b/layout/style/nsStyleStruct.h @@ -2202,7 +2202,7 @@ struct nsStyleSVG { nsChangeHint CalcDifference(const nsStyleSVG& aOther) const; static nsChangeHint MaxDifference() { return NS_CombineHint(NS_CombineHint(nsChangeHint_UpdateEffects, - nsChangeHint_AllReflowHints), + NS_CombineHint(nsChangeHint_NeedReflow, nsChangeHint_NeedDirtyReflow)), // XXX remove nsChangeHint_NeedDirtyReflow: bug 876085 nsChangeHint_RepaintFrame); } From 25717c7a0fdb59b3bffad56c16ed849c3a1e37e4 Mon Sep 17 00:00:00 2001 From: Jonathan Watt Date: Fri, 24 May 2013 18:32:33 +0100 Subject: [PATCH 84/91] Bug 854765 - Get rid of the DidSetStyleContext overrides in SVG frame code to avoid some major perf hits. r=roc --- layout/svg/SVGFEContainerFrame.cpp | 9 --------- layout/svg/SVGFEImageFrame.cpp | 9 --------- layout/svg/SVGFELeafFrame.cpp | 9 --------- layout/svg/nsSVGForeignObjectFrame.cpp | 17 ----------------- layout/svg/nsSVGForeignObjectFrame.h | 2 -- layout/svg/nsSVGGradientFrame.cpp | 7 ------- layout/svg/nsSVGGradientFrame.h | 2 -- layout/svg/nsSVGMaskFrame.cpp | 7 ------- layout/svg/nsSVGMaskFrame.h | 2 -- layout/svg/nsSVGPathGeometryFrame.cpp | 15 --------------- layout/svg/nsSVGPathGeometryFrame.h | 2 -- layout/svg/nsSVGPatternFrame.cpp | 7 ------- layout/svg/nsSVGPatternFrame.h | 2 -- layout/svg/nsSVGStopFrame.cpp | 9 --------- 14 files changed, 99 deletions(-) diff --git a/layout/svg/SVGFEContainerFrame.cpp b/layout/svg/SVGFEContainerFrame.cpp index 512d1d25c96..3e866622e3d 100644 --- a/layout/svg/SVGFEContainerFrame.cpp +++ b/layout/svg/SVGFEContainerFrame.cpp @@ -44,8 +44,6 @@ public: } #endif - virtual void DidSetStyleContext(nsStyleContext* aOldStyleContext); - #ifdef DEBUG virtual void Init(nsIContent* aContent, nsIFrame* aParent, @@ -76,13 +74,6 @@ NS_NewSVGFEContainerFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) NS_IMPL_FRAMEARENA_HELPERS(SVGFEContainerFrame) -/* virtual */ void -SVGFEContainerFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext) -{ - SVGFEContainerFrameBase::DidSetStyleContext(aOldStyleContext); - nsSVGEffects::InvalidateRenderingObservers(this); -} - #ifdef DEBUG void SVGFEContainerFrame::Init(nsIContent* aContent, diff --git a/layout/svg/SVGFEImageFrame.cpp b/layout/svg/SVGFEImageFrame.cpp index 8f89ee8c619..dd769ff9d7c 100644 --- a/layout/svg/SVGFEImageFrame.cpp +++ b/layout/svg/SVGFEImageFrame.cpp @@ -48,8 +48,6 @@ public: } #endif - virtual void DidSetStyleContext(nsStyleContext* aOldStyleContext); - /** * Get the "type" of the frame * @@ -75,13 +73,6 @@ NS_NewSVGFEImageFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) NS_IMPL_FRAMEARENA_HELPERS(SVGFEImageFrame) -/* virtual */ void -SVGFEImageFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext) -{ - SVGFEImageFrameBase::DidSetStyleContext(aOldStyleContext); - nsSVGEffects::InvalidateRenderingObservers(this); -} - /* virtual */ void SVGFEImageFrame::DestroyFrom(nsIFrame* aDestructRoot) { diff --git a/layout/svg/SVGFELeafFrame.cpp b/layout/svg/SVGFELeafFrame.cpp index f93fb946084..4f578f27dd6 100644 --- a/layout/svg/SVGFELeafFrame.cpp +++ b/layout/svg/SVGFELeafFrame.cpp @@ -47,8 +47,6 @@ public: } #endif - virtual void DidSetStyleContext(nsStyleContext* aOldStyleContext); - /** * Get the "type" of the frame * @@ -74,13 +72,6 @@ NS_NewSVGFELeafFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) NS_IMPL_FRAMEARENA_HELPERS(SVGFELeafFrame) -/* virtual */ void -SVGFELeafFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext) -{ - SVGFELeafFrameBase::DidSetStyleContext(aOldStyleContext); - nsSVGEffects::InvalidateRenderingObservers(this); -} - #ifdef DEBUG void SVGFELeafFrame::Init(nsIContent* aContent, diff --git a/layout/svg/nsSVGForeignObjectFrame.cpp b/layout/svg/nsSVGForeignObjectFrame.cpp index 98688874657..c9ac187acb9 100644 --- a/layout/svg/nsSVGForeignObjectFrame.cpp +++ b/layout/svg/nsSVGForeignObjectFrame.cpp @@ -118,23 +118,6 @@ nsSVGForeignObjectFrame::AttributeChanged(int32_t aNameSpaceID, return NS_OK; } -/* virtual */ void -nsSVGForeignObjectFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext) -{ - nsSVGForeignObjectFrameBase::DidSetStyleContext(aOldStyleContext); - - // No need to invalidate before first reflow - that will happen elsewhere. - // Moreover we haven't been initialised properly yet so we may not have the - // right state bits. - if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW)) { - // XXXperf: probably only need a bounds update if 'font-size' changed and - // we have em unit width/height. Or, once we map 'transform' into style, - // if some transform property changed. - nsSVGEffects::InvalidateRenderingObservers(this); - nsSVGUtils::ScheduleReflowSVG(this); - } -} - NS_IMETHODIMP nsSVGForeignObjectFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, diff --git a/layout/svg/nsSVGForeignObjectFrame.h b/layout/svg/nsSVGForeignObjectFrame.h index c460e887248..04696cfd22b 100644 --- a/layout/svg/nsSVGForeignObjectFrame.h +++ b/layout/svg/nsSVGForeignObjectFrame.h @@ -39,8 +39,6 @@ public: nsIAtom* aAttribute, int32_t aModType); - virtual void DidSetStyleContext(nsStyleContext* aOldStyleContext) MOZ_OVERRIDE; - virtual nsIFrame* GetContentInsertionFrame() { return GetFirstPrincipalChild()->GetContentInsertionFrame(); } diff --git a/layout/svg/nsSVGGradientFrame.cpp b/layout/svg/nsSVGGradientFrame.cpp index be6fea879e9..c89fd4acdf3 100644 --- a/layout/svg/nsSVGGradientFrame.cpp +++ b/layout/svg/nsSVGGradientFrame.cpp @@ -57,13 +57,6 @@ NS_IMPL_FRAMEARENA_HELPERS(nsSVGGradientFrame) //---------------------------------------------------------------------- // nsIFrame methods: -/* virtual */ void -nsSVGGradientFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext) -{ - nsSVGEffects::InvalidateDirectRenderingObservers(this); - nsSVGGradientFrameBase::DidSetStyleContext(aOldStyleContext); -} - NS_IMETHODIMP nsSVGGradientFrame::AttributeChanged(int32_t aNameSpaceID, nsIAtom* aAttribute, diff --git a/layout/svg/nsSVGGradientFrame.h b/layout/svg/nsSVGGradientFrame.h index a31d9adcbba..203d7fd1d6c 100644 --- a/layout/svg/nsSVGGradientFrame.h +++ b/layout/svg/nsSVGGradientFrame.h @@ -53,8 +53,6 @@ public: const gfxRect *aOverrideBounds); // nsIFrame interface: - virtual void DidSetStyleContext(nsStyleContext* aOldStyleContext); - NS_IMETHOD AttributeChanged(int32_t aNameSpaceID, nsIAtom* aAttribute, int32_t aModType); diff --git a/layout/svg/nsSVGMaskFrame.cpp b/layout/svg/nsSVGMaskFrame.cpp index 90c39f27a51..0dcbb952409 100644 --- a/layout/svg/nsSVGMaskFrame.cpp +++ b/layout/svg/nsSVGMaskFrame.cpp @@ -131,13 +131,6 @@ nsSVGMaskFrame::ComputeMaskAlpha(nsRenderingContext *aContext, return retval.forget(); } -/* virtual */ void -nsSVGMaskFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext) -{ - nsSVGEffects::InvalidateDirectRenderingObservers(this); - nsSVGMaskFrameBase::DidSetStyleContext(aOldStyleContext); -} - NS_IMETHODIMP nsSVGMaskFrame::AttributeChanged(int32_t aNameSpaceID, nsIAtom* aAttribute, diff --git a/layout/svg/nsSVGMaskFrame.h b/layout/svg/nsSVGMaskFrame.h index ad078a83836..53d103b83c0 100644 --- a/layout/svg/nsSVGMaskFrame.h +++ b/layout/svg/nsSVGMaskFrame.h @@ -37,8 +37,6 @@ public: const gfxMatrix &aMatrix, float aOpacity = 1.0f); - virtual void DidSetStyleContext(nsStyleContext* aOldStyleContext); - NS_IMETHOD AttributeChanged(int32_t aNameSpaceID, nsIAtom* aAttribute, int32_t aModType); diff --git a/layout/svg/nsSVGPathGeometryFrame.cpp b/layout/svg/nsSVGPathGeometryFrame.cpp index 7d746db38d3..5eba955662b 100644 --- a/layout/svg/nsSVGPathGeometryFrame.cpp +++ b/layout/svg/nsSVGPathGeometryFrame.cpp @@ -120,21 +120,6 @@ nsSVGPathGeometryFrame::AttributeChanged(int32_t aNameSpaceID, return NS_OK; } -/* virtual */ void -nsSVGPathGeometryFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext) -{ - nsSVGPathGeometryFrameBase::DidSetStyleContext(aOldStyleContext); - - // XXX: we'd like to use the style_hint mechanism and the - // ContentStateChanged/AttributeChanged functions for style changes - // to get slightly finer granularity, but unfortunately the - // style_hints don't map very well onto svg. Here seems to be the - // best place to deal with style changes: - - nsSVGEffects::InvalidateRenderingObservers(this); - nsSVGUtils::ScheduleReflowSVG(this); -} - nsIAtom * nsSVGPathGeometryFrame::GetType() const { diff --git a/layout/svg/nsSVGPathGeometryFrame.h b/layout/svg/nsSVGPathGeometryFrame.h index 8e29cb3f88e..c7618f2b366 100644 --- a/layout/svg/nsSVGPathGeometryFrame.h +++ b/layout/svg/nsSVGPathGeometryFrame.h @@ -56,8 +56,6 @@ public: nsIAtom* aAttribute, int32_t aModType); - virtual void DidSetStyleContext(nsStyleContext* aOldStyleContext); - /** * Get the "type" of the frame * diff --git a/layout/svg/nsSVGPatternFrame.cpp b/layout/svg/nsSVGPatternFrame.cpp index 7ac54f881ba..207a1c516f0 100644 --- a/layout/svg/nsSVGPatternFrame.cpp +++ b/layout/svg/nsSVGPatternFrame.cpp @@ -62,13 +62,6 @@ NS_IMPL_FRAMEARENA_HELPERS(nsSVGPatternFrame) //---------------------------------------------------------------------- // nsIFrame methods: -/* virtual */ void -nsSVGPatternFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext) -{ - nsSVGEffects::InvalidateDirectRenderingObservers(this); - nsSVGPatternFrameBase::DidSetStyleContext(aOldStyleContext); -} - NS_IMETHODIMP nsSVGPatternFrame::AttributeChanged(int32_t aNameSpaceID, nsIAtom* aAttribute, diff --git a/layout/svg/nsSVGPatternFrame.h b/layout/svg/nsSVGPatternFrame.h index 3d35110480c..a1b276b750b 100644 --- a/layout/svg/nsSVGPatternFrame.h +++ b/layout/svg/nsSVGPatternFrame.h @@ -52,8 +52,6 @@ public: virtual gfxMatrix GetCanvasTM(uint32_t aFor); // nsIFrame interface: - virtual void DidSetStyleContext(nsStyleContext* aOldStyleContext); - NS_IMETHOD AttributeChanged(int32_t aNameSpaceID, nsIAtom* aAttribute, int32_t aModType); diff --git a/layout/svg/nsSVGStopFrame.cpp b/layout/svg/nsSVGStopFrame.cpp index 10073eee941..815fb8b92ce 100644 --- a/layout/svg/nsSVGStopFrame.cpp +++ b/layout/svg/nsSVGStopFrame.cpp @@ -40,8 +40,6 @@ public: const nsRect& aDirtyRect, const nsDisplayListSet& aLists) MOZ_OVERRIDE {} - virtual void DidSetStyleContext(nsStyleContext* aOldStyleContext); - NS_IMETHOD AttributeChanged(int32_t aNameSpaceID, nsIAtom* aAttribute, int32_t aModType); @@ -86,13 +84,6 @@ nsSVGStopFrame::Init(nsIContent* aContent, } #endif /* DEBUG */ -/* virtual */ void -nsSVGStopFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext) -{ - nsSVGStopFrameBase::DidSetStyleContext(aOldStyleContext); - nsSVGEffects::InvalidateRenderingObservers(this); -} - nsIAtom * nsSVGStopFrame::GetType() const { From dcadb82aee21dcf33a3c5615a48c06b354aa3536 Mon Sep 17 00:00:00 2001 From: Geoff Lankow Date: Sat, 25 May 2013 23:58:02 +1200 Subject: [PATCH 85/91] Bug 864251 - Link to update history from about:support; r=adw --- toolkit/content/aboutSupport.js | 6 ++++++ toolkit/content/aboutSupport.xhtml | 12 ++++++++++++ toolkit/locales/en-US/chrome/global/aboutSupport.dtd | 5 +++++ 3 files changed, 23 insertions(+) diff --git a/toolkit/content/aboutSupport.js b/toolkit/content/aboutSupport.js index 1da21097c23..a9f2d570e39 100644 --- a/toolkit/content/aboutSupport.js +++ b/toolkit/content/aboutSupport.js @@ -506,6 +506,12 @@ function openProfileDirectory() { new nsLocalFile(profileDir).reveal(); } +function showUpdateHistory() { + var prompter = Cc["@mozilla.org/updates/update-prompt;1"] + .createInstance(Ci.nsIUpdatePrompt); + prompter.showUpdateHistory(window); +} + /** * Profile reset is only supported for the default profile if the appropriate migrator exists. */ diff --git a/toolkit/content/aboutSupport.xhtml b/toolkit/content/aboutSupport.xhtml index 9367b6fcf45..934e0a51cfc 100644 --- a/toolkit/content/aboutSupport.xhtml +++ b/toolkit/content/aboutSupport.xhtml @@ -81,6 +81,18 @@ + + + &aboutSupport.appBasicsUpdateHistory; + + + + + + + &aboutSupport.appBasicsUserAgent; diff --git a/toolkit/locales/en-US/chrome/global/aboutSupport.dtd b/toolkit/locales/en-US/chrome/global/aboutSupport.dtd index 0fec1a2676b..c6d814bc49e 100644 --- a/toolkit/locales/en-US/chrome/global/aboutSupport.dtd +++ b/toolkit/locales/en-US/chrome/global/aboutSupport.dtd @@ -21,6 +21,11 @@ + + + +