diff --git a/b2g/installer/package-manifest.in b/b2g/installer/package-manifest.in index 6685ea175e..c3a9ff6290 100644 --- a/b2g/installer/package-manifest.in +++ b/b2g/installer/package-manifest.in @@ -130,7 +130,7 @@ #endif #ifdef MOZ_GTK3 @BINPATH@/@DLL_PREFIX@mozgtk@DLL_SUFFIX@ -@BINPATH@/@DLL_PREFIX@mozgtk2@DLL_SUFFIX@ +@BINPATH@/gtk2/@DLL_PREFIX@mozgtk@DLL_SUFFIX@ #endif [browser] diff --git a/browser/installer/package-manifest.in b/browser/installer/package-manifest.in index 656e430055..86b36dc55c 100644 --- a/browser/installer/package-manifest.in +++ b/browser/installer/package-manifest.in @@ -141,7 +141,7 @@ #endif #ifdef MOZ_GTK3 @BINPATH@/@DLL_PREFIX@mozgtk@DLL_SUFFIX@ -@BINPATH@/@DLL_PREFIX@mozgtk2@DLL_SUFFIX@ +@BINPATH@/gtk2/@DLL_PREFIX@mozgtk@DLL_SUFFIX@ #endif [browser] diff --git a/browser/themes/shared/notification-icons.inc.css b/browser/themes/shared/notification-icons.inc.css index dffdae3115..61f192d7a3 100644 --- a/browser/themes/shared/notification-icons.inc.css +++ b/browser/themes/shared/notification-icons.inc.css @@ -102,16 +102,16 @@ } .notification-anchor-icon { +%ifdef MOZ_WIDGET_GTK + list-style-image: url(moz-icon://stock/gtk-dialog-info?size=16); +%else + list-style-image: url(chrome://global/skin/icons/information-16.png); +%endif width: 16px; height: 16px; margin: 0 2px; } -.default-notification-icon, -#default-notification-icon { - list-style-image: url(chrome://global/skin/icons/information-16.png); -} - .identity-notification-icon, #identity-notification-icon { list-style-image: url(chrome://mozapps/skin/profile/profileicon.png); @@ -280,19 +280,22 @@ 100% { transform: translateX(0); } } -%ifdef XP_MACOSX /* HiDPI notification icons */ - -@media (min-resolution: 2dppx) { +@media (min-resolution: 1.1dppx) { #notification-popup-box { border-image: url("chrome://browser/skin/urlbar-arrow@2x.png") 0 16 0 0 fill; } - .default-notification-icon, - #default-notification-icon { + .notification-anchor-icon { +%ifdef MOZ_WIDGET_GTK + list-style-image: url(moz-icon://stock/gtk-dialog-info?size=dialog); +%else list-style-image: url(chrome://global/skin/icons/information-32.png); +%endif } +%ifdef XP_MACOSX +/* OSX only until we have icons for Windows and Linux */ .geo-notification-icon, #geo-notification-icon { list-style-image: url(chrome://browser/skin/Geolocation-16@2x.png); @@ -311,6 +314,7 @@ list-style-image: url(chrome://mozapps/skin/extensions/extensionGeneric.png); } + #login-fill-notification-icon, #password-notification-icon { list-style-image: url(chrome://mozapps/skin/passwordmgr/key-16@2x.png); } @@ -451,5 +455,5 @@ #servicesInstall-notification-icon { list-style-image: url(chrome://browser/skin/social/services-16@2x.png); } -} %endif +} diff --git a/configure.in b/configure.in index ad2a8a83aa..4ec9eba1ea 100644 --- a/configure.in +++ b/configure.in @@ -8338,7 +8338,9 @@ else # so if the file is named libsomething.so. The lib/ path is also required # because the unpacked file will be under the lib/ subdirectory and will # need to be executed from that path. - MOZ_CHILD_PROCESS_NAME="lib/libplugin-container.so" + MOZ_CHILD_PROCESS_NAME="libplugin-container.so" + MOZ_CHILD_PROCESS_NAME_PIE="libplugin-container-pie.so" + AC_SUBST(MOZ_CHILD_PROCESS_NAME_PIE) fi MOZ_CHILD_PROCESS_BUNDLE="plugin-container.app/Contents/MacOS/" diff --git a/dom/html/nsFormSubmission.cpp b/dom/html/nsFormSubmission.cpp index 4188ee8fe4..8fac3f3931 100644 --- a/dom/html/nsFormSubmission.cpp +++ b/dom/html/nsFormSubmission.cpp @@ -848,7 +848,6 @@ GetSubmissionFromForm(nsGenericHTMLElement* aForm, *aFormSubmission = new nsFSURLEncoded(charset, method, doc, aOriginatingElement); } - NS_ENSURE_TRUE(*aFormSubmission, NS_ERROR_OUT_OF_MEMORY); return NS_OK; } diff --git a/dom/json/nsJSON.cpp b/dom/json/nsJSON.cpp index e814abcef3..c13ddd9f39 100644 --- a/dom/json/nsJSON.cpp +++ b/dom/json/nsJSON.cpp @@ -298,8 +298,6 @@ nsJSONWriter::Write(const char16_t *aBuffer, uint32_t aLength) if (!mDidWrite) { mBuffer = new char16_t[JSON_STREAM_BUFSIZE]; - if (!mBuffer) - return NS_ERROR_OUT_OF_MEMORY; mDidWrite = true; } @@ -481,9 +479,6 @@ nsresult NS_NewJSON(nsISupports* aOuter, REFNSIID aIID, void** aResult) { nsJSON* json = new nsJSON(); - if (!json) - return NS_ERROR_OUT_OF_MEMORY; - NS_ADDREF(json); *aResult = json; diff --git a/dom/jsurl/nsJSProtocolHandler.cpp b/dom/jsurl/nsJSProtocolHandler.cpp index 1a9738a69f..a900e5a392 100644 --- a/dom/jsurl/nsJSProtocolHandler.cpp +++ b/dom/jsurl/nsJSProtocolHandler.cpp @@ -410,8 +410,6 @@ nsresult nsJSChannel::Init(nsIURI *aURI) // Create the nsIStreamIO layer used by the nsIStreamIOChannel. mIOThunk = new nsJSThunk(); - if (!mIOThunk) - return NS_ERROR_OUT_OF_MEMORY; // Create a stock input stream channel... // Remember, until AsyncOpen is called, the script will not be evaluated @@ -1129,8 +1127,6 @@ nsJSProtocolHandler::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult) return NS_ERROR_NO_AGGREGATION; nsJSProtocolHandler* ph = new nsJSProtocolHandler(); - if (!ph) - return NS_ERROR_OUT_OF_MEMORY; NS_ADDREF(ph); nsresult rv = ph->Init(); if (NS_SUCCEEDED(rv)) { diff --git a/dom/media/MediaCache.cpp b/dom/media/MediaCache.cpp index 108ff0c91e..2c87a376d4 100644 --- a/dom/media/MediaCache.cpp +++ b/dom/media/MediaCache.cpp @@ -640,9 +640,6 @@ InitMediaCache() return; gMediaCache = new MediaCache(); - if (!gMediaCache) - return; - nsresult rv = gMediaCache->Init(); if (NS_FAILED(rv)) { delete gMediaCache; diff --git a/dom/media/MediaResource.cpp b/dom/media/MediaResource.cpp index 9cb5c8ec21..9b50fcbc62 100644 --- a/dom/media/MediaResource.cpp +++ b/dom/media/MediaResource.cpp @@ -548,8 +548,6 @@ nsresult ChannelMediaResource::OpenChannel(nsIStreamListener** aStreamListener) } mListener = new Listener(this); - NS_ENSURE_TRUE(mListener, NS_ERROR_OUT_OF_MEMORY); - if (aStreamListener) { *aStreamListener = mListener; NS_ADDREF(*aStreamListener); diff --git a/dom/media/webrtc/MediaEngineWebRTC.cpp b/dom/media/webrtc/MediaEngineWebRTC.cpp index 18496e234d..650656c5c0 100644 --- a/dom/media/webrtc/MediaEngineWebRTC.cpp +++ b/dom/media/webrtc/MediaEngineWebRTC.cpp @@ -120,7 +120,9 @@ MediaEngineWebRTC::EnumerateVideoDevices(dom::MediaSourceEnum aMediaSource, #ifdef MOZ_WIDGET_ANDROID // get the JVM - JavaVM *jvm = mozilla::AndroidBridge::Bridge()->GetVM(); + JavaVM* jvm; + JNIEnv* const env = jni::GetEnvForThread(); + MOZ_ALWAYS_TRUE(!env->GetJavaVM(&jvm)); if (webrtc::VideoEngine::SetAndroidObjects(jvm) != 0) { LOG(("VieCapture:SetAndroidObjects Failed")); @@ -245,8 +247,9 @@ MediaEngineWebRTC::EnumerateAudioDevices(dom::MediaSourceEnum aMediaSource, jobject context = mozilla::AndroidBridge::Bridge()->GetGlobalContextRef(); // get the JVM - JavaVM *jvm = mozilla::AndroidBridge::Bridge()->GetVM(); - JNIEnv *env = GetJNIForThread(); + JavaVM* jvm; + JNIEnv* const env = jni::GetEnvForThread(); + MOZ_ALWAYS_TRUE(!env->GetJavaVM(&jvm)); if (webrtc::VoiceEngine::SetAndroidObjects(jvm, env, (void*)context) != 0) { LOG(("VoiceEngine:SetAndroidObjects Failed")); diff --git a/dom/notification/NotificationDB.jsm b/dom/notification/NotificationDB.jsm index 09cf91a85a..fdd30c4e62 100644 --- a/dom/notification/NotificationDB.jsm +++ b/dom/notification/NotificationDB.jsm @@ -39,7 +39,7 @@ const kMessages = [ "Notification:GetAllCrossOrigin" ]; -let NotificationDB = { +var NotificationDB = { // Ensure we won't call init() while xpcom-shutdown is performed _shutdownInProgress: false, diff --git a/dom/notification/NotificationStorage.js b/dom/notification/NotificationStorage.js index a48fd6fed5..34300e2f8a 100644 --- a/dom/notification/NotificationStorage.js +++ b/dom/notification/NotificationStorage.js @@ -4,7 +4,7 @@ "use strict"; -const DEBUG = true; +const DEBUG = false; function debug(s) { dump("-*- NotificationStorage.js: " + s + "\n"); } const Cc = Components.classes; diff --git a/dom/plugins/base/PluginPRLibrary.cpp b/dom/plugins/base/PluginPRLibrary.cpp index 00c756f518..1d55bc4bf1 100644 --- a/dom/plugins/base/PluginPRLibrary.cpp +++ b/dom/plugins/base/PluginPRLibrary.cpp @@ -37,7 +37,7 @@ nsresult PluginPRLibrary::NP_Initialize(NPNetscapeFuncs* bFuncs, NPPluginFuncs* pFuncs, NPError* error) { - JNIEnv* env = GetJNIForThread(); + JNIEnv* env = jni::GetEnvForThread(); mozilla::AutoLocalJNIFrame jniFrame(env); diff --git a/dom/plugins/base/android/ANPAudio.cpp b/dom/plugins/base/android/ANPAudio.cpp index f32cc2eeb1..b23fdbcbe9 100644 --- a/dom/plugins/base/android/ANPAudio.cpp +++ b/dom/plugins/base/android/ANPAudio.cpp @@ -124,7 +124,7 @@ AudioRunnable::Run() { PR_SetCurrentThreadName("Android Audio"); - JNIEnv* jenv = GetJNIForThread(); + JNIEnv* const jenv = mozilla::jni::GetEnvForThread(); mozilla::AutoLocalJNIFrame autoFrame(jenv, 2); @@ -207,7 +207,7 @@ anp_audio_newTrack(uint32_t sampleRate, // sampling rate in Hz return nullptr; } - JNIEnv *jenv = GetJNIForThread(); + JNIEnv* const jenv = mozilla::jni::GetEnvForThread(); s->at_class = init_jni_bindings(jenv); s->rate = sampleRate; @@ -303,7 +303,7 @@ anp_audio_start(ANPAudioTrack* s) return; } - JNIEnv *jenv = GetJNIForThread(); + JNIEnv* const jenv = mozilla::jni::GetEnvForThread(); mozilla::AutoLocalJNIFrame autoFrame(jenv, 0); jenv->CallVoidMethod(s->output_unit, at.play); @@ -331,7 +331,7 @@ anp_audio_pause(ANPAudioTrack* s) return; } - JNIEnv *jenv = GetJNIForThread(); + JNIEnv* const jenv = mozilla::jni::GetEnvForThread(); mozilla::AutoLocalJNIFrame autoFrame(jenv, 0); jenv->CallVoidMethod(s->output_unit, at.pause); @@ -345,7 +345,7 @@ anp_audio_stop(ANPAudioTrack* s) } s->isStopped = true; - JNIEnv *jenv = GetJNIForThread(); + JNIEnv* const jenv = mozilla::jni::GetEnvForThread(); mozilla::AutoLocalJNIFrame autoFrame(jenv, 0); jenv->CallVoidMethod(s->output_unit, at.stop); diff --git a/dom/plugins/base/nsNPAPIPlugin.cpp b/dom/plugins/base/nsNPAPIPlugin.cpp index 738917c447..01ffdfaedd 100644 --- a/dom/plugins/base/nsNPAPIPlugin.cpp +++ b/dom/plugins/base/nsNPAPIPlugin.cpp @@ -833,7 +833,6 @@ AsyncCallbackAutoLock::~AsyncCallbackAutoLock() } } - NPP NPPStack::sCurrentNPP = nullptr; const char * @@ -884,7 +883,6 @@ _geturl(NPP npp, const char* relativeURL, const char* target) (strncmp(relativeURL, "ftp:", 4) != 0)) { nsNPAPIPluginInstance *inst = (nsNPAPIPluginInstance *) npp->ndata; - const char *name = nullptr; RefPtr host = nsPluginHost::GetInst(); host->GetPluginName(inst, &name); @@ -1037,7 +1035,7 @@ NPError _destroystream(NPP npp, NPStream *pstream, NPError reason) { if (!NS_IsMainThread()) { - NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_write called from the wrong thread\n")); + NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_destroystream called from the wrong thread\n")); return NPERR_INVALID_PARAM; } NPN_PLUGIN_LOG(PLUGIN_LOG_NORMAL, @@ -1946,7 +1944,7 @@ _getvalue(NPP npp, NPNVariable variable, void *result) return NPERR_GENERIC_ERROR; #endif -#if defined(XP_WIN) || (MOZ_WIDGET_GTK == 2) || defined(MOZ_WIDGET_QT) +#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) || defined(MOZ_WIDGET_QT) case NPNVnetscapeWindow: { if (!npp || !npp->ndata) return NPERR_INVALID_INSTANCE_ERROR; @@ -2105,9 +2103,7 @@ _getvalue(NPP npp, NPNVariable variable, void *result) return NPERR_NO_ERROR; } } - else { - return NPERR_GENERIC_ERROR; - } + return NPERR_GENERIC_ERROR; } #ifndef NP_NO_QUICKDRAW @@ -2336,14 +2332,13 @@ _getvalue(NPP npp, NPNVariable variable, void *result) // we no longer hand out any XPCOM objects case NPNVDOMElement: - // fall through case NPNVDOMWindow: - // fall through case NPNVserviceManager: // old XPCOM objects, no longer supported, but null out the out // param to avoid crashing plugins that still try to use this. *(nsISupports**)result = nullptr; - // fall through + MOZ_FALLTHROUGH; + default: NPN_PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("NPN_getvalue unhandled get value: %d\n", variable)); return NPERR_GENERIC_ERROR; @@ -2631,6 +2626,11 @@ NPError _getvalueforurl(NPP instance, NPNURLVariable variable, const char *url, char **value, uint32_t *len) { + if (!NS_IsMainThread()) { + NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_getvalueforurl called from the wrong thread\n")); + return NPERR_GENERIC_ERROR; + } + if (!instance) { return NPERR_INVALID_PARAM; } @@ -2689,6 +2689,11 @@ NPError _setvalueforurl(NPP instance, NPNURLVariable variable, const char *url, const char *value, uint32_t len) { + if (!NS_IsMainThread()) { + NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_setvalueforurl called from the wrong thread\n")); + return NPERR_GENERIC_ERROR; + } + if (!instance) { return NPERR_INVALID_PARAM; } @@ -2700,7 +2705,7 @@ _setvalueforurl(NPP instance, NPNURLVariable variable, const char *url, switch (variable) { case NPNURLVCookie: { - if (!url || !value || (0 >= len)) + if (!value || 0 == len) return NPERR_INVALID_PARAM; nsresult rv = NS_ERROR_FAILURE; @@ -2745,6 +2750,11 @@ _getauthenticationinfo(NPP instance, const char *protocol, const char *host, char **username, uint32_t *ulen, char **password, uint32_t *plen) { + if (!NS_IsMainThread()) { + NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_getauthenticationinfo called from the wrong thread\n")); + return NPERR_GENERIC_ERROR; + } + if (!instance || !protocol || !host || !scheme || !realm || !username || !ulen || !password || !plen) return NPERR_INVALID_PARAM; @@ -2801,6 +2811,11 @@ _getauthenticationinfo(NPP instance, const char *protocol, const char *host, uint32_t _scheduletimer(NPP instance, uint32_t interval, NPBool repeat, PluginTimerFunc timerFunc) { + if (!NS_IsMainThread()) { + NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_scheduletimer called from the wrong thread\n")); + return 0; + } + nsNPAPIPluginInstance *inst = (nsNPAPIPluginInstance *)instance->ndata; if (!inst) return 0; @@ -2811,6 +2826,11 @@ _scheduletimer(NPP instance, uint32_t interval, NPBool repeat, PluginTimerFunc t void _unscheduletimer(NPP instance, uint32_t timerID) { + if (!NS_IsMainThread()) { + NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_unscheduletimer called from the wrong thread\n")); + return; + } + #ifdef MOZ_WIDGET_ANDROID // Sometimes Flash calls this with a dead NPP instance. Ensure the one we have // here is valid and maps to a nsNPAPIPluginInstance. @@ -2827,6 +2847,11 @@ _unscheduletimer(NPP instance, uint32_t timerID) NPError _popupcontextmenu(NPP instance, NPMenu* menu) { + if (!NS_IsMainThread()) { + NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_popupcontextmenu called from the wrong thread\n")); + return 0; + } + #ifdef MOZ_WIDGET_COCOA nsNPAPIPluginInstance *inst = (nsNPAPIPluginInstance *)instance->ndata; @@ -2876,6 +2901,11 @@ _popupcontextmenu(NPP instance, NPMenu* menu) NPBool _convertpoint(NPP instance, double sourceX, double sourceY, NPCoordinateSpace sourceSpace, double *destX, double *destY, NPCoordinateSpace destSpace) { + if (!NS_IsMainThread()) { + NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_convertpoint called from the wrong thread\n")); + return 0; + } + nsNPAPIPluginInstance *inst = (nsNPAPIPluginInstance *)instance->ndata; if (!inst) return false; @@ -2886,6 +2916,11 @@ _convertpoint(NPP instance, double sourceX, double sourceY, NPCoordinateSpace so void _urlredirectresponse(NPP instance, void* notifyData, NPBool allow) { + if (!NS_IsMainThread()) { + NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_convertpoint called from the wrong thread\n")); + return; + } + nsNPAPIPluginInstance *inst = (nsNPAPIPluginInstance *)instance->ndata; if (!inst) { return; diff --git a/dom/plugins/base/nsNPAPIPluginInstance.cpp b/dom/plugins/base/nsNPAPIPluginInstance.cpp index 7b1d696001..3fd47abfc4 100644 --- a/dom/plugins/base/nsNPAPIPluginInstance.cpp +++ b/dom/plugins/base/nsNPAPIPluginInstance.cpp @@ -582,9 +582,6 @@ nsNPAPIPluginInstance::NewStreamFromPlugin(const char* type, const char* target, nsIOutputStream* *result) { nsPluginStreamToFile* stream = new nsPluginStreamToFile(target, mOwner); - if (!stream) - return NS_ERROR_OUT_OF_MEMORY; - return stream->QueryInterface(kIOutputStreamIID, (void**)result); } diff --git a/dom/plugins/base/nsNPAPIPluginInstance.h b/dom/plugins/base/nsNPAPIPluginInstance.h index 4c991bee66..64a516e518 100644 --- a/dom/plugins/base/nsNPAPIPluginInstance.h +++ b/dom/plugins/base/nsNPAPIPluginInstance.h @@ -32,6 +32,7 @@ class SharedPluginTexture; #include "mozilla/TimeStamp.h" #include "mozilla/PluginLibrary.h" #include "mozilla/RefPtr.h" +#include "mozilla/WeakPtr.h" class nsPluginStreamListenerPeer; // browser-initiated stream class class nsNPAPIPluginStreamListener; // plugin-initiated stream class @@ -77,11 +78,13 @@ public: }; class nsNPAPIPluginInstance final : public nsIAudioChannelAgentCallback + , public mozilla::SupportsWeakPtr { private: typedef mozilla::PluginLibrary PluginLibrary; public: + MOZ_DECLARE_WEAKREFERENCE_TYPENAME(nsNPAPIPluginInstance) NS_DECL_THREADSAFE_ISUPPORTS NS_DECL_NSIAUDIOCHANNELAGENTCALLBACK diff --git a/dom/plugins/base/nsPluginHost.cpp b/dom/plugins/base/nsPluginHost.cpp index 3877be0787..5922999400 100644 --- a/dom/plugins/base/nsPluginHost.cpp +++ b/dom/plugins/base/nsPluginHost.cpp @@ -2158,9 +2158,6 @@ nsresult nsPluginHost::ScanPluginsDirectory(nsIFile *pluginsDir, pluginTag = new nsPluginTag(&info, fileModTime, fromExtension); pluginFile.FreePluginInfo(info); - if (!pluginTag) - return NS_ERROR_OUT_OF_MEMORY; - pluginTag->mLibrary = library; uint32_t state; rv = pluginTag->GetBlocklistState(&state); @@ -3251,8 +3248,6 @@ nsPluginHost::EnsurePrivateDirServiceProvider() if (!mPrivateDirServiceProvider) { nsresult rv; mPrivateDirServiceProvider = new nsPluginDirServiceProvider(); - if (!mPrivateDirServiceProvider) - return NS_ERROR_OUT_OF_MEMORY; nsCOMPtr dirService(do_GetService(kDirectoryServiceContractID, &rv)); if (NS_FAILED(rv)) return rv; diff --git a/dom/plugins/base/nsPluginInstanceOwner.cpp b/dom/plugins/base/nsPluginInstanceOwner.cpp index f4839e1ffa..908bd18278 100644 --- a/dom/plugins/base/nsPluginInstanceOwner.cpp +++ b/dom/plugins/base/nsPluginInstanceOwner.cpp @@ -328,13 +328,10 @@ nsPluginInstanceOwner::nsPluginInstanceOwner() mPluginFrame = nullptr; mWidgetCreationComplete = false; #ifdef XP_MACOSX - memset(&mCGPluginPortCopy, 0, sizeof(NP_CGContext)); - mInCGPaintLevel = 0; mSentInitialTopLevelWindowEvent = false; mLastWindowIsActive = false; mLastContentFocused = false; mLastScaleFactor = 1.0; - mColorProfile = nullptr; mShouldBlurOnActivate = false; #endif mContentFocused = false; @@ -1133,9 +1130,6 @@ void nsPluginInstanceOwner::AddToCARefreshTimer() { if (!sCARefreshListeners) { sCARefreshListeners = new nsTArray(); - if (!sCARefreshListeners) { - return; - } } if (sCARefreshListeners->Contains(this)) { @@ -1146,9 +1140,6 @@ void nsPluginInstanceOwner::AddToCARefreshTimer() { if (!sCATimer) { sCATimer = new nsCOMPtr(); - if (!sCATimer) { - return; - } } if (sCARefreshListeners->Length() == 1) { @@ -1176,91 +1167,6 @@ void nsPluginInstanceOwner::RemoveFromCARefreshTimer() { } } -void nsPluginInstanceOwner::RenderCoreAnimation(CGContextRef aCGContext, - int aWidth, int aHeight) -{ - if (aWidth == 0 || aHeight == 0) - return; - - if (!mCARenderer) { - mCARenderer = new nsCARenderer(); - } - - // aWidth and aHeight are in "display pixels". In non-HiDPI modes - // "display pixels" are device pixels. But in HiDPI modes each - // display pixel corresponds to more than one device pixel. - double scaleFactor = 1.0; - GetContentsScaleFactor(&scaleFactor); - - if (!mIOSurface || - (mIOSurface->GetWidth() != (size_t)aWidth || - mIOSurface->GetHeight() != (size_t)aHeight || - mIOSurface->GetContentsScaleFactor() != scaleFactor)) { - mIOSurface = nullptr; - - // If the renderer is backed by an IOSurface, resize it as required. - mIOSurface = MacIOSurface::CreateIOSurface(aWidth, aHeight, scaleFactor); - if (mIOSurface) { - RefPtr attachSurface = MacIOSurface::LookupSurface( - mIOSurface->GetIOSurfaceID(), - scaleFactor); - if (attachSurface) { - mCARenderer->AttachIOSurface(attachSurface); - } else { - NS_ERROR("IOSurface attachment failed"); - mIOSurface = nullptr; - } - } - } - - if (!mColorProfile) { - mColorProfile = CreateSystemColorSpace(); - } - - if (mCARenderer->isInit() == false) { - void *caLayer = nullptr; - nsresult rv = mInstance->GetValueFromPlugin(NPPVpluginCoreAnimationLayer, &caLayer); - if (NS_FAILED(rv) || !caLayer) { - return; - } - - // We don't run Flash in-process so we can unconditionally disallow - // the offliner renderer. - mCARenderer->SetupRenderer(caLayer, aWidth, aHeight, scaleFactor, - DISALLOW_OFFLINE_RENDERER); - - // Setting up the CALayer requires resetting the painting otherwise we - // get garbage for the first few frames. - FixUpPluginWindow(ePluginPaintDisable); - FixUpPluginWindow(ePluginPaintEnable); - } - - CGImageRef caImage = nullptr; - nsresult rt = mCARenderer->Render(aWidth, aHeight, scaleFactor, &caImage); - if (rt == NS_OK && mIOSurface && mColorProfile) { - nsCARenderer::DrawSurfaceToCGContext(aCGContext, mIOSurface, mColorProfile, - 0, 0, aWidth, aHeight); - } else if (rt == NS_OK && caImage != nullptr) { - // Significant speed up by resetting the scaling - ::CGContextSetInterpolationQuality(aCGContext, kCGInterpolationNone ); - ::CGContextTranslateCTM(aCGContext, 0, (double) aHeight * scaleFactor); - ::CGContextScaleCTM(aCGContext, scaleFactor, -scaleFactor); - - ::CGContextDrawImage(aCGContext, CGRectMake(0,0,aWidth,aHeight), caImage); - } else { - NS_NOTREACHED("nsCARenderer::Render failure"); - } -} - -void* nsPluginInstanceOwner::GetPluginPortCopy() -{ - if (GetDrawingModel() == NPDrawingModelCoreGraphics || - GetDrawingModel() == NPDrawingModelCoreAnimation || - GetDrawingModel() == NPDrawingModelInvalidatingCoreAnimation) - return &mCGPluginPortCopy; - return nullptr; -} - void nsPluginInstanceOwner::SetPluginPort() { void* pluginPort = GetPluginPort(); @@ -1268,18 +1174,6 @@ void nsPluginInstanceOwner::SetPluginPort() return; mPluginWindow->window = pluginPort; } - -void nsPluginInstanceOwner::BeginCGPaint() -{ - ++mInCGPaintLevel; -} - -void nsPluginInstanceOwner::EndCGPaint() -{ - --mInCGPaintLevel; - NS_ASSERTION(mInCGPaintLevel >= 0, "Mismatched call to nsPluginInstanceOwner::EndCGPaint()!"); -} - #endif // static @@ -1367,7 +1261,7 @@ bool nsPluginInstanceOwner::AddPluginView(const LayoutDeviceRect& aRect /* = Lay if (!mJavaView) return false; - mJavaView = (void*)AndroidBridge::GetJNIEnv()->NewGlobalRef((jobject)mJavaView); + mJavaView = (void*)jni::GetGeckoThreadEnv()->NewGlobalRef((jobject)mJavaView); } if (AndroidBridge::Bridge()) @@ -1386,7 +1280,7 @@ void nsPluginInstanceOwner::RemovePluginView() widget::GeckoAppShell::RemovePluginView( jni::Object::Ref::From(jobject(mJavaView)), mFullScreen); - AndroidBridge::GetJNIEnv()->DeleteGlobalRef((jobject)mJavaView); + jni::GetGeckoThreadEnv()->DeleteGlobalRef((jobject)mJavaView); mJavaView = nullptr; if (mFullScreen) @@ -1461,7 +1355,7 @@ void nsPluginInstanceOwner::ExitFullScreen() { } void nsPluginInstanceOwner::ExitFullScreen(jobject view) { - JNIEnv* env = AndroidBridge::GetJNIEnv(); + JNIEnv* env = jni::GetGeckoThreadEnv(); if (sFullScreenInstance && sFullScreenInstance->mInstance && env->IsSameObject(view, (jobject)sFullScreenInstance->mInstance->GetJavaSurface())) { @@ -2506,8 +2400,6 @@ nsPluginInstanceOwner::Destroy() #ifdef XP_MACOSX RemoveFromCARefreshTimer(); - if (mColorProfile) - ::CGColorSpaceRelease(mColorProfile); #endif nsCOMPtr content = do_QueryReferent(mContent); @@ -3089,11 +2981,7 @@ void nsPluginInstanceOwner::FixUpPluginWindow(int32_t inPaintState) return; } - // If we've already set up a CGContext in nsPluginFrame::PaintPlugin(), we - // don't want calls to SetPluginPort() to step on our work. - if (mInCGPaintLevel < 1) { - SetPluginPort(); - } + SetPluginPort(); nsIntSize widgetClip = mPluginFrame->GetWidgetlessClipRect().Size(); diff --git a/dom/plugins/base/nsPluginInstanceOwner.h b/dom/plugins/base/nsPluginInstanceOwner.h index f37a5ac61b..fb7cda96eb 100644 --- a/dom/plugins/base/nsPluginInstanceOwner.h +++ b/dom/plugins/base/nsPluginInstanceOwner.h @@ -135,19 +135,9 @@ public: // This calls into the plugin (NPP_SetWindow) and can run script. void FixUpPluginWindow(int32_t inPaintState); void HidePluginWindow(); - // Return a pointer to the internal nsPluginPort structure that's used to - // store a copy of plugin port info and to detect when it's been changed. - void* GetPluginPortCopy(); // Set plugin port info in the plugin (in the 'window' member of the // NPWindow structure passed to the plugin by SetWindow()). void SetPluginPort(); - // Flag when we've set up a Thebes (and CoreGraphics) context in - // nsPluginFrame::PaintPlugin(). We need to know this in - // FixUpPluginWindow() (i.e. we need to know when FixUpPluginWindow() has - // been called from nsPluginFrame::PaintPlugin() when we're using the - // CoreGraphics drawing model). - void BeginCGPaint(); - void EndCGPaint(); #else // XP_MACOSX void UpdateWindowPositionAndClipRect(bool aSetWindow); void UpdateWindowVisibility(bool aVisible); @@ -292,11 +282,6 @@ private: RefPtr mPluginHost; #ifdef XP_MACOSX - NP_CGContext mCGPluginPortCopy; - int32_t mInCGPaintLevel; - RefPtr mIOSurface; - RefPtr mCARenderer; - CGColorSpaceRef mColorProfile; static nsCOMPtr *sCATimer; static nsTArray *sCARefreshListeners; bool mSentInitialTopLevelWindowEvent; diff --git a/dom/plugins/base/nsPluginNativeWindow.cpp b/dom/plugins/base/nsPluginNativeWindow.cpp index 7851819e74..f9baf5b813 100644 --- a/dom/plugins/base/nsPluginNativeWindow.cpp +++ b/dom/plugins/base/nsPluginNativeWindow.cpp @@ -41,7 +41,7 @@ nsresult PLUG_NewPluginNativeWindow(nsPluginNativeWindow ** aPluginNativeWindow) { NS_ENSURE_ARG_POINTER(aPluginNativeWindow); *aPluginNativeWindow = new nsPluginNativeWindowPLATFORM(); - return *aPluginNativeWindow ? NS_OK : NS_ERROR_OUT_OF_MEMORY; + return NS_OK; } nsresult PLUG_DeletePluginNativeWindow(nsPluginNativeWindow * aPluginNativeWindow) diff --git a/dom/plugins/base/nsPluginNativeWindowGtk.cpp b/dom/plugins/base/nsPluginNativeWindowGtk.cpp index 4d2865e25e..f3180c9d7b 100644 --- a/dom/plugins/base/nsPluginNativeWindowGtk.cpp +++ b/dom/plugins/base/nsPluginNativeWindowGtk.cpp @@ -57,7 +57,7 @@ nsresult PLUG_NewPluginNativeWindow(nsPluginNativeWindow ** aPluginNativeWindow) { NS_ENSURE_ARG_POINTER(aPluginNativeWindow); *aPluginNativeWindow = new nsPluginNativeWindowGtk(); - return *aPluginNativeWindow ? NS_OK : NS_ERROR_OUT_OF_MEMORY; + return NS_OK; } nsresult PLUG_DeletePluginNativeWindow(nsPluginNativeWindow * aPluginNativeWindow) diff --git a/dom/plugins/base/nsPluginNativeWindowQt.cpp b/dom/plugins/base/nsPluginNativeWindowQt.cpp index cb7fa488b2..3249842f4c 100644 --- a/dom/plugins/base/nsPluginNativeWindowQt.cpp +++ b/dom/plugins/base/nsPluginNativeWindowQt.cpp @@ -62,7 +62,7 @@ nsresult PLUG_NewPluginNativeWindow(nsPluginNativeWindow **aPluginNativeWindow) { NS_ENSURE_ARG_POINTER(aPluginNativeWindow); *aPluginNativeWindow = new nsPluginNativeWindowQt(); - return *aPluginNativeWindow ? NS_OK : NS_ERROR_OUT_OF_MEMORY; + return NS_OK; } nsresult PLUG_DeletePluginNativeWindow(nsPluginNativeWindow * aPluginNativeWindow) diff --git a/dom/plugins/base/nsPluginNativeWindowWin.cpp b/dom/plugins/base/nsPluginNativeWindowWin.cpp index dd9fcf6b3a..72fc011cfc 100644 --- a/dom/plugins/base/nsPluginNativeWindowWin.cpp +++ b/dom/plugins/base/nsPluginNativeWindowWin.cpp @@ -575,13 +575,11 @@ nsPluginNativeWindowWin::GetPluginWindowEvent(HWND aWnd, UINT aMsg, WPARAM aWPar if (!mCachedPluginWindowEvent) { event = new PluginWindowEvent(); - if (!event) return nullptr; mCachedPluginWindowEvent = event; } else if (mCachedPluginWindowEvent->InUse()) { event = new PluginWindowEvent(); - if (!event) return nullptr; } else { @@ -739,8 +737,7 @@ nsresult PLUG_NewPluginNativeWindow(nsPluginNativeWindow ** aPluginNativeWindow) NS_ENSURE_ARG_POINTER(aPluginNativeWindow); *aPluginNativeWindow = new nsPluginNativeWindowWin(); - - return *aPluginNativeWindow ? NS_OK : NS_ERROR_OUT_OF_MEMORY; + return NS_OK; } nsresult PLUG_DeletePluginNativeWindow(nsPluginNativeWindow * aPluginNativeWindow) diff --git a/dom/plugins/base/nsPluginStreamListenerPeer.cpp b/dom/plugins/base/nsPluginStreamListenerPeer.cpp index b2cd38f715..4c18e2e341 100644 --- a/dom/plugins/base/nsPluginStreamListenerPeer.cpp +++ b/dom/plugins/base/nsPluginStreamListenerPeer.cpp @@ -735,12 +735,7 @@ nsPluginStreamListenerPeer::RequestRead(NPByteRange* rangeList) } else { nsWeakPtr weakpeer = do_GetWeakReference(static_cast(this)); - nsPluginByteRangeStreamListener *brrListener = - new nsPluginByteRangeStreamListener(weakpeer); - if (brrListener) - converter = brrListener; - else - return NS_ERROR_OUT_OF_MEMORY; + converter = new nsPluginByteRangeStreamListener(weakpeer); } mPendingRequests += numRequests; diff --git a/dom/plugins/base/nsPluginsDirDarwin.cpp b/dom/plugins/base/nsPluginsDirDarwin.cpp index 3cad48ea86..41f3bf10c9 100644 --- a/dom/plugins/base/nsPluginsDirDarwin.cpp +++ b/dom/plugins/base/nsPluginsDirDarwin.cpp @@ -23,8 +23,12 @@ #include "nsPluginsDirUtils.h" #include "nsILocalFileMac.h" +#include "mozilla/UniquePtr.h" #include "nsCocoaFeatures.h" +#if defined(MOZ_CRASHREPORTER) +#include "nsExceptionHandler.h" +#endif #include #include @@ -39,7 +43,6 @@ typedef NS_NPAPIPLUGIN_CALLBACK(const char *, NP_GETMIMEDESCRIPTION) (); typedef NS_NPAPIPLUGIN_CALLBACK(OSErr, BP_GETSUPPORTEDMIMETYPES) (BPSupportedMIMETypes *mimeInfo, UInt32 flags); - /* ** Returns a CFBundleRef if the path refers to a Mac OS X bundle directory. ** The caller is responsible for calling CFRelease() to deallocate. @@ -235,16 +238,16 @@ static void ParsePlistPluginInfo(nsPluginInfo& info, CFBundleRef bundle) memset(info.fMimeDescriptionArray, 0, mimeDataArraySize); // Allocate memory for mime dictionary keys and values - nsAutoArrayPtr keys(new CFTypeRef[mimeDictKeyCount]); + mozilla::UniquePtr keys(new CFTypeRef[mimeDictKeyCount]); if (!keys) return; - nsAutoArrayPtr values(new CFTypeRef[mimeDictKeyCount]); + mozilla::UniquePtr values(new CFTypeRef[mimeDictKeyCount]); if (!values) return; info.fVariantCount = 0; - ::CFDictionaryGetKeysAndValues(mimeDict, keys, values); + ::CFDictionaryGetKeysAndValues(mimeDict, keys.get(), values.get()); for (int i = 0; i < mimeDictKeyCount; i++) { CFTypeRef mimeString = keys[i]; if (!mimeString || ::CFGetTypeID(mimeString) != ::CFStringGetTypeID()) { @@ -484,6 +487,14 @@ nsresult nsPluginFile::GetPluginInfo(nsPluginInfo& info, PRLibrary **outLibrary) NS_WARNING(msg.get()); return NS_ERROR_FAILURE; } +#if defined(MOZ_CRASHREPORTER) + // The block above assumes that "fbplugin" is the filename of the plugin + // to be blocked, or that the filename starts with "fbplugin_". But we + // don't yet know for sure if this is always true. So for the time being + // record extra information in our crash logs. + CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("Bug_1086977"), + fileName); +#endif } // It's possible that our plugin has 2 entry points that'll give us mime type @@ -493,6 +504,14 @@ nsresult nsPluginFile::GetPluginInfo(nsPluginInfo& info, PRLibrary **outLibrary) // Sadly we have to load the library for this to work. rv = LoadPlugin(outLibrary); +#if defined(MOZ_CRASHREPORTER) + if (nsCocoaFeatures::OnYosemiteOrLater()) { + // If we didn't crash in LoadPlugin(), change the previous annotation so we + // don't sow confusion. + CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("Bug_1086977"), + NS_LITERAL_CSTRING("Didn't crash, please ignore")); + } +#endif if (NS_FAILED(rv)) return rv; diff --git a/dom/plugins/ipc/BrowserStreamParent.cpp b/dom/plugins/ipc/BrowserStreamParent.cpp index f3eade194d..0d0a006d45 100644 --- a/dom/plugins/ipc/BrowserStreamParent.cpp +++ b/dom/plugins/ipc/BrowserStreamParent.cpp @@ -9,6 +9,7 @@ #include "PluginInstanceParent.h" #include "nsNPAPIPlugin.h" +#include "mozilla/UniquePtr.h" #include "mozilla/unused.h" // How much data are we willing to send across the wire @@ -35,6 +36,7 @@ BrowserStreamParent::BrowserStreamParent(PluginInstanceParent* npp, BrowserStreamParent::~BrowserStreamParent() { + mStream->pdata = nullptr; } void @@ -51,7 +53,6 @@ BrowserStreamParent::RecvAsyncNPP_NewStreamResult(const NPError& rv, PluginAsyncSurrogate* surrogate = mNPP->GetAsyncSurrogate(); MOZ_ASSERT(surrogate); surrogate->AsyncCallArriving(); - RefPtr streamListener = mStreamListener.forget(); if (mState == DEFERRING_DESTROY) { // We've been asked to destroy ourselves before init was complete. mState = DYING; @@ -61,10 +62,10 @@ BrowserStreamParent::RecvAsyncNPP_NewStreamResult(const NPError& rv, NPError error = rv; if (error == NPERR_NO_ERROR) { - if (!streamListener) { + if (!mStreamListener) { return false; } - if (streamListener->SetStreamType(stype)) { + if (mStreamListener->SetStreamType(stype)) { mState = ALIVE; } else { error = NPERR_GENERIC_ERROR; @@ -109,7 +110,7 @@ BrowserStreamParent::AnswerNPN_RequestRead(const IPCByteRanges& ranges, if (ranges.Length() > INT32_MAX) return false; - nsAutoArrayPtr rp(new NPByteRange[ranges.Length()]); + UniquePtr rp(new NPByteRange[ranges.Length()]); for (uint32_t i = 0; i < ranges.Length(); ++i) { rp[i].offset = ranges[i].offset; rp[i].length = ranges[i].length; @@ -117,7 +118,7 @@ BrowserStreamParent::AnswerNPN_RequestRead(const IPCByteRanges& ranges, } rp[ranges.Length() - 1].next = nullptr; - *result = mNPP->mNPNIface->requestread(mStream, rp); + *result = mNPP->mNPNIface->requestread(mStream, rp.get()); return true; } diff --git a/dom/plugins/ipc/PluginAsyncSurrogate.cpp b/dom/plugins/ipc/PluginAsyncSurrogate.cpp index b8be0e0c49..da07116ccd 100644 --- a/dom/plugins/ipc/PluginAsyncSurrogate.cpp +++ b/dom/plugins/ipc/PluginAsyncSurrogate.cpp @@ -36,6 +36,7 @@ AsyncNPObject::AsyncNPObject(PluginAsyncSurrogate* aSurrogate) AsyncNPObject::~AsyncNPObject() { if (mRealObject) { + --mRealObject->asyncWrapperCount; parent::_releaseobject(mRealObject); mRealObject = nullptr; } @@ -51,11 +52,19 @@ AsyncNPObject::GetRealObject() if (!instance) { return nullptr; } + NPObject* realObject = nullptr; NPError err = instance->NPP_GetValue(NPPVpluginScriptableNPObject, - &mRealObject); + &realObject); if (err != NPERR_NO_ERROR) { return nullptr; } + if (realObject->_class != PluginScriptableObjectParent::GetClass()) { + NS_ERROR("Don't know what kind of object this is!"); + parent::_releaseobject(realObject); + return nullptr; + } + mRealObject = static_cast(realObject); + ++mRealObject->asyncWrapperCount; return mRealObject; } @@ -92,7 +101,6 @@ bool RecursionGuard::sHasEntered = false; PluginAsyncSurrogate::PluginAsyncSurrogate(PluginModuleParent* aParent) : mParent(aParent) - , mInstance(nullptr) , mMode(0) , mWindow(nullptr) , mAcceptCalls(false) @@ -114,7 +122,10 @@ PluginAsyncSurrogate::Init(NPMIMEType aPluginType, NPP aInstance, uint16_t aMode int16_t aArgc, char* aArgn[], char* aArgv[]) { mMimeType = aPluginType; - mInstance = aInstance; + nsNPAPIPluginInstance* instance = + static_cast(aInstance->ndata); + MOZ_ASSERT(instance); + mInstance = instance; mMode = aMode; for (int i = 0; i < aArgc; ++i) { mNames.AppendElement(NullableString(aArgn[i])); @@ -153,7 +164,11 @@ PluginAsyncSurrogate::Cast(NPP aInstance) nsresult PluginAsyncSurrogate::NPP_New(NPError* aError) { - nsresult rv = mParent->NPP_NewInternal(mMimeType.BeginWriting(), mInstance, + if (!mInstance) { + return NS_ERROR_NULL_POINTER; + } + + nsresult rv = mParent->NPP_NewInternal(mMimeType.BeginWriting(), GetNPP(), mMode, mNames, mValues, nullptr, aError); if (NS_FAILED(rv)) { @@ -190,11 +205,21 @@ PluginAsyncSurrogate::NotifyDestroyPending(NPP aInstance) surrogate->NotifyDestroyPending(); } +NPP +PluginAsyncSurrogate::GetNPP() +{ + MOZ_ASSERT(mInstance); + NPP npp; + DebugOnly rv = mInstance->GetNPP(&npp); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + return npp; +} + void PluginAsyncSurrogate::NotifyDestroyPending() { mDestroyPending = true; - nsJSNPRuntime::OnPluginDestroyPending(mInstance); + nsJSNPRuntime::OnPluginDestroyPending(GetNPP()); } NPError @@ -204,7 +229,7 @@ PluginAsyncSurrogate::NPP_Destroy(NPSavedData** aSave) if (!WaitForInit()) { return NPERR_GENERIC_ERROR; } - return PluginModuleParent::NPP_Destroy(mInstance, aSave); + return PluginModuleParent::NPP_Destroy(GetNPP(), aSave); } NPError @@ -214,12 +239,13 @@ PluginAsyncSurrogate::NPP_GetValue(NPPVariable aVariable, void* aRetval) if (!WaitForInit()) { return NPERR_GENERIC_ERROR; } - PluginInstanceParent* instance = PluginInstanceParent::Cast(mInstance); + + PluginInstanceParent* instance = PluginInstanceParent::Cast(GetNPP()); MOZ_ASSERT(instance); return instance->NPP_GetValue(aVariable, aRetval); } - NPObject* npobject = parent::_createobject(mInstance, + NPObject* npobject = parent::_createobject(GetNPP(), const_cast(GetClass())); MOZ_ASSERT(npobject); MOZ_ASSERT(npobject->_class == GetClass()); @@ -234,7 +260,7 @@ PluginAsyncSurrogate::NPP_SetValue(NPNVariable aVariable, void* aValue) if (!WaitForInit()) { return NPERR_GENERIC_ERROR; } - return PluginModuleParent::NPP_SetValue(mInstance, aVariable, aValue); + return PluginModuleParent::NPP_SetValue(GetNPP(), aVariable, aValue); } NPError @@ -420,7 +446,10 @@ PluginAsyncSurrogate::DestroyAsyncStream(NPStream* aStream) // streamListener was suspended during async init. We must resume the stream // request prior to calling _destroystream for cleanup to work correctly. streamListener->ResumeRequest(); - parent::_destroystream(mInstance, aStream, NPRES_DONE); + if (!mInstance) { + return; + } + parent::_destroystream(GetNPP(), aStream, NPRES_DONE); } /* static */ bool @@ -436,16 +465,20 @@ PluginAsyncSurrogate::SetStreamType(NPStream* aStream, uint16_t aStreamType) void PluginAsyncSurrogate::OnInstanceCreated(PluginInstanceParent* aInstance) { - for (uint32_t i = 0, len = mPendingNewStreamCalls.Length(); i < len; ++i) { - PendingNewStreamCall& curPendingCall = mPendingNewStreamCalls[i]; - uint16_t streamType = NP_NORMAL; - NPError curError = aInstance->NPP_NewStream( - const_cast(NullableStringGet(curPendingCall.mType)), - curPendingCall.mStream, curPendingCall.mSeekable, - &streamType); - if (curError != NPERR_NO_ERROR) { - // If we failed here then the send failed and we need to clean up - DestroyAsyncStream(curPendingCall.mStream); + if (!mDestroyPending) { + // If NPP_Destroy has already been called then these streams have already + // been cleaned up on the browser side and are no longer valid. + for (uint32_t i = 0, len = mPendingNewStreamCalls.Length(); i < len; ++i) { + PendingNewStreamCall& curPendingCall = mPendingNewStreamCalls[i]; + uint16_t streamType = NP_NORMAL; + NPError curError = aInstance->NPP_NewStream( + const_cast(NullableStringGet(curPendingCall.mType)), + curPendingCall.mStream, curPendingCall.mSeekable, + &streamType); + if (curError != NPERR_NO_ERROR) { + // If we failed here then the send failed and we need to clean up + DestroyAsyncStream(curPendingCall.mStream); + } } } mPendingNewStreamCalls.Clear(); @@ -543,23 +576,26 @@ PluginAsyncSurrogate::AsyncCallArriving() void PluginAsyncSurrogate::NotifyAsyncInitFailed() { - // Clean up any pending NewStream requests - for (uint32_t i = 0, len = mPendingNewStreamCalls.Length(); i < len; ++i) { - PendingNewStreamCall& curPendingCall = mPendingNewStreamCalls[i]; - DestroyAsyncStream(curPendingCall.mStream); + if (!mDestroyPending) { + // Clean up any pending NewStream requests + for (uint32_t i = 0, len = mPendingNewStreamCalls.Length(); i < len; ++i) { + PendingNewStreamCall& curPendingCall = mPendingNewStreamCalls[i]; + DestroyAsyncStream(curPendingCall.mStream); + } } mPendingNewStreamCalls.Clear(); - nsNPAPIPluginInstance* inst = - static_cast(mInstance->ndata); - if (!inst) { + // Make sure that any WaitForInit calls on this surrogate will fail, or else + // we'll be perma-blocked + mInitCancelled = true; + + if (!mInstance) { return; } - nsPluginInstanceOwner* owner = inst->GetOwner(); - if (!owner) { - return; + nsPluginInstanceOwner* owner = mInstance->GetOwner(); + if (owner) { + owner->NotifyHostAsyncInitFailed(); } - owner->NotifyHostAsyncInitFailed(); } // static @@ -645,11 +681,11 @@ PluginAsyncSurrogate::ScriptableHasMethod(NPObject* aObject, // initialization, we should try again. const NPNetscapeFuncs* npn = object->mSurrogate->mParent->GetNetscapeFuncs(); NPObject* pluginObject = nullptr; - NPError nperror = npn->getvalue(object->mSurrogate->mInstance, + NPError nperror = npn->getvalue(object->mSurrogate->GetNPP(), NPNVPluginElementNPObject, (void*)&pluginObject); if (nperror == NPERR_NO_ERROR) { - NPPAutoPusher nppPusher(object->mSurrogate->mInstance); + NPPAutoPusher nppPusher(object->mSurrogate->GetNPP()); result = pluginObject->_class->hasMethod(pluginObject, aName); npn->releaseobject(pluginObject); NPUTF8* idstr = npn->utf8fromidentifier(aName); @@ -698,10 +734,10 @@ PluginAsyncSurrogate::GetPropertyHelper(NPObject* aObject, NPIdentifier aName, if (!success) { const NPNetscapeFuncs* npn = mParent->GetNetscapeFuncs(); NPObject* pluginObject = nullptr; - NPError nperror = npn->getvalue(mInstance, NPNVPluginElementNPObject, + NPError nperror = npn->getvalue(GetNPP(), NPNVPluginElementNPObject, (void*)&pluginObject); if (nperror == NPERR_NO_ERROR) { - NPPAutoPusher nppPusher(mInstance); + NPPAutoPusher nppPusher(GetNPP()); bool hasProperty = nsJSObjWrapper::HasOwnProperty(pluginObject, aName); NPUTF8* idstr = npn->utf8fromidentifier(aName); npn->memfree(idstr); @@ -806,11 +842,11 @@ PluginAsyncSurrogate::ScriptableHasProperty(NPObject* aObject, // object hadn't been set yet. Now that we're further along in // initialization, we should try again. NPObject* pluginObject = nullptr; - NPError nperror = npn->getvalue(object->mSurrogate->mInstance, + NPError nperror = npn->getvalue(object->mSurrogate->GetNPP(), NPNVPluginElementNPObject, (void*)&pluginObject); if (nperror == NPERR_NO_ERROR) { - NPPAutoPusher nppPusher(object->mSurrogate->mInstance); + NPPAutoPusher nppPusher(object->mSurrogate->GetNPP()); result = nsJSObjWrapper::HasOwnProperty(pluginObject, aName); npn->releaseobject(pluginObject); idstr = npn->utf8fromidentifier(aName); diff --git a/dom/plugins/ipc/PluginAsyncSurrogate.h b/dom/plugins/ipc/PluginAsyncSurrogate.h index 8256269ee3..b1dc0e2f61 100644 --- a/dom/plugins/ipc/PluginAsyncSurrogate.h +++ b/dom/plugins/ipc/PluginAsyncSurrogate.h @@ -21,6 +21,7 @@ namespace mozilla { namespace plugins { +struct ParentNPObject; class PluginInstanceParent; class PluginModuleParent; @@ -59,7 +60,7 @@ public: virtual PluginInstanceParent* GetInstance() { return nullptr; } - NPP GetNPP() { return mInstance; } + NPP GetNPP(); bool GetPropertyHelper(NPObject* aObject, NPIdentifier aName, bool* aHasProperty, bool* aHasMethod, @@ -140,7 +141,7 @@ private: PluginModuleParent* mParent; // These values are used to construct the plugin instance nsCString mMimeType; - NPP mInstance; + mozilla::WeakPtr mInstance; uint16_t mMode; InfallibleTArray mNames; InfallibleTArray mValues; @@ -168,7 +169,7 @@ struct AsyncNPObject : NPObject NPObject* GetRealObject(); RefPtr mSurrogate; - NPObject* mRealObject; + ParentNPObject* mRealObject; }; class MOZ_STACK_CLASS PushSurrogateAcceptCalls diff --git a/dom/plugins/ipc/PluginInstanceChild.cpp b/dom/plugins/ipc/PluginInstanceChild.cpp index 5eb100f36c..05006a983b 100644 --- a/dom/plugins/ipc/PluginInstanceChild.cpp +++ b/dom/plugins/ipc/PluginInstanceChild.cpp @@ -33,6 +33,7 @@ using mozilla::gfx::SharedDIBSurface; #include "mozilla/ipc/MessageChannel.h" #include "mozilla/AutoRestore.h" #include "mozilla/StaticPtr.h" +#include "mozilla/UniquePtr.h" #include "ImageContainer.h" using namespace mozilla; @@ -188,9 +189,6 @@ PluginInstanceChild::PluginInstanceChild(const NPPluginFuncs* aPluginIface, mWsInfo.display = DefaultXDisplay(); #endif #endif // MOZ_X11 && XP_UNIX && !XP_MACOSX -#if defined(OS_WIN) - memset(&mAlphaExtract, 0, sizeof(mAlphaExtract)); -#endif // OS_WIN #if defined(OS_WIN) InitPopupMenuHook(); if (GetQuirks() & QUIRK_UNITY_FIXUP_MOUSE_CAPTURE) { @@ -231,8 +229,8 @@ PluginInstanceChild::DoNPP_New() NS_ASSERTION(argc == (int) mValues.Length(), "argn.length != argv.length"); - nsAutoArrayPtr argn(new char*[1 + argc]); - nsAutoArrayPtr argv(new char*[1 + argc]); + UniquePtr argn(new char*[1 + argc]); + UniquePtr argv(new char*[1 + argc]); argn[argc] = 0; argv[argc] = 0; @@ -244,7 +242,7 @@ PluginInstanceChild::DoNPP_New() NPP npp = GetNPP(); NPError rv = mPluginIface->newp((char*)NullableStringGet(mMimeType), npp, - mMode, argc, argn, argv, 0); + mMode, argc, argn.get(), argv.get(), 0); if (NPERR_NO_ERROR != rv) { return rv; } @@ -502,6 +500,7 @@ PluginInstanceChild::NPN_GetValue(NPNVariable aVar, case NPNVSupportsXEmbedBool: case NPNVSupportsWindowless: NS_NOTREACHED("NPNVariable should be handled in PluginModuleChild."); + MOZ_FALLTHROUGH; #endif default: @@ -510,7 +509,6 @@ PluginInstanceChild::NPN_GetValue(NPNVariable aVar, (int) aVar, NPNVariableToString(aVar))); return NPERR_GENERIC_ERROR; } - } #ifdef MOZ_WIDGET_COCOA @@ -821,21 +819,6 @@ PluginInstanceChild::AnswerNPP_HandleEvent(const NPRemoteEvent& event, if (WM_NULL == evcopy.event) return true; - // Painting for win32. SharedSurfacePaint handles everything. - if (mWindow.type == NPWindowTypeDrawable) { - if (evcopy.event == WM_PAINT) { - *handled = SharedSurfacePaint(evcopy); - return true; - } - else if (DoublePassRenderingEvent() == evcopy.event) { - // We'll render to mSharedSurfaceDib first, then render to a cached bitmap - // we store locally. The two passes are for alpha extraction, so the second - // pass must be to a flat white surface in order for things to work. - mAlphaExtract.doublePass = RENDER_BACK_ONE; - *handled = true; - return true; - } - } *handled = WinlessHandleEvent(evcopy); return true; #endif @@ -1296,13 +1279,6 @@ PluginInstanceChild::AnswerNPP_SetWindow(const NPRemoteWindow& aWindow) } break; - case NPWindowTypeDrawable: - mWindow.type = aWindow.type; - if (GetQuirks() & QUIRK_FLASH_THROTTLE_WMUSER_EVENTS) - SetupFlashMsgThrottle(); - return SharedSurfaceSetWindow(aWindow); - break; - default: NS_NOTREACHED("Bad plugin window type."); return false; @@ -2024,187 +2000,6 @@ PluginInstanceChild::WinlessHandleEvent(NPEvent& event) return handled; } -/* windowless drawing helpers */ - -bool -PluginInstanceChild::SharedSurfaceSetWindow(const NPRemoteWindow& aWindow) -{ - // If the surfaceHandle is empty, parent is telling us we can reuse our cached - // memory surface and hdc. Otherwise, we need to reset, usually due to a - // expanding plugin port size. - if (!aWindow.surfaceHandle) { - if (!mSharedSurfaceDib.IsValid()) { - return false; - } - } - else { - // Attach to the new shared surface parent handed us. - if (NS_FAILED(mSharedSurfaceDib.Attach((SharedDIB::Handle)aWindow.surfaceHandle, - aWindow.width, aWindow.height, false))) - return false; - // Free any alpha extraction resources if needed. This will be reset - // the next time it's used. - AlphaExtractCacheRelease(); - } - - // NPRemoteWindow's origin is the origin of our shared dib. - mWindow.x = aWindow.x; - mWindow.y = aWindow.y; - mWindow.width = aWindow.width; - mWindow.height = aWindow.height; - mWindow.type = aWindow.type; - - mWindow.window = reinterpret_cast(mSharedSurfaceDib.GetHDC()); - ::SetViewportOrgEx(mSharedSurfaceDib.GetHDC(), - -aWindow.x, -aWindow.y, nullptr); - - if (mPluginIface->setwindow) - mPluginIface->setwindow(&mData, &mWindow); - - return true; -} - -void -PluginInstanceChild::SharedSurfaceRelease() -{ - mSharedSurfaceDib.Close(); - AlphaExtractCacheRelease(); -} - -/* double pass cache buffer - (rarely) used in cases where alpha extraction - * occurs for windowless plugins. */ - -bool -PluginInstanceChild::AlphaExtractCacheSetup() -{ - AlphaExtractCacheRelease(); - - mAlphaExtract.hdc = ::CreateCompatibleDC(nullptr); - - if (!mAlphaExtract.hdc) - return false; - - BITMAPINFOHEADER bmih; - memset((void*)&bmih, 0, sizeof(BITMAPINFOHEADER)); - bmih.biSize = sizeof(BITMAPINFOHEADER); - bmih.biWidth = mWindow.width; - bmih.biHeight = mWindow.height; - bmih.biPlanes = 1; - bmih.biBitCount = 32; - bmih.biCompression = BI_RGB; - - void* ppvBits = nullptr; - mAlphaExtract.bmp = ::CreateDIBSection(mAlphaExtract.hdc, - (BITMAPINFO*)&bmih, - DIB_RGB_COLORS, - (void**)&ppvBits, - nullptr, - (unsigned long)sizeof(BITMAPINFOHEADER)); - if (!mAlphaExtract.bmp) - return false; - - DeleteObject(::SelectObject(mAlphaExtract.hdc, mAlphaExtract.bmp)); - return true; -} - -void -PluginInstanceChild::AlphaExtractCacheRelease() -{ - if (mAlphaExtract.bmp) - ::DeleteObject(mAlphaExtract.bmp); - - if (mAlphaExtract.hdc) - ::DeleteObject(mAlphaExtract.hdc); - - mAlphaExtract.bmp = nullptr; - mAlphaExtract.hdc = nullptr; -} - -void -PluginInstanceChild::UpdatePaintClipRect(RECT* aRect) -{ - if (aRect) { - // Update the clip rect on our internal hdc - HRGN clip = ::CreateRectRgnIndirect(aRect); - ::SelectClipRgn(mSharedSurfaceDib.GetHDC(), clip); - ::DeleteObject(clip); - } -} - -int16_t -PluginInstanceChild::SharedSurfacePaint(NPEvent& evcopy) -{ - if (!mPluginIface->event) - return false; - - RECT* pRect = reinterpret_cast(evcopy.lParam); - - switch(mAlphaExtract.doublePass) { - case RENDER_NATIVE: - // pass the internal hdc to the plugin - UpdatePaintClipRect(pRect); - evcopy.wParam = WPARAM(mSharedSurfaceDib.GetHDC()); - return mPluginIface->event(&mData, reinterpret_cast(&evcopy)); - break; - case RENDER_BACK_ONE: - // Handle a double pass render used in alpha extraction for transparent - // plugins. (See nsPluginFrame and gfxWindowsNativeDrawing for details.) - // We render twice, once to the shared dib, and once to a cache which - // we copy back on a second paint. These paints can't be spread across - // multiple rpc messages as delays cause animation frame changes. - if (!mAlphaExtract.bmp && !AlphaExtractCacheSetup()) { - mAlphaExtract.doublePass = RENDER_NATIVE; - return false; - } - - // See gfxWindowsNativeDrawing, color order doesn't have to match. - UpdatePaintClipRect(pRect); - ::FillRect(mSharedSurfaceDib.GetHDC(), pRect, (HBRUSH)GetStockObject(WHITE_BRUSH)); - evcopy.wParam = WPARAM(mSharedSurfaceDib.GetHDC()); - if (!mPluginIface->event(&mData, reinterpret_cast(&evcopy))) { - mAlphaExtract.doublePass = RENDER_NATIVE; - return false; - } - - // Copy to cache. We render to shared dib so we don't have to call - // setwindow between calls (flash issue). - ::BitBlt(mAlphaExtract.hdc, - pRect->left, - pRect->top, - pRect->right - pRect->left, - pRect->bottom - pRect->top, - mSharedSurfaceDib.GetHDC(), - pRect->left, - pRect->top, - SRCCOPY); - - ::FillRect(mSharedSurfaceDib.GetHDC(), pRect, (HBRUSH)GetStockObject(BLACK_BRUSH)); - if (!mPluginIface->event(&mData, reinterpret_cast(&evcopy))) { - mAlphaExtract.doublePass = RENDER_NATIVE; - return false; - } - mAlphaExtract.doublePass = RENDER_BACK_TWO; - return true; - break; - case RENDER_BACK_TWO: - // copy our cached surface back - UpdatePaintClipRect(pRect); - ::BitBlt(mSharedSurfaceDib.GetHDC(), - pRect->left, - pRect->top, - pRect->right - pRect->left, - pRect->bottom - pRect->top, - mAlphaExtract.hdc, - pRect->left, - pRect->top, - SRCCOPY); - mAlphaExtract.doublePass = RENDER_NATIVE; - return true; - break; - } - return false; -} - /* flash msg throttling helpers */ // Flash has the unfortunate habit of flooding dispatch loops with custom @@ -2313,7 +2108,6 @@ PluginInstanceChild::EnumThreadWindowsCallback(HWND hWnd, return TRUE; } - void PluginInstanceChild::SetupFlashMsgThrottle() { @@ -2367,9 +2161,6 @@ PluginInstanceChild::FlashThrottleMessage(HWND aWnd, // that's done in Destroy. FlashThrottleAsyncMsg* task = new FlashThrottleAsyncMsg(this, aWnd, aMsg, aWParam, aLParam, isWindowed); - if (!task) - return; - { MutexAutoLock lock(mAsyncCallMutex); mPendingAsyncCalls.AppendElement(task); @@ -2610,7 +2401,6 @@ StreamNotifyChild::ActorDestroy(ActorDestroyReason why) } } - void StreamNotifyChild::SetAssociatedStream(BrowserStreamChild* bs) { @@ -4051,7 +3841,6 @@ PluginInstanceChild::Destroy() mCachedElementActor = nullptr; #if defined(OS_WIN) - SharedSurfaceRelease(); DestroyWinlessPopupSurrogate(); UnhookWinlessFlashThrottle(); DestroyPluginWindow(); diff --git a/dom/plugins/ipc/PluginInstanceChild.h b/dom/plugins/ipc/PluginInstanceChild.h index 7b41c37728..eabae22f74 100644 --- a/dom/plugins/ipc/PluginInstanceChild.h +++ b/dom/plugins/ipc/PluginInstanceChild.h @@ -427,29 +427,6 @@ private: */ nsAutoPtr< nsTHashtable > mDeletingHash; -#if defined(OS_WIN) -private: - // Shared dib rendering management for windowless plugins. - bool SharedSurfaceSetWindow(const NPRemoteWindow& aWindow); - int16_t SharedSurfacePaint(NPEvent& evcopy); - void SharedSurfaceRelease(); - bool AlphaExtractCacheSetup(); - void AlphaExtractCacheRelease(); - void UpdatePaintClipRect(RECT* aRect); - -private: - enum { - RENDER_NATIVE, - RENDER_BACK_ONE, - RENDER_BACK_TWO - }; - gfx::SharedDIBWin mSharedSurfaceDib; - struct { - uint16_t doublePass; - HDC hdc; - HBITMAP bmp; - } mAlphaExtract; -#endif // defined(OS_WIN) #if defined(MOZ_WIDGET_COCOA) private: #if defined(__i386__) diff --git a/dom/plugins/ipc/PluginInstanceParent.cpp b/dom/plugins/ipc/PluginInstanceParent.cpp index 8f5d51e616..45e0b0e535 100644 --- a/dom/plugins/ipc/PluginInstanceParent.cpp +++ b/dom/plugins/ipc/PluginInstanceParent.cpp @@ -196,7 +196,6 @@ PluginInstanceParent::ActorDestroy(ActorDestroyReason why) if (why == AbnormalShutdown) { // If the plugin process crashes, this is the only // chance we get to destroy resources. - SharedSurfaceRelease(); UnsubclassPluginWindow(); } #endif @@ -224,7 +223,6 @@ PluginInstanceParent::Destroy() } #if defined(OS_WIN) - SharedSurfaceRelease(); UnsubclassPluginWindow(); #endif @@ -957,11 +955,6 @@ PluginInstanceParent::NPP_SetWindow(const NPWindow* aWindow) #if defined(OS_WIN) // On windowless controls, reset the shared memory surface as needed. if (mWindowType == NPWindowTypeDrawable) { - // SharedSurfaceSetWindow will take care of NPRemoteWindow. - if (!SharedSurfaceSetWindow(aWindow, window)) { - return NPERR_OUT_OF_MEMORY_ERROR; - } - MaybeCreateChildPopupSurrogate(); } else { SubclassPluginWindow(reinterpret_cast(aWindow->window)); @@ -1196,23 +1189,7 @@ PluginInstanceParent::NPP_HandleEvent(void* event) #if defined(OS_WIN) if (mWindowType == NPWindowTypeDrawable) { - if (DoublePassRenderingEvent() == npevent->event) { - return CallPaint(npremoteevent, &handled) && handled; - } - switch (npevent->event) { - case WM_PAINT: - { - RECT rect; - SharedSurfaceBeforePaint(rect, npremoteevent); - if (!CallPaint(npremoteevent, &handled)) { - handled = false; - } - SharedSurfaceAfterPaint(npevent); - return handled; - } - break; - case WM_KILLFOCUS: { // When the user selects fullscreen mode in Flash video players, @@ -1444,6 +1421,12 @@ PluginInstanceParent::NPP_DestroyStream(NPStream* stream, NPReason reason) FULLFUNCTION, (void*) stream, (int) reason)); AStream* s = static_cast(stream->pdata); + if (!s) { + // The stream has already been deleted by other means. + // With async plugin init this could happen if async NPP_NewStream + // returns an error code. + return NPERR_NO_ERROR; + } if (s->IsBrowserStream()) { BrowserStreamParent* sp = static_cast(s); @@ -1593,11 +1576,6 @@ PluginInstanceParent::GetActorForNPObject(NPObject* aObject) } actor = new PluginScriptableObjectParent(LocalObject); - if (!actor) { - NS_ERROR("Out of memory!"); - return nullptr; - } - if (!SendPPluginScriptableObjectConstructor(actor)) { NS_WARNING("Failed to send constructor message!"); return nullptr; @@ -1764,10 +1742,17 @@ PluginInstanceParent::RecvAsyncNPP_NewResult(const NPError& aResult) mSurrogate->SetAcceptingCalls(true); } + // It is possible for a plugin instance to outlive its owner (eg. When a + // PluginDestructionGuard was on the stack at the time the owner was being + // destroyed). We need to handle that case. nsPluginInstanceOwner* owner = GetOwner(); - // It is possible for a plugin instance to outlive its owner when async - // plugin init is turned on, so we need to handle that case. - if (aResult != NPERR_NO_ERROR || !owner) { + if (!owner) { + // We can't do anything at this point, just return. Any pending browser + // streams will be cleaned up when the plugin instance is destroyed. + return true; + } + + if (aResult != NPERR_NO_ERROR) { mSurrogate->NotifyAsyncInitFailed(); return true; } @@ -1939,108 +1924,6 @@ PluginInstanceParent::UnsubclassPluginWindow() * painting: mPluginPort (nsIntRect, saved in SetWindow) */ -void -PluginInstanceParent::SharedSurfaceRelease() -{ - mSharedSurfaceDib.Close(); -} - -bool -PluginInstanceParent::SharedSurfaceSetWindow(const NPWindow* aWindow, - NPRemoteWindow& aRemoteWindow) -{ - aRemoteWindow.window = 0; - aRemoteWindow.x = aWindow->x; - aRemoteWindow.y = aWindow->y; - aRemoteWindow.width = aWindow->width; - aRemoteWindow.height = aWindow->height; - aRemoteWindow.type = aWindow->type; - - nsIntRect newPort(aWindow->x, aWindow->y, aWindow->width, aWindow->height); - - // save the the rect location within the browser window. - mPluginPort = newPort; - - // move the port to our shared surface origin - newPort.MoveTo(0,0); - - // check to see if we have the room in shared surface - if (mSharedSurfaceDib.IsValid() && mSharedSize.Contains(newPort)) { - // ok to paint - aRemoteWindow.surfaceHandle = 0; - return true; - } - - // allocate a new shared surface - SharedSurfaceRelease(); - if (NS_FAILED(mSharedSurfaceDib.Create(reinterpret_cast(aWindow->window), - newPort.width, newPort.height, false))) - return false; - - // save the new shared surface size we just allocated - mSharedSize = newPort; - - base::SharedMemoryHandle handle; - if (NS_FAILED(mSharedSurfaceDib.ShareToProcess(OtherPid(), &handle))) { - return false; - } - - aRemoteWindow.surfaceHandle = handle; - - return true; -} - -void -PluginInstanceParent::SharedSurfaceBeforePaint(RECT& rect, - NPRemoteEvent& npremoteevent) -{ - RECT* dr = (RECT*)npremoteevent.event.lParam; - HDC parentHdc = (HDC)npremoteevent.event.wParam; - - nsIntRect dirtyRect(dr->left, dr->top, dr->right-dr->left, dr->bottom-dr->top); - dirtyRect.MoveBy(-mPluginPort.x, -mPluginPort.y); // should always be smaller than dirtyRect - - ::BitBlt(mSharedSurfaceDib.GetHDC(), - dirtyRect.x, - dirtyRect.y, - dirtyRect.width, - dirtyRect.height, - parentHdc, - dr->left, - dr->top, - SRCCOPY); - - // setup the translated dirty rect we'll send to the child - rect.left = dirtyRect.x; - rect.top = dirtyRect.y; - rect.right = dirtyRect.x + dirtyRect.width; - rect.bottom = dirtyRect.y + dirtyRect.height; - - npremoteevent.event.wParam = WPARAM(0); - npremoteevent.event.lParam = LPARAM(&rect); -} - -void -PluginInstanceParent::SharedSurfaceAfterPaint(NPEvent* npevent) -{ - RECT* dr = (RECT*)npevent->lParam; - HDC parentHdc = (HDC)npevent->wParam; - - nsIntRect dirtyRect(dr->left, dr->top, dr->right-dr->left, dr->bottom-dr->top); - dirtyRect.MoveBy(-mPluginPort.x, -mPluginPort.y); - - // src copy the shared dib into the parent surface we are handed. - ::BitBlt(parentHdc, - dr->left, - dr->top, - dirtyRect.width, - dirtyRect.height, - mSharedSurfaceDib.GetHDC(), - dirtyRect.x, - dirtyRect.y, - SRCCOPY); -} - bool PluginInstanceParent::MaybeCreateAndParentChildPluginWindow() { diff --git a/dom/plugins/ipc/PluginInstanceParent.h b/dom/plugins/ipc/PluginInstanceParent.h index 9499c1ade9..9875a68cfb 100644 --- a/dom/plugins/ipc/PluginInstanceParent.h +++ b/dom/plugins/ipc/PluginInstanceParent.h @@ -353,11 +353,6 @@ private: #if defined(OS_WIN) private: - // Used in rendering windowless plugins in other processes. - bool SharedSurfaceSetWindow(const NPWindow* aWindow, NPRemoteWindow& aRemoteWindow); - void SharedSurfaceBeforePaint(RECT &rect, NPRemoteEvent& npremoteevent); - void SharedSurfaceAfterPaint(NPEvent* npevent); - void SharedSurfaceRelease(); // Used in handling parent/child forwarding of events. static LRESULT CALLBACK PluginWindowHookProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); @@ -368,7 +363,6 @@ private: void MaybeCreateChildPopupSurrogate(); private: - gfx::SharedDIBWin mSharedSurfaceDib; nsIntRect mPluginPort; nsIntRect mSharedSize; HWND mPluginHWND; diff --git a/dom/plugins/ipc/PluginMessageUtils.cpp b/dom/plugins/ipc/PluginMessageUtils.cpp index b09d9c3988..178bd25baa 100644 --- a/dom/plugins/ipc/PluginMessageUtils.cpp +++ b/dom/plugins/ipc/PluginMessageUtils.cpp @@ -54,9 +54,6 @@ NPRemoteWindow::NPRemoteWindow() : , visualID(0) , colormap(0) #endif /* XP_UNIX */ -#if defined(XP_WIN) - ,surfaceHandle(0) -#endif #if defined(XP_MACOSX) ,contentsScaleFactor(1.0) #endif @@ -156,18 +153,5 @@ void DeferNPVariantLastRelease(const NPNetscapeFuncs* f, NPVariant* v) VOID_TO_NPVARIANT(*v); } -#ifdef XP_WIN - -// The private event used for double-pass widgetless plugin rendering. -UINT DoublePassRenderingEvent() -{ - static UINT gEventID = 0; - if (!gEventID) - gEventID = ::RegisterWindowMessage(L"MozDoublePassMsg"); - return gEventID; -} - -#endif - } // namespace plugins } // namespace mozilla diff --git a/dom/plugins/ipc/PluginMessageUtils.h b/dom/plugins/ipc/PluginMessageUtils.h index 4d86ad0b6d..1caba2b365 100644 --- a/dom/plugins/ipc/PluginMessageUtils.h +++ b/dom/plugins/ipc/PluginMessageUtils.h @@ -12,6 +12,7 @@ #include "mozilla/ipc/MessageChannel.h" #include "mozilla/ipc/CrossProcessMutex.h" +#include "mozilla/UniquePtr.h" #include "gfxipc/ShadowLayerUtils.h" #include "npapi.h" @@ -90,9 +91,6 @@ struct NPRemoteWindow VisualID visualID; Colormap colormap; #endif /* XP_UNIX */ -#if defined(XP_WIN) - base::SharedMemoryHandle surfaceHandle; -#endif #if defined(XP_MACOSX) double contentsScaleFactor; #endif @@ -251,11 +249,6 @@ struct DeletingObjectEntry : public nsPtrHashKey bool mDeleted; }; -#ifdef XP_WIN -// The private event used for double-pass widgetless plugin rendering. -UINT DoublePassRenderingEvent(); -#endif - } /* namespace plugins */ } /* namespace mozilla */ @@ -342,9 +335,6 @@ struct ParamTraits aMsg->WriteULong(aParam.visualID); aMsg->WriteULong(aParam.colormap); #endif -#if defined(XP_WIN) - WriteParam(aMsg, aParam.surfaceHandle); -#endif #if defined(XP_MACOSX) aMsg->WriteDouble(aParam.contentsScaleFactor); #endif @@ -374,12 +364,6 @@ struct ParamTraits return false; #endif -#if defined(XP_WIN) - base::SharedMemoryHandle surfaceHandle; - if (!ReadParam(aMsg, aIter, &surfaceHandle)) - return false; -#endif - #if defined(XP_MACOSX) double contentsScaleFactor; if (!aMsg->ReadDouble(aIter, &contentsScaleFactor)) @@ -397,9 +381,6 @@ struct ParamTraits aResult->visualID = visualID; aResult->colormap = colormap; #endif -#if defined(XP_WIN) - aResult->surfaceHandle = surfaceHandle; -#endif #if defined(XP_MACOSX) aResult->contentsScaleFactor = contentsScaleFactor; #endif @@ -437,10 +418,10 @@ struct ParamTraits } const char* messageBuffer = nullptr; - nsAutoArrayPtr newBuffer(new char[byteCount]); + mozilla::UniquePtr newBuffer(new char[byteCount]); if (newBuffer && aMsg->ReadBytes(aIter, &messageBuffer, byteCount )) { memcpy((void*)messageBuffer, newBuffer.get(), byteCount); - aResult->UTF8Characters = newBuffer.forget(); + aResult->UTF8Characters = newBuffer.release(); return true; } } diff --git a/dom/plugins/ipc/PluginModuleChild.cpp b/dom/plugins/ipc/PluginModuleChild.cpp index 4795237117..8d93ee49eb 100644 --- a/dom/plugins/ipc/PluginModuleChild.cpp +++ b/dom/plugins/ipc/PluginModuleChild.cpp @@ -45,6 +45,8 @@ #ifdef XP_WIN #include "nsWindowsDllInterceptor.h" #include "mozilla/widget/AudioSession.h" +#include "WinUtils.h" +#include #endif #ifdef MOZ_WIDGET_COCOA @@ -159,7 +161,7 @@ PluginModuleChild::PluginModuleChild(bool aIsChrome) PluginModuleChild::~PluginModuleChild() { if (mTransport) { - // For some reason IPDL doesn't autmatically delete the channel for a + // For some reason IPDL doesn't automatically delete the channel for a // bridged protocol (bug 1090570). So we have to do it ourselves. This // code is only invoked for PluginModuleChild instances created via // bridging; otherwise mTransport is null. @@ -302,6 +304,8 @@ PluginModuleChild::InitForChrome(const std::string& aPluginFilename, return false; } + GetIPCChannel()->SetAbortOnError(true); + // TODO: use PluginPRLibrary here #if defined(OS_LINUX) || defined(OS_BSD) @@ -315,10 +319,6 @@ PluginModuleChild::InitForChrome(const std::string& aPluginFilename, NS_ASSERTION(mInitializeFunc, "couldn't find NP_Initialize()"); #elif defined(OS_WIN) || defined(OS_MACOSX) - - // no, don't do that - //GetIPCChannel()->SetAbortOnError(true); - mShutdownFunc = (NP_PLUGINSHUTDOWN)PR_FindFunctionSymbol(mLibrary, "NP_Shutdown"); @@ -1336,8 +1336,6 @@ void _memfree(void* aPtr) { PLUGIN_LOG_DEBUG_FUNCTION; - // Only assert plugin thread here for consistency with in-process plugins. - AssertPluginThread(); NS_Free(aPtr); } @@ -1345,8 +1343,6 @@ uint32_t _memflush(uint32_t aSize) { PLUGIN_LOG_DEBUG_FUNCTION; - // Only assert plugin thread here for consistency with in-process plugins. - AssertPluginThread(); return 0; } @@ -1405,8 +1401,6 @@ void* _memalloc(uint32_t aSize) { PLUGIN_LOG_DEBUG_FUNCTION; - // Only assert plugin thread here for consistency with in-process plugins. - AssertPluginThread(); return NS_Alloc(aSize); } @@ -1819,7 +1813,7 @@ _popupcontextmenu(NPP instance, NPMenu* menu) if (success) { return mozilla::plugins::PluginUtilsOSX::ShowCocoaContextMenu(menu, screenX, screenY, - PluginModuleChild::GetChrome(), + InstCast(instance)->Manager(), ProcessBrowserEvents); } else { NS_WARNING("Convertpoint failed, could not created contextmenu."); @@ -1978,6 +1972,29 @@ CreateFileAHookFn(LPCSTR fname, DWORD access, DWORD share, ftemplate); } +static bool +GetLocalLowTempPath(size_t aLen, LPWSTR aPath) +{ + NS_NAMED_LITERAL_STRING(tempname, "\\Temp"); + LPWSTR path; + if (SUCCEEDED(WinUtils::SHGetKnownFolderPath(FOLDERID_LocalAppDataLow, 0, + nullptr, &path))) { + if (wcslen(path) + tempname.Length() < aLen) { + wcscpy(aPath, path); + wcscat(aPath, tempname.get()); + ::CoTaskMemFree(path); + return true; + } + ::CoTaskMemFree(path); + } + + // XP doesn't support SHGetKnownFolderPath and LocalLow + if (!GetTempPathW(aLen, aPath)) { + return false; + } + return true; +} + HANDLE WINAPI CreateFileWHookFn(LPCWSTR fname, DWORD access, DWORD share, LPSECURITY_ATTRIBUTES security, DWORD creation, DWORD flags, @@ -1997,7 +2014,7 @@ CreateFileWHookFn(LPCWSTR fname, DWORD access, DWORD share, // This is the config file we want to rewrite WCHAR tempPath[MAX_PATH+1]; - if (GetTempPathW(MAX_PATH, tempPath) == 0) { + if (GetLocalLowTempPath(MAX_PATH, tempPath) == 0) { break; } WCHAR tempFile[MAX_PATH+1]; @@ -2170,6 +2187,17 @@ private: NPError mResult; }; +static void +RunAsyncNPP_New(void* aChildInstance) +{ + MOZ_ASSERT(aChildInstance); + PluginInstanceChild* childInstance = + static_cast(aChildInstance); + NPError rv = childInstance->DoNPP_New(); + AsyncNewResultSender* task = new AsyncNewResultSender(childInstance, rv); + childInstance->PostChildAsyncCall(task); +} + bool PluginModuleChild::RecvAsyncNPP_New(PPluginInstanceChild* aActor) { @@ -2177,9 +2205,8 @@ PluginModuleChild::RecvAsyncNPP_New(PPluginInstanceChild* aActor) PluginInstanceChild* childInstance = reinterpret_cast(aActor); AssertPluginThread(); - NPError rv = childInstance->DoNPP_New(); - AsyncNewResultSender* task = new AsyncNewResultSender(childInstance, rv); - childInstance->PostChildAsyncCall(task); + // We don't want to run NPP_New async from within nested calls + childInstance->AsyncCall(&RunAsyncNPP_New, childInstance); return true; } diff --git a/dom/plugins/ipc/PluginProcessParent.cpp b/dom/plugins/ipc/PluginProcessParent.cpp index ac8399e334..d6106c205a 100644 --- a/dom/plugins/ipc/PluginProcessParent.cpp +++ b/dom/plugins/ipc/PluginProcessParent.cpp @@ -67,14 +67,15 @@ AddSandboxAllowedFile(vector& aAllowedFiles, nsIProperties* aDirSv if (!aSuffix.IsEmpty()) { userDirPath.Append(aSuffix); } - aAllowedFiles.push_back(userDirPath.get()); + aAllowedFiles.push_back(std::wstring(userDirPath.get())); return; } static void AddSandboxAllowedFiles(int32_t aSandboxLevel, vector& aAllowedFilesRead, - vector& aAllowedFilesReadWrite) + vector& aAllowedFilesReadWrite, + vector& aAllowedDirectories) { if (aSandboxLevel < 2) { return; @@ -95,18 +96,27 @@ AddSandboxAllowedFiles(int32_t aSandboxLevel, } // Level 2 and above is now using low integrity, so we need to give write - // access to the Flash directories. + // access to the Flash directories. Access also has to be given to create + // the parent directories as they may not exist. + // This should be made Flash specific (Bug 1171396). AddSandboxAllowedFile(aAllowedFilesReadWrite, dirSvc, NS_WIN_APPDATA_DIR, NS_LITERAL_STRING("\\Macromedia\\Flash Player\\*")); + AddSandboxAllowedFile(aAllowedDirectories, dirSvc, NS_WIN_APPDATA_DIR, + NS_LITERAL_STRING("\\Macromedia\\Flash Player")); + AddSandboxAllowedFile(aAllowedDirectories, dirSvc, NS_WIN_APPDATA_DIR, + NS_LITERAL_STRING("\\Macromedia")); AddSandboxAllowedFile(aAllowedFilesReadWrite, dirSvc, NS_WIN_APPDATA_DIR, NS_LITERAL_STRING("\\Adobe\\Flash Player\\*")); + AddSandboxAllowedFile(aAllowedDirectories, dirSvc, NS_WIN_APPDATA_DIR, + NS_LITERAL_STRING("\\Adobe\\Flash Player")); + AddSandboxAllowedFile(aAllowedDirectories, dirSvc, NS_WIN_APPDATA_DIR, + NS_LITERAL_STRING("\\Adobe")); -#if defined(_X86_) - // Write access to the Temp directory should only be needed for 32-bit as - // it is used to turn off protected mode, which only applies to x86. + // Write access to the Temp directory is needed in some mochitest crash + // tests. + // Bug 1171393 tracks removing this requirement. AddSandboxAllowedFile(aAllowedFilesReadWrite, dirSvc, NS_OS_TEMP_DIR, NS_LITERAL_STRING("\\*")); -#endif } #endif @@ -117,7 +127,7 @@ PluginProcessParent::Launch(mozilla::UniquePtr aLaunchComple #if defined(XP_WIN) && defined(MOZ_SANDBOX) mSandboxLevel = aSandboxLevel; AddSandboxAllowedFiles(mSandboxLevel, mAllowedFilesRead, - mAllowedFilesReadWrite); + mAllowedFilesReadWrite, mAllowedDirectories); #else if (aSandboxLevel != 0) { MOZ_ASSERT(false, @@ -153,6 +163,9 @@ PluginProcessParent::Launch(mozilla::UniquePtr aLaunchComple else if (base::PROCESS_ARCH_ARM & pluginLibArchitectures & containerArchitectures) { selectedArchitecture = base::PROCESS_ARCH_ARM; } + else if (base::PROCESS_ARCH_MIPS & pluginLibArchitectures & containerArchitectures) { + selectedArchitecture = base::PROCESS_ARCH_MIPS; + } else { return false; } diff --git a/dom/plugins/ipc/PluginScriptableObjectParent.cpp b/dom/plugins/ipc/PluginScriptableObjectParent.cpp index d7b31567a6..5119df3b74 100644 --- a/dom/plugins/ipc/PluginScriptableObjectParent.cpp +++ b/dom/plugins/ipc/PluginScriptableObjectParent.cpp @@ -166,6 +166,15 @@ PluginScriptableObjectParent::ScriptableDeallocate(NPObject* aObject) } ParentNPObject* object = reinterpret_cast(aObject); + + if (object->asyncWrapperCount > 0) { + // In this case we should just drop the refcount to the asyncWrapperCount + // instead of deallocating because there are still some async wrappers + // out there that are referencing this object. + object->referenceCount = object->asyncWrapperCount; + return; + } + PluginScriptableObjectParent* actor = object->parent; if (actor) { NS_ASSERTION(actor->Type() == Proxy, "Bad type!"); diff --git a/dom/plugins/ipc/PluginScriptableObjectParent.h b/dom/plugins/ipc/PluginScriptableObjectParent.h index 0b75152bf1..8a1cdc0282 100644 --- a/dom/plugins/ipc/PluginScriptableObjectParent.h +++ b/dom/plugins/ipc/PluginScriptableObjectParent.h @@ -22,12 +22,17 @@ class PluginScriptableObjectParent; struct ParentNPObject : NPObject { ParentNPObject() - : NPObject(), parent(nullptr), invalidated(false) { } + : NPObject() + , parent(nullptr) + , invalidated(false) + , asyncWrapperCount(0) + {} // |parent| is always valid as long as the actor is alive. Once the actor is // destroyed this will be set to null. PluginScriptableObjectParent* parent; bool invalidated; + int32_t asyncWrapperCount; }; class PluginScriptableObjectParent : public PPluginScriptableObjectParent diff --git a/dom/plugins/test/testplugin/nptest.cpp b/dom/plugins/test/testplugin/nptest.cpp index fa4951aa52..a00ff0cba0 100644 --- a/dom/plugins/test/testplugin/nptest.cpp +++ b/dom/plugins/test/testplugin/nptest.cpp @@ -772,8 +772,6 @@ NPP_New(NPMIMEType pluginType, NPP instance, uint16_t mode, int16_t argc, char* // set up our our instance data InstanceData* instanceData = new InstanceData; - if (!instanceData) - return NPERR_OUT_OF_MEMORY_ERROR; instanceData->npp = instance; instanceData->streamMode = NP_ASFILEONLY; instanceData->testFunction = FUNCTION_NONE; diff --git a/dom/presentation/provider/MulticastDNSDeviceProvider.cpp b/dom/presentation/provider/MulticastDNSDeviceProvider.cpp index b4359220a7..79d8857432 100644 --- a/dom/presentation/provider/MulticastDNSDeviceProvider.cpp +++ b/dom/presentation/provider/MulticastDNSDeviceProvider.cpp @@ -175,9 +175,6 @@ MulticastDNSDeviceProvider::Init() } mWrappedListener = new DNSServiceWrappedListener(); - if (NS_WARN_IF(!mWrappedListener)) { - return NS_ERROR_OUT_OF_MEMORY; - } if (NS_WARN_IF(NS_FAILED(rv = mWrappedListener->SetListener(this)))) { return rv; } diff --git a/dom/xbl/nsXBLProtoImplMethod.cpp b/dom/xbl/nsXBLProtoImplMethod.cpp index 5e64417697..0ab6be9926 100644 --- a/dom/xbl/nsXBLProtoImplMethod.cpp +++ b/dom/xbl/nsXBLProtoImplMethod.cpp @@ -50,8 +50,6 @@ nsXBLProtoImplMethod::AppendBodyText(const nsAString& aText) nsXBLUncompiledMethod* uncompiledMethod = GetUncompiledMethod(); if (!uncompiledMethod) { uncompiledMethod = new nsXBLUncompiledMethod(); - if (!uncompiledMethod) - return; SetUncompiledMethod(uncompiledMethod); } @@ -72,8 +70,6 @@ nsXBLProtoImplMethod::AddParameter(const nsAString& aText) nsXBLUncompiledMethod* uncompiledMethod = GetUncompiledMethod(); if (!uncompiledMethod) { uncompiledMethod = new nsXBLUncompiledMethod(); - if (!uncompiledMethod) - return; SetUncompiledMethod(uncompiledMethod); } @@ -89,8 +85,6 @@ nsXBLProtoImplMethod::SetLineNumber(uint32_t aLineNumber) nsXBLUncompiledMethod* uncompiledMethod = GetUncompiledMethod(); if (!uncompiledMethod) { uncompiledMethod = new nsXBLUncompiledMethod(); - if (!uncompiledMethod) - return; SetUncompiledMethod(uncompiledMethod); } @@ -168,8 +162,6 @@ nsXBLProtoImplMethod::CompileMember(AutoJSAPI& jsapi, const nsString& aClassStr, char** args = nullptr; if (paramCount > 0) { args = new char*[paramCount]; - if (!args) - return NS_ERROR_OUT_OF_MEMORY; // Add our parameters to our args array. int32_t argPos = 0; diff --git a/dom/xbl/nsXBLProtoImplMethod.h b/dom/xbl/nsXBLProtoImplMethod.h index ff26c453a8..0afffcb2c3 100644 --- a/dom/xbl/nsXBLProtoImplMethod.h +++ b/dom/xbl/nsXBLProtoImplMethod.h @@ -65,8 +65,6 @@ struct nsXBLUncompiledMethod { void AddParameter(const nsAString& aText) { nsXBLParameter* param = new nsXBLParameter(aText); - if (!param) - return; if (!mParameters) mParameters = param; else diff --git a/dom/xbl/nsXBLResourceLoader.cpp b/dom/xbl/nsXBLResourceLoader.cpp index 8a9bae9f75..96e397d0db 100644 --- a/dom/xbl/nsXBLResourceLoader.cpp +++ b/dom/xbl/nsXBLResourceLoader.cpp @@ -197,9 +197,6 @@ void nsXBLResourceLoader::AddResource(nsIAtom* aResourceType, const nsAString& aSrc) { nsXBLResource* res = new nsXBLResource(aResourceType, aSrc); - if (!res) - return; - if (!mResourceList) mResourceList = res; else diff --git a/gfx/gl/AndroidSurfaceTexture.cpp b/gfx/gl/AndroidSurfaceTexture.cpp index 9d2e285a4e..0a239f1d53 100644 --- a/gfx/gl/AndroidSurfaceTexture.cpp +++ b/gfx/gl/AndroidSurfaceTexture.cpp @@ -165,7 +165,7 @@ AndroidSurfaceTexture::Init(GLContext* aContext, GLuint aTexture) return false; } - mNativeWindow = AndroidNativeWindow::CreateFromSurface(GetJNIForThread(), + mNativeWindow = AndroidNativeWindow::CreateFromSurface(jni::GetEnvForThread(), mSurface.Get()); MOZ_ASSERT(mNativeWindow, "Failed to create native window from surface"); @@ -206,7 +206,7 @@ AndroidSurfaceTexture::UpdateTexImage() void AndroidSurfaceTexture::GetTransformMatrix(gfx::Matrix4x4& aMatrix) { - JNIEnv* env = GetJNIForThread(); + JNIEnv* const env = jni::GetEnvForThread(); auto jarray = FloatArray::LocalRef::Adopt(env, env->NewFloatArray(16)); mSurfaceTexture->GetTransformMatrix(jarray); diff --git a/gfx/thebes/gfxPrefs.h b/gfx/thebes/gfxPrefs.h index 11610905c4..4a87e7293b 100644 --- a/gfx/thebes/gfxPrefs.h +++ b/gfx/thebes/gfxPrefs.h @@ -295,6 +295,7 @@ private: DECL_GFX_PREF(Live, "layers.acceleration.draw-fps.print-histogram", FPSPrintHistogram, bool, false); DECL_GFX_PREF(Live, "layers.acceleration.draw-fps.write-to-file", WriteFPSToFile, bool, false); DECL_GFX_PREF(Once, "layers.acceleration.force-enabled", LayersAccelerationForceEnabled, bool, false); + DECL_GFX_PREF(Once, "layers.amd-switchable-gfx.enabled", LayersAMDSwitchableGfxEnabled, bool, false); DECL_GFX_PREF(Once, "layers.async-pan-zoom.enabled", AsyncPanZoomEnabledDoNotUseDirectly, bool, true); DECL_GFX_PREF(Once, "layers.async-pan-zoom.separate-event-thread", AsyncPanZoomSeparateEventThread, bool, false); DECL_GFX_PREF(Live, "layers.bench.enabled", LayersBenchEnabled, bool, false); diff --git a/gfx/thebes/gfxUserFontSet.cpp b/gfx/thebes/gfxUserFontSet.cpp index f75f05a0cd..a6138c8792 100644 --- a/gfx/thebes/gfxUserFontSet.cpp +++ b/gfx/thebes/gfxUserFontSet.cpp @@ -16,6 +16,7 @@ #include "gfxFontConstants.h" #include "mozilla/Preferences.h" #include "mozilla/Services.h" +#include "mozilla/Telemetry.h" #include "mozilla/gfx/2D.h" #include "gfxPlatformFontList.h" @@ -433,6 +434,8 @@ gfxUserFontEntry::LoadNextSrc() gfxUserFontData::kUnknownCompression); mPlatformFontEntry = fe; SetLoadState(STATUS_LOADED); + Telemetry::Accumulate(Telemetry::WEBFONT_SRCTYPE, + currSrc.mSourceType + 1); return; } else { LOG(("userfonts (%p) [src %d] failed local: (%s) for (%s)\n", @@ -463,6 +466,14 @@ gfxUserFontEntry::LoadNextSrc() if (fe) { mPlatformFontEntry = fe; SetLoadState(STATUS_LOADED); + if (LOG_ENABLED()) { + nsAutoCString fontURI; + currSrc.mURI->GetSpec(fontURI); + LOG(("userfonts (%p) [src %d] " + "loaded uri from cache: (%s) for (%s)\n", + mFontSet, mSrcIndex, fontURI.get(), + NS_ConvertUTF16toUTF8(mFamilyName).get())); + } return; } } @@ -488,6 +499,8 @@ gfxUserFontEntry::LoadNextSrc() if (NS_SUCCEEDED(rv) && LoadPlatformFont(buffer, bufferLength)) { SetLoadState(STATUS_LOADED); + Telemetry::Accumulate(Telemetry::WEBFONT_SRCTYPE, + currSrc.mSourceType + 1); return; } else { mFontSet->LogMessage(this, @@ -542,6 +555,8 @@ gfxUserFontEntry::LoadNextSrc() // LoadPlatformFont takes ownership of the buffer, so no need // to free it here. SetLoadState(STATUS_LOADED); + Telemetry::Accumulate(Telemetry::WEBFONT_SRCTYPE, + currSrc.mSourceType + 1); return; } else { mFontSet->LogMessage(this, @@ -583,6 +598,7 @@ gfxUserFontEntry::LoadPlatformFont(const uint8_t* aFontData, uint32_t& aLength) gfxUserFontType fontType = gfxFontUtils::DetermineFontDataType(aFontData, aLength); + Telemetry::Accumulate(Telemetry::WEBFONT_FONTTYPE, uint32_t(fontType)); // Unwrap/decompress/sanitize or otherwise munge the downloaded data // to make a usable sfnt structure. @@ -595,12 +611,24 @@ gfxUserFontEntry::LoadPlatformFont(const uint8_t* aFontData, uint32_t& aLength) // Call the OTS sanitizer; this will also decode WOFF to sfnt // if necessary. The original data in aFontData is left unchanged. uint32_t saneLen; + uint32_t fontCompressionRatio = 0; const uint8_t* saneData = SanitizeOpenTypeData(aFontData, aLength, saneLen, fontType); if (!saneData) { mFontSet->LogMessage(this, "rejected by sanitizer"); } if (saneData) { + if (saneLen) { + fontCompressionRatio = uint32_t(100.0 * aLength / saneLen + 0.5); + if (fontType == GFX_USERFONT_WOFF || + fontType == GFX_USERFONT_WOFF2) { + Telemetry::Accumulate(fontType == GFX_USERFONT_WOFF ? + Telemetry::WEBFONT_COMPRESSION_WOFF : + Telemetry::WEBFONT_COMPRESSION_WOFF2, + fontCompressionRatio); + } + } + // The sanitizer ensures that we have a valid sfnt and a usable // name table, so this should never fail unless we're out of // memory, and GetFullNameFromSFNT is not directly exposed to @@ -647,11 +675,11 @@ gfxUserFontEntry::LoadPlatformFont(const uint8_t* aFontData, uint32_t& aLength) if (LOG_ENABLED()) { nsAutoCString fontURI; mSrcList[mSrcIndex].mURI->GetSpec(fontURI); - LOG(("userfonts (%p) [src %d] loaded uri: (%s) for (%s) (%p) gen: %8.8x\n", + LOG(("userfonts (%p) [src %d] loaded uri: (%s) for (%s) " + "(%p) gen: %8.8x compress: %d%%\n", mFontSet, mSrcIndex, fontURI.get(), NS_ConvertUTF16toUTF8(mFamilyName).get(), - this, - uint32_t(mFontSet->mGeneration))); + this, uint32_t(mFontSet->mGeneration), fontCompressionRatio)); } mPlatformFontEntry = fe; SetLoadState(STATUS_LOADED); @@ -744,7 +772,10 @@ gfxUserFontEntry::GetUserFontSets(nsTArray& aResult) } gfxUserFontSet::gfxUserFontSet() - : mFontFamilies(4), mLocalRulesUsed(false) + : mFontFamilies(4), + mLocalRulesUsed(false), + mDownloadCount(0), + mDownloadSize(0) { IncrementGeneration(true); gfxPlatformFontList* fp = gfxPlatformFontList::PlatformFontList(); diff --git a/gfx/thebes/gfxUserFontSet.h b/gfx/thebes/gfxUserFontSet.h index 96c12e4033..8403e306bd 100644 --- a/gfx/thebes/gfxUserFontSet.h +++ b/gfx/thebes/gfxUserFontSet.h @@ -468,6 +468,15 @@ public: static mozilla::LogModule* GetUserFontsLog(); + // record statistics about font completion + virtual void RecordFontLoadDone(uint32_t aFontSize, + mozilla::TimeStamp aDoneTime) {} + + void GetLoadStatistics(uint32_t& aLoadCount, uint64_t& aLoadSize) const { + aLoadCount = mDownloadCount; + aLoadSize = mDownloadSize; + } + protected: // Protected destructor, to discourage deletion outside of Release(): virtual ~gfxUserFontSet(); @@ -513,6 +522,10 @@ protected: // true when local names have been looked up, false otherwise bool mLocalRulesUsed; + + // performance stats + uint32_t mDownloadCount; + uint64_t mDownloadSize; }; // acts a placeholder until the real font is downloaded diff --git a/gfx/thebes/gfxWindowsNativeDrawing.cpp b/gfx/thebes/gfxWindowsNativeDrawing.cpp index 08bb04a0db..88f00bd766 100644 --- a/gfx/thebes/gfxWindowsNativeDrawing.cpp +++ b/gfx/thebes/gfxWindowsNativeDrawing.cpp @@ -184,28 +184,6 @@ gfxWindowsNativeDrawing::BeginNativeDrawing() } } -bool -gfxWindowsNativeDrawing::IsDoublePass() -{ - if (mContext->GetDrawTarget()->GetBackendType() != mozilla::gfx::BackendType::CAIRO || - mContext->GetDrawTarget()->IsDualDrawTarget()) { - return true; - } - - RefPtr surf = mContext->CurrentSurface(&mDeviceOffset.x, &mDeviceOffset.y); - if (!surf || surf->CairoStatus()) - return false; - if (surf->GetType() != gfxSurfaceType::Win32 && - surf->GetType() != gfxSurfaceType::Win32Printing) { - return true; - } - if ((surf->GetContentType() != gfxContentType::COLOR || - (surf->GetContentType() == gfxContentType::COLOR_ALPHA && - !(mNativeDrawFlags & CAN_DRAW_TO_COLOR_ALPHA)))) - return true; - return false; -} - bool gfxWindowsNativeDrawing::ShouldRenderAgain() { diff --git a/gfx/thebes/gfxWindowsNativeDrawing.h b/gfx/thebes/gfxWindowsNativeDrawing.h index ab2544736e..3490326c29 100644 --- a/gfx/thebes/gfxWindowsNativeDrawing.h +++ b/gfx/thebes/gfxWindowsNativeDrawing.h @@ -79,9 +79,6 @@ public: /* Returns true if the native drawing should be executed again */ bool ShouldRenderAgain(); - /* Returns true if double pass alpha extraction is taking place. */ - bool IsDoublePass(); - /* Places the result to the context, if necessary */ void PaintToContext(); diff --git a/gfx/thebes/gfxWindowsPlatform.cpp b/gfx/thebes/gfxWindowsPlatform.cpp index 8fe78a03fe..33e6fe1bc7 100644 --- a/gfx/thebes/gfxWindowsPlatform.cpp +++ b/gfx/thebes/gfxWindowsPlatform.cpp @@ -1682,7 +1682,7 @@ gfxWindowsPlatform::GetDXGIAdapter() return mAdapter; } -bool DoesD3D11DeviceWork(ID3D11Device *device) +bool DoesD3D11DeviceWork() { static bool checked = false; static bool result = false; @@ -1840,7 +1840,11 @@ bool DoesRenderTargetViewNeedsRecreating(ID3D11Device *device) deviceContext->CopyResource(cpuTexture, offscreenTexture); D3D11_MAPPED_SUBRESOURCE mapped; - deviceContext->Map(cpuTexture, 0, D3D11_MAP_READ, 0, &mapped); + hr = deviceContext->Map(cpuTexture, 0, D3D11_MAP_READ, 0, &mapped); + if (FAILED(hr)) { + gfxCriticalNote << "DoesRecreatingMapFailed " << hexa(hr); + return false; + } int resultColor = *(int*)mapped.pData; deviceContext->Unmap(cpuTexture, 0); cpuTexture->Release(); @@ -1859,6 +1863,22 @@ bool DoesRenderTargetViewNeedsRecreating(ID3D11Device *device) return result; } +static bool TryCreateTexture2D(ID3D11Device *device, + D3D11_TEXTURE2D_DESC* desc, + D3D11_SUBRESOURCE_DATA* data, + RefPtr& texture) +{ + // Older Intel driver version (see bug 1221348 for version #s) crash when + // creating a texture with shared keyed mutex and data. + MOZ_SEH_TRY { + return !FAILED(device->CreateTexture2D(desc, data, getter_AddRefs(texture))); + } MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { + // For now we want to aggregrate all the crash signature to a known crash. + MOZ_CRASH("Crash creating texture. See bug 1221348."); + return false; + } +} + // See bug 1083071. On some drivers, Direct3D 11 CreateShaderResourceView fails // with E_OUTOFMEMORY. @@ -1883,6 +1903,9 @@ bool DoesD3D11TextureSharingWorkInternal(ID3D11Device *device, DXGI_FORMAT forma gfxInfo->GetAdapterVendorID(vendorID); gfxInfo->GetAdapterVendorID2(vendorID2); if (vendorID.EqualsLiteral("0x8086") && vendorID2.IsEmpty()) { + if (!gfxPrefs::LayersAMDSwitchableGfxEnabled()) { + return false; + } gfxCriticalError(CriticalLog::DefaultOptions(false)) << "PossiblyBrokenSurfaceSharing_UnexpectedAMDGPU"; } } @@ -1907,12 +1930,30 @@ bool DoesD3D11TextureSharingWorkInternal(ID3D11Device *device, DXGI_FORMAT forma for (size_t i = 0; i < sizeof(color)/sizeof(color[0]); i++) { color[i] = 0xff00ffff; } - // We're going to check that sharing actually works with this format - D3D11_SUBRESOURCE_DATA data; - data.pSysMem = color; - data.SysMemPitch = texture_size * 4; - data.SysMemSlicePitch = 0; - if (FAILED(device->CreateTexture2D(&desc, &data, getter_AddRefs(texture)))) { + // XXX If we pass the data directly at texture creation time we + // get a crash on Intel 8.5.10.[18xx-1994] drivers. + // We can work around this issue by doing UpdateSubresource. + if (!TryCreateTexture2D(device, &desc, nullptr, texture)) { + gfxCriticalError() << "DoesD3D11TextureSharingWork_TryCreateTextureFailure"; + return false; + } + + RefPtr sourceSharedMutex; + texture->QueryInterface(__uuidof(IDXGIKeyedMutex), (void**)getter_AddRefs(sourceSharedMutex)); + if (FAILED(sourceSharedMutex->AcquireSync(0, 30*1000))) { + gfxCriticalError() << "DoesD3D11TextureSharingWork_SourceMutexTimeout"; + // only wait for 30 seconds + return false; + } + + RefPtr deviceContext; + device->GetImmediateContext(getter_AddRefs(deviceContext)); + + int stride = texture_size * 4; + deviceContext->UpdateSubresource(texture, 0, nullptr, color, stride, stride * texture_size); + + if (FAILED(sourceSharedMutex->ReleaseSync(0))) { + gfxCriticalError() << "DoesD3D11TextureSharingWork_SourceReleaseSyncTimeout"; return false; } @@ -1921,10 +1962,12 @@ bool DoesD3D11TextureSharingWorkInternal(ID3D11Device *device, DXGI_FORMAT forma if (FAILED(texture->QueryInterface(__uuidof(IDXGIResource), getter_AddRefs(otherResource)))) { + gfxCriticalError() << "DoesD3D11TextureSharingWork_GetResourceFailure"; return false; } if (FAILED(otherResource->GetSharedHandle(&shareHandle))) { + gfxCriticalError() << "DoesD3D11TextureSharingWork_GetSharedTextureFailure"; return false; } @@ -1940,6 +1983,7 @@ bool DoesD3D11TextureSharingWorkInternal(ID3D11Device *device, DXGI_FORMAT forma if (FAILED(sharedResource->QueryInterface(__uuidof(ID3D11Texture2D), getter_AddRefs(sharedTexture)))) { + gfxCriticalError() << "DoesD3D11TextureSharingWork_GetSharedTextureFailure"; return false; } @@ -1950,13 +1994,12 @@ bool DoesD3D11TextureSharingWorkInternal(ID3D11Device *device, DXGI_FORMAT forma desc.MiscFlags = 0; desc.BindFlags = 0; if (FAILED(device->CreateTexture2D(&desc, nullptr, getter_AddRefs(cpuTexture)))) { + gfxCriticalError() << "DoesD3D11TextureSharingWork_CreateTextureFailure"; return false; } RefPtr sharedMutex; - RefPtr deviceContext; sharedResource->QueryInterface(__uuidof(IDXGIKeyedMutex), (void**)getter_AddRefs(sharedMutex)); - device->GetImmediateContext(getter_AddRefs(deviceContext)); if (FAILED(sharedMutex->AcquireSync(0, 30*1000))) { gfxCriticalError() << "DoesD3D11TextureSharingWork_AcquireSyncTimeout"; // only wait for 30 seconds @@ -2117,7 +2160,7 @@ gfxWindowsPlatform::AttemptD3D11DeviceCreation() gfxCriticalError() << "D3D11 device creation failed: " << hexa(hr); return FeatureStatus::Failed; } - if (!DoesD3D11DeviceWork(mD3D11Device)) { + if (!DoesD3D11DeviceWork()) { mD3D11Device = nullptr; return FeatureStatus::Blocked; } @@ -2651,7 +2694,7 @@ gfxWindowsPlatform::CreateD3D11DecoderDevice() return nullptr; } - if (FAILED(hr) || !device || !DoesD3D11DeviceWork(device)) { + if (FAILED(hr) || !device || !DoesD3D11DeviceWork()) { return nullptr; } diff --git a/ipc/app/Makefile.in b/ipc/app/Makefile.in index 03f2b54ffd..1a16fee869 100644 --- a/ipc/app/Makefile.in +++ b/ipc/app/Makefile.in @@ -2,10 +2,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/. -ifneq ($(dir $(PROGRAM)),./) - GENERATED_DIRS = $(dir $(PROGRAM)) -endif - ifndef MOZ_WINCONSOLE ifdef MOZ_DEBUG MOZ_WINCONSOLE = 1 diff --git a/ipc/app/moz.build b/ipc/app/moz.build index 3bdb97b12b..a32a3df60d 100644 --- a/ipc/app/moz.build +++ b/ipc/app/moz.build @@ -4,11 +4,15 @@ # 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/. +# Any changes that affect Android need to be made in pie/moz.build as well. + if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android': Program(CONFIG['MOZ_CHILD_PROCESS_NAME']) SOURCES += [ 'MozillaRuntimeMainAndroid.cpp', ] + + DIRS += ['pie'] else: kwargs = { 'linkage': None, diff --git a/ipc/app/pie/moz.build b/ipc/app/pie/moz.build new file mode 100644 index 0000000000..d631af46e2 --- /dev/null +++ b/ipc/app/pie/moz.build @@ -0,0 +1,32 @@ +# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +Program(CONFIG['MOZ_CHILD_PROCESS_NAME_PIE']) +SOURCES += [ + '../MozillaRuntimeMainAndroid.cpp', +] + +include('/ipc/chromium/chromium-config.mozbuild') + +LOCAL_INCLUDES += [ + '/toolkit/xre', + '/xpcom/base', +] + +if CONFIG['MOZ_SANDBOX']: + USE_LIBS += [ + 'mozsandbox', + ] + + # gcc lto likes to put the top level asm in syscall.cc in a different partition + # from the function using it which breaks the build. Work around that by + # forcing there to be only one partition. + if '-flto' in CONFIG['OS_CXXFLAGS'] and not CONFIG['CLANG_CXX']: + LDFLAGS += ['--param lto-partitions=1'] + +LDFLAGS += ['-pie'] + +FAIL_ON_WARNINGS = True diff --git a/ipc/chromium/src/base/command_line.h b/ipc/chromium/src/base/command_line.h index 24a106bbc2..8ae11dabc9 100644 --- a/ipc/chromium/src/base/command_line.h +++ b/ipc/chromium/src/base/command_line.h @@ -119,6 +119,12 @@ class CommandLine { // Append a loose value to the command line. void AppendLooseValue(const std::wstring& value); +#if defined(OS_WIN) + void AppendLooseValue(const wchar_t* value) { + AppendLooseValue(std::wstring(value)); + } +#endif + // Append the arguments from another command line to this one. // If |include_program| is true, include |other|'s program as well. void AppendArguments(const CommandLine& other, diff --git a/ipc/chromium/src/base/file_path.h b/ipc/chromium/src/base/file_path.h index 73aa6830f8..a8317cd4e4 100644 --- a/ipc/chromium/src/base/file_path.h +++ b/ipc/chromium/src/base/file_path.h @@ -116,6 +116,10 @@ class FilePath { FilePath(const FilePath& that) : path_(that.path_) {} explicit FilePath(const StringType& path) : path_(path) {} +#if defined(OS_WIN) + explicit FilePath(const wchar_t* path) : path_(path) {} +#endif + FilePath& operator=(const FilePath& that) { path_ = that.path_; return *this; diff --git a/ipc/chromium/src/base/process_util.h b/ipc/chromium/src/base/process_util.h index c12e64a7cd..a8ce30437a 100644 --- a/ipc/chromium/src/base/process_util.h +++ b/ipc/chromium/src/base/process_util.h @@ -71,7 +71,8 @@ enum ProcessArchitecture { PROCESS_ARCH_I386 = 0x1, PROCESS_ARCH_X86_64 = 0x2, PROCESS_ARCH_PPC = 0x4, - PROCESS_ARCH_ARM = 0x8 + PROCESS_ARCH_ARM = 0x8, + PROCESS_ARCH_MIPS = 0x10 }; inline ProcessArchitecture GetCurrentProcessArchitecture() @@ -85,6 +86,8 @@ inline ProcessArchitecture GetCurrentProcessArchitecture() currentArchitecture = base::PROCESS_ARCH_PPC; #elif defined(ARCH_CPU_ARMEL) currentArchitecture = base::PROCESS_ARCH_ARM; +#elif defined(ARCH_CPU_MIPS) + currentArchitecture = base::PROCESS_ARCH_MIPS; #endif return currentArchitecture; } diff --git a/ipc/glue/GeckoChildProcessHost.cpp b/ipc/glue/GeckoChildProcessHost.cpp index e88e7f3300..241e4a569d 100644 --- a/ipc/glue/GeckoChildProcessHost.cpp +++ b/ipc/glue/GeckoChildProcessHost.cpp @@ -56,6 +56,10 @@ using mozilla::ipc::GeckoChildProcessHost; static const int kMagicAndroidSystemPropFd = 5; #endif +#ifdef MOZ_WIDGET_ANDROID +#include "AndroidBridge.h" +#endif + static const bool kLowRightsSubprocesses = // We currently only attempt to drop privileges on gonk, because we // have no plugins or extensions to worry about breaking. @@ -94,11 +98,11 @@ GeckoChildProcessHost::GeckoChildProcessHost(GeckoProcessType aProcessType, mPrivileges(aPrivileges), mMonitor("mozilla.ipc.GeckChildProcessHost.mMonitor"), mProcessState(CREATING_CHANNEL), - mDelegate(nullptr), #if defined(MOZ_SANDBOX) && defined(XP_WIN) mEnableSandboxLogging(false), mSandboxLevel(0), #endif + mDelegate(nullptr), mChildProcessHandle(0) #if defined(MOZ_WIDGET_COCOA) , mChildTask(MACH_PORT_NULL) @@ -168,7 +172,17 @@ GeckoChildProcessHost::GetPathToBinary(FilePath& exePath) exePath = exePath.DirName(); } +#ifdef MOZ_WIDGET_ANDROID + exePath = exePath.AppendASCII("lib"); + + // We must use the PIE binary on 5.0 and higher + const char* processName = mozilla::AndroidBridge::Bridge()->GetAPIVersion() >= 21 ? + MOZ_CHILD_PROCESS_NAME_PIE : MOZ_CHILD_PROCESS_NAME; + + exePath = exePath.AppendASCII(processName); +#else exePath = exePath.AppendASCII(MOZ_CHILD_PROCESS_NAME); +#endif } #ifdef MOZ_WIDGET_COCOA @@ -519,12 +533,15 @@ AddAppDirToCommandLine(std::vector& aCmdLine) NS_GET_IID(nsIFile), getter_AddRefs(appDir)); if (NS_SUCCEEDED(rv)) { +#if defined(XP_WIN) + nsString path; + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(appDir->GetPath(path))); + aCmdLine.AppendLooseValue(UTF8ToWide("-appdir")); + std::wstring wpath = path.get(); + aCmdLine.AppendLooseValue(wpath); +#else nsAutoCString path; MOZ_ALWAYS_TRUE(NS_SUCCEEDED(appDir->GetNativePath(path))); -#if defined(XP_WIN) - aCmdLine.AppendLooseValue(UTF8ToWide("-appdir")); - aCmdLine.AppendLooseValue(UTF8ToWide(path.get())); -#else aCmdLine.push_back("-appdir"); aCmdLine.push_back(path.get()); #endif @@ -542,26 +559,27 @@ MaybeAddNsprLogFileAccess(std::vector& aAllowedFilesReadWrite) return; } - nsCOMPtr file; - nsresult rv = NS_GetSpecialDirectory(NS_OS_CURRENT_WORKING_DIR, - getter_AddRefs(file)); - if (NS_FAILED(rv) || !file) { - NS_WARNING("Failed to get current working directory"); - return; - } - - nsDependentCString nsprLogFile(nsprLogFileEnv); - rv = file->AppendRelativeNativePath(nsprLogFile); + nsDependentCString nsprLogFilePath(nsprLogFileEnv); + nsCOMPtr nsprLogFile; + nsresult rv = NS_NewNativeLocalFile(nsprLogFilePath, true, + getter_AddRefs(nsprLogFile)); if (NS_FAILED(rv)) { - // Not a relative path, try it as an absolute one. - rv = file->InitWithNativePath(nsprLogFile); + // Not an absolute path, try it as a relative one. + nsresult rv = NS_GetSpecialDirectory(NS_OS_CURRENT_WORKING_DIR, + getter_AddRefs(nsprLogFile)); + if (NS_FAILED(rv) || !nsprLogFile) { + NS_WARNING("Failed to get current working directory"); + return; + } + + rv = nsprLogFile->AppendRelativeNativePath(nsprLogFilePath); if (NS_WARN_IF(NS_FAILED(rv))) { return; } } nsAutoString resolvedFilePath; - rv = file->GetPath(resolvedFilePath); + rv = nsprLogFile->GetPath(resolvedFilePath); if (NS_WARN_IF(NS_FAILED(rv))) { return; } @@ -570,7 +588,7 @@ MaybeAddNsprLogFileAccess(std::vector& aAllowedFilesReadWrite) // Chromium sandbox can only allow access to fully qualified file paths. This // only affects the environment for the child process we're about to create, // because this will get reset to the original value in PerformAsyncLaunch. - aAllowedFilesReadWrite.push_back(resolvedFilePath.get()); + aAllowedFilesReadWrite.push_back(std::wstring(resolvedFilePath.get())); nsAutoCString resolvedEnvVar("NSPR_LOG_FILE="); AppendUTF16toUTF8(resolvedFilePath, resolvedEnvVar); PR_SetEnv(resolvedEnvVar.get()); @@ -624,32 +642,19 @@ GeckoChildProcessHost::PerformAsyncLaunchInternal(std::vector& aExt path += "/lib"; # endif // MOZ_WIDGET_ANDROID const char *ld_library_path = PR_GetEnv("LD_LIBRARY_PATH"); - nsCString new_ld_lib_path; - if (ld_library_path && *ld_library_path) { - new_ld_lib_path.Assign(path.get()); - new_ld_lib_path.Append(':'); - new_ld_lib_path.Append(ld_library_path); - newEnvVars["LD_LIBRARY_PATH"] = new_ld_lib_path.get(); - } else { - newEnvVars["LD_LIBRARY_PATH"] = path.get(); - } + nsCString new_ld_lib_path(path.get()); # if (MOZ_WIDGET_GTK == 3) if (mProcessType == GeckoProcessType_Plugin) { - const char *ld_preload = PR_GetEnv("LD_PRELOAD"); - nsCString new_ld_preload; - - new_ld_preload.Assign(path.get()); - new_ld_preload.AppendLiteral("/" DLL_PREFIX "mozgtk2" DLL_SUFFIX); - - if (ld_preload && *ld_preload) { - new_ld_preload.AppendLiteral(":"); - new_ld_preload.Append(ld_preload); - } - newEnvVars["LD_PRELOAD"] = new_ld_preload.get(); + new_ld_lib_path.Append("/gtk2:"); + new_ld_lib_path.Append(path.get()); } -# endif // MOZ_WIDGET_GTK - +#endif + if (ld_library_path && *ld_library_path) { + new_ld_lib_path.Append(':'); + new_ld_lib_path.Append(ld_library_path); + } + newEnvVars["LD_LIBRARY_PATH"] = new_ld_lib_path.get(); # elif OS_MACOSX newEnvVars["DYLD_LIBRARY_PATH"] = path.get(); @@ -665,7 +670,7 @@ GeckoChildProcessHost::PerformAsyncLaunchInternal(std::vector& aExt // been set up by whatever may have launched the browser. const char* prevInterpose = PR_GetEnv("DYLD_INSERT_LIBRARIES"); nsCString interpose; - if (prevInterpose) { + if (prevInterpose && strlen(prevInterpose) > 0) { interpose.Assign(prevInterpose); interpose.Append(':'); } @@ -919,6 +924,12 @@ GeckoChildProcessHost::PerformAsyncLaunchInternal(std::vector& aExt ++it) { mSandboxBroker.AllowReadWriteFile(it->c_str()); } + + for (auto it = mAllowedDirectories.begin(); + it != mAllowedDirectories.end(); + ++it) { + mSandboxBroker.AllowDirectory(it->c_str()); + } } #endif // XP_WIN && MOZ_SANDBOX @@ -948,6 +959,16 @@ GeckoChildProcessHost::PerformAsyncLaunchInternal(std::vector& aExt #endif { base::LaunchApp(cmdLine, false, false, &process); + +#ifdef MOZ_SANDBOX + // We need to be able to duplicate handles to non-sandboxed content + // processes, so add it as a target peer. + if (mProcessType == GeckoProcessType_Content) { + if (!mSandboxBroker.AddTargetPeer(process)) { + NS_WARNING("Failed to add content process as target peer."); + } + } +#endif } #else diff --git a/ipc/glue/GeckoChildProcessHost.h b/ipc/glue/GeckoChildProcessHost.h index 96eef639af..7d45b57f82 100644 --- a/ipc/glue/GeckoChildProcessHost.h +++ b/ipc/glue/GeckoChildProcessHost.h @@ -160,6 +160,7 @@ protected: SandboxBroker mSandboxBroker; std::vector mAllowedFilesRead; std::vector mAllowedFilesReadWrite; + std::vector mAllowedDirectories; bool mEnableSandboxLogging; int32_t mSandboxLevel; #endif diff --git a/ipc/glue/moz.build b/ipc/glue/moz.build index e724cc0b53..53bdee0b38 100644 --- a/ipc/glue/moz.build +++ b/ipc/glue/moz.build @@ -165,8 +165,8 @@ include('/ipc/chromium/chromium-config.mozbuild') FINAL_LIBRARY = 'xul' -for var in ('MOZ_CHILD_PROCESS_NAME', 'MOZ_CHILD_PROCESS_BUNDLE', - 'DLL_PREFIX', 'DLL_SUFFIX'): +for var in ('MOZ_CHILD_PROCESS_NAME', 'MOZ_CHILD_PROCESS_NAME_PIE', + 'MOZ_CHILD_PROCESS_BUNDLE', 'DLL_PREFIX', 'DLL_SUFFIX'): DEFINES[var] = '"%s"' % CONFIG[var] if CONFIG['MOZ_SANDBOX'] and CONFIG['OS_ARCH'] == 'WINNT': diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index bc390c0634..c9c6c134d4 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -1088,6 +1088,20 @@ PresShell::Destroy() LogTextPerfStats(tp, this, tp->cumulative, 0.0, eLog_totals, nullptr); } } + if (mPresContext) { + gfxUserFontSet* fs = mPresContext->GetUserFontSet(); + if (fs) { + uint32_t fontCount; + uint64_t fontSize; + fs->GetLoadStatistics(fontCount, fontSize); + Telemetry::Accumulate(Telemetry::WEBFONT_PER_PAGE, fontCount); + Telemetry::Accumulate(Telemetry::WEBFONT_SIZE_PER_PAGE, + uint32_t(fontSize/1024)); + } else { + Telemetry::Accumulate(Telemetry::WEBFONT_PER_PAGE, 0); + Telemetry::Accumulate(Telemetry::WEBFONT_SIZE_PER_PAGE, 0); + } + } #ifdef MOZ_REFLOW_PERF DumpReflows(); diff --git a/layout/generic/AsyncScrollBase.h b/layout/generic/AsyncScrollBase.h index 0487491686..9a1725ba88 100644 --- a/layout/generic/AsyncScrollBase.h +++ b/layout/generic/AsyncScrollBase.h @@ -20,7 +20,7 @@ public: typedef mozilla::TimeStamp TimeStamp; typedef mozilla::TimeDuration TimeDuration; - AsyncScrollBase(nsPoint aStartPos); + explicit AsyncScrollBase(nsPoint aStartPos); void Update(TimeStamp aTime, nsPoint aDestination, @@ -87,6 +87,6 @@ protected: nsSMILKeySpline mTimingFunctionY; }; -} +} // namespace mozilla #endif // mozilla_layout_AsyncScrollBase_h_ diff --git a/layout/generic/ScrollbarActivity.cpp b/layout/generic/ScrollbarActivity.cpp index dfe6ed7557..e61e693324 100644 --- a/layout/generic/ScrollbarActivity.cpp +++ b/layout/generic/ScrollbarActivity.cpp @@ -363,7 +363,11 @@ SetOpacityOnElement(nsIContent* aContent, double aOpacity) bool ScrollbarActivity::UpdateOpacity(TimeStamp aTime) { - double progress = (aTime - mFadeBeginTime) / FadeDuration(); + // Avoid division by zero if mScrollbarFadeDuration is zero, just jump + // to the end of the fade animation + double progress = mScrollbarFadeDuration + ? ((aTime - mFadeBeginTime) / FadeDuration()) + : 1.0; double opacity = 1.0 - std::max(0.0, std::min(1.0, progress)); // 'this' may be getting destroyed during SetOpacityOnElement calls. diff --git a/layout/generic/crashtests/crashtests.list b/layout/generic/crashtests/crashtests.list index 62d679127b..a439554a88 100644 --- a/layout/generic/crashtests/crashtests.list +++ b/layout/generic/crashtests/crashtests.list @@ -367,6 +367,7 @@ asserts-if(winWidget,0-3) load 499885-1.xhtml load 501535-1.html load 503961-1.xhtml load 503961-2.html +load 505912-1.html load 508168-1.html load 508816-1.xul load 508908-1.html diff --git a/layout/generic/nsPluginFrame.cpp b/layout/generic/nsPluginFrame.cpp index 6cfafff35b..720f439d50 100644 --- a/layout/generic/nsPluginFrame.cpp +++ b/layout/generic/nsPluginFrame.cpp @@ -301,8 +301,8 @@ nsPluginFrame::PrepForDrawing(nsIWidget *aWidget) viewMan->InsertChild(view, mInnerView, nullptr, true); mWidget->SetParent(parentWidget); - mWidget->Show(true); mWidget->Enable(true); + mWidget->Show(true); // Set the plugin window to have an empty clip region until we know // what our true position, size and clip region are. These @@ -1090,7 +1090,7 @@ void nsPluginFrame::DidSetWidgetGeometry() { #if defined(XP_MACOSX) - if (mInstanceOwner) { + if (mInstanceOwner && !IsHidden()) { mInstanceOwner->FixUpPluginWindow(nsPluginInstanceOwner::ePluginPaintEnable); } #else @@ -1535,10 +1535,6 @@ nsPluginFrame::PaintPlugin(nsDisplayListBuilder* aBuilder, nsRenderingContext& aRenderingContext, const nsRect& aDirtyRect, const nsRect& aPluginRect) { -#if defined(XP_MACOSX) - DrawTarget& aDrawTarget = *aRenderingContext.GetDrawTarget(); -#endif - #if defined(MOZ_WIDGET_ANDROID) if (mInstanceOwner) { gfxRect frameGfxRect = @@ -1551,219 +1547,17 @@ nsPluginFrame::PaintPlugin(nsDisplayListBuilder* aBuilder, mInstanceOwner->Paint(ctx, frameGfxRect, dirtyGfxRect); return; } -#endif - - // Screen painting code -#if defined(XP_MACOSX) - // delegate all painting to the plugin instance. +#else +# if defined(DEBUG) + // On Desktop, we should have built a layer as we no longer support in-process + // plugins or synchronous painting. We can only get here for windowed plugins + // (which draw themselves), or via some error/unload state. if (mInstanceOwner) { - if (mInstanceOwner->GetDrawingModel() == NPDrawingModelCoreGraphics || - mInstanceOwner->GetDrawingModel() == NPDrawingModelCoreAnimation || - mInstanceOwner->GetDrawingModel() == - NPDrawingModelInvalidatingCoreAnimation) { - int32_t appUnitsPerDevPixel = PresContext()->AppUnitsPerDevPixel(); - // Clip to the content area where the plugin should be drawn. If - // we don't do this, the plugin can draw outside its bounds. - nsIntRect contentPixels = aPluginRect.ToNearestPixels(appUnitsPerDevPixel); - nsIntRect dirtyPixels = aDirtyRect.ToOutsidePixels(appUnitsPerDevPixel); - nsIntRect clipPixels; - clipPixels.IntersectRect(contentPixels, dirtyPixels); - - // Don't invoke the drawing code if the clip is empty. - if (clipPixels.IsEmpty()) - return; - - gfxRect nativeClipRect(clipPixels.x, clipPixels.y, - clipPixels.width, clipPixels.height); - gfxContext* ctx = aRenderingContext.ThebesContext(); - - gfxContextAutoSaveRestore save(ctx); - ctx->NewPath(); - ctx->Rectangle(nativeClipRect); - ctx->Clip(); - gfxPoint offset(contentPixels.x, contentPixels.y); - ctx->SetMatrix( - ctx->CurrentMatrix().Translate(offset)); - - gfxQuartzNativeDrawing nativeDrawing(aDrawTarget, - ToRect(nativeClipRect - offset)); - - CGContextRef cgContext = nativeDrawing.BeginNativeDrawing(); - if (!cgContext) { - NS_WARNING("null CGContextRef during PaintPlugin"); - return; - } - - RefPtr inst; - GetPluginInstance(getter_AddRefs(inst)); - if (!inst) { - NS_WARNING("null plugin instance during PaintPlugin"); - nativeDrawing.EndNativeDrawing(); - return; - } - NPWindow* window; - mInstanceOwner->GetWindow(window); - if (!window) { - NS_WARNING("null plugin window during PaintPlugin"); - nativeDrawing.EndNativeDrawing(); - return; - } - NP_CGContext* cgPluginPortCopy = - static_cast(mInstanceOwner->GetPluginPortCopy()); - if (!cgPluginPortCopy) { - NS_WARNING("null plugin port copy during PaintPlugin"); - nativeDrawing.EndNativeDrawing(); - return; - } - - mInstanceOwner->BeginCGPaint(); - if (mInstanceOwner->GetDrawingModel() == NPDrawingModelCoreAnimation || - mInstanceOwner->GetDrawingModel() == - NPDrawingModelInvalidatingCoreAnimation) { - // CoreAnimation is updated, render the layer and perform a readback. - mInstanceOwner->RenderCoreAnimation(cgContext, window->width, window->height); - } else { - mInstanceOwner->Paint(nativeClipRect - offset, cgContext); - } - mInstanceOwner->EndCGPaint(); - - nativeDrawing.EndNativeDrawing(); - } else { - gfxContext* ctx = aRenderingContext.ThebesContext(); - - // Translate the context: - gfxPoint devPixelPt = - nsLayoutUtils::PointToGfxPoint(aPluginRect.TopLeft(), - PresContext()->AppUnitsPerDevPixel()); - - gfxContextMatrixAutoSaveRestore autoSR(ctx); - ctx->SetMatrix(ctx->CurrentMatrix().Translate(devPixelPt)); - - // FIXME - Bug 385435: Doesn't aDirtyRect need translating too? - - // this rect is used only in the CoreGraphics drawing model - gfxRect tmpRect(0, 0, 0, 0); - mInstanceOwner->Paint(tmpRect, nullptr); - } - } -#elif defined(MOZ_X11) - if (mInstanceOwner) { - NPWindow *window; + NPWindow *window = nullptr; mInstanceOwner->GetWindow(window); - if (window->type == NPWindowTypeDrawable) { - gfxRect frameGfxRect = - PresContext()->AppUnitsToGfxUnits(aPluginRect); - gfxRect dirtyGfxRect = - PresContext()->AppUnitsToGfxUnits(aDirtyRect); - gfxContext* ctx = aRenderingContext.ThebesContext(); - - mInstanceOwner->Paint(ctx, frameGfxRect, dirtyGfxRect); - } - } -#elif defined(XP_WIN) - RefPtr inst; - GetPluginInstance(getter_AddRefs(inst)); - if (inst) { - gfxRect frameGfxRect = - PresContext()->AppUnitsToGfxUnits(aPluginRect); - gfxRect dirtyGfxRect = - PresContext()->AppUnitsToGfxUnits(aDirtyRect); - gfxContext *ctx = aRenderingContext.ThebesContext(); - gfxMatrix currentMatrix = ctx->CurrentMatrix(); - - if (ctx->UserToDevicePixelSnapped(frameGfxRect, false)) { - dirtyGfxRect = ctx->UserToDevice(dirtyGfxRect); - ctx->SetMatrix(gfxMatrix()); - } - dirtyGfxRect.RoundOut(); - - // Look if it's windowless - NPWindow *window; - mInstanceOwner->GetWindow(window); - - if (window->type == NPWindowTypeDrawable) { - // the offset of the DC - nsPoint origin; - - gfxWindowsNativeDrawing nativeDraw(ctx, frameGfxRect); - if (nativeDraw.IsDoublePass()) { - // OOP plugin specific: let the shim know before we paint if we are doing a - // double pass render. If this plugin isn't oop, the register window message - // will be ignored. - NPEvent pluginEvent; - pluginEvent.event = plugins::DoublePassRenderingEvent(); - pluginEvent.wParam = 0; - pluginEvent.lParam = 0; - if (pluginEvent.event) - inst->HandleEvent(&pluginEvent, nullptr); - } - do { - HDC hdc = nativeDraw.BeginNativeDrawing(); - if (!hdc) - return; - - RECT dest; - nativeDraw.TransformToNativeRect(frameGfxRect, dest); - RECT dirty; - nativeDraw.TransformToNativeRect(dirtyGfxRect, dirty); - - window->window = hdc; - window->x = dest.left; - window->y = dest.top; - window->clipRect.left = 0; - window->clipRect.top = 0; - // if we're painting, we're visible. - window->clipRect.right = window->width; - window->clipRect.bottom = window->height; - - // Windowless plugins on windows need a special event to update their location, - // see bug 135737. - // - // bug 271442: note, the rectangle we send is now purely the bounds of the plugin - // relative to the window it is contained in, which is useful for the plugin to - // correctly translate mouse coordinates. - // - // this does not mesh with the comments for bug 135737 which imply that the rectangle - // must be clipped in some way to prevent the plugin attempting to paint over areas - // it shouldn't. - // - // since the two uses of the rectangle are mutually exclusive in some cases, and - // since I don't see any incorrect painting (at least with Flash and ViewPoint - - // the originator of bug 135737), it seems that windowless plugins are not relying - // on information here for clipping their drawing, and we can safely use this message - // to tell the plugin exactly where it is in all cases. - - nsIntPoint origin = GetWindowOriginInPixels(true); - nsIntRect winlessRect = nsIntRect(origin, nsIntSize(window->width, window->height)); - - if (!mWindowlessRect.IsEqualEdges(winlessRect)) { - mWindowlessRect = winlessRect; - - WINDOWPOS winpos; - memset(&winpos, 0, sizeof(winpos)); - winpos.x = mWindowlessRect.x; - winpos.y = mWindowlessRect.y; - winpos.cx = mWindowlessRect.width; - winpos.cy = mWindowlessRect.height; - - // finally, update the plugin by sending it a WM_WINDOWPOSCHANGED event - NPEvent pluginEvent; - pluginEvent.event = WM_WINDOWPOSCHANGED; - pluginEvent.wParam = 0; - pluginEvent.lParam = (LPARAM)&winpos; - inst->HandleEvent(&pluginEvent, nullptr); - } - - inst->SetWindow(window); - - mInstanceOwner->Paint(dirty, hdc); - nativeDraw.EndNativeDrawing(); - } while (nativeDraw.ShouldRenderAgain()); - nativeDraw.PaintToContext(); - } - - ctx->SetMatrix(currentMatrix); + MOZ_ASSERT(!window || window->type == NPWindowTypeWindow); } +# endif #endif } diff --git a/layout/style/FontFaceSet.cpp b/layout/style/FontFaceSet.cpp index 589cd3ca9f..217e3162dd 100644 --- a/layout/style/FontFaceSet.cpp +++ b/layout/style/FontFaceSet.cpp @@ -18,6 +18,7 @@ #include "mozilla/Logging.h" #include "mozilla/Preferences.h" #include "mozilla/Snprintf.h" +#include "mozilla/Telemetry.h" #include "nsCORSListenerProxy.h" #include "nsCSSParser.h" #include "nsDeviceContext.h" @@ -39,6 +40,7 @@ #include "nsPrintfCString.h" #include "nsStyleSet.h" #include "nsUTF8Utils.h" +#include "nsDOMNavigationTiming.h" using namespace mozilla; using namespace mozilla::css; @@ -305,6 +307,17 @@ FontFaceSet::FindMatchingFontFaces(const nsAString& aFont, } } +TimeStamp +FontFaceSet::GetNavigationStartTimeStamp() +{ + TimeStamp navStart; + RefPtr timing(mDocument->GetNavigationTiming()); + if (timing) { + navStart = timing->GetNavigationStartTimeStamp(); + } + return navStart; +} + already_AddRefed FontFaceSet::Load(JSContext* aCx, const nsAString& aFont, @@ -1720,6 +1733,26 @@ FontFaceSet::UserFontSet::StartLoad(gfxUserFontEntry* aUserFontEntry, return mFontFaceSet->StartLoad(aUserFontEntry, aFontFaceSrc); } +void +FontFaceSet::UserFontSet::RecordFontLoadDone(uint32_t aFontSize, + TimeStamp aDoneTime) +{ + mDownloadCount++; + mDownloadSize += aFontSize; + Telemetry::Accumulate(Telemetry::WEBFONT_SIZE, aFontSize / 1024); + + if (!mFontFaceSet) { + return; + } + + TimeStamp navStart = mFontFaceSet->GetNavigationStartTimeStamp(); + TimeStamp zero; + if (navStart != zero) { + Telemetry::AccumulateTimeDelta(Telemetry::WEBFONT_DOWNLOAD_TIME_AFTER_START, + navStart, aDoneTime); + } +} + /* virtual */ nsresult FontFaceSet::UserFontSet::LogMessage(gfxUserFontEntry* aUserFontEntry, const char* aMessage, diff --git a/layout/style/FontFaceSet.h b/layout/style/FontFaceSet.h index 91ff5c6b2b..a18734368a 100644 --- a/layout/style/FontFaceSet.h +++ b/layout/style/FontFaceSet.h @@ -68,6 +68,9 @@ public: virtual nsresult StartLoad(gfxUserFontEntry* aUserFontEntry, const gfxFontFaceSrc* aFontFaceSrc) override; + void RecordFontLoadDone(uint32_t aFontSize, + mozilla::TimeStamp aDoneTime) override; + protected: virtual bool GetPrivateBrowsing() override; virtual nsresult SyncLoadFontData(gfxUserFontEntry* aFontToLoad, @@ -293,6 +296,8 @@ private: nsTArray& aFontFaces, mozilla::ErrorResult& aRv); + TimeStamp GetNavigationStartTimeStamp(); + RefPtr mUserFontSet; // The document this is a FontFaceSet for. diff --git a/layout/style/nsFontFaceLoader.cpp b/layout/style/nsFontFaceLoader.cpp index 8ccbd23357..2b8fb0698b 100644 --- a/layout/style/nsFontFaceLoader.cpp +++ b/layout/style/nsFontFaceLoader.cpp @@ -13,6 +13,7 @@ #include "nsError.h" #include "nsContentUtils.h" #include "mozilla/Preferences.h" +#include "mozilla/Telemetry.h" #include "FontFaceSet.h" #include "nsPresContext.h" #include "nsIPrincipal.h" @@ -39,6 +40,7 @@ nsFontFaceLoader::nsFontFaceLoader(gfxUserFontEntry* aUserFontEntry, mFontFaceSet(aFontFaceSet), mChannel(aChannel) { + mStartTime = TimeStamp::Now(); } nsFontFaceLoader::~nsFontFaceLoader() @@ -148,12 +150,17 @@ nsFontFaceLoader::OnStreamComplete(nsIStreamLoader* aLoader, mFontFaceSet->RemoveLoader(this); + TimeStamp doneTime = TimeStamp::Now(); + TimeDuration downloadTime = doneTime - mStartTime; + uint32_t downloadTimeMS = uint32_t(downloadTime.ToMilliseconds()); + Telemetry::Accumulate(Telemetry::WEBFONT_DOWNLOAD_TIME, downloadTimeMS); + if (LOG_ENABLED()) { nsAutoCString fontURI; mFontURI->GetSpec(fontURI); if (NS_SUCCEEDED(aStatus)) { - LOG(("userfonts (%p) download completed - font uri: (%s)\n", - this, fontURI.get())); + LOG(("userfonts (%p) download completed - font uri: (%s) time: %d ms\n", + this, fontURI.get(), downloadTimeMS)); } else { LOG(("userfonts (%p) download failed - font uri: (%s) error: %8.8x\n", this, fontURI.get(), aStatus)); @@ -188,6 +195,8 @@ nsFontFaceLoader::OnStreamComplete(nsIStreamLoader* aLoader, bool fontUpdate = mUserFontEntry->FontDataDownloadComplete(aString, aStringLen, aStatus); + mFontFaceSet->GetUserFontSet()->RecordFontLoadDone(aStringLen, doneTime); + // when new font loaded, need to reflow if (fontUpdate) { nsTArray fontSets; diff --git a/layout/style/nsFontFaceLoader.h b/layout/style/nsFontFaceLoader.h index 5203c68c8b..6ccda0f0f2 100644 --- a/layout/style/nsFontFaceLoader.h +++ b/layout/style/nsFontFaceLoader.h @@ -10,6 +10,7 @@ #define nsFontFaceLoader_h_ #include "mozilla/Attributes.h" +#include "mozilla/TimeStamp.h" #include "nsCOMPtr.h" #include "nsIStreamLoader.h" #include "nsIChannel.h" @@ -55,7 +56,7 @@ private: RefPtr mFontFaceSet; nsCOMPtr mChannel; nsCOMPtr mLoadTimer; - + TimeStamp mStartTime; nsIStreamLoader* mStreamLoader; }; diff --git a/media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.cpp b/media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.cpp index 865563f977..1707273d5c 100644 --- a/media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.cpp +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.cpp @@ -205,6 +205,7 @@ EverySecondTelemetryCallback_s(nsAutoPtr aQueryList) { for (auto q = aQueryList->begin(); q != aQueryList->end(); ++q) { PeerConnectionImpl::ExecuteStatsQuery_s(*q); auto& r = *(*q)->report; + bool isHello = (*q)->isHello; if (r.mInboundRTPStreamStats.WasPassed()) { // First, get reports from a second ago, if any, for calculations below const Sequence *lastInboundStats = nullptr; @@ -237,9 +238,15 @@ EverySecondTelemetryCallback_s(nsAutoPtr aQueryList) { } if (s.mMozRtt.WasPassed()) { MOZ_ASSERT(s.mIsRemote); - Accumulate(isAudio? WEBRTC_AUDIO_QUALITY_OUTBOUND_RTT : - WEBRTC_VIDEO_QUALITY_OUTBOUND_RTT, - s.mMozRtt.Value()); + ID id; + if (isAudio) { + id = isHello ? LOOP_AUDIO_QUALITY_OUTBOUND_RTT : + WEBRTC_AUDIO_QUALITY_OUTBOUND_RTT; + } else { + id = isHello ? LOOP_VIDEO_QUALITY_OUTBOUND_RTT : + WEBRTC_VIDEO_QUALITY_OUTBOUND_RTT; + } + Accumulate(id, s.mMozRtt.Value()); } if (lastInboundStats && s.mBytesReceived.WasPassed()) { auto& laststats = *lastInboundStats; @@ -249,15 +256,32 @@ EverySecondTelemetryCallback_s(nsAutoPtr aQueryList) { if (lasts.mBytesReceived.WasPassed()) { auto delta_ms = int32_t(s.mTimestamp.Value() - lasts.mTimestamp.Value()); - if (delta_ms > 0 && delta_ms < 60000) { - Accumulate(s.mIsRemote? - (isAudio? WEBRTC_AUDIO_QUALITY_OUTBOUND_BANDWIDTH_KBITS : - WEBRTC_VIDEO_QUALITY_OUTBOUND_BANDWIDTH_KBITS) : - (isAudio? WEBRTC_AUDIO_QUALITY_INBOUND_BANDWIDTH_KBITS : - WEBRTC_VIDEO_QUALITY_INBOUND_BANDWIDTH_KBITS), - ((s.mBytesReceived.Value() - - lasts.mBytesReceived.Value()) * 8) / delta_ms); + // In theory we're called every second, so delta *should* be in that range. + // Small deltas could cause errors due to division + if (delta_ms > 500 && delta_ms < 60000) { + ID id; + if (s.mIsRemote) { + if (isAudio) { + id = isHello ? LOOP_AUDIO_QUALITY_OUTBOUND_BANDWIDTH_KBITS : + WEBRTC_AUDIO_QUALITY_OUTBOUND_BANDWIDTH_KBITS; + } else { + id = isHello ? LOOP_VIDEO_QUALITY_OUTBOUND_BANDWIDTH_KBITS : + WEBRTC_VIDEO_QUALITY_OUTBOUND_BANDWIDTH_KBITS; + } + } else { + if (isAudio) { + id = isHello ? LOOP_AUDIO_QUALITY_INBOUND_BANDWIDTH_KBITS : + WEBRTC_AUDIO_QUALITY_INBOUND_BANDWIDTH_KBITS; + } else { + id = isHello ? LOOP_VIDEO_QUALITY_INBOUND_BANDWIDTH_KBITS : + WEBRTC_VIDEO_QUALITY_INBOUND_BANDWIDTH_KBITS; + } + } + Accumulate(id, ((s.mBytesReceived.Value() - + lasts.mBytesReceived.Value()) * 8) / delta_ms); } + // We could accumulate values until enough time has passed + // and then Accumulate() but this isn't that important. } } } diff --git a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp index 8c5abc2133..9165294f60 100644 --- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp @@ -381,6 +381,7 @@ PeerConnectionImpl::PeerConnectionImpl(const GlobalObject* aGlobal) , mIdentity(nullptr) #endif , mPrivacyRequested(false) + , mIsLoop(false) , mSTSThread(nullptr) , mAllowIceLoopback(false) , mAllowIceLinkLocal(false) @@ -698,6 +699,12 @@ PeerConnectionImpl::Initialize(PeerConnectionObserver& aObserver, location->Release(); CopyUTF16toUTF8(locationAStr, locationCStr); +#define HELLO_CLICKER_URL_START "https://hello.firefox.com/" +#define HELLO_INITIATOR_URL_START "about:loop" + mIsLoop = (strncmp(HELLO_CLICKER_URL_START, locationCStr.get(), + strlen(HELLO_CLICKER_URL_START)) == 0) || + (strncmp(HELLO_INITIATOR_URL_START, locationCStr.get(), + strlen(HELLO_INITIATOR_URL_START)) == 0); } PR_snprintf( @@ -2480,7 +2487,9 @@ PeerConnectionImpl::ShutdownMedia() // End of call to be recorded in Telemetry if (!mStartTime.IsNull()){ TimeDuration timeDelta = TimeStamp::Now() - mStartTime; - Telemetry::Accumulate(Telemetry::WEBRTC_CALL_DURATION, timeDelta.ToSeconds()); + Telemetry::Accumulate(mIsLoop ? Telemetry::LOOP_CALL_DURATION : + Telemetry::WEBRTC_CALL_DURATION, + timeDelta.ToSeconds()); } #endif @@ -2759,10 +2768,12 @@ void PeerConnectionImpl::IceConnectionStateChange( if (!mIceStartTime.IsNull()){ TimeDuration timeDelta = TimeStamp::Now() - mIceStartTime; if (isSucceeded(domState)) { - Telemetry::Accumulate(Telemetry::WEBRTC_ICE_SUCCESS_TIME, + Telemetry::Accumulate(mIsLoop ? Telemetry::LOOP_ICE_SUCCESS_TIME : + Telemetry::WEBRTC_ICE_SUCCESS_TIME, timeDelta.ToMilliseconds()); } else if (isFailed(domState)) { - Telemetry::Accumulate(Telemetry::WEBRTC_ICE_FAILURE_TIME, + Telemetry::Accumulate(mIsLoop ? Telemetry::LOOP_ICE_FAILURE_TIME : + Telemetry::WEBRTC_ICE_FAILURE_TIME, timeDelta.ToMilliseconds()); } } @@ -2921,6 +2932,7 @@ PeerConnectionImpl::BuildStatsQuery_m( query->iceStartTime = mIceStartTime; query->failed = isFailed(mIceConnectionState); + query->isHello = mIsLoop; // Populate SDP on main if (query->internalStats) { @@ -3379,8 +3391,11 @@ PeerConnectionImpl::startCallTelem() { mStartTime = TimeStamp::Now(); // Increment session call counter + // If we want to track Loop calls independently here, we need two mConnectionCounters int &cnt = PeerConnectionCtx::GetInstance()->mConnectionCounter; - Telemetry::GetHistogramById(Telemetry::WEBRTC_CALL_COUNT)->Subtract(cnt); + if (cnt > 0) { + Telemetry::GetHistogramById(Telemetry::WEBRTC_CALL_COUNT)->Subtract(cnt); + } cnt++; Telemetry::GetHistogramById(Telemetry::WEBRTC_CALL_COUNT)->Add(cnt); } diff --git a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h index 33967d9ea8..9f64c7774f 100644 --- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h @@ -214,6 +214,7 @@ class RTCStatsQuery { std::string error; // A timestamp to help with telemetry. mozilla::TimeStamp iceStartTime; + bool isHello; // Just for convenience, maybe integrate into the report later bool failed; @@ -476,6 +477,8 @@ public: } #endif + bool IsLoop() const { return mIsLoop; } + // this method checks to see if we've made a promise to protect media. bool PrivacyRequested() const { return mPrivacyRequested; } @@ -741,6 +744,7 @@ private: // A name for this PC that we are willing to expose to content. std::string mName; + bool mIsLoop; // For telemetry; doesn't have to be 100% right // The target to run stuff on nsCOMPtr mSTSThread; diff --git a/media/webrtc/signaling/src/peerconnection/WebrtcGlobalInformation.cpp b/media/webrtc/signaling/src/peerconnection/WebrtcGlobalInformation.cpp index 18c15587d4..361ea83473 100644 --- a/media/webrtc/signaling/src/peerconnection/WebrtcGlobalInformation.cpp +++ b/media/webrtc/signaling/src/peerconnection/WebrtcGlobalInformation.cpp @@ -819,13 +819,44 @@ MOZ_IMPLICIT WebrtcGlobalChild::~WebrtcGlobalChild() struct StreamResult { StreamResult() : candidateTypeBitpattern(0), streamSucceeded(false) {} - uint8_t candidateTypeBitpattern; + uint32_t candidateTypeBitpattern; bool streamSucceeded; }; +static uint32_t GetCandidateIpAndTransportMask(const RTCIceCandidateStats *cand) { + + enum { + CANDIDATE_BITMASK_UDP = 1, + CANDIDATE_BITMASK_TCP = 1 << 1, + CANDIDATE_BITMASK_IPV6 = 1 << 2, + }; + + uint32_t res = 0; + + nsAutoCString transport; + // prefer local transport for local relay candidates + if (cand->mMozLocalTransport.WasPassed()) { + transport.Assign(NS_ConvertUTF16toUTF8(cand->mMozLocalTransport.Value())); + } else { + transport.Assign(NS_ConvertUTF16toUTF8(cand->mTransport.Value())); + } + if (transport == kNrIceTransportUdp) { + res |= CANDIDATE_BITMASK_UDP; + } else if (transport == kNrIceTransportTcp) { + res |= CANDIDATE_BITMASK_TCP; + } + + if (cand->mIpAddress.Value().FindChar(':') != -1) { + res |= CANDIDATE_BITMASK_IPV6; + } + + return res; +}; + static void StoreLongTermICEStatisticsImpl_m( nsresult result, - nsAutoPtr query) { + nsAutoPtr query, + bool aIsLoop) { using namespace Telemetry; @@ -837,17 +868,6 @@ static void StoreLongTermICEStatisticsImpl_m( query->report->mClosed.Construct(true); - // First, store stuff in telemetry - enum { - REMOTE_GATHERED_SERVER_REFLEXIVE = 1, - REMOTE_GATHERED_TURN = 1 << 1, - LOCAL_GATHERED_SERVER_REFLEXIVE = 1 << 2, - LOCAL_GATHERED_TURN_UDP = 1 << 3, - LOCAL_GATHERED_TURN_TCP = 1 << 4, - LOCAL_GATHERED_TURN_TLS = 1 << 5, - LOCAL_GATHERED_TURN_HTTPS = 1 << 6, - }; - // TODO(bcampen@mozilla.com): Do we need to watch out for cases where the // components within a stream didn't have the same types of relayed // candidates? I have a feeling that late trickle could cause this, but right @@ -886,51 +906,74 @@ static void StoreLongTermICEStatisticsImpl_m( if (!cand.mType.WasPassed() || !cand.mCandidateType.WasPassed() || + !cand.mTransport.WasPassed() || + !cand.mIpAddress.WasPassed() || !cand.mComponentId.WasPassed()) { // Crash on debug, ignore this candidate otherwise. MOZ_CRASH(); continue; } + /* The bitmask after examaning a candidate should look like this: + * REMOTE_GATHERED_HOST_UDP = 1, + * REMOTE_GATHERED_HOST_TCP = 1 << 1, + * REMOTE_GATHERED_HOST_IPV6 = 1 << 2, + * REMOTE_GATHERED_SERVER_REFLEXIVE_UDP = 1 << 3, + * REMOTE_GATHERED_SERVER_REFLEXIVE_TCP = 1 << 4, + * REMOTE_GATHERED_SERVER_REFLEXIVE_IPV6 = 1 << 5, + * REMOTE_GATHERED_TURN_UDP = 1 << 6, + * REMOTE_GATHERED_TURN_TCP = 1 << 7, // dummy place holder + * REMOTE_GATHERED_TURN_IPV6 = 1 << 8, + * REMOTE_GATHERED_PEER_REFLEXIVE_UDP = 1 << 9, + * REMOTE_GATHERED_PEER_REFLEXIVE_TCP = 1 << 10, + * REMOTE_GATHERED_PEER_REFLEXIVE_IPV6 = 1 << 11, + * LOCAL_GATHERED_HOST_UDP = 1 << 16, + * LOCAL_GATHERED_HOST_TCP = 1 << 17, + * LOCAL_GATHERED_HOST_IPV6 = 1 << 18, + * LOCAL_GATHERED_SERVER_REFLEXIVE_UDP = 1 << 19, + * LOCAL_GATHERED_SERVER_REFLEXIVE_TCP = 1 << 20, + * LOCAL_GATHERED_SERVER_REFLEXIVE_IPV6 = 1 << 21, + * LOCAL_GATHERED_TURN_UDP = 1 << 22, + * LOCAL_GATHERED_TURN_TCP = 1 << 23, + * LOCAL_GATHERED_TURN_IPV6 = 1 << 24, + * LOCAL_GATHERED_PEERREFLEXIVE_UDP = 1 << 25, + * LOCAL_GATHERED_PEERREFLEXIVE_TCP = 1 << 26, + * LOCAL_GATHERED_PEERREFLEXIVE_IPV6 = 1 << 27, + * + * This results in following shift values + */ + static const uint32_t kLocalShift = 16; + static const uint32_t kSrflxShift = 3; + static const uint32_t kRelayShift = 6; + static const uint32_t kPrflxShift = 9; + + uint32_t candBitmask = GetCandidateIpAndTransportMask(&cand); + + // Note: shift values need to result in the above enum table + if (cand.mType.Value() == RTCStatsType::Localcandidate) { + candBitmask <<= kLocalShift; + } + + if (cand.mCandidateType.Value() == RTCStatsIceCandidateType::Serverreflexive) { + candBitmask <<= kSrflxShift; + } else if (cand.mCandidateType.Value() == RTCStatsIceCandidateType::Relayed) { + candBitmask <<= kRelayShift; + } else if (cand.mCandidateType.Value() == RTCStatsIceCandidateType::Peerreflexive) { + candBitmask <<= kPrflxShift; + } + // Note: this is not a "component" in the ICE definition, this is really a - // stream ID. This is just the way the stats API is standardized right now + // stream ID. This is just the way the stats API is standardized right now. // Very confusing. std::string streamId( NS_ConvertUTF16toUTF8(cand.mComponentId.Value()).get()); - if (cand.mCandidateType.Value() == RTCStatsIceCandidateType::Relayed) { - if (cand.mType.Value() == RTCStatsType::Localcandidate) { - NS_ConvertUTF16toUTF8 transport(cand.mMozLocalTransport.Value()); - if (transport == kNrIceTransportUdp) { - streamResults[streamId].candidateTypeBitpattern |= - LOCAL_GATHERED_TURN_UDP; - } else if (transport == kNrIceTransportTcp) { - streamResults[streamId].candidateTypeBitpattern |= - LOCAL_GATHERED_TURN_TCP; - } - } else { - streamResults[streamId].candidateTypeBitpattern |= REMOTE_GATHERED_TURN; - } - } else if (cand.mCandidateType.Value() == - RTCStatsIceCandidateType::Serverreflexive) { - if (cand.mType.Value() == RTCStatsType::Localcandidate) { - streamResults[streamId].candidateTypeBitpattern |= - LOCAL_GATHERED_SERVER_REFLEXIVE; - } else { - streamResults[streamId].candidateTypeBitpattern |= - REMOTE_GATHERED_SERVER_REFLEXIVE; - } - } + streamResults[streamId].candidateTypeBitpattern |= candBitmask; } for (auto i = streamResults.begin(); i != streamResults.end(); ++i) { - if (i->second.streamSucceeded) { - Telemetry::Accumulate(Telemetry::WEBRTC_CANDIDATE_TYPES_GIVEN_SUCCESS, - i->second.candidateTypeBitpattern); - } else { - Telemetry::Accumulate(Telemetry::WEBRTC_CANDIDATE_TYPES_GIVEN_FAILURE, - i->second.candidateTypeBitpattern); - } + Telemetry::RecordWebrtcIceCandidates(i->second.candidateTypeBitpattern, + i->second.streamSucceeded, aIsLoop); } // Beyond ICE, accumulate telemetry for various PER_CALL settings here. @@ -944,25 +987,30 @@ static void StoreLongTermICEStatisticsImpl_m( continue; } if (s.mBitrateMean.WasPassed()) { - Accumulate(WEBRTC_VIDEO_ENCODER_BITRATE_AVG_PER_CALL_KBPS, + Accumulate(aIsLoop ? LOOP_VIDEO_ENCODER_BITRATE_AVG_PER_CALL_KBPS : + WEBRTC_VIDEO_ENCODER_BITRATE_AVG_PER_CALL_KBPS, uint32_t(s.mBitrateMean.Value() / 1000)); } if (s.mBitrateStdDev.WasPassed()) { - Accumulate(WEBRTC_VIDEO_ENCODER_BITRATE_STD_DEV_PER_CALL_KBPS, + Accumulate(aIsLoop? LOOP_VIDEO_ENCODER_BITRATE_STD_DEV_PER_CALL_KBPS : + WEBRTC_VIDEO_ENCODER_BITRATE_STD_DEV_PER_CALL_KBPS, uint32_t(s.mBitrateStdDev.Value() / 1000)); } if (s.mFramerateMean.WasPassed()) { - Accumulate(WEBRTC_VIDEO_ENCODER_FRAMERATE_AVG_PER_CALL, + Accumulate(aIsLoop ? LOOP_VIDEO_ENCODER_FRAMERATE_AVG_PER_CALL : + WEBRTC_VIDEO_ENCODER_FRAMERATE_AVG_PER_CALL, uint32_t(s.mFramerateMean.Value())); } if (s.mFramerateStdDev.WasPassed()) { - Accumulate(WEBRTC_VIDEO_ENCODER_FRAMERATE_10X_STD_DEV_PER_CALL, + Accumulate(aIsLoop ? LOOP_VIDEO_ENCODER_FRAMERATE_10X_STD_DEV_PER_CALL : + WEBRTC_VIDEO_ENCODER_FRAMERATE_10X_STD_DEV_PER_CALL, uint32_t(s.mFramerateStdDev.Value() * 10)); } if (s.mDroppedFrames.WasPassed() && !query->iceStartTime.IsNull()) { double mins = (TimeStamp::Now() - query->iceStartTime).ToSeconds() / 60; if (mins > 0) { - Accumulate(WEBRTC_VIDEO_ENCODER_DROPPED_FRAMES_PER_CALL_FPM, + Accumulate(aIsLoop ? LOOP_VIDEO_ENCODER_DROPPED_FRAMES_PER_CALL_FPM : + WEBRTC_VIDEO_ENCODER_DROPPED_FRAMES_PER_CALL_FPM, uint32_t(double(s.mDroppedFrames.Value()) / mins)); } } @@ -978,25 +1026,30 @@ static void StoreLongTermICEStatisticsImpl_m( continue; } if (s.mBitrateMean.WasPassed()) { - Accumulate(WEBRTC_VIDEO_DECODER_BITRATE_AVG_PER_CALL_KBPS, + Accumulate(aIsLoop ? LOOP_VIDEO_DECODER_BITRATE_AVG_PER_CALL_KBPS : + WEBRTC_VIDEO_DECODER_BITRATE_AVG_PER_CALL_KBPS, uint32_t(s.mBitrateMean.Value() / 1000)); } if (s.mBitrateStdDev.WasPassed()) { - Accumulate(WEBRTC_VIDEO_DECODER_BITRATE_STD_DEV_PER_CALL_KBPS, + Accumulate(aIsLoop ? LOOP_VIDEO_DECODER_BITRATE_STD_DEV_PER_CALL_KBPS : + WEBRTC_VIDEO_DECODER_BITRATE_STD_DEV_PER_CALL_KBPS, uint32_t(s.mBitrateStdDev.Value() / 1000)); } if (s.mFramerateMean.WasPassed()) { - Accumulate(WEBRTC_VIDEO_DECODER_FRAMERATE_AVG_PER_CALL, + Accumulate(aIsLoop ? LOOP_VIDEO_DECODER_FRAMERATE_AVG_PER_CALL : + WEBRTC_VIDEO_DECODER_FRAMERATE_AVG_PER_CALL, uint32_t(s.mFramerateMean.Value())); } if (s.mFramerateStdDev.WasPassed()) { - Accumulate(WEBRTC_VIDEO_DECODER_FRAMERATE_10X_STD_DEV_PER_CALL, + Accumulate(aIsLoop ? LOOP_VIDEO_DECODER_FRAMERATE_10X_STD_DEV_PER_CALL : + WEBRTC_VIDEO_DECODER_FRAMERATE_10X_STD_DEV_PER_CALL, uint32_t(s.mFramerateStdDev.Value() * 10)); } if (s.mDiscardedPackets.WasPassed() && !query->iceStartTime.IsNull()) { double mins = (TimeStamp::Now() - query->iceStartTime).ToSeconds() / 60; if (mins > 0) { - Accumulate(WEBRTC_VIDEO_DECODER_DISCARDED_PACKETS_PER_CALL_PPM, + Accumulate(aIsLoop ? LOOP_VIDEO_DECODER_DISCARDED_PACKETS_PER_CALL_PPM : + WEBRTC_VIDEO_DECODER_DISCARDED_PACKETS_PER_CALL_PPM, uint32_t(double(s.mDiscardedPackets.Value()) / mins)); } } @@ -1012,7 +1065,8 @@ static void StoreLongTermICEStatisticsImpl_m( } static void GetStatsForLongTermStorage_s( - nsAutoPtr query) { + nsAutoPtr query, + bool aIsLoop) { MOZ_ASSERT(query); @@ -1048,13 +1102,15 @@ static void GetStatsForLongTermStorage_s( WrapRunnableNM( &StoreLongTermICEStatisticsImpl_m, rv, - query), + query, + aIsLoop), NS_DISPATCH_NORMAL); } void WebrtcGlobalInformation::StoreLongTermICEStatistics( PeerConnectionImpl& aPc) { - Telemetry::Accumulate(Telemetry::WEBRTC_ICE_FINAL_CONNECTION_STATE, + Telemetry::Accumulate(aPc.IsLoop() ? Telemetry::LOOP_ICE_FINAL_CONNECTION_STATE : + Telemetry::WEBRTC_ICE_FINAL_CONNECTION_STATE, static_cast(aPc.IceConnectionState())); if (aPc.IceConnectionState() == PCImplIceConnectionState::New) { @@ -1071,7 +1127,7 @@ void WebrtcGlobalInformation::StoreLongTermICEStatistics( RUN_ON_THREAD(aPc.GetSTSThread(), WrapRunnableNM(&GetStatsForLongTermStorage_s, - query), + query, aPc.IsLoop()), NS_DISPATCH_NORMAL); } diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index cce9f9072a..0b440e8288 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -562,6 +562,9 @@ pref("media.video_stats.enabled", true); // Whether to enable the audio writing APIs on the audio element pref("media.audio_data.enabled", false); +// Weather we allow AMD switchable graphics +pref("layers.amd-switchable-gfx.enabled", true); + // Whether to use async panning and zooming pref("layers.async-pan-zoom.enabled", false); diff --git a/netwerk/base/Tickler.cpp b/netwerk/base/Tickler.cpp index c6669cca4e..896eaf6662 100644 --- a/netwerk/base/Tickler.cpp +++ b/netwerk/base/Tickler.cpp @@ -13,7 +13,8 @@ #include "nsThreadUtils.h" #include "prnetdb.h" -#include "AndroidBridge.h" +#include "mozilla/jni/Utils.h" +#include "GeneratedJNIWrappers.h" namespace mozilla { namespace net { @@ -80,7 +81,7 @@ Tickler::Init() MOZ_ASSERT(!mThread); MOZ_ASSERT(!mFD); - if (AndroidBridge::HasEnv()) { + if (jni::IsAvailable()) { widget::GeckoAppShell::EnableNetworkNotifications(); } diff --git a/security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp b/security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp index cd420ffc9e..bf7f3cfece 100644 --- a/security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp +++ b/security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp @@ -223,6 +223,12 @@ SandboxBroker::SetSecurityLevelForPluginProcess(int32_t aSandboxLevel) L"\\??\\pipe\\chrome.*"); ret = ret && (sandbox::SBOX_ALL_OK == result); + // Add the policy for the client side of the crash server pipe. + result = mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES, + sandbox::TargetPolicy::FILES_ALLOW_ANY, + L"\\??\\pipe\\gecko-crash-server-pipe.*"); + ret = ret && (sandbox::SBOX_ALL_OK == result); + // The NPAPI process needs to be able to duplicate shared memory to the // content process and broker process, which are Section type handles. // Content and broker are for e10s and non-e10s cases. @@ -410,6 +416,13 @@ SandboxBroker::AllowDirectory(wchar_t const *dir) return (sandbox::SBOX_ALL_OK == result); } +bool +SandboxBroker::AddTargetPeer(HANDLE aPeerProcess) +{ + sandbox::ResultCode result = sBrokerService->AddTargetPeer(aPeerProcess); + return (sandbox::SBOX_ALL_OK == result); +} + SandboxBroker::~SandboxBroker() { if (mPolicy) { diff --git a/security/sandbox/win/src/sandboxbroker/sandboxBroker.h b/security/sandbox/win/src/sandboxbroker/sandboxBroker.h index 79a217ff74..3a54c78364 100644 --- a/security/sandbox/win/src/sandboxbroker/sandboxBroker.h +++ b/security/sandbox/win/src/sandboxbroker/sandboxBroker.h @@ -14,6 +14,7 @@ #endif #include +#include namespace sandbox { class BrokerServices; @@ -45,6 +46,9 @@ public: bool AllowReadWriteFile(wchar_t const *file); bool AllowDirectory(wchar_t const *dir); + // Exposes AddTargetPeer from broker services, so that none sandboxed + // processes can be added as handle duplication targets. + bool AddTargetPeer(HANDLE aPeerProcess); private: static sandbox::BrokerServices *sBrokerService; sandbox::TargetPolicy *mPolicy; diff --git a/toolkit/components/alerts/moz.build b/toolkit/components/alerts/moz.build index 9c42d222b2..ddcc602912 100644 --- a/toolkit/components/alerts/moz.build +++ b/toolkit/components/alerts/moz.build @@ -30,3 +30,6 @@ JAR_MANIFESTS += ['jar.mn'] with Files('**'): BUG_COMPONENT = ('Toolkit', 'Notifications and Alerts') + +if CONFIG['GNU_CXX']: + CXXFLAGS += ['-Wshadow'] diff --git a/toolkit/components/alerts/nsAlertsService.cpp b/toolkit/components/alerts/nsAlertsService.cpp index 0e35e407a4..2e1344268c 100644 --- a/toolkit/components/alerts/nsAlertsService.cpp +++ b/toolkit/components/alerts/nsAlertsService.cpp @@ -25,7 +25,7 @@ using namespace mozilla; using mozilla::dom::ContentChild; -NS_IMPL_ISUPPORTS(nsAlertsService, nsIAlertsService, nsIAlertsProgressListener) +NS_IMPL_ISUPPORTS(nsAlertsService, nsIAlertsService, nsIAlertsDoNotDisturb, nsIAlertsProgressListener) nsAlertsService::nsAlertsService() { @@ -150,6 +150,45 @@ NS_IMETHODIMP nsAlertsService::CloseAlert(const nsAString& aAlertName, } +// nsIAlertsDoNotDisturb +NS_IMETHODIMP nsAlertsService::GetManualDoNotDisturb(bool* aRetVal) +{ +#ifdef MOZ_WIDGET_ANDROID + return NS_ERROR_NOT_IMPLEMENTED; +#else + // Try the system notification service. + nsCOMPtr sysAlerts(do_GetService(NS_SYSTEMALERTSERVICE_CONTRACTID)); + if (sysAlerts) { + nsCOMPtr alertsDND(do_GetService(NS_SYSTEMALERTSERVICE_CONTRACTID)); + if (!alertsDND) { + return NS_ERROR_NOT_IMPLEMENTED; + } + return alertsDND->GetManualDoNotDisturb(aRetVal); + } + + return mXULAlerts.GetManualDoNotDisturb(aRetVal); +#endif +} + +NS_IMETHODIMP nsAlertsService::SetManualDoNotDisturb(bool aDoNotDisturb) +{ +#ifdef MOZ_WIDGET_ANDROID + return NS_ERROR_NOT_IMPLEMENTED; +#else + // Try the system notification service. + nsCOMPtr sysAlerts(do_GetService(NS_SYSTEMALERTSERVICE_CONTRACTID)); + if (sysAlerts) { + nsCOMPtr alertsDND(do_GetService(NS_SYSTEMALERTSERVICE_CONTRACTID)); + if (!alertsDND) { + return NS_ERROR_NOT_IMPLEMENTED; + } + return alertsDND->SetManualDoNotDisturb(aDoNotDisturb); + } + + return mXULAlerts.SetManualDoNotDisturb(aDoNotDisturb); +#endif +} + NS_IMETHODIMP nsAlertsService::OnProgress(const nsAString & aAlertName, int64_t aProgress, int64_t aProgressMax, diff --git a/toolkit/components/alerts/nsAlertsService.h b/toolkit/components/alerts/nsAlertsService.h index bc8449fc90..71921da397 100644 --- a/toolkit/components/alerts/nsAlertsService.h +++ b/toolkit/components/alerts/nsAlertsService.h @@ -28,10 +28,12 @@ typedef HRESULT (__stdcall *SHQueryUserNotificationStatePtr)(MOZ_QUERY_USER_NOTI #endif // defined(XP_WIN) class nsAlertsService : public nsIAlertsService, + public nsIAlertsDoNotDisturb, public nsIAlertsProgressListener { public: NS_DECL_NSIALERTSPROGRESSLISTENER + NS_DECL_NSIALERTSDONOTDISTURB NS_DECL_NSIALERTSSERVICE NS_DECL_ISUPPORTS diff --git a/toolkit/components/alerts/nsIAlertsService.idl b/toolkit/components/alerts/nsIAlertsService.idl index 71df72fbbe..5ebc8586df 100644 --- a/toolkit/components/alerts/nsIAlertsService.idl +++ b/toolkit/components/alerts/nsIAlertsService.idl @@ -3,7 +3,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/. */ - #include "nsISupports.idl" #include "nsIObserver.idl" @@ -12,99 +11,117 @@ interface nsIPrincipal; [scriptable, uuid(9d0284bf-db40-42da-8f0d-c2769dbde7aa)] interface nsIAlertsService : nsISupports { - /** - * Displays a sliding notification window. - * - * @param imageUrl A URL identifying the image to put in the alert. - * The OS X implemenation limits the amount of time it - * will wait for an icon to load to six seconds. After - * that time the alert will show with no icon. - * @param title The title for the alert. - * @param text The contents of the alert. - * @param textClickable If true, causes the alert text to look like a link - * and notifies the listener when user attempts to - * click the alert text. - * @param cookie A blind cookie the alert will pass back to the - * consumer during the alert listener callbacks. - * @param alertListener Used for callbacks. May be null if the caller - * doesn't care about callbacks. - * @param name The name of the notification. This is currently only - * used on Android and OS X. On Android the name is - * hashed and used as a notification ID. Notifications - * will replace previous notifications with the same name. - * @param dir Bidi override for the title. Valid values are - * "auto", "ltr" or "rtl". Only available on supported - * platforms. - * @param lang Language of title and text of the alert. Only available - * on supported platforms. - * @param inPrivateBrowsing If set to true, imageUrl will be loaded in private - * browsing mode. - * @throws NS_ERROR_NOT_AVAILABLE If the notification cannot be displayed. - * - * The following arguments will be passed to the alertListener's observe() - * method: - * subject - null - * topic - "alertfinished" when the alert goes away - * "alertclickcallback" when the text is clicked - * "alertshow" when the alert is shown - * data - the value of the cookie parameter passed to showAlertNotification. - * - * @note Depending on current circumstances (if the user's in a fullscreen - * application, for instance), the alert might not be displayed at all. - * In that case, if an alert listener is passed in it will receive the - * "alertfinished" notification immediately. - */ - void showAlertNotification(in AString imageUrl, - in AString title, - in AString text, - [optional] in boolean textClickable, - [optional] in AString cookie, - [optional] in nsIObserver alertListener, - [optional] in AString name, - [optional] in AString dir, - [optional] in AString lang, - [optional] in AString data, - [optional] in nsIPrincipal principal, - [optional] in boolean inPrivateBrowsing); + /** + * Displays a sliding notification window. + * + * @param imageUrl A URL identifying the image to put in the alert. + * The OS X implemenation limits the amount of time it + * will wait for an icon to load to six seconds. After + * that time the alert will show with no icon. + * @param title The title for the alert. + * @param text The contents of the alert. + * @param textClickable If true, causes the alert text to look like a link + * and notifies the listener when user attempts to + * click the alert text. + * @param cookie A blind cookie the alert will pass back to the + * consumer during the alert listener callbacks. + * @param alertListener Used for callbacks. May be null if the caller + * doesn't care about callbacks. + * @param name The name of the notification. This is currently only + * used on Android and OS X. On Android the name is + * hashed and used as a notification ID. Notifications + * will replace previous notifications with the same name. + * @param dir Bidi override for the title. Valid values are + * "auto", "ltr" or "rtl". Only available on supported + * platforms. + * @param lang Language of title and text of the alert. Only available + * on supported platforms. + * @param inPrivateBrowsing If set to true, imageUrl will be loaded in private + * browsing mode. + * @throws NS_ERROR_NOT_AVAILABLE If the notification cannot be displayed. + * + * The following arguments will be passed to the alertListener's observe() + * method: + * subject - null + * topic - "alertfinished" when the alert goes away + * "alertdisablecallback" when alerts should be disabled for the principal + * "alertsettingscallback" when alert settings should be opened + * "alertclickcallback" when the text is clicked + * "alertshow" when the alert is shown + * data - the value of the cookie parameter passed to showAlertNotification. + * + * @note Depending on current circumstances (if the user's in a fullscreen + * application, for instance), the alert might not be displayed at all. + * In that case, if an alert listener is passed in it will receive the + * "alertfinished" notification immediately. + */ + void showAlertNotification(in AString imageUrl, + in AString title, + in AString text, + [optional] in boolean textClickable, + [optional] in AString cookie, + [optional] in nsIObserver alertListener, + [optional] in AString name, + [optional] in AString dir, + [optional] in AString lang, + [optional] in AString data, + [optional] in nsIPrincipal principal, + [optional] in boolean inPrivateBrowsing); - /** - * Close alerts created by the service. - * - * @param name The name of the notification to close. If no name - * is provided then only a notification created with - * no name (if any) will be closed. - */ - void closeAlert([optional] in AString name, - [optional] in nsIPrincipal principal); + /** + * Close alerts created by the service. + * + * @param name The name of the notification to close. If no name + * is provided then only a notification created with + * no name (if any) will be closed. + */ + void closeAlert([optional] in AString name, + [optional] in nsIPrincipal principal); + +}; + +[scriptable, uuid(c5d63e3a-259d-45a8-b964-8377967cb4d2)] +interface nsIAlertsDoNotDisturb : nsISupports +{ + /** + * Toggles a manual Do Not Disturb mode for the service to reduce the amount + * of disruption that alerts cause the user. + * This may mean only displaying them in a notification tray/center or not + * displaying them at all. If a system backend already supports a similar + * feature controlled by the user, enabling this may not have any impact on + * code to show an alert. e.g. on OS X, the system will take care not + * disrupting a user if we simply create a notification like usual. + */ + attribute bool manualDoNotDisturb; }; [scriptable, uuid(df1bd4b0-3a8c-40e6-806a-203f38b0bd9f)] interface nsIAlertsProgressListener : nsISupports { - /** - * Called to notify the alert service that progress has occurred for the - * given notification previously displayed with showAlertNotification(). - * - * @param name The name of the notification displaying the - * progress. On Android the name is hashed and used - * as a notification ID. - * @param progress Numeric value in the range 0 to progressMax - * indicating the current progress. - * @param progressMax Numeric value indicating the maximum progress. - * @param text The contents of the alert. If not provided, - * the percentage will be displayed. - */ - void onProgress(in AString name, - in long long progress, - in long long progressMax, - [optional] in AString text); + /** + * Called to notify the alert service that progress has occurred for the + * given notification previously displayed with showAlertNotification(). + * + * @param name The name of the notification displaying the + * progress. On Android the name is hashed and used + * as a notification ID. + * @param progress Numeric value in the range 0 to progressMax + * indicating the current progress. + * @param progressMax Numeric value indicating the maximum progress. + * @param text The contents of the alert. If not provided, + * the percentage will be displayed. + */ + void onProgress(in AString name, + in long long progress, + in long long progressMax, + [optional] in AString text); - /** - * Called to cancel and hide the given notification previously displayed - * with showAlertNotification(). - * - * @param name The name of the notification. - */ - void onCancel(in AString name); + /** + * Called to cancel and hide the given notification previously displayed + * with showAlertNotification(). + * + * @param name The name of the notification. + */ + void onCancel(in AString name); }; diff --git a/toolkit/components/alerts/nsXULAlerts.cpp b/toolkit/components/alerts/nsXULAlerts.cpp index 72de225e17..c10789a49e 100644 --- a/toolkit/components/alerts/nsXULAlerts.cpp +++ b/toolkit/components/alerts/nsXULAlerts.cpp @@ -48,6 +48,10 @@ nsXULAlerts::ShowAlertNotification(const nsAString& aImageUrl, const nsAString& const nsAString& aLang, nsIPrincipal* aPrincipal, bool aInPrivateBrowsing) { + if (mDoNotDisturb) { + return NS_OK; + } + nsCOMPtr wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID)); nsCOMPtr argsArray; @@ -161,6 +165,20 @@ nsXULAlerts::ShowAlertNotification(const nsAString& aImageUrl, const nsAString& return NS_OK; } +nsresult +nsXULAlerts::SetManualDoNotDisturb(bool aDoNotDisturb) +{ + mDoNotDisturb = aDoNotDisturb; + return NS_OK; +} + +nsresult +nsXULAlerts::GetManualDoNotDisturb(bool* aRetVal) +{ + *aRetVal = mDoNotDisturb; + return NS_OK; +} + nsresult nsXULAlerts::CloseAlert(const nsAString& aAlertName) { diff --git a/toolkit/components/alerts/nsXULAlerts.h b/toolkit/components/alerts/nsXULAlerts.h index 1e913c1639..06e55bd62e 100644 --- a/toolkit/components/alerts/nsXULAlerts.h +++ b/toolkit/components/alerts/nsXULAlerts.h @@ -29,8 +29,13 @@ public: bool aInPrivateBrowsing); nsresult CloseAlert(const nsAString& aAlertName); + + nsresult GetManualDoNotDisturb(bool* aRetVal); + nsresult SetManualDoNotDisturb(bool aDoNotDisturb); + protected: nsInterfaceHashtable mNamedWindows; + bool mDoNotDisturb = false; }; /** diff --git a/toolkit/components/alerts/resources/content/alert.js b/toolkit/components/alerts/resources/content/alert.js index 0ebaf1337b..254a8afabb 100644 --- a/toolkit/components/alerts/resources/content/alert.js +++ b/toolkit/components/alerts/resources/content/alert.js @@ -4,21 +4,7 @@ var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; -/* - * This indicates from which corner of the screen alerts slide in, - * and from which direction (horizontal/vertical). - * 0, the default, represents bottom right, sliding vertically. - * Use any bitwise combination of the following constants: - * NS_ALERT_HORIZONTAL (1), NS_ALERT_LEFT (2), NS_ALERT_TOP (4). - * - * 6 4 - * +-----------+ - * 7| |5 - * | | - * 3| |1 - * +-----------+ - * 2 0 - */ +// Copied from nsILookAndFeel.h, see comments on eMetric_AlertNotificationOrigin const NS_ALERT_HORIZONTAL = 1; const NS_ALERT_LEFT = 2; const NS_ALERT_TOP = 4; @@ -229,15 +215,7 @@ function moveWindowToEnd() { let windows = Services.wm.getEnumerator("alert:alert"); while (windows.hasMoreElements()) { let alertWindow = windows.getNext(); - let alertWindowTime = Number( - alertWindow.document.getElementById('alertTime').getAttribute('value')); - let windowTime = Number( - window.document.getElementById('alertTime').getAttribute('value')); - // The time of window creation. - // Otherwise calling the notification twice (and more) in a row - // does not work. - // See https://bugzilla.mozilla.org/show_bug.cgi?id=1263155 - if ((alertWindow != window) && (alertWindowTime <= windowTime)) { + if (alertWindow != window) { if (gOrigin & NS_ALERT_TOP) { y = Math.max(y, alertWindow.screenY + alertWindow.outerHeight); } else { diff --git a/toolkit/components/autocomplete/moz.build b/toolkit/components/autocomplete/moz.build index 84ed033413..930e8ce304 100644 --- a/toolkit/components/autocomplete/moz.build +++ b/toolkit/components/autocomplete/moz.build @@ -26,3 +26,6 @@ FINAL_LIBRARY = 'xul' with Files('**'): BUG_COMPONENT = ('Toolkit', 'Autocomplete') + +if CONFIG['GNU_CXX']: + CXXFLAGS += ['-Wshadow'] diff --git a/toolkit/components/autocomplete/nsAutoCompleteController.cpp b/toolkit/components/autocomplete/nsAutoCompleteController.cpp index fe9bc6f8f4..fb3d006b9c 100644 --- a/toolkit/components/autocomplete/nsAutoCompleteController.cpp +++ b/toolkit/components/autocomplete/nsAutoCompleteController.cpp @@ -44,8 +44,10 @@ NS_INTERFACE_MAP_END nsAutoCompleteController::nsAutoCompleteController() : mDefaultIndexCompleted(false), - mBackspaced(false), mPopupClosedByCompositionStart(false), + mProhibitAutoFill(false), + mUserClearedAutoFill(false), + mClearingAutoFillSearchesAgain(false), mCompositionState(eCompositionState_None), mSearchStatus(nsAutoCompleteController::STATUS_NONE), mRowCount(0), @@ -119,7 +121,7 @@ nsAutoCompleteController::SetInput(nsIAutoCompleteInput *aInput) mSearchString = newValue; mPlaceholderCompletionString.Truncate(); mDefaultIndexCompleted = false; - mBackspaced = false; + mProhibitAutoFill = false; mSearchStatus = nsIAutoCompleteController::STATUS_NONE; mRowCount = 0; mSearchesOngoing = 0; @@ -130,11 +132,13 @@ nsAutoCompleteController::SetInput(nsIAutoCompleteInput *aInput) aInput->GetSearchCount(&searchCount); mResults.SetCapacity(searchCount); mSearches.SetCapacity(searchCount); - mMatchCounts.SetLength(searchCount); mImmediateSearchesCount = 0; const char *searchCID = kAutoCompleteSearchCID; + // Since the controller can be used as a service it's important to reset this. + mClearingAutoFillSearchesAgain = false; + for (uint32_t i = 0; i < searchCount; ++i) { // Use the search name to create the contract id string for the search service nsAutoCString searchName; @@ -148,12 +152,19 @@ nsAutoCompleteController::SetInput(nsIAutoCompleteInput *aInput) mSearches.AppendObject(search); // Count immediate searches. - uint16_t searchType = nsIAutoCompleteSearchDescriptor::SEARCH_TYPE_DELAYED; nsCOMPtr searchDesc = do_QueryInterface(search); - if (searchDesc && NS_SUCCEEDED(searchDesc->GetSearchType(&searchType)) && - searchType == nsIAutoCompleteSearchDescriptor::SEARCH_TYPE_IMMEDIATE) - mImmediateSearchesCount++; + if (searchDesc) { + uint16_t searchType = nsIAutoCompleteSearchDescriptor::SEARCH_TYPE_DELAYED; + if (NS_SUCCEEDED(searchDesc->GetSearchType(&searchType)) && + searchType == nsIAutoCompleteSearchDescriptor::SEARCH_TYPE_IMMEDIATE) { + mImmediateSearchesCount++; + } + + if (!mClearingAutoFillSearchesAgain) { + searchDesc->GetClearingAutoFillSearchesAgain(&mClearingAutoFillSearchesAgain); + } + } } } @@ -199,8 +210,8 @@ nsAutoCompleteController::HandleText() return NS_OK; } - nsAutoString newValue; nsCOMPtr input(mInput); + nsAutoString newValue; input->GetTextValue(newValue); // Stop all searches in case they are async. @@ -217,25 +228,45 @@ nsAutoCompleteController::HandleText() input->GetDisableAutoComplete(&disabled); NS_ENSURE_TRUE(!disabled, NS_OK); - // Don't search again if the new string is the same as the last search + // Usually we don't search again if the new string is the same as the last one. // However, if this is called immediately after compositionend event, // we need to search the same value again since the search was canceled // at compositionstart event handler. - if (!handlingCompositionCommit && newValue.Length() > 0 && - newValue.Equals(mSearchString)) { + // The new string might also be the same as the last search if the autofilled + // portion was cleared. In this case, we may want to search again. + + // Whether the user removed some text at the end. + bool userRemovedText = + newValue.Length() < mSearchString.Length() && + Substring(mSearchString, 0, newValue.Length()).Equals(newValue); + + // Whether the user is repeating the previous search. + bool repeatingPreviousSearch = !userRemovedText && + newValue.Equals(mSearchString); + + mUserClearedAutoFill = + repeatingPreviousSearch && + newValue.Length() < mPlaceholderCompletionString.Length() && + Substring(mPlaceholderCompletionString, 0, newValue.Length()).Equals(newValue); + bool searchAgainOnAutoFillClear = mUserClearedAutoFill && mClearingAutoFillSearchesAgain; + + if (!handlingCompositionCommit && + !searchAgainOnAutoFillClear && + newValue.Length() > 0 && + repeatingPreviousSearch) { return NS_OK; } - // Determine if the user has removed text from the end (probably by backspacing) - if (newValue.Length() < mSearchString.Length() && - Substring(mSearchString, 0, newValue.Length()).Equals(newValue)) - { - // We need to throw away previous results so we don't try to search through them again - ClearResults(); - mBackspaced = true; + if (userRemovedText || searchAgainOnAutoFillClear) { + if (userRemovedText) { + // We need to throw away previous results so we don't try to search + // through them again. + ClearResults(); + } + mProhibitAutoFill = true; mPlaceholderCompletionString.Truncate(); } else { - mBackspaced = false; + mProhibitAutoFill = false; } mSearchString = newValue; @@ -579,7 +610,7 @@ nsAutoCompleteController::HandleDelete(bool *_retval) RowIndexToSearch(index, &searchIndex, &rowIndex); NS_ENSURE_TRUE(searchIndex >= 0 && rowIndex >= 0, NS_ERROR_FAILURE); - nsIAutoCompleteResult *result = mResults[searchIndex]; + nsIAutoCompleteResult *result = mResults.SafeObjectAt(searchIndex); NS_ENSURE_TRUE(result, NS_ERROR_FAILURE); nsAutoString search; @@ -641,7 +672,7 @@ nsAutoCompleteController::GetResultAt(int32_t aIndex, nsIAutoCompleteResult** aR RowIndexToSearch(aIndex, &searchIndex, aRowIndex); NS_ENSURE_TRUE(searchIndex >= 0 && *aRowIndex >= 0, NS_ERROR_FAILURE); - *aResult = mResults[searchIndex]; + *aResult = mResults.SafeObjectAt(searchIndex); NS_ENSURE_TRUE(*aResult, NS_ERROR_FAILURE); return NS_OK; } @@ -1133,6 +1164,13 @@ nsAutoCompleteController::StartSearch(uint16_t aSearchType) if (NS_FAILED(rv)) return rv; + // FormFill expects the searchParam to only contain the input element id, + // other consumers may have other expectations, so this modifies it only + // for new consumers handling autoFill by themselves. + if (mProhibitAutoFill && mClearingAutoFillSearchesAgain) { + searchParam.AppendLiteral(" prohibit-autofill"); + } + rv = search->StartSearch(mSearchString, searchParam, result, static_cast(this)); if (NS_FAILED(rv)) { ++mSearchesFailed; @@ -1207,6 +1245,7 @@ nsAutoCompleteController::MaybeCompletePlaceholder() // In addition, the selection must be at the end of the current input to // trigger the placeholder completion. bool usePlaceholderCompletion = + !mUserClearedAutoFill && !mPlaceholderCompletionString.IsEmpty() && mPlaceholderCompletionString.Length() > mSearchString.Length() && selectionEnd == selectionStart && @@ -1457,39 +1496,50 @@ nsresult nsAutoCompleteController::ProcessResult(int32_t aSearchIndex, nsIAutoCompleteResult *aResult) { NS_ENSURE_STATE(mInput); + MOZ_ASSERT(aResult, "ProcessResult should always receive a result"); + NS_ENSURE_ARG(aResult); nsCOMPtr input(mInput); - uint16_t result = 0; - if (aResult) - aResult->GetSearchResult(&result); + uint16_t searchResult = 0; + aResult->GetSearchResult(&searchResult); - uint32_t oldMatchCount = 0; - uint32_t matchCount = 0; - if (aResult) - aResult->GetMatchCount(&matchCount); - - int32_t resultIndex = mResults.IndexOf(aResult); - if (resultIndex == -1) { - // cache the result - mResults.AppendObject(aResult); - mMatchCounts.AppendElement(matchCount); - resultIndex = mResults.Count() - 1; - } - else { - oldMatchCount = mMatchCounts[aSearchIndex]; - mMatchCounts[resultIndex] = matchCount; + // The following code supports incremental updating results in 2 ways: + // * The search may reuse the same result, just by adding entries to it. + // * The search may send a new result every time. In this case we merge + // the results and proceed on the same code path as before. + // This way both mSearches and mResults can be indexed by the search index, + // cause we'll always have only one result per search. + if (mResults.IndexOf(aResult) == -1) { + nsIAutoCompleteResult* oldResult = mResults.SafeObjectAt(aSearchIndex); + if (oldResult) { + MOZ_ASSERT(false, "Passing new matches to OnSearchResult with a new " + "nsIAutoCompleteResult every time is deprecated, please " + "update the same result until the search is done"); + // Build a new nsIAutocompleteSimpleResult and merge results into it. + RefPtr mergedResult = + new nsAutoCompleteSimpleResult(); + mergedResult->AppendResult(oldResult); + mergedResult->AppendResult(aResult); + mResults.ReplaceObjectAt(mergedResult, aSearchIndex); + } else { + // This inserts and grows the array if needed. + mResults.ReplaceObjectAt(aResult, aSearchIndex); + } } + // When found the result should have the same index as the search. + MOZ_ASSERT_IF(mResults.IndexOf(aResult) != -1, + mResults.IndexOf(aResult) == aSearchIndex); + MOZ_ASSERT(mResults.Count() >= aSearchIndex + 1, + "aSearchIndex should always be valid for mResults"); bool isTypeAheadResult = false; - if (aResult) { - aResult->GetTypeAheadResult(&isTypeAheadResult); - } + aResult->GetTypeAheadResult(&isTypeAheadResult); if (!isTypeAheadResult) { uint32_t oldRowCount = mRowCount; // If the search failed, increase the match count to include the error // description. - if (result == nsIAutoCompleteResult::RESULT_FAILURE) { + if (searchResult == nsIAutoCompleteResult::RESULT_FAILURE) { nsAutoString error; aResult->GetErrorDescription(error); if (!error.IsEmpty()) { @@ -1498,13 +1548,28 @@ nsAutoCompleteController::ProcessResult(int32_t aSearchIndex, nsIAutoCompleteRes mTree->RowCountChanged(oldRowCount, 1); } } - } else if (result == nsIAutoCompleteResult::RESULT_SUCCESS || - result == nsIAutoCompleteResult::RESULT_SUCCESS_ONGOING) { + } else if (searchResult == nsIAutoCompleteResult::RESULT_SUCCESS || + searchResult == nsIAutoCompleteResult::RESULT_SUCCESS_ONGOING) { // Increase the match count for all matches in this result. - mRowCount += matchCount - oldMatchCount; + uint32_t totalMatchCount = 0; + for (uint32_t i = 0; i < mResults.Length(); i++) { + nsIAutoCompleteResult* result = mResults.SafeObjectAt(i); + if (result) { + // not all results implement this, so it can likely fail. + bool typeAhead = false; + result->GetTypeAheadResult(&typeAhead); + if (!typeAhead) { + uint32_t matchCount = 0; + result->GetMatchCount(&matchCount); + totalMatchCount += matchCount; + } + } + } + uint32_t delta = totalMatchCount - oldRowCount; + mRowCount += delta; if (mTree) { - mTree->RowCountChanged(oldRowCount, matchCount - oldMatchCount); + mTree->RowCountChanged(oldRowCount, delta); } } @@ -1527,10 +1592,10 @@ nsAutoCompleteController::ProcessResult(int32_t aSearchIndex, nsIAutoCompleteRes } } - if (result == nsIAutoCompleteResult::RESULT_SUCCESS || - result == nsIAutoCompleteResult::RESULT_SUCCESS_ONGOING) { + if (searchResult == nsIAutoCompleteResult::RESULT_SUCCESS || + searchResult == nsIAutoCompleteResult::RESULT_SUCCESS_ONGOING) { // Try to autocomplete the default index for this search. - CompleteDefaultIndex(resultIndex); + CompleteDefaultIndex(aSearchIndex); } return NS_OK; @@ -1568,7 +1633,6 @@ nsAutoCompleteController::ClearResults() int32_t oldRowCount = mRowCount; mRowCount = 0; mResults.Clear(); - mMatchCounts.Clear(); if (oldRowCount != 0) { if (mTree) mTree->RowCountChanged(0, -oldRowCount); @@ -1588,7 +1652,7 @@ nsAutoCompleteController::ClearResults() nsresult nsAutoCompleteController::CompleteDefaultIndex(int32_t aResultIndex) { - if (mDefaultIndexCompleted || mBackspaced || mSearchString.Length() == 0 || !mInput) + if (mDefaultIndexCompleted || mProhibitAutoFill || mSearchString.Length() == 0 || !mInput) return NS_OK; nsCOMPtr input(mInput); @@ -1636,14 +1700,16 @@ nsAutoCompleteController::GetDefaultCompleteResult(int32_t aResultIndex, // If a result index was not provided, find the first defaultIndex result. for (int32_t i = 0; resultIndex < 0 && i < mResults.Count(); ++i) { - nsIAutoCompleteResult *result = mResults[i]; + nsIAutoCompleteResult *result = mResults.SafeObjectAt(i); if (result && NS_SUCCEEDED(result->GetDefaultIndex(_defaultIndex)) && *_defaultIndex >= 0) { resultIndex = i; } } - NS_ENSURE_TRUE(resultIndex >= 0, NS_ERROR_FAILURE); + if (resultIndex < 0) { + return NS_ERROR_FAILURE; + } *_result = mResults.SafeObjectAt(resultIndex); NS_ENSURE_TRUE(*_result, NS_ERROR_FAILURE); diff --git a/toolkit/components/autocomplete/nsAutoCompleteController.h b/toolkit/components/autocomplete/nsAutoCompleteController.h index 9a6dae7825..7ecd9914bd 100644 --- a/toolkit/components/autocomplete/nsAutoCompleteController.h +++ b/toolkit/components/autocomplete/nsAutoCompleteController.h @@ -123,10 +123,8 @@ protected: nsCOMPtr mInput; nsCOMArray mSearches; + // This is used as a sparse array, always use SafeObjectAt to access it. nsCOMArray mResults; - // Caches the match counts for the current ongoing results to allow - // incremental results to keep the rowcount up to date. - nsTArray mMatchCounts; // Temporarily keeps the results alive while invoking startSearch() for each // search. This is needed to allow the searches to reuse the previous result, // since otherwise the first search clears mResults. @@ -139,8 +137,19 @@ protected: nsString mSearchString; nsString mPlaceholderCompletionString; bool mDefaultIndexCompleted; - bool mBackspaced; bool mPopupClosedByCompositionStart; + + // Whether autofill is allowed for the next search. May be retrieved by the + // search through the "prohibit-autofill" searchParam. + bool mProhibitAutoFill; + + // Indicates whether the user cleared the autofilled part, returning to the + // originally entered search string. + bool mUserClearedAutoFill; + + // Indicates whether clearing the autofilled string should issue a new search. + bool mClearingAutoFillSearchesAgain; + enum CompositionState { eCompositionState_None, eCompositionState_Composing, diff --git a/toolkit/components/autocomplete/nsAutoCompleteSimpleResult.cpp b/toolkit/components/autocomplete/nsAutoCompleteSimpleResult.cpp index 9c4c464eee..683ac462a4 100644 --- a/toolkit/components/autocomplete/nsAutoCompleteSimpleResult.cpp +++ b/toolkit/components/autocomplete/nsAutoCompleteSimpleResult.cpp @@ -4,10 +4,43 @@ #include "nsAutoCompleteSimpleResult.h" +#define CHECK_MATCH_INDEX(_index, _insert) \ + if (_index < 0 || \ + static_cast(_index) > mMatches.Length() || \ + (!_insert && static_cast(_index) == mMatches.Length())) { \ + MOZ_ASSERT(false, "Trying to use an invalid index on mMatches"); \ + return NS_ERROR_ILLEGAL_VALUE; \ + } \ + NS_IMPL_ISUPPORTS(nsAutoCompleteSimpleResult, nsIAutoCompleteResult, nsIAutoCompleteSimpleResult) +struct AutoCompleteSimpleResultMatch +{ + AutoCompleteSimpleResultMatch(const nsAString& aValue, + const nsAString& aComment, + const nsAString& aImage, + const nsAString& aStyle, + const nsAString& aFinalCompleteValue, + const nsAString& aLabel) + : mValue(aValue) + , mComment(aComment) + , mImage(aImage) + , mStyle(aStyle) + , mFinalCompleteValue(aFinalCompleteValue) + , mLabel(aLabel) + { + } + + nsString mValue; + nsString mComment; + nsString mImage; + nsString mStyle; + nsString mFinalCompleteValue; + nsString mLabel; +}; + nsAutoCompleteSimpleResult::nsAutoCompleteSimpleResult() : mDefaultIndex(-1), mSearchResult(RESULT_NOMATCH), @@ -15,6 +48,74 @@ nsAutoCompleteSimpleResult::nsAutoCompleteSimpleResult() : { } +nsresult +nsAutoCompleteSimpleResult::AppendResult(nsIAutoCompleteResult* aResult) +{ + nsAutoString searchString; + nsresult rv = aResult->GetSearchString(searchString); + NS_ENSURE_SUCCESS(rv, rv); + mSearchString = searchString; + + uint16_t searchResult; + rv = aResult->GetSearchResult(&searchResult); + NS_ENSURE_SUCCESS(rv, rv); + mSearchResult = searchResult; + + nsAutoString errorDescription; + if (NS_SUCCEEDED(aResult->GetErrorDescription(errorDescription)) && + !errorDescription.IsEmpty()) { + mErrorDescription = errorDescription; + } + + bool typeAheadResult = false; + if (NS_SUCCEEDED(aResult->GetTypeAheadResult(&typeAheadResult)) && + typeAheadResult) { + mTypeAheadResult = typeAheadResult; + } + + int32_t defaultIndex = -1; + if (NS_SUCCEEDED(aResult->GetDefaultIndex(&defaultIndex)) && + defaultIndex >= 0) { + mDefaultIndex = defaultIndex; + } + + nsCOMPtr simpleResult = + do_QueryInterface(aResult); + if (simpleResult) { + nsCOMPtr listener; + if (NS_SUCCEEDED(simpleResult->GetListener(getter_AddRefs(listener))) && + listener) { + listener.swap(mListener); + } + } + + // Copy matches. + uint32_t matchCount = 0; + rv = aResult->GetMatchCount(&matchCount); + NS_ENSURE_SUCCESS(rv, rv); + for (size_t i = 0; i < matchCount; ++i) { + nsAutoString value, comment, image, style, finalCompleteValue, label; + + rv = aResult->GetValueAt(i, value); + NS_ENSURE_SUCCESS(rv, rv); + rv = aResult->GetCommentAt(i, comment); + NS_ENSURE_SUCCESS(rv, rv); + rv = aResult->GetImageAt(i, image); + NS_ENSURE_SUCCESS(rv, rv); + rv = aResult->GetStyleAt(i, style); + NS_ENSURE_SUCCESS(rv, rv); + rv = aResult->GetFinalCompleteValueAt(i, finalCompleteValue); + NS_ENSURE_SUCCESS(rv, rv); + rv = aResult->GetLabelAt(i, label); + NS_ENSURE_SUCCESS(rv, rv); + + rv = AppendMatch(value, comment, image, style, finalCompleteValue, label); + NS_ENSURE_SUCCESS(rv, rv); + } + + return NS_OK; +} + // searchString NS_IMETHODIMP nsAutoCompleteSimpleResult::GetSearchString(nsAString &aSearchString) @@ -86,95 +187,85 @@ nsAutoCompleteSimpleResult::SetTypeAheadResult(bool aTypeAheadResult) return NS_OK; } +NS_IMETHODIMP +nsAutoCompleteSimpleResult::InsertMatchAt(int32_t aIndex, + const nsAString& aValue, + const nsAString& aComment, + const nsAString& aImage, + const nsAString& aStyle, + const nsAString& aFinalCompleteValue, + const nsAString& aLabel) +{ + CHECK_MATCH_INDEX(aIndex, true); + + AutoCompleteSimpleResultMatch match(aValue, aComment, aImage, aStyle, aFinalCompleteValue, aLabel); + + if (!mMatches.InsertElementAt(aIndex, match)) { + return NS_ERROR_OUT_OF_MEMORY; + } + + return NS_OK; +} + NS_IMETHODIMP nsAutoCompleteSimpleResult::AppendMatch(const nsAString& aValue, const nsAString& aComment, const nsAString& aImage, const nsAString& aStyle, - const nsAString& aFinalCompleteValue) + const nsAString& aFinalCompleteValue, + const nsAString& aLabel) { - CheckInvariants(); - - if (! mValues.AppendElement(aValue)) - return NS_ERROR_OUT_OF_MEMORY; - if (! mComments.AppendElement(aComment)) { - mValues.RemoveElementAt(mValues.Length() - 1); - return NS_ERROR_OUT_OF_MEMORY; - } - if (! mImages.AppendElement(aImage)) { - mValues.RemoveElementAt(mValues.Length() - 1); - mComments.RemoveElementAt(mComments.Length() - 1); - return NS_ERROR_OUT_OF_MEMORY; - } - if (! mStyles.AppendElement(aStyle)) { - mValues.RemoveElementAt(mValues.Length() - 1); - mComments.RemoveElementAt(mComments.Length() - 1); - mImages.RemoveElementAt(mImages.Length() - 1); - return NS_ERROR_OUT_OF_MEMORY; - } - if (!mFinalCompleteValues.AppendElement(aFinalCompleteValue)) { - mValues.RemoveElementAt(mValues.Length() - 1); - mComments.RemoveElementAt(mComments.Length() - 1); - mImages.RemoveElementAt(mImages.Length() - 1); - mStyles.RemoveElementAt(mStyles.Length() - 1); - return NS_ERROR_OUT_OF_MEMORY; - } - return NS_OK; + return InsertMatchAt(mMatches.Length(), aValue, aComment, aImage, aStyle, + aFinalCompleteValue, aLabel); } NS_IMETHODIMP nsAutoCompleteSimpleResult::GetMatchCount(uint32_t *aMatchCount) { - CheckInvariants(); - - *aMatchCount = mValues.Length(); + *aMatchCount = mMatches.Length(); return NS_OK; } NS_IMETHODIMP nsAutoCompleteSimpleResult::GetValueAt(int32_t aIndex, nsAString& _retval) { - NS_ENSURE_TRUE(aIndex >= 0 && aIndex < int32_t(mValues.Length()), - NS_ERROR_ILLEGAL_VALUE); - CheckInvariants(); - - _retval = mValues[aIndex]; + CHECK_MATCH_INDEX(aIndex, false); + _retval = mMatches[aIndex].mValue; return NS_OK; } NS_IMETHODIMP nsAutoCompleteSimpleResult::GetLabelAt(int32_t aIndex, nsAString& _retval) { - return GetValueAt(aIndex, _retval); + CHECK_MATCH_INDEX(aIndex, false); + _retval = mMatches[aIndex].mLabel; + if (_retval.IsEmpty()) { + _retval = mMatches[aIndex].mValue; + } + return NS_OK; } NS_IMETHODIMP nsAutoCompleteSimpleResult::GetCommentAt(int32_t aIndex, nsAString& _retval) { - NS_ENSURE_TRUE(aIndex >= 0 && aIndex < int32_t(mComments.Length()), - NS_ERROR_ILLEGAL_VALUE); - CheckInvariants(); - _retval = mComments[aIndex]; + CHECK_MATCH_INDEX(aIndex, false); + _retval = mMatches[aIndex].mComment; return NS_OK; } NS_IMETHODIMP nsAutoCompleteSimpleResult::GetImageAt(int32_t aIndex, nsAString& _retval) { - NS_ENSURE_TRUE(aIndex >= 0 && aIndex < int32_t(mImages.Length()), - NS_ERROR_ILLEGAL_VALUE); - CheckInvariants(); - _retval = mImages[aIndex]; + CHECK_MATCH_INDEX(aIndex, false); + _retval = mMatches[aIndex].mImage; return NS_OK; } NS_IMETHODIMP nsAutoCompleteSimpleResult::GetStyleAt(int32_t aIndex, nsAString& _retval) { - NS_ENSURE_TRUE(aIndex >= 0 && aIndex < int32_t(mStyles.Length()), - NS_ERROR_ILLEGAL_VALUE); - CheckInvariants(); - _retval = mStyles[aIndex]; + CHECK_MATCH_INDEX(aIndex, false); + _retval = mMatches[aIndex].mStyle; return NS_OK; } @@ -182,12 +273,11 @@ NS_IMETHODIMP nsAutoCompleteSimpleResult::GetFinalCompleteValueAt(int32_t aIndex, nsAString& _retval) { - NS_ENSURE_TRUE(aIndex >= 0 && aIndex < int32_t(mFinalCompleteValues.Length()), - NS_ERROR_ILLEGAL_VALUE); - CheckInvariants(); - _retval = mFinalCompleteValues[aIndex]; - if (_retval.Length() == 0) - _retval = mValues[aIndex]; + CHECK_MATCH_INDEX(aIndex, false); + _retval = mMatches[aIndex].mFinalCompleteValue; + if (_retval.IsEmpty()) { + _retval = mMatches[aIndex].mValue; + } return NS_OK; } @@ -198,22 +288,26 @@ nsAutoCompleteSimpleResult::SetListener(nsIAutoCompleteSimpleResultListener* aLi return NS_OK; } +NS_IMETHODIMP +nsAutoCompleteSimpleResult::GetListener(nsIAutoCompleteSimpleResultListener** aListener) +{ + nsCOMPtr listener(mListener); + listener.forget(aListener); + return NS_OK; +} + NS_IMETHODIMP nsAutoCompleteSimpleResult::RemoveValueAt(int32_t aRowIndex, bool aRemoveFromDb) { - NS_ENSURE_TRUE(aRowIndex >= 0 && aRowIndex < int32_t(mValues.Length()), - NS_ERROR_ILLEGAL_VALUE); + CHECK_MATCH_INDEX(aRowIndex, false); - nsAutoString removedValue(mValues[aRowIndex]); - mValues.RemoveElementAt(aRowIndex); - mComments.RemoveElementAt(aRowIndex); - mImages.RemoveElementAt(aRowIndex); - mStyles.RemoveElementAt(aRowIndex); - mFinalCompleteValues.RemoveElementAt(aRowIndex); + nsString value = mMatches[aRowIndex].mValue; + mMatches.RemoveElementAt(aRowIndex); - if (mListener) - mListener->OnValueRemoved(this, removedValue, aRemoveFromDb); + if (mListener) { + mListener->OnValueRemoved(this, value, aRemoveFromDb); + } return NS_OK; } diff --git a/toolkit/components/autocomplete/nsAutoCompleteSimpleResult.h b/toolkit/components/autocomplete/nsAutoCompleteSimpleResult.h index 1c322744b3..61ee542e47 100644 --- a/toolkit/components/autocomplete/nsAutoCompleteSimpleResult.h +++ b/toolkit/components/autocomplete/nsAutoCompleteSimpleResult.h @@ -13,34 +13,25 @@ #include "nsTArray.h" #include "mozilla/Attributes.h" +struct AutoCompleteSimpleResultMatch; + class nsAutoCompleteSimpleResult final : public nsIAutoCompleteSimpleResult { public: nsAutoCompleteSimpleResult(); - inline void CheckInvariants() { - NS_ASSERTION(mValues.Length() == mComments.Length(), "Arrays out of sync"); - NS_ASSERTION(mValues.Length() == mImages.Length(), "Arrays out of sync"); - NS_ASSERTION(mValues.Length() == mStyles.Length(), "Arrays out of sync"); - NS_ASSERTION(mValues.Length() == mFinalCompleteValues.Length(), "Arrays out of sync"); - } NS_DECL_ISUPPORTS NS_DECL_NSIAUTOCOMPLETERESULT NS_DECL_NSIAUTOCOMPLETESIMPLERESULT + nsresult AppendResult(nsIAutoCompleteResult* aResult); + private: ~nsAutoCompleteSimpleResult() {} protected: - - // What we really want is an array of structs with value/comment/image/style contents. - // But then we'd either have to use COM or manage object lifetimes ourselves. - // Having four arrays of string simplifies this, but is stupid. - nsTArray mValues; - nsTArray mComments; - nsTArray mImages; - nsTArray mStyles; - nsTArray mFinalCompleteValues; + typedef nsTArray MatchesArray; + MatchesArray mMatches; nsString mSearchString; nsString mErrorDescription; diff --git a/toolkit/components/autocomplete/nsIAutoCompleteSearch.idl b/toolkit/components/autocomplete/nsIAutoCompleteSearch.idl index f4fe13f546..188c333ac6 100644 --- a/toolkit/components/autocomplete/nsIAutoCompleteSearch.idl +++ b/toolkit/components/autocomplete/nsIAutoCompleteSearch.idl @@ -50,7 +50,7 @@ interface nsIAutoCompleteObserver : nsISupports void onUpdateSearchResult(in nsIAutoCompleteSearch search, in nsIAutoCompleteResult result); }; -[scriptable, uuid(02314d6e-b730-40cc-a215-221554d77064)] +[scriptable, uuid(4c3e7462-fbfb-4310-8f4b-239238392b75)] interface nsIAutoCompleteSearchDescriptor : nsISupports { // The search is started after the timeout specified by the corresponding @@ -65,4 +65,10 @@ interface nsIAutoCompleteSearchDescriptor : nsISupports * Defaults to SEARCH_TYPE_DELAYED. */ readonly attribute unsigned short searchType; + + /* + * Whether a new search should be triggered when the user deletes the + * autofilled part. + */ + readonly attribute boolean clearingAutoFillSearchesAgain; }; diff --git a/toolkit/components/autocomplete/nsIAutoCompleteSimpleResult.idl b/toolkit/components/autocomplete/nsIAutoCompleteSimpleResult.idl index 6503c6ca5c..6a8827ab84 100644 --- a/toolkit/components/autocomplete/nsIAutoCompleteSimpleResult.idl +++ b/toolkit/components/autocomplete/nsIAutoCompleteSimpleResult.idl @@ -14,7 +14,7 @@ interface nsIAutoCompleteSimpleResultListener; * an array. */ -[scriptable, uuid(fe8802f9-c2b7-4141-8e5b-280df3f62251)] +[scriptable, uuid(23de9c96-becb-4d0d-a9bb-1d131ce361b5)] interface nsIAutoCompleteSimpleResult : nsIAutoCompleteResult { /** @@ -47,6 +47,31 @@ interface nsIAutoCompleteSimpleResult : nsIAutoCompleteResult */ void setTypeAheadResult(in boolean aHidden); + /** + * Inserts a match consisting of the given value, comment, image, style and + * the value to use for defaultIndex completion at a given position. + * @param aIndex + * The index to insert at + * @param aValue + * The value to autocomplete to + * @param aComment + * Comment shown in the autocomplete widget to describe this match + * @param aImage + * Image shown in the autocomplete widget for this match. + * @param aStyle + * Describes how to style the match in the autocomplete widget + * @param aFinalCompleteValue + * Value used when the user confirms selecting this match. If not + * provided, aValue will be used. + */ + void insertMatchAt(in long aIndex, + in AString aValue, + in AString aComment, + [optional] in AString aImage, + [optional] in AString aStyle, + [optional] in AString aFinalCompleteValue, + [optional] in AString aLabel); + /** * Appends a match consisting of the given value, comment, image, style and * the value to use for defaultIndex completion. @@ -66,7 +91,13 @@ interface nsIAutoCompleteSimpleResult : nsIAutoCompleteResult in AString aComment, [optional] in AString aImage, [optional] in AString aStyle, - [optional] in AString aFinalCompleteValue); + [optional] in AString aFinalCompleteValue, + [optional] in AString aLabel); + + /** + * Gets the listener for changes in the result. + */ + nsIAutoCompleteSimpleResultListener getListener(); /** * Sets a listener for changes in the result. diff --git a/toolkit/components/autocomplete/tests/unit/test_393191.js b/toolkit/components/autocomplete/tests/unit/test_393191.js index b73ec9c93e..30c782e717 100644 --- a/toolkit/components/autocomplete/tests/unit/test_393191.js +++ b/toolkit/components/autocomplete/tests/unit/test_393191.js @@ -146,6 +146,7 @@ AutoCompleteResult.prototype = { */ function AutoCompleteSearch(aName, aResult) { this.name = aName; + this._result = aResult; } AutoCompleteSearch.prototype = { constructor: AutoCompleteSearch, @@ -154,7 +155,7 @@ AutoCompleteSearch.prototype = { name: null, // AutoCompleteResult - _result:null, + _result: null, /** diff --git a/toolkit/components/autocomplete/tests/unit/test_660156.js b/toolkit/components/autocomplete/tests/unit/test_660156.js index 112616ed4c..98acb243e0 100644 --- a/toolkit/components/autocomplete/tests/unit/test_660156.js +++ b/toolkit/components/autocomplete/tests/unit/test_660156.js @@ -1,6 +1,3 @@ -/* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ - /** * Search object that returns results at different times. * First, the search that returns results asynchronously. @@ -10,11 +7,16 @@ function AutoCompleteAsyncSearch(aName, aResult) { this._result = aResult; } AutoCompleteAsyncSearch.prototype = Object.create(AutoCompleteSearchBase.prototype); -AutoCompleteAsyncSearch.prototype.startSearch = function(aSearchString, - aSearchParam, - aPreviousResult, +AutoCompleteAsyncSearch.prototype.startSearch = function(aSearchString, + aSearchParam, + aPreviousResult, aListener) { - setTimeout(this._returnResults.bind(this), 500, aListener); + this._result.searchResult = Ci.nsIAutoCompleteResult.RESULT_NOMATCH_ONGOING; + aListener.onSearchResult(this, this._result); + + do_timeout(500, () => { + this._returnResults(aListener); + }); }; AutoCompleteAsyncSearch.prototype._returnResults = function(aListener) { @@ -32,9 +34,9 @@ function AutoCompleteSyncSearch(aName, aResult) { this._result = aResult; } AutoCompleteSyncSearch.prototype = Object.create(AutoCompleteAsyncSearch.prototype); -AutoCompleteSyncSearch.prototype.startSearch = function(aSearchString, - aSearchParam, - aPreviousResult, +AutoCompleteSyncSearch.prototype.startSearch = function(aSearchString, + aSearchParam, + aPreviousResult, aListener) { this._returnResults(aListener); }; @@ -49,7 +51,7 @@ function AutoCompleteResult(aValues, aDefaultIndex) { AutoCompleteResult.prototype = Object.create(AutoCompleteResultBase.prototype); -/** +/** * Test AutoComplete with multiple AutoCompleteSearch sources, with one of them * (index != 0) returning before the rest. */ @@ -60,19 +62,19 @@ function run_test() { var inputStr = "moz"; // Async search - var asyncSearch = new AutoCompleteAsyncSearch("Async", + var asyncSearch = new AutoCompleteAsyncSearch("Async", new AutoCompleteResult(results, -1)); // Sync search var syncSearch = new AutoCompleteSyncSearch("Sync", new AutoCompleteResult(results, 0)); - + // Register searches so AutoCompleteController can find them registerAutoCompleteSearch(asyncSearch); registerAutoCompleteSearch(syncSearch); - + var controller = Cc["@mozilla.org/autocomplete/controller;1"]. - getService(Ci.nsIAutoCompleteController); - + getService(Ci.nsIAutoCompleteController); + // Make an AutoCompleteInput that uses our searches // and confirms results on search complete. // Async search MUST be FIRST to trigger the bug this tests. diff --git a/toolkit/components/autocomplete/tests/unit/test_insertMatchAt.js b/toolkit/components/autocomplete/tests/unit/test_insertMatchAt.js new file mode 100644 index 0000000000..14ee388b80 --- /dev/null +++ b/toolkit/components/autocomplete/tests/unit/test_insertMatchAt.js @@ -0,0 +1,14 @@ +function run_test() { + let result = Cc["@mozilla.org/autocomplete/simple-result;1"] + .createInstance(Ci.nsIAutoCompleteSimpleResult); + result.appendMatch("a", ""); + result.appendMatch("c", ""); + result.insertMatchAt(1, "b", ""); + result.insertMatchAt(3, "d", ""); + + Assert.equal(result.matchCount, 4); + Assert.equal(result.getValueAt(0), "a"); + Assert.equal(result.getValueAt(1), "b"); + Assert.equal(result.getValueAt(2), "c"); + Assert.equal(result.getValueAt(3), "d"); +} diff --git a/toolkit/components/autocomplete/tests/unit/xpcshell.ini b/toolkit/components/autocomplete/tests/unit/xpcshell.ini index 7f0ba577e6..6692529ae5 100644 --- a/toolkit/components/autocomplete/tests/unit/xpcshell.ini +++ b/toolkit/components/autocomplete/tests/unit/xpcshell.ini @@ -18,6 +18,7 @@ skip-if = toolkit == 'gonk' [test_finalDefaultCompleteValue.js] [test_hiddenResult.js] [test_immediate_search.js] +[test_insertMatchAt.js] [test_popupSelectionVsDefaultCompleteValue.js] [test_previousResult.js] [test_stopSearch.js] diff --git a/toolkit/components/build/moz.build b/toolkit/components/build/moz.build index 4c78e9ae2f..7b5e6b9451 100644 --- a/toolkit/components/build/moz.build +++ b/toolkit/components/build/moz.build @@ -32,3 +32,6 @@ if not CONFIG['MOZ_DISABLE_PARENTAL_CONTROLS']: LOCAL_INCLUDES += [ '../parentalcontrols', ] + +if CONFIG['GNU_CXX']: + CXXFLAGS += ['-Wshadow'] diff --git a/toolkit/components/commandlines/moz.build b/toolkit/components/commandlines/moz.build index 060f223c86..7877b4db59 100644 --- a/toolkit/components/commandlines/moz.build +++ b/toolkit/components/commandlines/moz.build @@ -28,3 +28,6 @@ FINAL_LIBRARY = 'xul' with Files('**'): BUG_COMPONENT = ('Toolkit', 'Startup and Profile System') + +if CONFIG['GNU_CXX']: + CXXFLAGS += ['-Wshadow'] diff --git a/toolkit/components/ctypes/moz.build b/toolkit/components/ctypes/moz.build index 616f5737c6..364352612c 100644 --- a/toolkit/components/ctypes/moz.build +++ b/toolkit/components/ctypes/moz.build @@ -22,3 +22,6 @@ FINAL_LIBRARY = 'xul' with Files('**'): BUG_COMPONENT = ('Core', 'js-ctypes') + +if CONFIG['GNU_CXX']: + CXXFLAGS += ['-Wshadow'] diff --git a/toolkit/components/ctypes/tests/moz.build b/toolkit/components/ctypes/tests/moz.build index 030d6e2ec7..4971524ee4 100644 --- a/toolkit/components/ctypes/tests/moz.build +++ b/toolkit/components/ctypes/tests/moz.build @@ -20,3 +20,6 @@ SharedLibrary('jsctypes-test') LOCAL_INCLUDES += [ '/js/src/ctypes', ] + +if CONFIG['GNU_CXX']: + CXXFLAGS += ['-Wshadow'] diff --git a/toolkit/components/diskspacewatcher/moz.build b/toolkit/components/diskspacewatcher/moz.build index d5b03fba16..29f382c44c 100644 --- a/toolkit/components/diskspacewatcher/moz.build +++ b/toolkit/components/diskspacewatcher/moz.build @@ -21,3 +21,6 @@ SOURCES = [ include('/ipc/chromium/chromium-config.mozbuild') FINAL_LIBRARY = 'xul' + +if CONFIG['GNU_CXX']: + CXXFLAGS += ['-Wshadow'] diff --git a/toolkit/components/filepicker/moz.build b/toolkit/components/filepicker/moz.build index aa6bce8a4d..9324c5ace5 100644 --- a/toolkit/components/filepicker/moz.build +++ b/toolkit/components/filepicker/moz.build @@ -25,3 +25,6 @@ if CONFIG['MOZ_XUL'] and \ FINAL_LIBRARY = 'xul' JAR_MANIFESTS += ['jar.mn'] + +if CONFIG['GNU_CXX']: + CXXFLAGS += ['-Wshadow'] diff --git a/toolkit/components/finalizationwitness/moz.build b/toolkit/components/finalizationwitness/moz.build index 10f772f9f0..11e93f7f73 100644 --- a/toolkit/components/finalizationwitness/moz.build +++ b/toolkit/components/finalizationwitness/moz.build @@ -23,3 +23,6 @@ LOCAL_INCLUDES += [ ] FINAL_LIBRARY = 'xul' + +if CONFIG['GNU_CXX']: + CXXFLAGS += ['-Wshadow'] diff --git a/toolkit/components/find/moz.build b/toolkit/components/find/moz.build index 9025febf78..db0508834c 100644 --- a/toolkit/components/find/moz.build +++ b/toolkit/components/find/moz.build @@ -15,3 +15,6 @@ SOURCES += [ ] FINAL_LIBRARY = 'xul' + +if CONFIG['GNU_CXX']: + CXXFLAGS += ['-Wshadow'] diff --git a/toolkit/components/jsdownloads/moz.build b/toolkit/components/jsdownloads/moz.build index 31e418630e..32e7ae7900 100644 --- a/toolkit/components/jsdownloads/moz.build +++ b/toolkit/components/jsdownloads/moz.build @@ -4,6 +4,9 @@ # 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/. +with Files('*'): + BUG_COMPONENT = ('Toolkit', 'Download Manager') + DIRS += ['public', 'src'] XPCSHELL_TESTS_MANIFESTS += ['test/data/xpcshell.ini', 'test/unit/xpcshell.ini'] diff --git a/toolkit/components/parentalcontrols/nsParentalControlsServiceAndroid.cpp b/toolkit/components/parentalcontrols/nsParentalControlsServiceAndroid.cpp index c827b2596a..663252b4df 100644 --- a/toolkit/components/parentalcontrols/nsParentalControlsServiceAndroid.cpp +++ b/toolkit/components/parentalcontrols/nsParentalControlsServiceAndroid.cpp @@ -13,7 +13,7 @@ NS_IMPL_ISUPPORTS(nsParentalControlsService, nsIParentalControlsService) nsParentalControlsService::nsParentalControlsService() : mEnabled(false) { - if (mozilla::AndroidBridge::HasEnv()) { + if (mozilla::jni::IsAvailable()) { mEnabled = mozilla::widget::RestrictedProfiles::IsUserRestricted(); } } @@ -85,7 +85,7 @@ nsParentalControlsService::IsAllowed(int16_t aAction, return rv; } - if (mozilla::AndroidBridge::HasEnv()) { + if (mozilla::jni::IsAvailable()) { nsAutoCString url; if (aUri) { rv = aUri->GetSpec(url); diff --git a/toolkit/components/satchel/nsFormAutoComplete.js b/toolkit/components/satchel/nsFormAutoComplete.js index 8be08b7783..f5a32ac33e 100644 --- a/toolkit/components/satchel/nsFormAutoComplete.js +++ b/toolkit/components/satchel/nsFormAutoComplete.js @@ -284,7 +284,7 @@ FormAutoComplete.prototype = { result.entries = aEntries; } - if (aDatalistResult) { + if (aDatalistResult && aDatalistResult.matchCount > 0) { result = this.mergeResults(result, aDatalistResult); } diff --git a/toolkit/components/satchel/nsFormAutoCompleteResult.jsm b/toolkit/components/satchel/nsFormAutoCompleteResult.jsm index 17b52adbc3..c9fd25bb0c 100644 --- a/toolkit/components/satchel/nsFormAutoCompleteResult.jsm +++ b/toolkit/components/satchel/nsFormAutoCompleteResult.jsm @@ -110,7 +110,7 @@ FormAutoCompleteResult.prototype = { getLabelAt: function(index) { this._checkIndexBounds(index); - return this._labels[index]; + return this._labels[index] || this._values[index]; }, /** diff --git a/toolkit/components/satchel/nsFormFillController.cpp b/toolkit/components/satchel/nsFormFillController.cpp index 26b1e82d03..0528c076aa 100644 --- a/toolkit/components/satchel/nsFormFillController.cpp +++ b/toolkit/components/satchel/nsFormFillController.cpp @@ -734,11 +734,12 @@ public: : mObserver(aObserver) , mSearch(aSearch) , mResult(aResult) - {} + { + MOZ_ASSERT(mResult, "Should have a valid result"); + MOZ_ASSERT(mObserver, "You shouldn't call this runnable with a null observer!"); + } NS_IMETHOD Run() { - NS_ASSERTION(mObserver, "You shouldn't call this runnable with a null observer!"); - mObserver->OnUpdateSearchResult(mSearch, mResult); return NS_OK; } @@ -1028,6 +1029,13 @@ nsFormFillController::KeyPress(nsIDOMEvent* aEvent) if (cancel) { aEvent->PreventDefault(); + // Don't let the page see the RETURN event when the popup is open + // (indicated by cancel=true) so sites don't manually submit forms + // (e.g. via submit.click()) without the autocompleted value being filled. + // Bug 286933 will fix this for other key events. + if (k == nsIDOMKeyEvent::DOM_VK_RETURN) { + aEvent->StopPropagation(); + } } return NS_OK; @@ -1103,6 +1111,7 @@ nsFormFillController::AddWindowListeners(nsIDOMWindow *aWindow) true, false); target->AddEventListener(NS_LITERAL_STRING("input"), this, true, false); + target->AddEventListener(NS_LITERAL_STRING("keypress"), this, true, false); target->AddEventListener(NS_LITERAL_STRING("compositionstart"), this, true, false); target->AddEventListener(NS_LITERAL_STRING("compositionend"), this, @@ -1141,6 +1150,7 @@ nsFormFillController::RemoveWindowListeners(nsIDOMWindow *aWindow) target->RemoveEventListener(NS_LITERAL_STRING("pagehide"), this, true); target->RemoveEventListener(NS_LITERAL_STRING("mousedown"), this, true); target->RemoveEventListener(NS_LITERAL_STRING("input"), this, true); + target->RemoveEventListener(NS_LITERAL_STRING("keypress"), this, true); target->RemoveEventListener(NS_LITERAL_STRING("compositionstart"), this, true); target->RemoveEventListener(NS_LITERAL_STRING("compositionend"), this, @@ -1148,22 +1158,6 @@ nsFormFillController::RemoveWindowListeners(nsIDOMWindow *aWindow) target->RemoveEventListener(NS_LITERAL_STRING("contextmenu"), this, true); } -void -nsFormFillController::AddKeyListener(nsINode* aInput) -{ - aInput->AddEventListener(NS_LITERAL_STRING("keypress"), this, - true, false); -} - -void -nsFormFillController::RemoveKeyListener() -{ - if (!mFocusedInputNode) - return; - - mFocusedInputNode->RemoveEventListener(NS_LITERAL_STRING("keypress"), this, true); -} - void nsFormFillController::StartControllingInput(nsIDOMHTMLInputElement *aInput) { @@ -1188,8 +1182,6 @@ nsFormFillController::StartControllingInput(nsIDOMHTMLInputElement *aInput) return; } - AddKeyListener(node); - node->AddMutationObserverUnlessExists(this); mFocusedInputNode = node; mFocusedInput = aInput; @@ -1202,15 +1194,12 @@ nsFormFillController::StartControllingInput(nsIDOMHTMLInputElement *aInput) mListNode = listNode; } - // Now we are the autocomplete controller's bitch mController->SetInput(this); } void nsFormFillController::StopControllingInput() { - RemoveKeyListener(); - if (mListNode) { mListNode->RemoveMutationObserver(this); mListNode = nullptr; diff --git a/toolkit/components/satchel/nsInputListAutoComplete.js b/toolkit/components/satchel/nsInputListAutoComplete.js index f529b3ae01..f42427862c 100644 --- a/toolkit/components/satchel/nsInputListAutoComplete.js +++ b/toolkit/components/satchel/nsInputListAutoComplete.js @@ -16,12 +16,12 @@ InputListAutoComplete.prototype = { autoCompleteSearch : function (aUntrimmedSearchString, aField) { let [values, labels] = this.getListSuggestions(aField); - if (values.length === 0) - return null; + let searchResult = values.length > 0 ? Ci.nsIAutoCompleteResult.RESULT_SUCCESS + : Ci.nsIAutoCompleteResult.RESULT_NOMATCH; + let defaultIndex = values.length > 0 ? 0 : -1; return new FormAutoCompleteResult(aUntrimmedSearchString, - Ci.nsIAutoCompleteResult.RESULT_SUCCESS, - 0, "", values, labels, - [], null); + searchResult, defaultIndex, "", + values, labels, [], null); }, getListSuggestions : function (aField) { diff --git a/toolkit/components/satchel/test/mochitest.ini b/toolkit/components/satchel/test/mochitest.ini index 99f1908a2f..78d05fa094 100644 --- a/toolkit/components/satchel/test/mochitest.ini +++ b/toolkit/components/satchel/test/mochitest.ini @@ -13,3 +13,4 @@ skip-if = true # Test disabled for too many intermittent failures (bug 874429) [test_form_submission.html] [test_form_submission_cap.html] [test_form_submission_cap2.html] +[test_popup_enter_event.html] diff --git a/toolkit/components/satchel/test/test_popup_enter_event.html b/toolkit/components/satchel/test/test_popup_enter_event.html new file mode 100644 index 0000000000..976d5bf76a --- /dev/null +++ b/toolkit/components/satchel/test/test_popup_enter_event.html @@ -0,0 +1,88 @@ + + + + + Test for events while the form history popup is open + + + + + + +Form History test: Test for events while the form history popup is open +

+ +
+
+ + +
+
+ +
+
+
+ + diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json index 29d8127d89..54f7986e60 100644 --- a/toolkit/components/telemetry/Histograms.json +++ b/toolkit/components/telemetry/Histograms.json @@ -5376,13 +5376,33 @@ "kind": "boolean", "description": "Count the number of times the user clicked 'allow' on the hidden-plugin infobar." }, - "POPUP_NOTIFICATION_MAINACTION_TRIGGERED_MS": { - "expires_in_version": "40", - "kind": "linear", - "low": 25, - "high": "80 * 25", - "n_buckets": "80 + 1", - "description": "The time (in milliseconds) after showing a PopupNotification that the mainAction was first triggered" + "POPUP_NOTIFICATION_STATS": { + "alert_emails": ["firefox-dev@mozilla.org"], + "expires_in_version": "48", + "kind": "enumerated", + "keyed": true, + "n_values": 40, + "description": "(Bug 1207089) Usage of popup notifications, keyed by ID (0 = Offered, 1..4 = Action, 5 = Click outside, 6 = Leave page, 7 = Use 'X', 8 = Not now, 10 = Open submenu, 11 = Learn more. Add 20 if happened after reopen.)" + }, + "POPUP_NOTIFICATION_MAIN_ACTION_MS": { + "alert_emails": ["firefox-dev@mozilla.org"], + "expires_in_version": "48", + "kind": "exponential", + "keyed": true, + "low": 100, + "high": 600000, + "n_buckets": 40, + "description": "(Bug 1207089) Time in ms between initially requesting a popup notification and triggering the main action, keyed by ID" + }, + "POPUP_NOTIFICATION_DISMISSAL_MS": { + "alert_emails": ["firefox-dev@mozilla.org"], + "expires_in_version": "48", + "kind": "exponential", + "keyed": true, + "low": 200, + "high": 20000, + "n_buckets": 50, + "description": "(Bug 1207089) Time in ms between displaying a popup notification and dismissing it without an action the first time, keyed by ID" }, "DEVTOOLS_DEBUGGER_RDP_LOCAL_RELOAD_MS": { "expires_in_version": "never", @@ -5969,18 +5989,6 @@ "kind": "boolean", "description": "The number of failed ICE Connections (0) vs. number of successful ICE connections (1)." }, - "WEBRTC_CANDIDATE_TYPES_GIVEN_SUCCESS": { - "expires_in_version": "never", - "kind": "enumerated", - "n_values": 128, - "description": "A bitpattern indicating what types of candidates were present. Bit 0: Remote server reflexive. Bit 1: Remote relayed. Bit 2: Local server reflexive. Bits 3-6: Local UDP, TCP, TLS, and HTTPS relay respectively." - }, - "WEBRTC_CANDIDATE_TYPES_GIVEN_FAILURE": { - "expires_in_version": "never", - "kind": "enumerated", - "n_values": 128, - "description": "Identical to WEBRTC_CANDIDATE_TYPES_GIVEN_SUCCEESS, except recorded only when ICE fails on the stream in question." - }, "WEBRTC_STUN_RATE_LIMIT_EXCEEDED_BY_TYPE_GIVEN_SUCCESS": { "expires_in_version": "never", "kind": "enumerated", @@ -7191,13 +7199,6 @@ "kind": "boolean", "description": "The result of the startup default desktop browser check." }, - "WIN_10_DEFAULT_BROWSER_AB_TEST": { - "alert_emails": ["jwein@mozilla.com"], - "expires_in_version": "45", - "kind": "enumerated", - "n_values": 4, - "description": "A/B test of different default browser dialogs on Windows 10 (0=openas-notdefault, 1=openas-default, 2=modernsettings-notdefault, 3=modernsettings-default)." - }, "BROWSER_IS_ASSIST_DEFAULT": { "expires_in_version": "never", "kind": "boolean", @@ -8807,18 +8808,6 @@ "kind": "boolean", "description": "The number of failed ICE Connections (0) vs. number of successful ICE connections (1)." }, - "LOOP_CANDIDATE_TYPES_GIVEN_SUCCESS": { - "expires_in_version": "never", - "kind": "enumerated", - "n_values": 128, - "description": "A bitpattern indicating what types of candidates were present. Bit 0: Remote server reflexive. Bit 1: Remote relayed. Bit 2: Local server reflexive. Bits 3-6: Local UDP, TCP, TLS, and HTTPS relay respectively." - }, - "LOOP_CANDIDATE_TYPES_GIVEN_FAILURE": { - "expires_in_version": "never", - "kind": "enumerated", - "n_values": 128, - "description": "Identical to LOOP_CANDIDATE_TYPES_GIVEN_SUCCEESS, except recorded only when ICE fails on the stream in question." - }, "LOOP_VIDEO_QUALITY_INBOUND_BANDWIDTH_KBITS": { "expires_in_version": "never", "kind": "exponential", @@ -9234,5 +9223,96 @@ "n_values": 100, "releaseChannelCollection": "opt-out", "description": "Graphics Crash Reason (...)" + }, + "PLUGIN_ACTIVATION_COUNT": { + "alert_emails": ["cpeterson@mozilla.com"], + "expires_in_version": "48", + "kind": "count", + "keyed": true, + "releaseChannelCollection": "opt-out", + "description": "Counts number of times a certain plugin has been activated." + }, + "YOUTUBE_EMBED_SEEN": { + "alert_emails": ["cpeterson@mozilla.com"], + "expires_in_version": "48", + "kind": "flag", + "description": "Flag activated whenever a youtube flash embed is seen during a session." + }, + "WEBFONT_DOWNLOAD_TIME": { + "alert_emails": ["jdaggett@mozilla.com"], + "expires_in_version": "never", + "kind": "exponential", + "high": "60000", + "n_buckets": 50, + "extended_statistics_ok": true, + "description": "Time to download a webfont (ms)" + }, + "WEBFONT_DOWNLOAD_TIME_AFTER_START": { + "alert_emails": ["jdaggett@mozilla.com"], + "expires_in_version": "never", + "kind": "exponential", + "high": "60000", + "n_buckets": 50, + "extended_statistics_ok": true, + "description": "Time after navigationStart webfont download completed (ms)" + }, + "WEBFONT_FONTTYPE": { + "alert_emails": ["jdaggett@mozilla.com"], + "expires_in_version": "never", + "kind": "enumerated", + "n_values": 10, + "description": "Font format type (woff/woff2/ttf/...)" + }, + "WEBFONT_SRCTYPE": { + "alert_emails": ["jdaggett@mozilla.com"], + "expires_in_version": "never", + "kind": "enumerated", + "n_values": 5, + "description": "Font src type loaded (1 = local, 2 = url, 3 = data)" + }, + "WEBFONT_PER_PAGE": { + "alert_emails": ["jdaggett@mozilla.com"], + "expires_in_version": "never", + "kind": "count", + "description": "Number of fonts loaded at page load" + }, + "WEBFONT_SIZE_PER_PAGE": { + "alert_emails": ["jdaggett@mozilla.com"], + "expires_in_version": "never", + "kind": "exponential", + "high": "5000", + "n_buckets": 50, + "extended_statistics_ok": true, + "description": "Size of all fonts loaded at page load (kb)" + }, + "WEBFONT_SIZE": { + "alert_emails": ["jdaggett@mozilla.com"], + "expires_in_version": "never", + "kind": "exponential", + "high": "5000", + "n_buckets": 50, + "extended_statistics_ok": true, + "description": "Size of font loaded (kb)" + }, + "WEBFONT_COMPRESSION_WOFF": { + "alert_emails": ["jdaggett@mozilla.com"], + "expires_in_version": "never", + "kind": "enumerated", + "n_values": 50, + "description": "Compression ratio of WOFF data (%)" + }, + "WEBFONT_COMPRESSION_WOFF2": { + "alert_emails": ["jdaggett@mozilla.com"], + "expires_in_version": "never", + "kind": "enumerated", + "n_values": 50, + "description": "Compression ratio of WOFF2 data (%)" + }, + "WEBRTC_ICE_CHECKING_RATE": { + "alert_emails": ["webrtc-ice-telemetry-alerts@mozilla.com"], + "expires_in_version": "53", + "kind": "boolean", + "bug_numbers": [1188391], + "description": "The number of ICE connections which immediately failed (0) vs. reached at least checking state (1)." } } diff --git a/toolkit/components/telemetry/Telemetry.cpp b/toolkit/components/telemetry/Telemetry.cpp index ba5287dc14..9d1689a334 100644 --- a/toolkit/components/telemetry/Telemetry.cpp +++ b/toolkit/components/telemetry/Telemetry.cpp @@ -10,6 +10,7 @@ #include +#include "mozilla/dom/ToJSValue.h" #include "mozilla/Attributes.h" #include "mozilla/DebugOnly.h" #include "mozilla/Likely.h" @@ -39,6 +40,8 @@ #include "nsIMemoryReporter.h" #include "nsISeekableStream.h" #include "Telemetry.h" +#include "TelemetryCommon.h" +#include "WebrtcTelemetry.h" #include "nsTHashtable.h" #include "nsHashKeys.h" #include "nsBaseHashtable.h" @@ -66,6 +69,9 @@ #include "mozilla/PoisonIOInterposer.h" #include "mozilla/StartupTimeline.h" #include "mozilla/HangMonitor.h" +#if defined(MOZ_ENABLE_PROFILER_SPS) +#include "shared-libraries.h" +#endif #define EXPIRED_ID "__expired__" @@ -81,6 +87,11 @@ using base::Histogram; using base::LinearHistogram; using base::StatisticsRecorder; +// The maximum number of chrome hangs stacks that we're keeping. +const size_t kMaxChromeStacksKept = 50; +// The maximum depth of a single chrome hang stack. +const size_t kMaxChromeStackDepth = 50; + #define KEYED_HISTOGRAM_NAME_SEPARATOR "#" #define SUBSESSION_HISTOGRAM_PREFIX "sub#" @@ -98,53 +109,24 @@ HistogramGet(const char *name, const char *expiration, uint32_t histogramType, enum reflectStatus ReflectHistogramSnapshot(JSContext *cx, JS::Handle obj, Histogram *h); -template -class AutoHashtable : public nsTHashtable -{ -public: - explicit AutoHashtable(uint32_t initLength = - PLDHashTable::kDefaultInitialLength); - typedef bool (*ReflectEntryFunc)(EntryType *entry, JSContext *cx, JS::Handle obj); - bool ReflectIntoJS(ReflectEntryFunc entryFunc, JSContext *cx, JS::Handle obj); -}; - -template -AutoHashtable::AutoHashtable(uint32_t initLength) - : nsTHashtable(initLength) -{ -} - -/** - * Reflect the individual entries of table into JS, usually by defining - * some property and value of obj. entryFunc is called for each entry. - */ -template -bool -AutoHashtable::ReflectIntoJS(ReflectEntryFunc entryFunc, - JSContext *cx, JS::Handle obj) -{ - for (auto iter = this->Iter(); !iter.Done(); iter.Next()) { - if (!entryFunc(iter.Get(), cx, obj)) { - return false; - } - } - return true; -} - // This class is conceptually a list of ProcessedStack objects, but it represents them // more efficiently by keeping a single global list of modules. class CombinedStacks { public: + CombinedStacks() : mNextIndex(0) {} typedef std::vector Stack; const Telemetry::ProcessedStack::Module& GetModule(unsigned aIndex) const; size_t GetModuleCount() const; const Stack& GetStack(unsigned aIndex) const; - void AddStack(const Telemetry::ProcessedStack& aStack); + size_t AddStack(const Telemetry::ProcessedStack& aStack); size_t GetStackCount() const; size_t SizeOfExcludingThis() const; private: std::vector mModules; + // A circular buffer to hold the stacks. std::vector mStacks; + // The index of the next buffer element to write to in mStacks. + size_t mNextIndex; }; static JSObject * @@ -160,10 +142,18 @@ CombinedStacks::GetModule(unsigned aIndex) const { return mModules[aIndex]; } -void +size_t CombinedStacks::AddStack(const Telemetry::ProcessedStack& aStack) { - mStacks.resize(mStacks.size() + 1); - CombinedStacks::Stack& adjustedStack = mStacks.back(); + // Advance the indices of the circular queue holding the stacks. + size_t index = mNextIndex++ % kMaxChromeStacksKept; + // Grow the vector up to the maximum size, if needed. + if (mStacks.size() < kMaxChromeStacksKept) { + mStacks.resize(mStacks.size() + 1); + } + // Get a reference to the location holding the new stack. + CombinedStacks::Stack& adjustedStack = mStacks[index]; + // If we're using an old stack to hold aStack, clear it. + adjustedStack.clear(); size_t stackSize = aStack.GetStackSize(); for (size_t i = 0; i < stackSize; ++i) { @@ -186,6 +176,7 @@ CombinedStacks::AddStack(const Telemetry::ProcessedStack& aStack) { Telemetry::ProcessedStack::Frame adjustedFrame = { frame.mOffset, modIndex }; adjustedStack.push_back(adjustedFrame); } + return index; } const CombinedStacks::Stack& @@ -217,6 +208,27 @@ CombinedStacks::SizeOfExcludingThis() const { return n; } +// This utility function generates a string key that is used to index the annotations +// in a hash map from |HangReports::AddHang|. +nsresult +ComputeAnnotationsKey(const HangAnnotationsPtr& aAnnotations, nsAString& aKeyOut) +{ + UniquePtr annotationsEnum = aAnnotations->GetEnumerator(); + if (!annotationsEnum) { + return NS_ERROR_FAILURE; + } + + // Append all the attributes to the key, to uniquely identify this annotation. + nsAutoString key; + nsAutoString value; + while (annotationsEnum->Next(key, value)) { + aKeyOut.Append(key); + aKeyOut.Append(value); + } + + return NS_OK; +} + class HangReports { public: /** @@ -226,21 +238,24 @@ public: struct AnnotationInfo { AnnotationInfo(uint32_t aHangIndex, HangAnnotationsPtr aAnnotations) - : mHangIndex(aHangIndex) - , mAnnotations(Move(aAnnotations)) - {} + : mAnnotations(Move(aAnnotations)) + { + mHangIndices.AppendElement(aHangIndex); + } AnnotationInfo(AnnotationInfo&& aOther) - : mHangIndex(aOther.mHangIndex) + : mHangIndices(aOther.mHangIndices) , mAnnotations(Move(aOther.mAnnotations)) {} ~AnnotationInfo() {} AnnotationInfo& operator=(AnnotationInfo&& aOther) { - mHangIndex = aOther.mHangIndex; + mHangIndices = aOther.mHangIndices; mAnnotations = Move(aOther.mAnnotations); return *this; } - uint32_t mHangIndex; + // To save memory, a single AnnotationInfo can be associated to multiple chrome + // hangs. The following array holds the index of each related chrome hang. + nsTArray mHangIndices; HangAnnotationsPtr mAnnotations; private: @@ -252,10 +267,11 @@ public: void AddHang(const Telemetry::ProcessedStack& aStack, uint32_t aDuration, int32_t aSystemUptime, int32_t aFirefoxUptime, HangAnnotationsPtr aAnnotations); + void PruneStackReferences(const size_t aRemovedStackIndex); uint32_t GetDuration(unsigned aIndex) const; int32_t GetSystemUptime(unsigned aIndex) const; int32_t GetFirefoxUptime(unsigned aIndex) const; - const nsTArray& GetAnnotationInfo() const; + const nsClassHashtable& GetAnnotationInfo() const; const CombinedStacks& GetStacks() const; private: /** @@ -271,7 +287,7 @@ private: int32_t mFirefoxUptime; }; std::vector mHangInfo; - nsTArray mAnnotationInfo; + nsClassHashtable mAnnotationInfo; CombinedStacks mStacks; }; @@ -281,14 +297,72 @@ HangReports::AddHang(const Telemetry::ProcessedStack& aStack, int32_t aSystemUptime, int32_t aFirefoxUptime, HangAnnotationsPtr aAnnotations) { + // Append the new stack to the stack's circular queue. + size_t hangIndex = mStacks.AddStack(aStack); + // Append the hang info at the same index, in mHangInfo. HangInfo info = { aDuration, aSystemUptime, aFirefoxUptime }; - mHangInfo.push_back(info); - if (aAnnotations) { - AnnotationInfo ainfo(static_cast(mHangInfo.size() - 1), - Move(aAnnotations)); - mAnnotationInfo.AppendElement(Move(ainfo)); + if (mHangInfo.size() < kMaxChromeStacksKept) { + mHangInfo.push_back(info); + } else { + mHangInfo[hangIndex] = info; + // Remove any reference to the stack overwritten in the circular queue + // from the annotations. + PruneStackReferences(hangIndex); + } + + if (!aAnnotations) { + return; + } + + nsAutoString annotationsKey; + // Generate a key to index aAnnotations in the hash map. + nsresult rv = ComputeAnnotationsKey(aAnnotations, annotationsKey); + if (NS_FAILED(rv)) { + return; + } + + AnnotationInfo* annotationsEntry = mAnnotationInfo.Get(annotationsKey); + if (annotationsEntry) { + // If the key is already in the hash map, append the index of the chrome hang + // to its indices. + annotationsEntry->mHangIndices.AppendElement(hangIndex); + return; + } + + // If the key was not found, add the annotations to the hash map. + mAnnotationInfo.Put(annotationsKey, new AnnotationInfo(hangIndex, Move(aAnnotations))); +} + +/** + * This function removes links to discarded chrome hangs stacks and prunes unused + * annotations. + */ +void +HangReports::PruneStackReferences(const size_t aRemovedStackIndex) { + // We need to adjust the indices that link annotations to chrome hangs. Since we + // removed a stack, we must remove all references to it and prune annotations + // linked to no stacks. + for (auto iter = mAnnotationInfo.Iter(); !iter.Done(); iter.Next()) { + nsTArray& stackIndices = iter.Data()->mHangIndices; + size_t toRemove = stackIndices.NoIndex; + for (size_t k = 0; k < stackIndices.Length(); k++) { + // Is this index referencing the removed stack? + if (stackIndices[k] == aRemovedStackIndex) { + toRemove = k; + break; + } + } + + // Remove the index referencing the old stack from the annotation. + if (toRemove != stackIndices.NoIndex) { + stackIndices.RemoveElementAt(toRemove); + } + + // If this annotation no longer references any stack, drop it. + if (!stackIndices.Length()) { + iter.Remove(); + } } - mStacks.AddStack(aStack); } size_t @@ -298,9 +372,11 @@ HangReports::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const { // This is a crude approximation. See comment on // CombinedStacks::SizeOfExcludingThis. n += mHangInfo.capacity() * sizeof(HangInfo); - n += mAnnotationInfo.Capacity() * sizeof(AnnotationInfo); - for (int32_t i = 0, l = mAnnotationInfo.Length(); i < l; ++i) { - n += mAnnotationInfo[i].mAnnotations->SizeOfIncludingThis(aMallocSizeOf); + n += mAnnotationInfo.ShallowSizeOfExcludingThis(aMallocSizeOf); + n += mAnnotationInfo.Count() * sizeof(AnnotationInfo); + for (auto iter = mAnnotationInfo.ConstIter(); !iter.Done(); iter.Next()) { + n += iter.Key().SizeOfExcludingThisIfUnshared(aMallocSizeOf); + n += iter.Data()->mAnnotations->SizeOfIncludingThis(aMallocSizeOf); } return n; } @@ -325,7 +401,7 @@ HangReports::GetFirefoxUptime(unsigned aIndex) const { return mHangInfo[aIndex].mFirefoxUptime; } -const nsTArray& +const nsClassHashtable& HangReports::GetAnnotationInfo() const { return mAnnotationInfo; } @@ -631,6 +707,14 @@ public: static void ShutdownTelemetry(); static void RecordSlowStatement(const nsACString &sql, const nsACString &dbName, uint32_t delay); +#if defined(MOZ_ENABLE_PROFILER_SPS) + static void RecordChromeHang(uint32_t aDuration, + Telemetry::ProcessedStack &aStack, + int32_t aSystemUptime, + int32_t aFirefoxUptime, + HangAnnotationsPtr aAnnotations); +#endif + static void RecordThreadHangStats(Telemetry::ThreadHangStats& aStats); static nsresult GetHistogramEnumId(const char *name, Telemetry::ID *id); size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf); struct Stat { @@ -645,6 +729,9 @@ public: static KeyedHistogram* GetKeyedHistogramById(const nsACString &id); + static void RecordIceCandidates(const uint32_t iceCandidateBitmask, + const bool success, + const bool loop); private: TelemetryImpl(); ~TelemetryImpl(); @@ -709,6 +796,9 @@ private: Mutex mHashMutex; HangReports mHangReports; Mutex mHangReportsMutex; + // mThreadHangStats stores recorded, inactive thread hang stats + Vector mThreadHangStats; + Mutex mThreadHangStatsMutex; CombinedStacks mLateWritesStacks; // This is collected out of the main thread. bool mCachedTelemetryData; @@ -717,14 +807,10 @@ private: nsCOMArray mCallbacks; friend class nsFetchTelemetryData; + WebrtcTelemetry mWebrtcTelemetry; + typedef nsClassHashtable KeyedHistogramMapType; KeyedHistogramMapType mKeyedHistograms; - - struct KeyedHistogramReflectArgs { - JSContext* jsContext; - JS::Handle object; - }; - static PLDHashOperator KeyedHistogramsReflector(const nsACString&, nsAutoPtr&, void* args); }; TelemetryImpl* TelemetryImpl::sTelemetry = nullptr; @@ -761,7 +847,9 @@ private: typedef nsBaseHashtableET KeyedHistogramEntry; typedef AutoHashtable KeyedHistogramMapType; KeyedHistogramMapType mHistogramMap; +#if !defined(MOZ_WIDGET_GONK) && !defined(MOZ_WIDGET_ANDROID) KeyedHistogramMapType mSubsessionMap; +#endif static bool ReflectKeyedHistogram(KeyedHistogramEntry* entry, JSContext* cx, @@ -1021,6 +1109,7 @@ CloneHistogram(const nsACString& newName, Telemetry::ID existingId) return CloneHistogram(newName, existingId, *existing); } +#if !defined(MOZ_WIDGET_GONK) && !defined(MOZ_WIDGET_ANDROID) Histogram* GetSubsessionHistogram(Histogram& existing) { @@ -1047,6 +1136,7 @@ GetSubsessionHistogram(Histogram& existing) subsession[id] = CloneHistogram(subsessionName, id, existing); return subsession[id]; } +#endif nsresult HistogramAdd(Histogram& histogram, int32_t value, uint32_t dataset) @@ -1252,6 +1342,7 @@ JSHistogram_Clear(JSContext *cx, unsigned argc, JS::Value *vp) } bool onlySubsession = false; +#if !defined(MOZ_WIDGET_GONK) && !defined(MOZ_WIDGET_ANDROID) JS::CallArgs args = JS::CallArgsFromVp(argc, vp); if (args.length() >= 1) { @@ -1262,6 +1353,7 @@ JSHistogram_Clear(JSContext *cx, unsigned argc, JS::Value *vp) onlySubsession = JS::ToBoolean(args[0]); } +#endif Histogram *h = static_cast(JS_GetPrivate(obj)); MOZ_ASSERT(h); @@ -1269,9 +1361,11 @@ JSHistogram_Clear(JSContext *cx, unsigned argc, JS::Value *vp) h->Clear(); } +#if !defined(MOZ_WIDGET_GONK) && !defined(MOZ_WIDGET_ANDROID) if (Histogram* subsession = GetSubsessionHistogram(*h)) { subsession->Clear(); } +#endif return true; } @@ -1453,12 +1547,15 @@ JSKeyedHistogram_Snapshot(JSContext *cx, unsigned argc, JS::Value *vp) return KeyedHistogram_SnapshotImpl(cx, argc, vp, false, false); } +#if !defined(MOZ_WIDGET_GONK) && !defined(MOZ_WIDGET_ANDROID) bool JSKeyedHistogram_SubsessionSnapshot(JSContext *cx, unsigned argc, JS::Value *vp) { return KeyedHistogram_SnapshotImpl(cx, argc, vp, true, false); } +#endif +#if !defined(MOZ_WIDGET_GONK) && !defined(MOZ_WIDGET_ANDROID) bool JSKeyedHistogram_SnapshotSubsessionAndClear(JSContext *cx, unsigned argc, JS::Value *vp) { @@ -1469,6 +1566,7 @@ JSKeyedHistogram_SnapshotSubsessionAndClear(JSContext *cx, unsigned argc, JS::Va return KeyedHistogram_SnapshotImpl(cx, argc, vp, true, true); } +#endif bool JSKeyedHistogram_Clear(JSContext *cx, unsigned argc, JS::Value *vp) @@ -1483,6 +1581,7 @@ JSKeyedHistogram_Clear(JSContext *cx, unsigned argc, JS::Value *vp) return false; } +#if !defined(MOZ_WIDGET_GONK) && !defined(MOZ_WIDGET_ANDROID) bool onlySubsession = false; JS::CallArgs args = JS::CallArgsFromVp(argc, vp); @@ -1496,6 +1595,9 @@ JSKeyedHistogram_Clear(JSContext *cx, unsigned argc, JS::Value *vp) } keyed->Clear(onlySubsession); +#else + keyed->Clear(false); +#endif return true; } @@ -1536,8 +1638,10 @@ WrapAndReturnKeyedHistogram(KeyedHistogram *h, JSContext *cx, JS::MutableHandle< return NS_ERROR_FAILURE; if (!(JS_DefineFunction(cx, obj, "add", JSKeyedHistogram_Add, 2, 0) && JS_DefineFunction(cx, obj, "snapshot", JSKeyedHistogram_Snapshot, 1, 0) +#if !defined(MOZ_WIDGET_GONK) && !defined(MOZ_WIDGET_ANDROID) && JS_DefineFunction(cx, obj, "subsessionSnapshot", JSKeyedHistogram_SubsessionSnapshot, 1, 0) && JS_DefineFunction(cx, obj, "snapshotSubsessionAndClear", JSKeyedHistogram_SnapshotSubsessionAndClear, 0, 0) +#endif && JS_DefineFunction(cx, obj, "keys", JSKeyedHistogram_Keys, 0, 0) && JS_DefineFunction(cx, obj, "clear", JSKeyedHistogram_Clear, 0, 0) && JS_DefineFunction(cx, obj, "dataset", JSKeyedHistogram_Dataset, 0, 0))) { @@ -1793,6 +1897,7 @@ mCanRecordBase(XRE_IsParentProcess() || XRE_IsContentProcess()), mCanRecordExtended(XRE_IsParentProcess() || XRE_IsContentProcess()), mHashMutex("Telemetry::mHashMutex"), mHangReportsMutex("Telemetry::mHangReportsMutex"), +mThreadHangStatsMutex("Telemetry::mThreadHangStatsMutex"), mCachedTelemetryData(false), mLastShutdownTime(0), mFailedLockCount(0) @@ -2207,12 +2312,14 @@ TelemetryImpl::CreateHistogramSnapshots(JSContext *cx, } Histogram* original = h; +#if !defined(MOZ_WIDGET_GONK) && !defined(MOZ_WIDGET_ANDROID) if (subsession) { h = GetSubsessionHistogram(*h); if (!h) { continue; } } +#endif hobj = JS_NewPlainObject(cx); if (!hobj) { @@ -2233,9 +2340,11 @@ TelemetryImpl::CreateHistogramSnapshots(JSContext *cx, } } +#if !defined(MOZ_WIDGET_GONK) && !defined(MOZ_WIDGET_ANDROID) if (subsession && clearSubsession) { h->Clear(); } +#endif } return NS_OK; } @@ -2251,7 +2360,11 @@ TelemetryImpl::SnapshotSubsessionHistograms(bool clearSubsession, JSContext *cx, JS::MutableHandle ret) { +#if !defined(MOZ_WIDGET_GONK) && !defined(MOZ_WIDGET_ANDROID) return CreateHistogramSnapshots(cx, ret, true, clearSubsession); +#else + return NS_OK; +#endif } bool @@ -2348,29 +2461,6 @@ TelemetryImpl::GetAddonHistogramSnapshots(JSContext *cx, JS::MutableHandle& entry, void* arg) -{ - KeyedHistogramReflectArgs* args = static_cast(arg); - JSContext *cx = args->jsContext; - JS::RootedObject snapshot(cx, JS_NewPlainObject(cx)); - if (!snapshot) { - return PL_DHASH_STOP; - } - - if (!NS_SUCCEEDED(entry->GetJSSnapshot(cx, snapshot, false, false))) { - return PL_DHASH_STOP; - } - - if (!JS_DefineProperty(cx, args->object, PromiseFlatCString(key).get(), - snapshot, JSPROP_ENUMERATE)) { - return PL_DHASH_STOP; - } - - return PL_DHASH_NEXT; -} - NS_IMETHODIMP TelemetryImpl::GetKeyedHistogramSnapshots(JSContext *cx, JS::MutableHandle ret) { @@ -2379,11 +2469,20 @@ TelemetryImpl::GetKeyedHistogramSnapshots(JSContext *cx, JS::MutableHandle(&reflectArgs)); - if (num != mKeyedHistograms.Count()) { - return NS_ERROR_FAILURE; + for (auto iter = mKeyedHistograms.Iter(); !iter.Done(); iter.Next()) { + JS::RootedObject snapshot(cx, JS_NewPlainObject(cx)); + if (!snapshot) { + return NS_ERROR_FAILURE; + } + + if (!NS_SUCCEEDED(iter.Data()->GetJSSnapshot(cx, snapshot, false, false))) { + return NS_ERROR_FAILURE; + } + + if (!JS_DefineProperty(cx, obj, PromiseFlatCString(iter.Key()).get(), + snapshot, JSPROP_ENUMERATE)) { + return NS_ERROR_FAILURE; + } } ret.setObject(*obj); @@ -2427,6 +2526,14 @@ TelemetryImpl::GetDebugSlowSQL(JSContext *cx, JS::MutableHandle ret) return NS_ERROR_FAILURE; } +NS_IMETHODIMP +TelemetryImpl::GetWebrtcStats(JSContext *cx, JS::MutableHandle ret) +{ + if (mWebrtcTelemetry.GetWebrtcStats(cx, ret)) + return NS_OK; + return NS_ERROR_FAILURE; +} + NS_IMETHODIMP TelemetryImpl::GetMaximalNumberOfConcurrentThreads(uint32_t *ret) { @@ -2495,30 +2602,46 @@ TelemetryImpl::GetChromeHangs(JSContext *cx, JS::MutableHandle ret) JSPROP_ENUMERATE)) { return NS_ERROR_FAILURE; } - const nsTArray& annotationInfo = - mHangReports.GetAnnotationInfo(); - for (uint32_t iterIndex = 0, arrayLen = annotationInfo.Length(); - iterIndex < arrayLen; ++iterIndex) { + + size_t annotationIndex = 0; + const nsClassHashtable& annotationInfo = + mHangReports.GetAnnotationInfo(); + + for (auto iter = annotationInfo.ConstIter(); !iter.Done(); iter.Next()) { + const HangReports::AnnotationInfo* info = iter.Data(); + JS::Rooted keyValueArray(cx, JS_NewArrayObject(cx, 0)); if (!keyValueArray) { return NS_ERROR_FAILURE; } - JS::RootedValue indexValue(cx); - indexValue.setNumber(annotationInfo[iterIndex].mHangIndex); - if (!JS_DefineElement(cx, keyValueArray, 0, indexValue, JSPROP_ENUMERATE)) { + + // Create an array containing all the indices of the chrome hangs relative to this + // annotation. + JS::Rooted indicesArray(cx); + if (!mozilla::dom::ToJSValue(cx, info->mHangIndices, &indicesArray)) { + return NS_ERROR_OUT_OF_MEMORY; + } + + // We're saving the annotation as [[indices], {annotation-data}], so add the indices + // array as the first element of that structure. + if (!JS_DefineElement(cx, keyValueArray, 0, indicesArray, JSPROP_ENUMERATE)) { return NS_ERROR_FAILURE; } + + // Create the annotations object... JS::Rooted jsAnnotation(cx, JS_NewPlainObject(cx)); if (!jsAnnotation) { return NS_ERROR_FAILURE; } UniquePtr annotationsEnum = - annotationInfo[iterIndex].mAnnotations->GetEnumerator(); + info->mAnnotations->GetEnumerator(); if (!annotationsEnum) { return NS_ERROR_FAILURE; } - nsAutoString key; - nsAutoString value; + + // ... fill it with key:value pairs... + nsAutoString key; + nsAutoString value; while (annotationsEnum->Next(key, value)) { JS::RootedValue jsValue(cx); jsValue.setString(JS_NewUCStringCopyN(cx, value.get(), value.Length())); @@ -2527,10 +2650,12 @@ TelemetryImpl::GetChromeHangs(JSContext *cx, JS::MutableHandle ret) return NS_ERROR_FAILURE; } } + + // ... and append it after the indices array. if (!JS_DefineElement(cx, keyValueArray, 1, jsAnnotation, JSPROP_ENUMERATE)) { return NS_ERROR_FAILURE; } - if (!JS_DefineElement(cx, annotationsArray, iterIndex, + if (!JS_DefineElement(cx, annotationsArray, annotationIndex++, keyValueArray, JSPROP_ENUMERATE)) { return NS_ERROR_FAILURE; } @@ -2728,6 +2853,241 @@ ReadStack(const char *aFileName, Telemetry::ProcessedStack &aStack) aStack = stack; } +static JSObject* +CreateJSTimeHistogram(JSContext* cx, const Telemetry::TimeHistogram& time) +{ + /* Create JS representation of TimeHistogram, + in the format of Chromium-style histograms. */ + JS::RootedObject ret(cx, JS_NewPlainObject(cx)); + if (!ret) { + return nullptr; + } + + if (!JS_DefineProperty(cx, ret, "min", time.GetBucketMin(0), + JSPROP_ENUMERATE) || + !JS_DefineProperty(cx, ret, "max", + time.GetBucketMax(ArrayLength(time) - 1), + JSPROP_ENUMERATE) || + !JS_DefineProperty(cx, ret, "histogram_type", + nsITelemetry::HISTOGRAM_EXPONENTIAL, + JSPROP_ENUMERATE)) { + return nullptr; + } + // TODO: calculate "sum", "log_sum", and "log_sum_squares" + if (!JS_DefineProperty(cx, ret, "sum", 0, JSPROP_ENUMERATE) || + !JS_DefineProperty(cx, ret, "log_sum", 0.0, JSPROP_ENUMERATE) || + !JS_DefineProperty(cx, ret, "log_sum_squares", 0.0, JSPROP_ENUMERATE)) { + return nullptr; + } + + JS::RootedObject ranges( + cx, JS_NewArrayObject(cx, ArrayLength(time) + 1)); + JS::RootedObject counts( + cx, JS_NewArrayObject(cx, ArrayLength(time) + 1)); + if (!ranges || !counts) { + return nullptr; + } + /* In a Chromium-style histogram, the first bucket is an "under" bucket + that represents all values below the histogram's range. */ + if (!JS_DefineElement(cx, ranges, 0, time.GetBucketMin(0), JSPROP_ENUMERATE) || + !JS_DefineElement(cx, counts, 0, 0, JSPROP_ENUMERATE)) { + return nullptr; + } + for (size_t i = 0; i < ArrayLength(time); i++) { + if (!JS_DefineElement(cx, ranges, i + 1, time.GetBucketMax(i), + JSPROP_ENUMERATE) || + !JS_DefineElement(cx, counts, i + 1, time[i], JSPROP_ENUMERATE)) { + return nullptr; + } + } + if (!JS_DefineProperty(cx, ret, "ranges", ranges, JSPROP_ENUMERATE) || + !JS_DefineProperty(cx, ret, "counts", counts, JSPROP_ENUMERATE)) { + return nullptr; + } + return ret; +} + +static JSObject* +CreateJSHangStack(JSContext* cx, const Telemetry::HangStack& stack) +{ + JS::RootedObject ret(cx, JS_NewArrayObject(cx, stack.length())); + if (!ret) { + return nullptr; + } + for (size_t i = 0; i < stack.length(); i++) { + JS::RootedString string(cx, JS_NewStringCopyZ(cx, stack[i])); + if (!JS_DefineElement(cx, ret, i, string, JSPROP_ENUMERATE)) { + return nullptr; + } + } + return ret; +} + +static void +CreateJSHangAnnotations(JSContext* cx, const HangAnnotationsVector& annotations, + JS::MutableHandleObject returnedObject) +{ + JS::RootedObject annotationsArray(cx, JS_NewArrayObject(cx, 0)); + if (!annotationsArray) { + returnedObject.set(nullptr); + return; + } + // We keep track of the annotations we reported in this hash set, so we can + // discard duplicated ones. + nsTHashtable reportedAnnotations; + size_t annotationIndex = 0; + for (const HangAnnotationsPtr *i = annotations.begin(), *e = annotations.end(); + i != e; ++i) { + JS::RootedObject jsAnnotation(cx, JS_NewPlainObject(cx)); + if (!jsAnnotation) { + continue; + } + const HangAnnotationsPtr& curAnnotations = *i; + // Build a key to index the current annotations in our hash set. + nsAutoString annotationsKey; + nsresult rv = ComputeAnnotationsKey(curAnnotations, annotationsKey); + if (NS_FAILED(rv)) { + continue; + } + // Check if the annotations are in the set. If that's the case, don't double report. + if (reportedAnnotations.GetEntry(annotationsKey)) { + continue; + } + // If not, report them. + reportedAnnotations.PutEntry(annotationsKey); + UniquePtr annotationsEnum = + curAnnotations->GetEnumerator(); + if (!annotationsEnum) { + continue; + } + nsAutoString key; + nsAutoString value; + while (annotationsEnum->Next(key, value)) { + JS::RootedValue jsValue(cx); + jsValue.setString(JS_NewUCStringCopyN(cx, value.get(), value.Length())); + if (!JS_DefineUCProperty(cx, jsAnnotation, key.get(), key.Length(), + jsValue, JSPROP_ENUMERATE)) { + returnedObject.set(nullptr); + return; + } + } + if (!JS_SetElement(cx, annotationsArray, annotationIndex, jsAnnotation)) { + continue; + } + ++annotationIndex; + } + // Return the array using a |MutableHandleObject| to avoid triggering a false + // positive rooting issue in the hazard analysis build. + returnedObject.set(annotationsArray); +} + +static JSObject* +CreateJSHangHistogram(JSContext* cx, const Telemetry::HangHistogram& hang) +{ + JS::RootedObject ret(cx, JS_NewPlainObject(cx)); + if (!ret) { + return nullptr; + } + + JS::RootedObject stack(cx, CreateJSHangStack(cx, hang.GetStack())); + JS::RootedObject time(cx, CreateJSTimeHistogram(cx, hang)); + auto& hangAnnotations = hang.GetAnnotations(); + JS::RootedObject annotations(cx); + CreateJSHangAnnotations(cx, hangAnnotations, &annotations); + + if (!stack || + !time || + !annotations || + !JS_DefineProperty(cx, ret, "stack", stack, JSPROP_ENUMERATE) || + !JS_DefineProperty(cx, ret, "histogram", time, JSPROP_ENUMERATE) || + (!hangAnnotations.empty() && // <-- Only define annotations when nonempty + !JS_DefineProperty(cx, ret, "annotations", annotations, JSPROP_ENUMERATE))) { + return nullptr; + } + + if (!hang.GetNativeStack().empty()) { + JS::RootedObject native(cx, CreateJSHangStack(cx, hang.GetNativeStack())); + if (!native || + !JS_DefineProperty(cx, ret, "nativeStack", native, JSPROP_ENUMERATE)) { + return nullptr; + } + } + return ret; +} + +static JSObject* +CreateJSThreadHangStats(JSContext* cx, const Telemetry::ThreadHangStats& thread) +{ + JS::RootedObject ret(cx, JS_NewPlainObject(cx)); + if (!ret) { + return nullptr; + } + JS::RootedString name(cx, JS_NewStringCopyZ(cx, thread.GetName())); + if (!name || + !JS_DefineProperty(cx, ret, "name", name, JSPROP_ENUMERATE)) { + return nullptr; + } + + JS::RootedObject activity(cx, CreateJSTimeHistogram(cx, thread.mActivity)); + if (!activity || + !JS_DefineProperty(cx, ret, "activity", activity, JSPROP_ENUMERATE)) { + return nullptr; + } + + JS::RootedObject hangs(cx, JS_NewArrayObject(cx, 0)); + if (!hangs) { + return nullptr; + } + for (size_t i = 0; i < thread.mHangs.length(); i++) { + JS::RootedObject obj(cx, CreateJSHangHistogram(cx, thread.mHangs[i])); + if (!JS_DefineElement(cx, hangs, i, obj, JSPROP_ENUMERATE)) { + return nullptr; + } + } + if (!JS_DefineProperty(cx, ret, "hangs", hangs, JSPROP_ENUMERATE)) { + return nullptr; + } + + return ret; +} + +NS_IMETHODIMP +TelemetryImpl::GetThreadHangStats(JSContext* cx, JS::MutableHandle ret) +{ + JS::RootedObject retObj(cx, JS_NewArrayObject(cx, 0)); + if (!retObj) { + return NS_ERROR_FAILURE; + } + size_t threadIndex = 0; + + if (!BackgroundHangMonitor::IsDisabled()) { + /* First add active threads; we need to hold |iter| (and its lock) + throughout this method to avoid a race condition where a thread can + be recorded twice if the thread is destroyed while this method is + running */ + BackgroundHangMonitor::ThreadHangStatsIterator iter; + for (Telemetry::ThreadHangStats* histogram = iter.GetNext(); + histogram; histogram = iter.GetNext()) { + JS::RootedObject obj(cx, CreateJSThreadHangStats(cx, *histogram)); + if (!JS_DefineElement(cx, retObj, threadIndex++, obj, JSPROP_ENUMERATE)) { + return NS_ERROR_FAILURE; + } + } + } + + // Add saved threads next + MutexAutoLock autoLock(mThreadHangStatsMutex); + for (size_t i = 0; i < mThreadHangStats.length(); i++) { + JS::RootedObject obj(cx, + CreateJSThreadHangStats(cx, mThreadHangStats[i])); + if (!JS_DefineElement(cx, retObj, threadIndex++, obj, JSPROP_ENUMERATE)) { + return NS_ERROR_FAILURE; + } + } + ret.setObject(*retObj); + return NS_OK; +} + void TelemetryImpl::ReadLateWritesStacks(nsIFile* aProfileDir) { @@ -3224,6 +3584,52 @@ TelemetryImpl::RecordSlowStatement(const nsACString &sql, StoreSlowSQL(fullSQL, delay, Unsanitized); } +void +TelemetryImpl::RecordIceCandidates(const uint32_t iceCandidateBitmask, + const bool success, const bool loop) +{ + if (!sTelemetry) + return; + + sTelemetry->mWebrtcTelemetry.RecordIceCandidateMask(iceCandidateBitmask, success, loop); +} + +#if defined(MOZ_ENABLE_PROFILER_SPS) +void +TelemetryImpl::RecordChromeHang(uint32_t aDuration, + Telemetry::ProcessedStack &aStack, + int32_t aSystemUptime, + int32_t aFirefoxUptime, + HangAnnotationsPtr aAnnotations) +{ + if (!sTelemetry || !sTelemetry->mCanRecordExtended) + return; + + HangAnnotationsPtr annotations; + // We only pass aAnnotations if it is not empty. + if (aAnnotations && !aAnnotations->IsEmpty()) { + annotations = Move(aAnnotations); + } + + MutexAutoLock hangReportMutex(sTelemetry->mHangReportsMutex); + + sTelemetry->mHangReports.AddHang(aStack, aDuration, + aSystemUptime, aFirefoxUptime, + Move(annotations)); +} +#endif + +void +TelemetryImpl::RecordThreadHangStats(Telemetry::ThreadHangStats& aStats) +{ + if (!sTelemetry || !sTelemetry->mCanRecordExtended) + return; + + MutexAutoLock autoLock(sTelemetry->mThreadHangStatsMutex); + + sTelemetry->mThreadHangStats.append(Move(aStats)); +} + NS_IMPL_ISUPPORTS(TelemetryImpl, nsITelemetry, nsIMemoryReporter) NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsITelemetry, TelemetryImpl::CreateTelemetryInstance) @@ -3290,6 +3696,7 @@ TelemetryImpl::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) // Ignore the hashtables in mAddonMap; they are not significant. n += mAddonMap.ShallowSizeOfExcludingThis(aMallocSizeOf); n += mHistogramMap.ShallowSizeOfExcludingThis(aMallocSizeOf); + n += mWebrtcTelemetry.SizeOfExcludingThis(aMallocSizeOf); { // Scope for mHashMutex lock MutexAutoLock lock(mHashMutex); n += mPrivateSQL.SizeOfExcludingThis(aMallocSizeOf); @@ -3299,6 +3706,10 @@ TelemetryImpl::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) MutexAutoLock lock(mHangReportsMutex); n += mHangReports.SizeOfExcludingThis(aMallocSizeOf); } + { // Scope for mThreadHangStatsMutex lock + MutexAutoLock lock(mThreadHangStatsMutex); + n += mThreadHangStats.sizeOfExcludingThis(aMallocSizeOf); + } // It's a bit gross that we measure this other stuff that lives outside of // TelemetryImpl... oh well. @@ -3388,8 +3799,6 @@ namespace Telemetry { void Accumulate(ID aHistogram, uint32_t aSample) { -return; -/* if (!TelemetryImpl::CanRecordBase()) { return; } @@ -3398,31 +3807,24 @@ return; if (NS_SUCCEEDED(rv)) { HistogramAdd(*h, aSample, gHistograms[aHistogram].dataset); } -*/ } void Accumulate(ID aID, const nsCString& aKey, uint32_t aSample) { -return; -/* if (!TelemetryImpl::CanRecordBase()) { return; } const TelemetryHistogram& th = gHistograms[aID]; KeyedHistogram* keyed = TelemetryImpl::GetKeyedHistogramById(nsDependentCString(th.id())); MOZ_ASSERT(keyed); - keyed->Add(aKey, aSample); -*/ } void Accumulate(const char* name, uint32_t sample) { -return; -/* - if (!TelemetryImpl::CanRecordExtended()) { + if (!TelemetryImpl::CanRecordBase()) { return; } ID id; @@ -3436,29 +3838,38 @@ return; if (NS_SUCCEEDED(rv)) { HistogramAdd(*h, sample, gHistograms[id].dataset); } -*/ +} + +void +Accumulate(const char *name, const nsCString& key, uint32_t sample) +{ + if (!TelemetryImpl::CanRecordBase()) { + return; + } + ID id; + nsresult rv = TelemetryImpl::GetHistogramEnumId(name, &id); + if (NS_SUCCEEDED(rv)) { + Accumulate(id, key, sample); + } } void AccumulateTimeDelta(ID aHistogram, TimeStamp start, TimeStamp end) { -return; -/* Accumulate(aHistogram, static_cast((end - start).ToMilliseconds())); -*/ } bool CanRecordBase() { - return false; // TelemetryImpl::CanRecordBase(); + return TelemetryImpl::CanRecordBase(); } bool CanRecordExtended() { - return false; // TelemetryImpl::CanRecordExtended(); + return TelemetryImpl::CanRecordExtended(); } base::Histogram* @@ -3481,9 +3892,14 @@ RecordSlowSQLStatement(const nsACString &statement, const nsACString &dbName, uint32_t delay) { -/* TelemetryImpl::RecordSlowStatement(statement, dbName, delay); -*/ +} + +void +RecordWebrtcIceCandidates(const uint32_t iceCandidateBitmask, + const bool success, const bool loop) +{ + TelemetryImpl::RecordIceCandidates(iceCandidateBitmask, success, loop); } void Init() @@ -3494,6 +3910,24 @@ void Init() MOZ_ASSERT(telemetryService); } +#if defined(MOZ_ENABLE_PROFILER_SPS) +void RecordChromeHang(uint32_t duration, + ProcessedStack &aStack, + int32_t aSystemUptime, + int32_t aFirefoxUptime, + HangAnnotationsPtr aAnnotations) +{ + TelemetryImpl::RecordChromeHang(duration, aStack, + aSystemUptime, aFirefoxUptime, + Move(aAnnotations)); +} +#endif + +void RecordThreadHangStats(ThreadHangStats& aStats) +{ + TelemetryImpl::RecordThreadHangStats(aStats); +} + ProcessedStack::ProcessedStack() { } @@ -3548,18 +3982,84 @@ struct StackFrame }; +#ifdef MOZ_ENABLE_PROFILER_SPS +static bool CompareByPC(const StackFrame &a, const StackFrame &b) +{ + return a.mPC < b.mPC; +} + +static bool CompareByIndex(const StackFrame &a, const StackFrame &b) +{ + return a.mIndex < b.mIndex; +} +#endif + ProcessedStack GetStackAndModules(const std::vector& aPCs) { std::vector rawStack; - for (std::vector::const_iterator i = aPCs.begin(), - e = aPCs.end(); i != e; ++i) { + auto stackEnd = aPCs.begin() + std::min(aPCs.size(), kMaxChromeStackDepth); + for (auto i = aPCs.begin(); i != stackEnd; ++i) { uintptr_t aPC = *i; StackFrame Frame = {aPC, static_cast(rawStack.size()), std::numeric_limits::max()}; rawStack.push_back(Frame); } +#ifdef MOZ_ENABLE_PROFILER_SPS + // Remove all modules not referenced by a PC on the stack + std::sort(rawStack.begin(), rawStack.end(), CompareByPC); + + size_t moduleIndex = 0; + size_t stackIndex = 0; + size_t stackSize = rawStack.size(); + + SharedLibraryInfo rawModules = SharedLibraryInfo::GetInfoForSelf(); + rawModules.SortByAddress(); + + while (moduleIndex < rawModules.GetSize()) { + const SharedLibrary& module = rawModules.GetEntry(moduleIndex); + uintptr_t moduleStart = module.GetStart(); + uintptr_t moduleEnd = module.GetEnd() - 1; + // the interval is [moduleStart, moduleEnd) + + bool moduleReferenced = false; + for (;stackIndex < stackSize; ++stackIndex) { + uintptr_t pc = rawStack[stackIndex].mPC; + if (pc >= moduleEnd) + break; + + if (pc >= moduleStart) { + // If the current PC is within the current module, mark + // module as used + moduleReferenced = true; + rawStack[stackIndex].mPC -= moduleStart; + rawStack[stackIndex].mModIndex = moduleIndex; + } else { + // PC does not belong to any module. It is probably from + // the JIT. Use a fixed mPC so that we don't get different + // stacks on different runs. + rawStack[stackIndex].mPC = + std::numeric_limits::max(); + } + } + + if (moduleReferenced) { + ++moduleIndex; + } else { + // Remove module if no PCs within its address range + rawModules.RemoveEntries(moduleIndex, moduleIndex + 1); + } + } + + for (;stackIndex < stackSize; ++stackIndex) { + // These PCs are past the last module. + rawStack[stackIndex].mPC = std::numeric_limits::max(); + } + + std::sort(rawStack.begin(), rawStack.end(), CompareByIndex); +#endif + // Copy the information to the return value. ProcessedStack Ret; for (std::vector::iterator i = rawStack.begin(), @@ -3569,6 +4069,28 @@ GetStackAndModules(const std::vector& aPCs) Ret.AddFrame(frame); } +#ifdef MOZ_ENABLE_PROFILER_SPS + for (unsigned i = 0, n = rawModules.GetSize(); i != n; ++i) { + const SharedLibrary &info = rawModules.GetEntry(i); + const std::string &name = info.GetName(); + std::string basename = name; +#ifdef XP_MACOSX + // FIXME: We want to use just the basename as the libname, but the + // current profiler addon needs the full path name, so we compute the + // basename in here. + size_t pos = name.rfind('/'); + if (pos != std::string::npos) { + basename = name.substr(pos + 1); + } +#endif + ProcessedStack::Module module = { + basename, + info.GetBreakpadId() + }; + Ret.AddModule(module); + } +#endif + return Ret; } @@ -3649,7 +4171,6 @@ SetProfileDir(nsIFile* aProfD) sTelemetryIOObserver->AddPath(profDirPath, NS_LITERAL_STRING("{profile}")); } - void TimeHistogram::Add(PRIntervalTime aTime) { @@ -3658,15 +4179,63 @@ TimeHistogram::Add(PRIntervalTime aTime) operator[](index)++; } +const char* +HangStack::InfallibleAppendViaBuffer(const char* aText, size_t aLength) +{ + MOZ_ASSERT(this->canAppendWithoutRealloc(1)); + // Include null-terminator in length count. + MOZ_ASSERT(mBuffer.canAppendWithoutRealloc(aLength + 1)); + + const char* const entry = mBuffer.end(); + mBuffer.infallibleAppend(aText, aLength); + mBuffer.infallibleAppend('\0'); // Explicitly append null-terminator + this->infallibleAppend(entry); + return entry; +} + +const char* +HangStack::AppendViaBuffer(const char* aText, size_t aLength) +{ + if (!this->reserve(this->length() + 1)) { + return nullptr; + } + + // Keep track of the previous buffer in case we need to adjust pointers later. + const char* const prevStart = mBuffer.begin(); + const char* const prevEnd = mBuffer.end(); + + // Include null-terminator in length count. + if (!mBuffer.reserve(mBuffer.length() + aLength + 1)) { + return nullptr; + } + + if (prevStart != mBuffer.begin()) { + // The buffer has moved; we have to adjust pointers in the stack. + for (const char** entry = this->begin(); entry != this->end(); entry++) { + if (*entry >= prevStart && *entry < prevEnd) { + // Move from old buffer to new buffer. + *entry += mBuffer.begin() - prevStart; + } + } + } + + return InfallibleAppendViaBuffer(aText, aLength); +} + uint32_t HangHistogram::GetHash(const HangStack& aStack) { uint32_t hash = 0; for (const char* const* label = aStack.begin(); label != aStack.end(); label++) { - /* We only need to hash the pointer instead of the text content - because we are assuming constant pointers */ - hash = AddToHash(hash, *label); + /* If the string is within our buffer, we need to hash its content. + Otherwise, the string is statically allocated, and we only need + to hash the pointer instead of the content. */ + if (aStack.IsInBuffer(*label)) { + hash = AddToHash(hash, HashString(*label)); + } else { + hash = AddToHash(hash, *label); + } } return hash; } @@ -3680,7 +4249,7 @@ HangHistogram::operator==(const HangHistogram& aOther) const if (mStack.length() != aOther.mStack.length()) { return false; } - return PodEqual(mStack.begin(), aOther.mStack.begin(), mStack.length()); + return mStack == aOther.mStack; } @@ -3696,14 +4265,16 @@ NSMODULE_DEFN(nsTelemetryModule) = &kTelemetryModule; void XRE_TelemetryAccumulate(int aID, uint32_t aSample) { -// mozilla::Telemetry::Accumulate((mozilla::Telemetry::ID) aID, aSample); + mozilla::Telemetry::Accumulate((mozilla::Telemetry::ID) aID, aSample); } KeyedHistogram::KeyedHistogram(const nsACString &name, const nsACString &expiration, uint32_t histogramType, uint32_t min, uint32_t max, uint32_t bucketCount, uint32_t dataset) : mHistogramMap() +#if !defined(MOZ_WIDGET_GONK) && !defined(MOZ_WIDGET_ANDROID) , mSubsessionMap() +#endif , mName(name) , mExpiration(expiration) , mHistogramType(histogramType) @@ -3718,7 +4289,11 @@ nsresult KeyedHistogram::GetHistogram(const nsCString& key, Histogram** histogram, bool subsession) { +#if !defined(MOZ_WIDGET_GONK) && !defined(MOZ_WIDGET_ANDROID) KeyedHistogramMapType& map = subsession ? mSubsessionMap : mHistogramMap; +#else + KeyedHistogramMapType& map = mHistogramMap; +#endif KeyedHistogramEntry* entry = map.GetEntry(key); if (entry) { *histogram = entry->mData; @@ -3782,14 +4357,22 @@ KeyedHistogram::Add(const nsCString& key, uint32_t sample) } Histogram* histogram = GetHistogram(key, false); - Histogram* subsession = GetHistogram(key, true); - MOZ_ASSERT(histogram && subsession); - if (!histogram || !subsession) { + MOZ_ASSERT(histogram); + if (!histogram) { return NS_ERROR_FAILURE; } +#if !defined(MOZ_WIDGET_GONK) && !defined(MOZ_WIDGET_ANDROID) + Histogram* subsession = GetHistogram(key, true); + MOZ_ASSERT(subsession); + if (!subsession) { + return NS_ERROR_FAILURE; + } +#endif histogram->Add(sample); +#if !defined(MOZ_WIDGET_GONK) && !defined(MOZ_WIDGET_ANDROID) subsession->Add(sample); +#endif return NS_OK; } @@ -3862,14 +4445,20 @@ nsresult KeyedHistogram::GetJSSnapshot(JSContext* cx, JS::Handle obj, bool subsession, bool clearSubsession) { +#if !defined(MOZ_WIDGET_GONK) && !defined(MOZ_WIDGET_ANDROID) KeyedHistogramMapType& map = subsession ? mSubsessionMap : mHistogramMap; +#else + KeyedHistogramMapType& map = mHistogramMap; +#endif if (!map.ReflectIntoJS(&KeyedHistogram::ReflectKeyedHistogram, cx, obj)) { return NS_ERROR_FAILURE; } +#if !defined(MOZ_WIDGET_GONK) && !defined(MOZ_WIDGET_ANDROID) if (subsession && clearSubsession) { Clear(true); } +#endif return NS_OK; } diff --git a/toolkit/components/telemetry/Telemetry.h b/toolkit/components/telemetry/Telemetry.h index 2f0400d287..9c7bc3b8c6 100644 --- a/toolkit/components/telemetry/Telemetry.h +++ b/toolkit/components/telemetry/Telemetry.h @@ -62,6 +62,18 @@ void Accumulate(ID id, const nsCString& key, uint32_t sample = 1); */ void Accumulate(const char* name, uint32_t sample); +/** + * Adds a sample to a histogram defined in TelemetryHistograms.h. + * This function is here to support telemetry measurements from Java, + * where we have only names and not numeric IDs. You should almost + * certainly be using the by-enum-id version instead of this one. + * + * @param name - histogram name + * @param key - the string key + * @param sample - sample - (optional) value to record, defaults to 1. + */ +void Accumulate(const char *name, const nsCString& key, uint32_t sample = 1); + /** * Adds time delta in milliseconds to a histogram defined in TelemetryHistograms.h * @@ -197,6 +209,18 @@ void RecordSlowSQLStatement(const nsACString &statement, const nsACString &dbName, uint32_t delay); +/** + * Record Webrtc ICE candidate type combinations in a 17bit bitmask + * + * @param iceCandidateBitmask - the bitmask representing local and remote ICE + * candidate types present for the connection + * @param success - did the peer connection connected + * @param loop - was this a Firefox Hello AKA Loop call + */ +void +RecordWebrtcIceCandidates(const uint32_t iceCandidateBitmask, + const bool success, + const bool loop); /** * Initialize I/O Reporting * Initially this only records I/O for files in the binary directory. @@ -239,6 +263,28 @@ class ProcessedStack; * @param aFirefoxUptime - Firefox uptime at the time of the hang, in minutes * @param aAnnotations - Any annotations to be added to the report */ +#if defined(MOZ_ENABLE_PROFILER_SPS) && !defined(MOZILLA_XPCOMRT_API) +void RecordChromeHang(uint32_t aDuration, + ProcessedStack &aStack, + int32_t aSystemUptime, + int32_t aFirefoxUptime, + mozilla::UniquePtr + aAnnotations); +#endif + +class ThreadHangStats; + +/** + * Move a ThreadHangStats to Telemetry storage. Normally Telemetry queries + * for active ThreadHangStats through BackgroundHangMonitor, but once a + * thread exits, the thread's copy of ThreadHangStats needs to be moved to + * inside Telemetry using this function. + * + * @param aStats ThreadHangStats to save; the data inside aStats + * will be moved and aStats should be treated as + * invalid after this function returns + */ +void RecordThreadHangStats(ThreadHangStats& aStats); /** * Record a failed attempt at locking the user's profile. diff --git a/toolkit/components/telemetry/TelemetryCommon.h b/toolkit/components/telemetry/TelemetryCommon.h new file mode 100644 index 0000000000..67dc7b13a9 --- /dev/null +++ b/toolkit/components/telemetry/TelemetryCommon.h @@ -0,0 +1,45 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef TelemetryCommon_h__ +#define TelemetryCommon_h__ + +#include "nsTHashtable.h" +#include "jsapi.h" + +template +class AutoHashtable : public nsTHashtable +{ +public: + explicit AutoHashtable(uint32_t initLength = + PLDHashTable::kDefaultInitialLength); + typedef bool (*ReflectEntryFunc)(EntryType *entry, JSContext *cx, JS::Handle obj); + bool ReflectIntoJS(ReflectEntryFunc entryFunc, JSContext *cx, JS::Handle obj); +}; + +template +AutoHashtable::AutoHashtable(uint32_t initLength) + : nsTHashtable(initLength) +{ +} + +/** + * Reflect the individual entries of table into JS, usually by defining + * some property and value of obj. entryFunc is called for each entry. + */ +template +bool +AutoHashtable::ReflectIntoJS(ReflectEntryFunc entryFunc, + JSContext *cx, JS::Handle obj) +{ + for (auto iter = this->Iter(); !iter.Done(); iter.Next()) { + if (!entryFunc(iter.Get(), cx, obj)) { + return false; + } + } + return true; +} + +#endif // TelemetryCommon_h__ diff --git a/toolkit/components/telemetry/TelemetrySession.jsm b/toolkit/components/telemetry/TelemetrySession.jsm index 64fc7da32d..3bfe9b6def 100644 --- a/toolkit/components/telemetry/TelemetrySession.jsm +++ b/toolkit/components/telemetry/TelemetrySession.jsm @@ -910,6 +910,17 @@ var Impl = { this._log.trace("assemblePayloadWithMeasurements - reason: " + reason + ", submitting subsession data: " + isSubsession); + // This allows wrapping data retrieval calls in a try-catch block so that + // failures don't break the rest of the ping assembly. + const protect = (fn) => { + try { + return fn(); + } catch (ex) { + this._log.error("assemblePayloadWithMeasurements - caught exception", ex); + return null; + } + }; + // Payload common to chrome and content processes. let payloadObj = { ver: PAYLOAD_VERSION, @@ -920,6 +931,14 @@ var Impl = { log: TelemetryLog.entries(), }; + // Add extended set measurements common to chrome & content processes + if (Telemetry.canRecordExtended) { + payloadObj.chromeHangs = protect(() => Telemetry.chromeHangs); + payloadObj.threadHangStats = protect(() => this.getThreadHangStats(Telemetry.threadHangStats)); + payloadObj.log = protect(() => TelemetryLog.entries()); + payloadObj.webrtc = protect(() => Telemetry.webrtcStats); + } + if (IS_CONTENT_PROCESS) { return payloadObj; } diff --git a/toolkit/components/telemetry/WebrtcTelemetry.cpp b/toolkit/components/telemetry/WebrtcTelemetry.cpp new file mode 100644 index 0000000000..4dc22bf5c4 --- /dev/null +++ b/toolkit/components/telemetry/WebrtcTelemetry.cpp @@ -0,0 +1,131 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 "Telemetry.h" +#include "TelemetryCommon.h" +#include "WebrtcTelemetry.h" +#include "jsapi.h" +#include "nsPrintfCString.h" +#include "nsTHashtable.h" + +void +WebrtcTelemetry::RecordIceCandidateMask(const uint32_t iceCandidateBitmask, + const bool success, + const bool loop) +{ + WebrtcIceCandidateType *entry = mWebrtcIceCandidates.GetEntry(iceCandidateBitmask); + if (!entry) { + entry = mWebrtcIceCandidates.PutEntry(iceCandidateBitmask); + if (MOZ_UNLIKELY(!entry)) + return; + } + + if (loop) { + if (success) { + entry->mData.loop.successCount++; + } else { + entry->mData.loop.failureCount++; + } + } else { + if (success) { + entry->mData.webrtc.successCount++; + } else { + entry->mData.webrtc.failureCount++; + } + } +} + +bool +ReflectIceEntry(const WebrtcTelemetry::WebrtcIceCandidateType *entry, + const WebrtcTelemetry::WebrtcIceCandidateStats *stat, JSContext *cx, + JS::Handle obj) +{ + if ((stat->successCount == 0) && (stat->failureCount == 0)) + return true; + + const uint32_t &bitmask = entry->GetKey(); + + JS::Rooted statsObj(cx, JS_NewPlainObject(cx)); + if (!statsObj) + return false; + if (!JS_DefineProperty(cx, obj, + nsPrintfCString("%lu", bitmask).BeginReading(), + statsObj, JSPROP_ENUMERATE)) { + return false; + } + if (stat->successCount && !JS_DefineProperty(cx, statsObj, "successCount", + stat->successCount, + JSPROP_ENUMERATE)) { + return false; + } + if (stat->failureCount && !JS_DefineProperty(cx, statsObj, "failureCount", + stat->failureCount, + JSPROP_ENUMERATE)) { + return false; + } + return true; +} + +bool +ReflectIceWebrtc(WebrtcTelemetry::WebrtcIceCandidateType *entry, JSContext *cx, + JS::Handle obj) +{ + return ReflectIceEntry(entry, &entry->mData.webrtc, cx, obj); +} + +bool +ReflectIceLoop(WebrtcTelemetry::WebrtcIceCandidateType *entry, JSContext *cx, + JS::Handle obj) +{ + return ReflectIceEntry(entry, &entry->mData.loop, cx, obj); +} + +bool +WebrtcTelemetry::AddIceInfo(JSContext *cx, JS::Handle iceObj, + const bool loop) +{ + JS::Rooted statsObj(cx, JS_NewPlainObject(cx)); + if (!statsObj) + return false; + + AutoHashtable::ReflectEntryFunc reflectFunction = + loop ? ReflectIceLoop : ReflectIceWebrtc; + if (!mWebrtcIceCandidates.ReflectIntoJS(reflectFunction, cx, statsObj)) { + return false; + } + + return JS_DefineProperty(cx, iceObj, loop ? "loop" : "webrtc", + statsObj, JSPROP_ENUMERATE); +} + +bool +WebrtcTelemetry::GetWebrtcStats(JSContext *cx, JS::MutableHandle ret) +{ + JS::Rooted root_obj(cx, JS_NewPlainObject(cx)); + if (!root_obj) + return false; + ret.setObject(*root_obj); + + JS::Rooted ice_obj(cx, JS_NewPlainObject(cx)); + if (!ice_obj) + return false; + JS_DefineProperty(cx, root_obj, "IceCandidatesStats", ice_obj, + JSPROP_ENUMERATE); + + if (!AddIceInfo(cx, ice_obj, false)) + return false; + if (!AddIceInfo(cx, ice_obj, true)) + return false; + + return true; +} + +size_t +WebrtcTelemetry::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const +{ + return mWebrtcIceCandidates.ShallowSizeOfExcludingThis(aMallocSizeOf); +} diff --git a/toolkit/components/telemetry/WebrtcTelemetry.h b/toolkit/components/telemetry/WebrtcTelemetry.h new file mode 100644 index 0000000000..756a02eab2 --- /dev/null +++ b/toolkit/components/telemetry/WebrtcTelemetry.h @@ -0,0 +1,43 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef WebrtcTelemetry_h__ +#define WebrtcTelemetry_h__ + +#include "nsBaseHashtable.h" +#include "nsHashKeys.h" + +class WebrtcTelemetry { +public: + struct WebrtcIceCandidateStats { + uint32_t successCount; + uint32_t failureCount; + WebrtcIceCandidateStats() : + successCount(0), + failureCount(0) + { + } + }; + struct WebrtcIceStatsCategory { + struct WebrtcIceCandidateStats webrtc; + struct WebrtcIceCandidateStats loop; + }; + typedef nsBaseHashtableET WebrtcIceCandidateType; + + void RecordIceCandidateMask(const uint32_t iceCandidateBitmask, bool success, bool loop); + + bool GetWebrtcStats(JSContext *cx, JS::MutableHandle ret); + + size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; + +private: + + bool AddIceInfo(JSContext *cx, JS::Handle rootObj, bool loop); + + AutoHashtable mWebrtcIceCandidates; +}; + +#endif // WebrtcTelemetry_h__ diff --git a/toolkit/components/telemetry/docs/main-ping.rst b/toolkit/components/telemetry/docs/main-ping.rst index 14023d809e..ebef4a2121 100644 --- a/toolkit/components/telemetry/docs/main-ping.rst +++ b/toolkit/components/telemetry/docs/main-ping.rst @@ -40,12 +40,13 @@ Structure:: childPayloads: {...}, // only present with e10s; a reduced payload from content processes - simpleMeasurements: { ... }, - histograms: {}, - keyedHistograms: {}, - chromeHangs: {}, - threadHangStats: {}, - log: [], + // The following properties may all be null if we fail to collect them. + histograms: {...}, + keyedHistograms: {...}, + chromeHangs: {...}, + threadHangStats: [...], + log: [...], + webrtc: {...}, fileIOReports: {...}, lateWrites: {...}, addonDetails: { ... }, diff --git a/toolkit/components/telemetry/moz.build b/toolkit/components/telemetry/moz.build index 313488ac91..ce4d6e7f15 100644 --- a/toolkit/components/telemetry/moz.build +++ b/toolkit/components/telemetry/moz.build @@ -20,6 +20,7 @@ EXPORTS.mozilla += [ SOURCES += [ 'Telemetry.cpp', + 'WebrtcTelemetry.cpp', ] EXTRA_COMPONENTS += [ diff --git a/toolkit/components/telemetry/nsITelemetry.idl b/toolkit/components/telemetry/nsITelemetry.idl index 23900cfbf3..0c0a4db5ef 100644 --- a/toolkit/components/telemetry/nsITelemetry.idl +++ b/toolkit/components/telemetry/nsITelemetry.idl @@ -12,7 +12,7 @@ interface nsIFetchTelemetryDataCallback : nsISupports void complete(); }; -[scriptable, uuid(68e82b42-cad2-411f-857b-b64ea9377929)] +[scriptable, uuid(fabde631-c128-41c3-b7cb-9eb96f1276ff)] interface nsITelemetry : nsISupports { /** @@ -106,6 +106,14 @@ interface nsITelemetry : nsISupports [implicit_jscontext] readonly attribute jsval debugSlowSQL; + /* + * An object containing information about Webrtc related stats. For now it + * only contains local and remote ICE candidates avaiable when a Webrtc + * PeerConnection gets terminated. + */ + [implicit_jscontext] + readonly attribute jsval webrtcStats; + /** * A number representing the highest number of concurrent threads * reached during this session. @@ -120,6 +128,23 @@ interface nsITelemetry : nsISupports [implicit_jscontext] readonly attribute jsval chromeHangs; + /* + * An array of thread hang stats, + * [, , ...] + * represents a single thread, + * {"name": "", + * "activity":