author | Bill McCloskey <wmccloskey@mozilla.com> |
Wed, 29 Oct 2014 08:05:36 -0700 | |
changeset 212942 | 0f7fd7a22708cddd947c73b1de162eb1206fea7b |
parent 212941 | a469d36c6b825de2b5f93490a7f4eeec57ac8f0f |
child 212943 | 52cdf66f6666be1c296ca3d878c117d05047d2ec |
push id | 27736 |
push user | ryanvm@gmail.com |
push date | Wed, 29 Oct 2014 20:49:13 +0000 |
treeherder | mozilla-central@80e18ff7c7b2 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | bsmedberg |
bugs | 641685, 1090576 |
milestone | 36.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -38,16 +38,17 @@ #include "mozilla/ipc/FileDescriptorUtils.h" #include "mozilla/ipc/GeckoChildProcessHost.h" #include "mozilla/ipc/TestShellChild.h" #include "mozilla/layers/CompositorChild.h" #include "mozilla/layers/ImageBridgeChild.h" #include "mozilla/layers/PCompositorChild.h" #include "mozilla/layers/SharedBufferManagerChild.h" #include "mozilla/net/NeckoChild.h" +#include "mozilla/plugins/PluginModuleParent.h" #if defined(MOZ_CONTENT_SANDBOX) #if defined(XP_WIN) #define TARGET_SANDBOX_EXPORTS #include "mozilla/sandboxTarget.h" #include "nsDirectoryServiceDefs.h" #elif defined(XP_LINUX) #include "mozilla/Sandbox.h" @@ -927,16 +928,23 @@ ContentChild::DeallocPCycleCollectWithLo { // ...so when we get here, there's nothing for us to do. // // Also, we're already in ~CycleCollectWithLogsChild (q.v.) at // this point, so we shouldn't touch the actor in any case. return true; } +mozilla::plugins::PPluginModuleParent* +ContentChild::AllocPPluginModuleParent(mozilla::ipc::Transport* aTransport, + base::ProcessId aOtherProcess) +{ + return plugins::PluginModuleContentParent::Create(aTransport, aOtherProcess); +} + PContentBridgeChild* ContentChild::AllocPContentBridgeChild(mozilla::ipc::Transport* aTransport, base::ProcessId aOtherProcess) { return ContentBridgeChild::Create(aTransport, aOtherProcess); } PContentBridgeParent*
--- a/dom/ipc/ContentChild.h +++ b/dom/ipc/ContentChild.h @@ -96,16 +96,20 @@ public: ContentBridgeParent* GetLastBridge() { MOZ_ASSERT(mLastBridge); ContentBridgeParent* parent = mLastBridge; mLastBridge = nullptr; return parent; } nsRefPtr<ContentBridgeParent> mLastBridge; + PPluginModuleParent * + AllocPPluginModuleParent(mozilla::ipc::Transport* transport, + base::ProcessId otherProcess) MOZ_OVERRIDE; + PContentBridgeParent* AllocPContentBridgeParent(mozilla::ipc::Transport* transport, base::ProcessId otherProcess) MOZ_OVERRIDE; PContentBridgeChild* AllocPContentBridgeChild(mozilla::ipc::Transport* transport, base::ProcessId otherProcess) MOZ_OVERRIDE; PCompositorChild*
--- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -65,16 +65,17 @@ #include "mozilla/ipc/FileDescriptorUtils.h" #include "mozilla/ipc/PFileDescriptorSetParent.h" #include "mozilla/ipc/TestShellParent.h" #include "mozilla/ipc/InputStreamUtils.h" #include "mozilla/layers/CompositorParent.h" #include "mozilla/layers/ImageBridgeParent.h" #include "mozilla/layers/SharedBufferManagerParent.h" #include "mozilla/net/NeckoParent.h" +#include "mozilla/plugins/PluginBridge.h" #include "mozilla/Preferences.h" #include "mozilla/Services.h" #include "mozilla/StaticPtr.h" #include "mozilla/unused.h" #include "nsAnonymousTemporaryFile.h" #include "nsAppRunner.h" #include "nsAutoPtr.h" #include "nsCDefaultURIFixup.h" @@ -917,16 +918,30 @@ ContentParent::AnswerBridgeToChildProces return PContentBridge::Bridge(this, cp); } else { // You can't bridge to a process you didn't open! KillHard(); return false; } } +bool +ContentParent::AnswerLoadPlugin(const uint32_t& aPluginId) +{ + return mozilla::plugins::SetupBridge(aPluginId, this); +} + +bool +ContentParent::RecvFindPlugins(const uint32_t& aPluginEpoch, + nsTArray<PluginTag>* aPlugins, + uint32_t* aNewPluginEpoch) +{ + return mozilla::plugins::FindPluginsForContent(aPluginEpoch, aPlugins, aNewPluginEpoch); +} + /*static*/ TabParent* ContentParent::CreateBrowserOrApp(const TabContext& aContext, Element* aFrameElement, ContentParent* aOpenerContentParent) { if (!sCanLaunchSubprocesses) { return nullptr; }
--- a/dom/ipc/ContentParent.h +++ b/dom/ipc/ContentParent.h @@ -143,16 +143,21 @@ public: virtual bool RecvCreateChildProcess(const IPCTabContext& aContext, const hal::ProcessPriority& aPriority, uint64_t* aId, bool* aIsForApp, bool* aIsForBrowser) MOZ_OVERRIDE; virtual bool AnswerBridgeToChildProcess(const uint64_t& id) MOZ_OVERRIDE; + virtual bool AnswerLoadPlugin(const uint32_t& aPluginId) MOZ_OVERRIDE; + virtual bool RecvFindPlugins(const uint32_t& aPluginEpoch, + nsTArray<PluginTag>* aPlugins, + uint32_t* aNewPluginEpoch) MOZ_OVERRIDE; + NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(ContentParent, nsIObserver) NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_NSIOBSERVER NS_DECL_NSIDOMGEOPOSITIONCALLBACK /** * MessageManagerCallback methods that we override.
--- a/dom/ipc/CrashReporterChild.cpp +++ b/dom/ipc/CrashReporterChild.cpp @@ -20,17 +20,17 @@ CrashReporterChild::GetCrashReporter() const InfallibleTArray<PCrashReporterChild*>* reporters = nullptr; switch (XRE_GetProcessType()) { case GeckoProcessType_Content: { ContentChild* child = ContentChild::GetSingleton(); reporters = &child->ManagedPCrashReporterChild(); break; } case GeckoProcessType_Plugin: { - PluginModuleChild* child = PluginModuleChild::current(); + PluginModuleChild* child = PluginModuleChild::GetChrome(); reporters = &child->ManagedPCrashReporterChild(); break; } default: break; } if (reporters && reporters->Length() > 0) { return reporters->ElementAt(0);
--- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -20,32 +20,34 @@ include protocol PDeviceStorageRequest; include protocol PFileDescriptorSet; include protocol PFMRadio; include protocol PFileSystemRequest; include protocol PHal; include protocol PImageBridge; include protocol PMemoryReportRequest; include protocol PMobileConnection; include protocol PNecko; +include protocol PPluginModule; include protocol PPrinting; include protocol PScreenManager; include protocol PSharedBufferManager; include protocol PSms; include protocol PSpeechSynthesis; include protocol PStorage; include protocol PTelephony; include protocol PTestShell; include protocol PVoicemail; include protocol PJavaScript; include protocol PRemoteSpellcheckEngine; include DOMTypes; include JavaScriptTypes; include InputStreamParams; include PTabContext; include URIParams; +include PluginTypes; include ProtocolTypes; // Workaround to prevent error if PContentChild.cpp & PContentBridgeParent.cpp // are put into different UnifiedProtocolsXX.cpp files. // XXX Remove this once bug 1069073 is fixed include "mozilla/dom/PContentBridgeParent.h"; include "mozilla/dom/indexedDB/SerializationHelpers.h"; @@ -323,16 +325,18 @@ struct ClipboardCapabilities { union MaybeFileDesc { FileDescriptor; void_t; }; prio(normal upto high) intr protocol PContent { + parent spawns PPluginModule; + parent opens PCompositor; parent opens PSharedBufferManager; parent opens PImageBridge; child opens PBackground; manages PAsmJSCacheEntry; manages PBlob; manages PBluetooth; @@ -527,16 +531,39 @@ parent: returns (bool isOffline, nsString[] dictionaries, ClipboardCapabilities clipboardCaps); sync CreateChildProcess(IPCTabContext context, ProcessPriority priority) returns (uint64_t id, bool isForApp, bool isForBrowser); intr BridgeToChildProcess(uint64_t id); + /** + * This call connects the content process to a plugin process. While this + * call runs, a new PluginModuleParent will be created in the ContentChild + * via bridging. The corresponding PluginModuleChild will live in the plugin + * process. We use intr semantics here to ensure that the PluginModuleParent + * allocation message is dispatched before LoadPlugin returns. + */ + intr LoadPlugin(uint32_t pluginId); + + /** + * This call returns the set of plugins loaded in the chrome + * process. However, in many cases this set will not have changed since the + * last FindPlugins message. Consequently, the chrome process increments an + * epoch number every time the set of plugins changes. The content process + * sends up the last epoch it observed. If the epochs are the same, the + * chrome process returns no plugins. Otherwise it returns a complete list. + * + * |pluginEpoch| is the epoch last observed by the content + * process. |newPluginEpoch| is the current epoch in the chrome process. If + * |pluginEpoch == newPluginEpoch|, then |plugins| will be left empty. + */ + sync FindPlugins(uint32_t pluginEpoch) returns (PluginTag[] plugins, uint32_t newPluginEpoch); + async PJavaScript(); sync PRemoteSpellcheckEngine(); PDeviceStorageRequest(DeviceStorageParams params); PFileSystemRequest(FileSystemParams params); sync PCrashReporter(NativeThreadId tid, uint32_t processType);
--- a/dom/plugins/base/nsNPAPIPlugin.cpp +++ b/dom/plugins/base/nsNPAPIPlugin.cpp @@ -75,17 +75,18 @@ #include "mozilla/Mutex.h" #include "mozilla/PluginLibrary.h" using mozilla::PluginLibrary; #include "mozilla/PluginPRLibrary.h" using mozilla::PluginPRLibrary; #include "mozilla/plugins/PluginModuleParent.h" -using mozilla::plugins::PluginModuleParent; +using mozilla::plugins::PluginModuleChromeParent; +using mozilla::plugins::PluginModuleContentParent; #ifdef MOZ_X11 #include "mozilla/X11Util.h" #endif #ifdef XP_WIN #include <windows.h> #include "mozilla/WindowsVersion.h" @@ -242,16 +243,20 @@ nsNPAPIPlugin::PluginCrashed(const nsASt { nsRefPtr<nsPluginHost> host = nsPluginHost::GetInst(); host->PluginCrashed(this, pluginDumpID, browserDumpID); } bool nsNPAPIPlugin::RunPluginOOP(const nsPluginTag *aPluginTag) { + if (XRE_GetProcessType() == GeckoProcessType_Content) { + return true; + } + #if (MOZ_WIDGET_GTK == 3) // We force OOP on Linux/GTK3 because some plugins use GTK2 and both GTK // libraries can't be loaded in the same process. return true; #else if (PR_GetEnv("MOZ_DISABLE_OOP_PLUGINS")) { return false; } @@ -383,18 +388,22 @@ nsNPAPIPlugin::RunPluginOOP(const nsPlug inline PluginLibrary* GetNewPluginLibrary(nsPluginTag *aPluginTag) { if (!aPluginTag) { return nullptr; } + if (XRE_GetProcessType() == GeckoProcessType_Content) { + return PluginModuleContentParent::LoadModule(aPluginTag->mId); + } + if (nsNPAPIPlugin::RunPluginOOP(aPluginTag)) { - return PluginModuleParent::LoadModule(aPluginTag->mFullPath.get()); + return PluginModuleChromeParent::LoadModule(aPluginTag->mFullPath.get(), aPluginTag->mId); } return new PluginPRLibrary(aPluginTag->mFullPath.get(), aPluginTag->mLibrary); } // Creates an nsNPAPIPlugin object. One nsNPAPIPlugin object exists per plugin (not instance). nsresult nsNPAPIPlugin::CreatePlugin(nsPluginTag *aPluginTag, nsNPAPIPlugin** aResult) {
--- a/dom/plugins/base/nsPluginHost.cpp +++ b/dom/plugins/base/nsPluginHost.cpp @@ -43,17 +43,20 @@ #include "nsPluginLogging.h" #include "nsIScriptChannel.h" #include "nsIBlocklistService.h" #include "nsVersionComparator.h" #include "nsIObjectLoadingContent.h" #include "nsIWritablePropertyBag2.h" #include "nsICategoryManager.h" #include "nsPluginStreamListenerPeer.h" +#include "mozilla/dom/ContentChild.h" #include "mozilla/LoadInfo.h" +#include "mozilla/plugins/PluginBridge.h" +#include "mozilla/plugins/PluginTypes.h" #include "mozilla/Preferences.h" #include "nsEnumeratorUtils.h" #include "nsXPCOM.h" #include "nsXPCOMCID.h" #include "nsISupportsPrimitives.h" #include "nsXULAppAPI.h" @@ -101,16 +104,17 @@ #endif #if MOZ_CRASHREPORTER #include "nsExceptionHandler.h" #endif using namespace mozilla; using mozilla::TimeStamp; +using mozilla::plugins::PluginTag; // Null out a strong ref to a linked list iteratively to avoid // exhausting the stack (bug 486349). #define NS_ITERATIVE_UNREF_LIST(type_, list_, mNext_) \ { \ while (list_) { \ type_ temp = list_->mNext_; \ list_->mNext_ = nullptr; \ @@ -304,16 +308,20 @@ nsPluginHost::GetInst() } bool nsPluginHost::IsRunningPlugin(nsPluginTag * aPluginTag) { if (!aPluginTag || !aPluginTag->mPlugin) { return false; } + if (aPluginTag->mContentProcessRunningCount) { + return true; + } + for (uint32_t i = 0; i < mInstances.Length(); i++) { nsNPAPIPluginInstance *instance = mInstances[i].get(); if (instance && instance->GetPlugin() == aPluginTag->mPlugin && instance->IsRunning()) { return true; } } @@ -1267,16 +1275,86 @@ nsresult nsPluginHost::EnsurePluginLoade if (NS_FAILED(rv)) { return rv; } aPluginTag->mPlugin = plugin; } return NS_OK; } +nsresult +nsPluginHost::GetPluginForContentProcess(uint32_t aPluginId, nsNPAPIPlugin** aPlugin) +{ + MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + + // If plugins haven't been scanned yet, do so now + LoadPlugins(); + + nsPluginTag* pluginTag = PluginWithId(aPluginId); + if (pluginTag) { + nsresult rv = EnsurePluginLoaded(pluginTag); + if (NS_FAILED(rv)) { + return rv; + } + + // We only get here if a content process doesn't have a PluginModuleParent + // for the given plugin already. Therefore, this counter is counting the + // number of outstanding PluginModuleParents for the plugin, excluding the + // one from the chrome process. + pluginTag->mContentProcessRunningCount++; + NS_ADDREF(*aPlugin = pluginTag->mPlugin); + return NS_OK; + } + + return NS_ERROR_FAILURE; +} + +class nsPluginUnloadRunnable : public nsRunnable +{ +public: + explicit nsPluginUnloadRunnable(uint32_t aPluginId) : mPluginId(aPluginId) {} + + NS_IMETHOD Run() + { + nsRefPtr<nsPluginHost> host = nsPluginHost::GetInst(); + if (!host) { + return NS_OK; + } + nsPluginTag* pluginTag = host->PluginWithId(mPluginId); + if (!pluginTag) { + return NS_OK; + } + + MOZ_ASSERT(pluginTag->mContentProcessRunningCount > 0); + pluginTag->mContentProcessRunningCount--; + + if (!pluginTag->mContentProcessRunningCount) { + if (!host->IsRunningPlugin(pluginTag)) { + pluginTag->TryUnloadPlugin(false); + } + } + return NS_OK; + } + +protected: + uint32_t mPluginId; +}; + +void +nsPluginHost::NotifyContentModuleDestroyed(uint32_t aPluginId) +{ + MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + + // This is called in response to a message from the plugin. Don't unload the + // plugin until the message handler is off the stack. + nsRefPtr<nsPluginUnloadRunnable> runnable = + new nsPluginUnloadRunnable(aPluginId); + NS_DispatchToMainThread(runnable); +} + nsresult nsPluginHost::GetPlugin(const char *aMimeType, nsNPAPIPlugin** aPlugin) { nsresult rv = NS_ERROR_FAILURE; *aPlugin = nullptr; if (!aMimeType) return NS_ERROR_ILLEGAL_VALUE; @@ -1609,16 +1687,27 @@ nsPluginHost::FirstPluginWithPath(const for (nsPluginTag* tag = mPlugins; tag; tag = tag->mNext) { if (tag->mFullPath.Equals(path)) { return tag; } } return nullptr; } +nsPluginTag* +nsPluginHost::PluginWithId(uint32_t aId) +{ + for (nsPluginTag* tag = mPlugins; tag; tag = tag->mNext) { + if (tag->mId == aId) { + return tag; + } + } + return nullptr; +} + namespace { int64_t GetPluginLastModifiedTime(const nsCOMPtr<nsIFile>& localfile) { PRTime fileModTime = 0; #if defined(XP_MACOSX) // On OS X the date of a bundle's "contents" (i.e. of its Info.plist file) @@ -1695,22 +1784,42 @@ struct CompareFilesByTime Equals(const nsCOMPtr<nsIFile>& a, const nsCOMPtr<nsIFile>& b) const { return GetPluginLastModifiedTime(a) == GetPluginLastModifiedTime(b); } }; } // anonymous namespace +void +nsPluginHost::AddPluginTag(nsPluginTag* aPluginTag) +{ + aPluginTag->mNext = mPlugins; + mPlugins = aPluginTag; + + if (aPluginTag->IsActive()) { + nsAdoptingCString disableFullPage = + Preferences::GetCString(kPrefDisableFullPage); + for (uint32_t i = 0; i < aPluginTag->mMimeTypes.Length(); i++) { + if (!IsTypeInList(aPluginTag->mMimeTypes[i], disableFullPage)) { + RegisterWithCategoryManager(aPluginTag->mMimeTypes[i], + ePluginRegister); + } + } + } +} + typedef NS_NPAPIPLUGIN_CALLBACK(char *, NP_GETMIMEDESCRIPTION)(void); nsresult nsPluginHost::ScanPluginsDirectory(nsIFile *pluginsDir, bool aCreatePluginList, bool *aPluginsChanged) { + MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + NS_ENSURE_ARG_POINTER(aPluginsChanged); nsresult rv; *aPluginsChanged = false; #ifdef PLUGIN_LOGGING nsAutoCString dirPath; pluginsDir->GetNativePath(dirPath); @@ -1890,65 +1999,32 @@ nsresult nsPluginHost::ScanPluginsDirect } // If we're not creating a plugin list, simply looking for changes, // then we're done. if (!aCreatePluginList) { return NS_OK; } - // Add plugin tags such that the list is ordered by modification date, - // newest to oldest. This is ugly, it'd be easier with just about anything - // other than a single-directional linked list. - if (mPlugins) { - nsPluginTag *prev = nullptr; - nsPluginTag *next = mPlugins; - while (next) { - if (pluginTag->mLastModifiedTime >= next->mLastModifiedTime) { - pluginTag->mNext = next; - if (prev) { - prev->mNext = pluginTag; - } else { - mPlugins = pluginTag; - } - break; - } - prev = next; - next = prev->mNext; - if (!next) { - prev->mNext = pluginTag; - } - } - } else { - mPlugins = pluginTag; - } - - if (pluginTag->IsActive()) { - nsAdoptingCString disableFullPage = - Preferences::GetCString(kPrefDisableFullPage); - for (uint32_t i = 0; i < pluginTag->mMimeTypes.Length(); i++) { - if (!IsTypeInList(pluginTag->mMimeTypes[i], disableFullPage)) { - RegisterWithCategoryManager(pluginTag->mMimeTypes[i], - ePluginRegister); - } - } - } + AddPluginTag(pluginTag); } if (warnOutdated) { Preferences::SetBool("plugins.update.notifyUser", true); } return NS_OK; } nsresult nsPluginHost::ScanPluginsDirectoryList(nsISimpleEnumerator *dirEnum, bool aCreatePluginList, bool *aPluginsChanged) { + MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + bool hasMore; while (NS_SUCCEEDED(dirEnum->HasMoreElements(&hasMore)) && hasMore) { nsCOMPtr<nsISupports> supports; nsresult rv = dirEnum->GetNext(getter_AddRefs(supports)); if (NS_FAILED(rv)) continue; nsCOMPtr<nsIFile> nextDir(do_QueryInterface(supports, &rv)); if (NS_FAILED(rv)) @@ -1963,16 +2039,44 @@ nsresult nsPluginHost::ScanPluginsDirect // if changes are detected and we are not creating the list, do not proceed if (!aCreatePluginList && *aPluginsChanged) break; } return NS_OK; } +void +nsPluginHost::IncrementChromeEpoch() +{ + MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + mPluginEpoch++; +} + +uint32_t +nsPluginHost::ChromeEpoch() +{ + MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + return mPluginEpoch; +} + +uint32_t +nsPluginHost::ChromeEpochForContent() +{ + MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Content); + return mPluginEpoch; +} + +void +nsPluginHost::SetChromeEpochForContent(uint32_t aEpoch) +{ + MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Content); + mPluginEpoch = aEpoch; +} + nsresult nsPluginHost::LoadPlugins() { #ifdef ANDROID if (XRE_GetProcessType() == GeckoProcessType_Content) { return NS_OK; } #endif // do not do anything if it is already done @@ -1985,35 +2089,92 @@ nsresult nsPluginHost::LoadPlugins() bool pluginschanged; nsresult rv = FindPlugins(true, &pluginschanged); if (NS_FAILED(rv)) return rv; // only if plugins have changed will we notify plugin-change observers if (pluginschanged) { + if (XRE_GetProcessType() == GeckoProcessType_Default) { + IncrementChromeEpoch(); + } + nsCOMPtr<nsIObserverService> obsService = mozilla::services::GetObserverService(); if (obsService) obsService->NotifyObservers(nullptr, "plugins-list-updated", nullptr); } return NS_OK; } +nsresult +nsPluginHost::FindPluginsInContent(bool aCreatePluginList, bool* aPluginsChanged) +{ + MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Content); + + dom::ContentChild* cp = dom::ContentChild::GetSingleton(); + nsTArray<PluginTag> plugins; + uint32_t parentEpoch; + if (!cp->SendFindPlugins(ChromeEpochForContent(), &plugins, &parentEpoch)) { + return NS_ERROR_NOT_AVAILABLE; + } + + if (parentEpoch != ChromeEpochForContent()) { + SetChromeEpochForContent(parentEpoch); + *aPluginsChanged = true; + if (!aCreatePluginList) { + return NS_OK; + } + + for (size_t i = 0; i < plugins.Length(); i++) { + PluginTag& tag = plugins[i]; + + // Don't add the same plugin again. + if (PluginWithId(tag.id())) { + continue; + } + + nsPluginTag *pluginTag = new nsPluginTag(tag.id(), + tag.name().get(), + tag.description().get(), + tag.filename().get(), + "", // aFullPath + tag.version().get(), + nsTArray<nsCString>(tag.mimeTypes()), + nsTArray<nsCString>(tag.mimeDescriptions()), + nsTArray<nsCString>(tag.extensions()), + tag.isJavaPlugin(), + tag.isFlashPlugin(), + tag.lastModifiedTime(), + tag.isFromExtension()); + AddPluginTag(pluginTag); + } + } + + mPluginsLoaded = true; + return NS_OK; +} + // if aCreatePluginList is false we will just scan for plugins // and see if any changes have been made to the plugins. // This is needed in ReloadPlugins to prevent possible recursive reloads nsresult nsPluginHost::FindPlugins(bool aCreatePluginList, bool * aPluginsChanged) { Telemetry::AutoTimer<Telemetry::FIND_PLUGINS> telemetry; NS_ENSURE_ARG_POINTER(aPluginsChanged); *aPluginsChanged = false; + + if (XRE_GetProcessType() == GeckoProcessType_Content) { + return FindPluginsInContent(aCreatePluginList, aPluginsChanged); + } + nsresult rv; // Read cached plugins info. If the profile isn't yet available then don't // scan for plugins if (ReadPluginInfo() == NS_ERROR_NOT_AVAILABLE) return NS_OK; #ifdef XP_WIN @@ -2162,19 +2323,68 @@ nsresult nsPluginHost::FindPlugins(bool // No more need for cached plugins. Clear it up. NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsPluginTag>, mCachedPlugins, mNext); NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsInvalidPluginTag>, mInvalidPlugins, mNext); return NS_OK; } +bool +mozilla::plugins::FindPluginsForContent(uint32_t aPluginEpoch, + nsTArray<PluginTag>* aPlugins, + uint32_t* aNewPluginEpoch) +{ + MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + + nsRefPtr<nsPluginHost> host = nsPluginHost::GetInst(); + host->FindPluginsForContent(aPluginEpoch, aPlugins, aNewPluginEpoch); + return true; +} + +void +nsPluginHost::FindPluginsForContent(uint32_t aPluginEpoch, + nsTArray<PluginTag>* aPlugins, + uint32_t* aNewPluginEpoch) +{ + MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + + // Load plugins so that the epoch is correct. + LoadPlugins(); + + *aNewPluginEpoch = ChromeEpoch(); + if (aPluginEpoch == ChromeEpoch()) { + return; + } + + nsTArray<nsRefPtr<nsPluginTag>> plugins; + GetPlugins(plugins); + + for (size_t i = 0; i < plugins.Length(); i++) { + nsRefPtr<nsPluginTag> tag = plugins[i]; + aPlugins->AppendElement(PluginTag(tag->mId, + tag->mName, + tag->mDescription, + tag->mMimeTypes, + tag->mMimeDescriptions, + tag->mExtensions, + tag->mIsJavaPlugin, + tag->mIsFlashPlugin, + tag->mFileName, + tag->mVersion, + tag->mLastModifiedTime, + tag->IsFromExtension())); + } +} + nsresult nsPluginHost::UpdatePluginInfo(nsPluginTag* aPluginTag) { + MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + ReadPluginInfo(); WritePluginInfo(); NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsPluginTag>, mCachedPlugins, mNext); NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsInvalidPluginTag>, mInvalidPlugins, mNext); if (!aPluginTag) { return NS_OK; } @@ -2256,16 +2466,17 @@ nsPluginHost::RegisterWithCategoryManage true); } } } nsresult nsPluginHost::WritePluginInfo() { + MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); nsresult rv = NS_OK; nsCOMPtr<nsIProperties> directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID,&rv)); if (NS_FAILED(rv)) return rv; directoryService->Get(NS_APP_USER_PROFILE_50_DIR, NS_GET_IID(nsIFile), getter_AddRefs(mPluginRegFile)); @@ -2400,16 +2611,18 @@ nsPluginHost::WritePluginInfo() NS_ENSURE_SUCCESS(rv, rv); rv = pluginReg->MoveToNative(parent, kPluginRegistryFilename); return rv; } nsresult nsPluginHost::ReadPluginInfo() { + MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + const long PLUGIN_REG_MIMETYPES_ARRAY_SIZE = 12; const long PLUGIN_REG_MAX_MIMETYPES = 1000; // we need to import the legacy flags from the plugin registry once const bool pluginStateImported = Preferences::GetDefaultBool("plugin.importedState", false); nsresult rv; @@ -3537,16 +3750,17 @@ nsPluginHost::PluginCrashed(nsNPAPIPlugi } } // Only after all instances have been invalidated is it safe to null // out nsPluginTag.mPlugin. The next time we try to create an // instance of this plugin we reload it (launch a new plugin process). crashedPluginTag->mPlugin = nullptr; + crashedPluginTag->mContentProcessRunningCount = 0; #ifdef XP_WIN CheckForDisabledWindows(); #endif } nsNPAPIPluginInstance* nsPluginHost::FindInstance(const char *mimetype)
--- a/dom/plugins/base/nsPluginHost.h +++ b/dom/plugins/base/nsPluginHost.h @@ -23,24 +23,26 @@ #include "nsTArray.h" #include "nsTObserverArray.h" #include "nsITimer.h" #include "nsPluginTags.h" #include "nsPluginPlayPreviewInfo.h" #include "nsIEffectiveTLDService.h" #include "nsIIDNService.h" #include "nsCRT.h" +#include "mozilla/plugins/PluginTypes.h" class nsNPAPIPlugin; class nsIComponentManager; class nsIFile; class nsIChannel; class nsPluginNativeWindow; class nsObjectLoadingContent; class nsPluginInstanceOwner; +class nsPluginUnloadRunnable; class nsNPAPIPluginInstance; class nsNPAPIPluginStreamListener; class nsIPluginInstanceOwner; class nsIInputStream; class nsIStreamListener; class nsInvalidPluginTag : public nsISupports { @@ -83,16 +85,19 @@ public: nsresult SetUpPluginInstance(const char *aMimeType, nsIURI *aURL, nsPluginInstanceOwner *aOwner); bool PluginExistsForType(const char* aMimeType); nsresult IsPluginEnabledForExtension(const char* aExtension, const char* &aMimeType); void GetPlugins(nsTArray<nsRefPtr<nsPluginTag> >& aPluginArray); + void FindPluginsForContent(uint32_t aPluginEpoch, + nsTArray<mozilla::plugins::PluginTag>* aPlugins, + uint32_t* aNewPluginEpoch); nsresult GetURL(nsISupports* pluginInst, const char* url, const char* target, nsNPAPIPluginStreamListener* streamListener, const char* altHost, const char* referrer, bool forceJSEnabled); @@ -186,22 +191,26 @@ public: nsresult InstantiatePluginInstance(const char *aMimeType, nsIURI* aURL, nsObjectLoadingContent *aContent, nsPluginInstanceOwner** aOwner); // Does not accept nullptr and should never fail. nsPluginTag* TagForPlugin(nsNPAPIPlugin* aPlugin); nsresult GetPlugin(const char *aMimeType, nsNPAPIPlugin** aPlugin); + nsresult GetPluginForContentProcess(uint32_t aPluginId, nsNPAPIPlugin** aPlugin); + void NotifyContentModuleDestroyed(uint32_t aPluginId); nsresult NewPluginStreamListener(nsIURI* aURL, nsNPAPIPluginInstance* aInstance, nsIStreamListener **aStreamListener); private: + friend class nsPluginUnloadRunnable; + nsresult TrySetUpPluginInstance(const char *aMimeType, nsIURI *aURL, nsPluginInstanceOwner *aOwner); nsPluginTag* FindPreferredPlugin(const InfallibleTArray<nsPluginTag*>& matches); // Return an nsPluginTag for this type, if any. If aCheckEnabled is // true, only enabled plugins will be returned. @@ -209,24 +218,28 @@ private: FindPluginForType(const char* aMimeType, bool aCheckEnabled); nsPluginTag* FindPluginEnabledForExtension(const char* aExtension, const char* &aMimeType); nsresult FindStoppedPluginForURL(nsIURI* aURL, nsIPluginInstanceOwner *aOwner); + nsresult FindPluginsInContent(bool aCreatePluginList, bool * aPluginsChanged); + nsresult FindPlugins(bool aCreatePluginList, bool * aPluginsChanged); // Registers or unregisters the given mime type with the category manager // (performs no checks - see UpdateCategoryManager) enum nsRegisterType { ePluginRegister, ePluginUnregister }; void RegisterWithCategoryManager(nsCString &aMimeType, nsRegisterType aType); + void AddPluginTag(nsPluginTag* aPluginTag); + nsresult ScanPluginsDirectory(nsIFile *pluginsDir, bool aCreatePluginList, bool *aPluginsChanged); nsresult ScanPluginsDirectoryList(nsISimpleEnumerator *dirEnum, bool aCreatePluginList, @@ -250,21 +263,33 @@ private: // Checks to see if a tag object is in our list of live tags. bool IsLiveTag(nsIPluginTag* tag); // Checks our list of live tags for an equivalent tag. nsPluginTag* HaveSamePlugin(const nsPluginTag * aPluginTag); // Returns the first plugin at |path| nsPluginTag* FirstPluginWithPath(const nsCString& path); + nsPluginTag* PluginWithId(uint32_t aId); nsresult EnsurePrivateDirServiceProvider(); void OnPluginInstanceDestroyed(nsPluginTag* aPluginTag); + // To be used by the chrome process whenever the set of plugins changes. + void IncrementChromeEpoch(); + + // To be used by the chrome process; returns the current epoch. + uint32_t ChromeEpoch(); + + // To be used by the content process to get/set the last observed epoch value + // from the chrome process. + uint32_t ChromeEpochForContent(); + void SetChromeEpochForContent(uint32_t aEpoch); + nsRefPtr<nsPluginTag> mPlugins; nsRefPtr<nsPluginTag> mCachedPlugins; nsRefPtr<nsInvalidPluginTag> mInvalidPlugins; nsTArray< nsRefPtr<nsPluginPlayPreviewInfo> > mPlayPreviewMimeTypes; bool mPluginsLoaded; // set by pref plugin.override_internal_types bool mOverrideInternalTypes; @@ -290,16 +315,22 @@ private: nsresult NormalizeHostname(nsCString& host); nsresult EnumerateSiteData(const nsACString& domain, const InfallibleTArray<nsCString>& sites, InfallibleTArray<nsCString>& result, bool firstMatchOnly); nsWeakPtr mCurrentDocument; // weak reference, we use it to id document only + // This epoch increases each time we load the list of plugins from disk. + // In the chrome process, this stores the actual epoch. + // In the content process, this stores the last epoch value observed + // when reading plugins from chrome. + uint32_t mPluginEpoch; + static nsIFile *sPluginTempDir; // We need to hold a global ptr to ourselves because we register for // two different CIDs for some reason... static nsPluginHost* sInst; }; class MOZ_STACK_CLASS PluginDestructionGuard : protected PRCList
--- a/dom/plugins/base/nsPluginTags.cpp +++ b/dom/plugins/base/nsPluginTags.cpp @@ -61,20 +61,24 @@ MakePrefNameForPlugin(const char* const static nsCString GetStatePrefNameForPlugin(nsPluginTag* aTag) { return MakePrefNameForPlugin("state", aTag); } /* nsPluginTag */ +uint32_t nsPluginTag::sNextId; + nsPluginTag::nsPluginTag(nsPluginInfo* aPluginInfo, int64_t aLastModifiedTime, bool fromExtension) - : mName(aPluginInfo->fName), + : mId(sNextId++), + mContentProcessRunningCount(0), + mName(aPluginInfo->fName), mDescription(aPluginInfo->fDescription), mLibrary(nullptr), mIsJavaPlugin(false), mIsFlashPlugin(false), mFileName(aPluginInfo->fFileName), mFullPath(aPluginInfo->fFullPath), mVersion(aPluginInfo->fVersion), mLastModifiedTime(aLastModifiedTime), @@ -98,17 +102,19 @@ nsPluginTag::nsPluginTag(const char* aNa const char* aVersion, const char* const* aMimeTypes, const char* const* aMimeDescriptions, const char* const* aExtensions, int32_t aVariants, int64_t aLastModifiedTime, bool fromExtension, bool aArgsAreUTF8) - : mName(aName), + : mId(sNextId++), + mContentProcessRunningCount(0), + mName(aName), mDescription(aDescription), mLibrary(nullptr), mIsJavaPlugin(false), mIsFlashPlugin(false), mFileName(aFileName), mFullPath(aFullPath), mVersion(aVersion), mLastModifiedTime(aLastModifiedTime), @@ -119,16 +125,49 @@ nsPluginTag::nsPluginTag(const char* aNa { InitMime(aMimeTypes, aMimeDescriptions, aExtensions, static_cast<uint32_t>(aVariants)); if (!aArgsAreUTF8) EnsureMembersAreUTF8(); FixupVersion(); } +nsPluginTag::nsPluginTag(uint32_t aId, + const char* aName, + const char* aDescription, + const char* aFileName, + const char* aFullPath, + const char* aVersion, + nsTArray<nsCString> aMimeTypes, + nsTArray<nsCString> aMimeDescriptions, + nsTArray<nsCString> aExtensions, + bool aIsJavaPlugin, + bool aIsFlashPlugin, + int64_t aLastModifiedTime, + bool aFromExtension) + : mId(aId), + mContentProcessRunningCount(0), + mName(aName), + mDescription(aDescription), + mMimeTypes(aMimeTypes), + mMimeDescriptions(aMimeDescriptions), + mExtensions(aExtensions), + mLibrary(nullptr), + mIsJavaPlugin(aIsJavaPlugin), + mIsFlashPlugin(aIsFlashPlugin), + mFileName(aFileName), + mVersion(aVersion), + mLastModifiedTime(aLastModifiedTime), + mNiceFileName(), + mCachedBlocklistState(nsIBlocklistService::STATE_NOT_BLOCKED), + mCachedBlocklistStateValid(false), + mIsFromExtension(aFromExtension) +{ +} + nsPluginTag::~nsPluginTag() { NS_ASSERTION(!mNext, "Risk of exhausting the stack space, bug 486349"); } NS_IMPL_ISUPPORTS(nsPluginTag, nsIPluginTag) void nsPluginTag::InitMime(const char* const* aMimeTypes,
--- a/dom/plugins/base/nsPluginTags.h +++ b/dom/plugins/base/nsPluginTags.h @@ -46,16 +46,29 @@ public: const char* aVersion, const char* const* aMimeTypes, const char* const* aMimeDescriptions, const char* const* aExtensions, int32_t aVariants, int64_t aLastModifiedTime, bool fromExtension, bool aArgsAreUTF8 = false); + nsPluginTag(uint32_t aId, + const char* aName, + const char* aDescription, + const char* aFileName, + const char* aFullPath, + const char* aVersion, + nsTArray<nsCString> aMimeTypes, + nsTArray<nsCString> aMimeDescriptions, + nsTArray<nsCString> aExtensions, + bool aIsJavaPlugin, + bool aIsFlashPlugin, + int64_t aLastModifiedTime, + bool aFromExtension); void TryUnloadPlugin(bool inShutdown); // plugin is enabled and not blocklisted bool IsActive(); bool IsEnabled(); void SetEnabled(bool enabled); @@ -69,16 +82,20 @@ public: void ImportFlagsToPrefs(uint32_t flag); bool HasSameNameAndMimes(const nsPluginTag *aPluginTag) const; nsCString GetNiceFileName(); bool IsFromExtension() const; nsRefPtr<nsPluginTag> mNext; + uint32_t mId; + + // Number of PluginModuleParents living in all content processes. + size_t mContentProcessRunningCount; nsCString mName; // UTF-8 nsCString mDescription; // UTF-8 nsTArray<nsCString> mMimeTypes; // UTF-8 nsTArray<nsCString> mMimeDescriptions; // UTF-8 nsTArray<nsCString> mExtensions; // UTF-8 PRLibrary *mLibrary; nsRefPtr<nsNPAPIPlugin> mPlugin; bool mIsJavaPlugin; @@ -101,11 +118,13 @@ private: bool mIsFromExtension; void InitMime(const char* const* aMimeTypes, const char* const* aMimeDescriptions, const char* const* aExtensions, uint32_t aVariantCount); nsresult EnsureMembersAreUTF8(); void FixupVersion(); + + static uint32_t sNextId; }; #endif // nsPluginTags_h_
--- a/dom/plugins/ipc/PPluginModule.ipdl +++ b/dom/plugins/ipc/PPluginModule.ipdl @@ -1,28 +1,31 @@ /* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */ /* 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 protocol PPluginInstance; include protocol PPluginScriptableObject; include protocol PCrashReporter; +include protocol PContent; using NPError from "npapi.h"; using NPNVariable from "npapi.h"; using mozilla::dom::NativeThreadId from "mozilla/dom/TabMessageUtils.h"; using class mac_plugin_interposing::NSCursorInfo from "mozilla/plugins/PluginMessageUtils.h"; using struct nsID from "nsID.h"; namespace mozilla { namespace plugins { intr protocol PPluginModule { + bridges PContent, PPluginModule; + manages PPluginInstance; manages PCrashReporter; both: // Window-specific message which instructs the interrupt mechanism to enter // a nested event loop for the current interrupt call. async ProcessNativeEventsInInterruptCall(); @@ -102,16 +105,20 @@ parent: // OS X Specific calls to allow the plugin to manage the cursor. async SetCursor(NSCursorInfo cursorInfo); async ShowCursor(bool show); async PushCursor(NSCursorInfo cursorInfo); async PopCursor(); sync GetNativeCursorsSupported() returns (bool supported); - sync NPN_SetException(nullable PPluginScriptableObject actor, - nsCString message); + sync NPN_SetException(nsCString message); async NPN_ReloadPlugins(bool aReloadPages); + + // Notifies the chrome process that a PluginModuleChild linked to a content + // process was destroyed. The chrome process may choose to asynchronously shut + // down the plugin process in response. + async NotifyContentModuleDestroyed(); }; } // namespace plugins } // namespace mozilla
new file mode 100644 --- /dev/null +++ b/dom/plugins/ipc/PluginBridge.h @@ -0,0 +1,29 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=2 ts=2 et : + * 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 mozilla_plugins_PluginBridge_h +#define mozilla_plugins_PluginBridge_h + +namespace mozilla { + +namespace dom { +class ContentParent; +} + +namespace plugins { + +bool +SetupBridge(uint32_t aPluginId, dom::ContentParent* aContentParent); + +bool +FindPluginsForContent(uint32_t aPluginEpoch, + nsTArray<PluginTag>* aPlugins, + uint32_t* aNewPluginEpoch); + +} // namespace plugins +} // namespace mozilla + +#endif // mozilla_plugins_PluginBridge_h
--- a/dom/plugins/ipc/PluginHangUIParent.cpp +++ b/dom/plugins/ipc/PluginHangUIParent.cpp @@ -64,17 +64,17 @@ private: uint32_t mResponseTimeMs; uint32_t mTimeoutMs; }; } // anonymous namespace namespace mozilla { namespace plugins { -PluginHangUIParent::PluginHangUIParent(PluginModuleParent* aModule, +PluginHangUIParent::PluginHangUIParent(PluginModuleChromeParent* aModule, const int32_t aHangUITimeoutPref, const int32_t aChildTimeoutPref) : mMutex("mozilla::plugins::PluginHangUIParent::mMutex"), mModule(aModule), mTimeoutPrefMs(static_cast<uint32_t>(aHangUITimeoutPref) * 1000U), mIPCTimeoutMs(static_cast<uint32_t>(aChildTimeoutPref) * 1000U), mMainThreadMessageLoop(MessageLoop::current()), mIsShowing(false),
--- a/dom/plugins/ipc/PluginHangUIParent.h +++ b/dom/plugins/ipc/PluginHangUIParent.h @@ -15,32 +15,32 @@ #include "mozilla/Mutex.h" #include "mozilla/plugins/PluginMessageUtils.h" #include "MiniShmParent.h" namespace mozilla { namespace plugins { -class PluginModuleParent; +class PluginModuleChromeParent; /** * This class is responsible for launching and communicating with the * plugin-hang-ui process. * * NOTE: PluginHangUIParent is *not* an IPDL actor! In this case, "Parent" * is describing the fact that firefox is the parent process to the * plugin-hang-ui process, which is the PluginHangUIChild. * PluginHangUIParent and PluginHangUIChild are a matched pair. * @see PluginHangUIChild */ class PluginHangUIParent : public MiniShmObserver { public: - PluginHangUIParent(PluginModuleParent* aModule, + PluginHangUIParent(PluginModuleChromeParent* aModule, const int32_t aHangUITimeoutPref, const int32_t aChildTimeoutPref); virtual ~PluginHangUIParent(); /** * Spawn the plugin-hang-ui.exe child process and terminate the given * plugin container process if the user elects to stop the hung plugin. * @@ -130,17 +130,17 @@ private: bool UnwatchHangUIChildProcess(bool aWait); static VOID CALLBACK SOnHangUIProcessExit(PVOID aContext, BOOLEAN aIsTimer); private: Mutex mMutex; - PluginModuleParent* mModule; + PluginModuleChromeParent* mModule; const uint32_t mTimeoutPrefMs; const uint32_t mIPCTimeoutMs; MessageLoop* mMainThreadMessageLoop; bool mIsShowing; unsigned int mLastUserResponse; base::ProcessHandle mHangUIProcessHandle; NativeWindowHandle mMainWindowHandle; HANDLE mRegWait;
--- a/dom/plugins/ipc/PluginInstanceChild.cpp +++ b/dom/plugins/ipc/PluginInstanceChild.cpp @@ -160,16 +160,17 @@ PluginInstanceChild::PluginInstanceChild , mIsTransparent(false) , mSurfaceType(gfxSurfaceType::Max) , mCurrentInvalidateTask(nullptr) , mCurrentAsyncSetWindowTask(nullptr) , mPendingPluginCall(false) , mDoAlphaExtraction(false) , mHasPainted(false) , mSurfaceDifferenceRect(0,0,0,0) + , mDestroyed(false) { memset(&mWindow, 0, sizeof(mWindow)); mWindow.type = NPWindowTypeWindow; mData.ndata = (void*) this; mData.pdata = nullptr; #if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX) mWindow.ws_info = &mWsInfo; memset(&mWsInfo, 0, sizeof(mWsInfo)); @@ -207,17 +208,17 @@ PluginInstanceChild::~PluginInstanceChil UnscheduleTimer(mCARefreshTimer); } #endif } int PluginInstanceChild::GetQuirks() { - return PluginModuleChild::current()->GetQuirks(); + return PluginModuleChild::GetChrome()->GetQuirks(); } NPError PluginInstanceChild::InternalGetNPObjectForValue(NPNVariable aValue, NPObject** aObject) { PluginScriptableObjectChild* actor = nullptr; NPError result = NPERR_NO_ERROR; @@ -3271,17 +3272,17 @@ PluginInstanceChild::ShowPluginFrame() XSync(mWsInfo.display, False); } else #endif #ifdef XP_WIN if (SharedDIBSurface::IsSharedDIBSurface(mCurrentSurface)) { SharedDIBSurface* s = static_cast<SharedDIBSurface*>(mCurrentSurface.get()); if (!mCurrentSurfaceActor) { base::SharedMemoryHandle handle = nullptr; - s->ShareToProcess(PluginModuleChild::current()->OtherProcess(), &handle); + s->ShareToProcess(OtherProcess(), &handle); mCurrentSurfaceActor = SendPPluginSurfaceConstructor(handle, mCurrentSurface->GetSize(), haveTransparentPixels); } currSurf = mCurrentSurfaceActor; s->Flush(); @@ -3673,22 +3674,23 @@ PluginInstanceChild::ClearAllSurfaces() mCGLayer = nullptr; } mDoubleBufferCARenderer.ClearFrontSurface(); mDoubleBufferCARenderer.ClearBackSurface(); #endif } -bool -PluginInstanceChild::AnswerNPP_Destroy(NPError* aResult) +void +PluginInstanceChild::Destroy() { - PLUGIN_LOG_DEBUG_METHOD; - AssertPluginThread(); - *aResult = NPERR_NO_ERROR; + if (mDestroyed) { + return; + } + mDestroyed = true; #if defined(OS_WIN) SetProp(mPluginWindowHWND, kPluginIgnoreSubclassProperty, (HANDLE)1); #endif InfallibleTArray<PBrowserStreamChild*> streams; ManagedPBrowserStreamChild(streams); @@ -3702,17 +3704,17 @@ PluginInstanceChild::AnswerNPP_Destroy(N for (uint32_t i = 0; i < streams.Length(); ++i) static_cast<BrowserStreamChild*>(streams[i])->FinishDelivery(); mTimers.Clear(); // NPP_Destroy() should be a synchronization point for plugin threads // calling NPN_AsyncCall: after this function returns, they are no longer // allowed to make async calls on this instance. - PluginModuleChild::current()->NPP_Destroy(this); + static_cast<PluginModuleChild *>(Manager())->NPP_Destroy(this); mData.ndata = 0; if (mCurrentInvalidateTask) { mCurrentInvalidateTask->Cancel(); mCurrentInvalidateTask = nullptr; } if (mCurrentAsyncSetWindowTask) { mCurrentAsyncSetWindowTask->Cancel(); @@ -3756,11 +3758,27 @@ PluginInstanceChild::AnswerNPP_Destroy(N #ifdef MOZ_WIDGET_GTK if (mWindow.type == NPWindowTypeWindow && !mXEmbed) { xt_client_xloop_destroy(); } #endif #if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX) DeleteWindow(); #endif +} + +bool +PluginInstanceChild::AnswerNPP_Destroy(NPError* aResult) +{ + PLUGIN_LOG_DEBUG_METHOD; + AssertPluginThread(); + *aResult = NPERR_NO_ERROR; + + Destroy(); return true; } + +void +PluginInstanceChild::ActorDestroy(ActorDestroyReason why) +{ + Destroy(); +}
--- a/dom/plugins/ipc/PluginInstanceChild.h +++ b/dom/plugins/ipc/PluginInstanceChild.h @@ -520,16 +520,20 @@ private: void ClearCurrentSurface(); // Swap mCurrentSurface/mBackSurface and their associated actors void SwapSurfaces(); // Clear all surfaces in response to NPP_Destroy void ClearAllSurfaces(); + void Destroy(); + + void ActorDestroy(ActorDestroyReason why); + // Set as true when SetupLayer called // and go with different path in InvalidateRect function bool mLayersRendering; // Current surface available for rendering nsRefPtr<gfxASurface> mCurrentSurface; // Back surface, just keeping reference to @@ -593,14 +597,17 @@ private: // that we ask a plugin to paint at least once even if it's invisible; // some plugin (instances) rely on this in order to work properly. bool mHasPainted; // Cached rectangle rendered to previous surface(mBackSurface) // Used for reading back to current surface and syncing data, // in plugin coordinates. nsIntRect mSurfaceDifferenceRect; + + // Has this instance been destroyed, either by ActorDestroy or NPP_Destroy? + bool mDestroyed; }; } // namespace plugins } // namespace mozilla #endif // ifndef dom_plugins_PluginInstanceChild_h
--- a/dom/plugins/ipc/PluginInstanceParent.cpp +++ b/dom/plugins/ipc/PluginInstanceParent.cpp @@ -1773,17 +1773,17 @@ PluginInstanceParent::SharedSurfaceSetWi if (NS_FAILED(mSharedSurfaceDib.Create(reinterpret_cast<HDC>(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(mParent->ChildProcessHandle(), &handle))) + if (NS_FAILED(mSharedSurfaceDib.ShareToProcess(OtherProcess(), &handle))) return false; aRemoteWindow.surfaceHandle = handle; return true; } void
--- a/dom/plugins/ipc/PluginInterposeOSX.mm +++ b/dom/plugins/ipc/PluginInterposeOSX.mm @@ -540,17 +540,17 @@ void NSCursorInfo::SetCustomImageData(ui } // This should never be called from the browser process -- only from the // plugin process. bool NSCursorInfo::GetNativeCursorsSupported() { if (mNativeCursorsSupported == -1) { AssertPluginThread(); - PluginModuleChild *pmc = PluginModuleChild::current(); + PluginModuleChild *pmc = PluginModuleChild::GetChrome(); if (pmc) { bool result = pmc->GetNativeCursorsSupported(); if (result) { mNativeCursorsSupported = 1; } else { mNativeCursorsSupported = 0; } } @@ -686,60 +686,60 @@ void FocusPluginProcess() { SetFrontProcess(&this_process); } } void NotifyBrowserOfPluginShowWindow(uint32_t window_id, CGRect bounds, bool modal) { AssertPluginThread(); - PluginModuleChild *pmc = PluginModuleChild::current(); + PluginModuleChild *pmc = PluginModuleChild::GetChrome(); if (pmc) pmc->PluginShowWindow(window_id, modal, bounds); } void NotifyBrowserOfPluginHideWindow(uint32_t window_id, CGRect bounds) { AssertPluginThread(); - PluginModuleChild *pmc = PluginModuleChild::current(); + PluginModuleChild *pmc = PluginModuleChild::GetChrome(); if (pmc) pmc->PluginHideWindow(window_id); } void NotifyBrowserOfSetCursor(NSCursorInfo& aCursorInfo) { AssertPluginThread(); - PluginModuleChild *pmc = PluginModuleChild::current(); + PluginModuleChild *pmc = PluginModuleChild::GetChrome(); if (pmc) { pmc->SetCursor(aCursorInfo); } } void NotifyBrowserOfShowCursor(bool show) { AssertPluginThread(); - PluginModuleChild *pmc = PluginModuleChild::current(); + PluginModuleChild *pmc = PluginModuleChild::GetChrome(); if (pmc) { pmc->ShowCursor(show); } } void NotifyBrowserOfPushCursor(NSCursorInfo& aCursorInfo) { AssertPluginThread(); - PluginModuleChild *pmc = PluginModuleChild::current(); + PluginModuleChild *pmc = PluginModuleChild::GetChrome(); if (pmc) { pmc->PushCursor(aCursorInfo); } } void NotifyBrowserOfPopCursor() { AssertPluginThread(); - PluginModuleChild *pmc = PluginModuleChild::current(); + PluginModuleChild *pmc = PluginModuleChild::GetChrome(); if (pmc) { pmc->PopCursor(); } } struct WindowInfo { uint32_t window_id; CGRect bounds;
--- a/dom/plugins/ipc/PluginModuleChild.cpp +++ b/dom/plugins/ipc/PluginModuleChild.cpp @@ -60,17 +60,18 @@ using mozilla::dom::CrashReporterChild; using mozilla::dom::PCrashReporterChild; #if defined(XP_WIN) const wchar_t * kFlashFullscreenClass = L"ShockwaveFlashFullScreen"; const wchar_t * kMozillaWindowClass = L"MozillaWindowClass"; #endif namespace { -PluginModuleChild* gInstance = nullptr; +PluginModuleChild* gChromeInstance = nullptr; +nsTArray<PluginModuleChild*>* gAllInstances; } #ifdef MOZ_WIDGET_QT typedef void (*_gtk_init_fn)(int argc, char **argv); static _gtk_init_fn s_gtk_init = nullptr; static PRLibrary *sGtkLib = nullptr; #endif @@ -79,80 +80,156 @@ static PRLibrary *sGtkLib = nullptr; static bool gDelayFlashFocusReplyUntilEval = false; // Used to fix GetWindowInfo problems with internal flash settings dialogs static WindowsDllInterceptor sUser32Intercept; typedef BOOL (WINAPI *GetWindowInfoPtr)(HWND hwnd, PWINDOWINFO pwi); static GetWindowInfoPtr sGetWindowInfoPtrStub = nullptr; static HWND sBrowserHwnd = nullptr; #endif -PluginModuleChild::PluginModuleChild() +/* static */ +PluginModuleChild* +PluginModuleChild::CreateForContentProcess(mozilla::ipc::Transport* aTransport, + base::ProcessId aOtherProcess) +{ + PluginModuleChild* child = new PluginModuleChild(false); + ProcessHandle handle; + if (!base::OpenProcessHandle(aOtherProcess, &handle)) { + // XXX need to kill |aOtherProcess|, it's boned + return nullptr; + } + + if (!child->InitForContent(handle, XRE_GetIOMessageLoop(), aTransport)) { + return nullptr; + } + + return child; +} + +PluginModuleChild::PluginModuleChild(bool aIsChrome) : mLibrary(0) , mPluginFilename("") , mQuirks(QUIRKS_NOT_INITIALIZED) + , mIsChrome(aIsChrome) + , mTransport(nullptr) , mShutdownFunc(0) , mInitializeFunc(0) #if defined(OS_WIN) || defined(OS_MACOSX) , mGetEntryPointsFunc(0) #elif defined(MOZ_WIDGET_GTK) , mNestedLoopTimerId(0) #elif defined(MOZ_WIDGET_QT) , mNestedLoopTimerObject(0) #endif #ifdef OS_WIN , mNestedEventHook(nullptr) , mGlobalCallWndProcHook(nullptr) #endif { - NS_ASSERTION(!gInstance, "Something terribly wrong here!"); + if (!gAllInstances) { + gAllInstances = new nsTArray<PluginModuleChild*>(1); + } + gAllInstances->AppendElement(this); + memset(&mFunctions, 0, sizeof(mFunctions)); - memset(&mSavedData, 0, sizeof(mSavedData)); - gInstance = this; + if (mIsChrome) { + MOZ_ASSERT(!gChromeInstance); + gChromeInstance = this; + } mUserAgent.SetIsVoid(true); #ifdef XP_MACOSX mac_plugin_interposing::child::SetUpCocoaInterposing(); #endif } PluginModuleChild::~PluginModuleChild() { - NS_ASSERTION(gInstance == this, "Something terribly wrong here!"); - - // We don't unload the plugin library in case it uses atexit handlers or - // other similar hooks. + if (mTransport) { + // For some reason IPDL doesn't autmatically 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. + XRE_GetIOMessageLoop()->PostTask(FROM_HERE, new DeleteTask<Transport>(mTransport)); + } - DeinitGraphics(); - PluginScriptableObjectChild::ClearIdentifiers(); + gAllInstances->RemoveElement(this); + MOZ_ASSERT_IF(mIsChrome, gAllInstances->Length() == 0); + if (gAllInstances->IsEmpty()) { + delete gAllInstances; + gAllInstances = nullptr; + } - gInstance = nullptr; + if (mIsChrome) { + MOZ_ASSERT(gChromeInstance == this); + + // We don't unload the plugin library in case it uses atexit handlers or + // other similar hooks. + + DeinitGraphics(); + PluginScriptableObjectChild::ClearIdentifiers(); + + gChromeInstance = nullptr; + } } // static PluginModuleChild* -PluginModuleChild::current() +PluginModuleChild::GetChrome() { - NS_ASSERTION(gInstance, "Null instance!"); - return gInstance; + MOZ_ASSERT(gChromeInstance); + return gChromeInstance; } bool -PluginModuleChild::Init(const std::string& aPluginFilename, - base::ProcessHandle aParentProcessHandle, - MessageLoop* aIOLoop, - IPC::Channel* aChannel) +PluginModuleChild::CommonInit(base::ProcessHandle aParentProcessHandle, + MessageLoop* aIOLoop, + IPC::Channel* aChannel) { PLUGIN_LOG_DEBUG_METHOD; - GetIPCChannel()->SetAbortOnError(true); - // Request Windows message deferral behavior on our channel. This // applies to the top level and all sub plugin protocols since they // all share the same channel. + // Bug 1090573 - Don't do this for connections to content processes. GetIPCChannel()->SetChannelFlags(MessageChannel::REQUIRE_DEFERRED_MESSAGE_PROTECTION); + if (!Open(aChannel, aParentProcessHandle, aIOLoop)) + return false; + + memset((void*) &mFunctions, 0, sizeof(mFunctions)); + mFunctions.size = sizeof(mFunctions); + mFunctions.version = (NP_VERSION_MAJOR << 8) | NP_VERSION_MINOR; + + return true; +} + +bool +PluginModuleChild::InitForContent(base::ProcessHandle aParentProcessHandle, + MessageLoop* aIOLoop, + IPC::Channel* aChannel) +{ + if (!CommonInit(aParentProcessHandle, aIOLoop, aChannel)) { + return false; + } + + mTransport = aChannel; + + mLibrary = GetChrome()->mLibrary; + mQuirks = GetChrome()->mQuirks; + mFunctions = GetChrome()->mFunctions; + + return true; +} + +bool +PluginModuleChild::InitForChrome(const std::string& aPluginFilename, + base::ProcessHandle aParentProcessHandle, + MessageLoop* aIOLoop, + IPC::Channel* aChannel) +{ #ifdef XP_WIN COMMessageFilter::Initialize(this); #endif NS_ASSERTION(aChannel, "need a channel"); if (!InitGraphics()) return false; @@ -196,22 +273,21 @@ PluginModuleChild::Init(const std::strin #endif { nsresult rv = pluginFile.LoadPlugin(&mLibrary); if (NS_FAILED(rv)) return false; } NS_ASSERTION(mLibrary, "couldn't open shared object"); - if (!Open(aChannel, aParentProcessHandle, aIOLoop)) + if (!CommonInit(aParentProcessHandle, aIOLoop, aChannel)) { return false; + } - memset((void*) &mFunctions, 0, sizeof(mFunctions)); - mFunctions.size = sizeof(mFunctions); - mFunctions.version = (NP_VERSION_MAJOR << 8) | NP_VERSION_MINOR; + GetIPCChannel()->SetAbortOnError(true); // TODO: use PluginPRLibrary here #if defined(OS_LINUX) || defined(OS_BSD) mShutdownFunc = (NP_PLUGINSHUTDOWN) PR_FindFunctionSymbol(mLibrary, "NP_Shutdown"); // create the new plugin handler @@ -586,16 +662,17 @@ PluginModuleChild::DeinitGraphics() XCloseDisplay(DefaultXDisplay()); #endif } bool PluginModuleChild::AnswerNP_Shutdown(NPError *rv) { AssertPluginThread(); + MOZ_ASSERT(mIsChrome); #if defined XP_WIN mozilla::widget::StopAudioSession(); #endif // the PluginModuleParent shuts down this process after this interrupt // call pops off its stack @@ -673,16 +750,23 @@ PluginModuleChild::RecvSetAudioSessionDa void PluginModuleChild::QuickExit() { NS_WARNING("plugin process _exit()ing"); _exit(0); } +PPluginModuleChild* +PluginModuleChild::AllocPPluginModuleChild(mozilla::ipc::Transport* aTransport, + base::ProcessId aOtherProcess) +{ + return PluginModuleChild::CreateForContentProcess(aTransport, aOtherProcess); +} + PCrashReporterChild* PluginModuleChild::AllocPCrashReporterChild(mozilla::dom::NativeThreadId* id, uint32_t* processType) { return new CrashReporterChild(); } bool @@ -703,16 +787,27 @@ PluginModuleChild::AnswerPCrashReporterC *processType = XRE_GetProcessType(); #endif return true; } void PluginModuleChild::ActorDestroy(ActorDestroyReason why) { + if (!mIsChrome) { + PluginModuleChild* chromeInstance = PluginModuleChild::GetChrome(); + if (chromeInstance) { + chromeInstance->SendNotifyContentModuleDestroyed(); + } + + // Destroy ourselves once we finish other teardown activities. + MessageLoop::current()->PostTask(FROM_HERE, new DeleteTask<PluginModuleChild>(this)); + return; + } + if (AbnormalShutdown == why) { NS_WARNING("shutting down early because of crash!"); QuickExit(); } // doesn't matter why we're being destroyed; it's up to us to // initiate (clean) shutdown XRE_ShutdownChildProcess(); @@ -1027,17 +1122,17 @@ NPError case NPNVjavascriptEnabledBool: // Intentional fall-through case NPNVasdEnabledBool: // Intentional fall-through case NPNVisOfflineBool: // Intentional fall-through case NPNVSupportsXEmbedBool: // Intentional fall-through case NPNVSupportsWindowless: { // Intentional fall-through NPError result; bool value; - PluginModuleChild::current()-> + PluginModuleChild::GetChrome()-> CallNPN_GetValue_WithBoolReturn(aVariable, &result, &value); *(NPBool*)aValue = value ? true : false; return result; } #if defined(MOZ_WIDGET_GTK) case NPNVxDisplay: { if (aNPP) { return InstCast(aNPP)->NPN_GetValue(aVariable, aValue); @@ -1218,17 +1313,20 @@ uint32_t } void _reloadplugins(NPBool aReloadPages) { PLUGIN_LOG_DEBUG_FUNCTION; ENSURE_PLUGIN_THREAD_VOID(); - PluginModuleChild::current()->SendNPN_ReloadPlugins(!!aReloadPages); + // Send the reload message to all modules. Chrome will need to reload from + // disk and content will need to request a new list of plugin tags from + // chrome. + PluginModuleChild::GetChrome()->SendNPN_ReloadPlugins(!!aReloadPages); } void _invalidaterect(NPP aNPP, NPRect* aInvalidRect) { PLUGIN_LOG_DEBUG_FUNCTION; ENSURE_PLUGIN_THREAD_VOID(); @@ -1257,17 +1355,17 @@ void // never be necessary. } const char* _useragent(NPP aNPP) { PLUGIN_LOG_DEBUG_FUNCTION; ENSURE_PLUGIN_THREAD(nullptr); - return PluginModuleChild::current()->GetUserAgent(); + return PluginModuleChild::GetChrome()->GetUserAgent(); } void* _memalloc(uint32_t aSize) { PLUGIN_LOG_DEBUG_FUNCTION; // Only assert plugin thread here for consistency with in-process plugins. AssertPluginThread(); @@ -1488,28 +1586,17 @@ void void _setexception(NPObject* aNPObj, const NPUTF8* aMessage) { PLUGIN_LOG_DEBUG_FUNCTION; ENSURE_PLUGIN_THREAD_VOID(); - PluginModuleChild* self = PluginModuleChild::current(); - PluginScriptableObjectChild* actor = nullptr; - if (aNPObj) { - actor = PluginScriptableObjectChild::GetActorForNPObject(aNPObj); - if (!actor) { - NS_ERROR("Failed to get actor!"); - return; - } - } - - self->SendNPN_SetException(static_cast<PPluginScriptableObjectChild*>(actor), - NullableString(aMessage)); + // Do nothing. We no longer support this API. } void _pushpopupsenabledstate(NPP aNPP, NPBool aEnabled) { PLUGIN_LOG_DEBUG_FUNCTION; ENSURE_PLUGIN_THREAD_VOID(); @@ -1689,17 +1776,17 @@ NPError NPBool success = _convertpoint(instance, pluginX, pluginY, NPCoordinateSpacePlugin, &screenX, &screenY, NPCoordinateSpaceScreen); if (success) { return mozilla::plugins::PluginUtilsOSX::ShowCocoaContextMenu(menu, screenX, screenY, - PluginModuleChild::current(), + PluginModuleChild::GetChrome(), ProcessBrowserEvents); } else { NS_WARNING("Convertpoint failed, could not created contextmenu."); return NPERR_GENERIC_ERROR; } #else NS_WARNING("Not supported on this platform!"); @@ -1747,32 +1834,34 @@ void //----------------------------------------------------------------------------- bool PluginModuleChild::AnswerNP_GetEntryPoints(NPError* _retval) { PLUGIN_LOG_DEBUG_METHOD; AssertPluginThread(); + MOZ_ASSERT(mIsChrome); #if defined(OS_LINUX) || defined(OS_BSD) return true; #elif defined(OS_WIN) || defined(OS_MACOSX) *_retval = mGetEntryPointsFunc(&mFunctions); return true; #else # error Please implement me for your platform #endif } bool PluginModuleChild::AnswerNP_Initialize(NPError* _retval) { PLUGIN_LOG_DEBUG_METHOD; AssertPluginThread(); + MOZ_ASSERT(mIsChrome); #ifdef OS_WIN SetEventHooks(); #endif #ifdef MOZ_X11 // Send the parent our X socket to act as a proxy reference for our X // resources. @@ -2193,17 +2282,17 @@ PluginModuleChild::CallWindowProcHook(in } return CallNextHookEx(nullptr, nCode, wParam, lParam); } LRESULT CALLBACK PluginModuleChild::NestedInputEventHook(int nCode, WPARAM wParam, LPARAM lParam) { - PluginModuleChild* self = current(); + PluginModuleChild* self = GetChrome(); uint32_t len = self->mIncallPumpingStack.Length(); if (nCode >= 0 && len && !self->mIncallPumpingStack[len - 1]._spinning) { MessageLoop* loop = MessageLoop::current(); self->SendProcessNativeEventsInInterruptCall(); IncallFrame& f = self->mIncallPumpingStack[len - 1]; f._spinning = true; f._savedNestableTasksAllowed = loop->NestableTasksAllowed(); loop->SetNestableTasksAllowed(true);
--- a/dom/plugins/ipc/PluginModuleChild.h +++ b/dom/plugins/ipc/PluginModuleChild.h @@ -71,16 +71,20 @@ protected: } virtual bool ShouldContinueFromReplyTimeout() MOZ_OVERRIDE; // Implement the PPluginModuleChild interface virtual bool AnswerNP_GetEntryPoints(NPError* rv) MOZ_OVERRIDE; virtual bool AnswerNP_Initialize(NPError* rv) MOZ_OVERRIDE; + virtual PPluginModuleChild* + AllocPPluginModuleChild(mozilla::ipc::Transport* aTransport, + base::ProcessId aOtherProcess) MOZ_OVERRIDE; + virtual PPluginInstanceChild* AllocPPluginInstanceChild(const nsCString& aMimeType, const uint16_t& aMode, const InfallibleTArray<nsCString>& aNames, const InfallibleTArray<nsCString>& aValues, NPError* rv) MOZ_OVERRIDE; virtual bool @@ -135,32 +139,44 @@ protected: virtual bool RecvProcessNativeEventsInInterruptCall() MOZ_OVERRIDE; virtual bool AnswerGeckoGetProfile(nsCString* aProfile) MOZ_OVERRIDE; public: - PluginModuleChild(); + PluginModuleChild(bool aIsChrome); virtual ~PluginModuleChild(); + bool CommonInit(base::ProcessHandle aParentProcessHandle, + MessageLoop* aIOLoop, + IPC::Channel* aChannel); + // aPluginFilename is UTF8, not native-charset! - bool Init(const std::string& aPluginFilename, - base::ProcessHandle aParentProcessHandle, - MessageLoop* aIOLoop, - IPC::Channel* aChannel); + bool InitForChrome(const std::string& aPluginFilename, + base::ProcessHandle aParentProcessHandle, + MessageLoop* aIOLoop, + IPC::Channel* aChannel); + + bool InitForContent(base::ProcessHandle aParentProcessHandle, + MessageLoop* aIOLoop, + IPC::Channel* aChannel); + + static PluginModuleChild* + CreateForContentProcess(mozilla::ipc::Transport* aTransport, + base::ProcessId aOtherProcess); void CleanUp(); const char* GetUserAgent(); static const NPNetscapeFuncs sBrowserFuncs; - static PluginModuleChild* current(); + static PluginModuleChild* GetChrome(); /** * The child implementation of NPN_CreateObject. */ static NPObject* NPN_CreateObject(NPP aNPP, NPClass* aClass); /** * The child implementation of NPN_RetainObject. */ @@ -283,27 +299,29 @@ private: virtual void ExitedCxxStack() MOZ_OVERRIDE; #endif PRLibrary* mLibrary; nsCString mPluginFilename; // UTF8 nsCString mUserAgent; int mQuirks; + bool mIsChrome; + Transport* mTransport; + // we get this from the plugin NP_PLUGINSHUTDOWN mShutdownFunc; #if defined(OS_LINUX) || defined(OS_BSD) NP_PLUGINUNIXINIT mInitializeFunc; #elif defined(OS_WIN) || defined(OS_MACOSX) NP_PLUGININIT mInitializeFunc; NP_GETENTRYPOINTS mGetEntryPointsFunc; #endif NPPluginFuncs mFunctions; - NPSavedData mSavedData; #if defined(MOZ_WIDGET_GTK) // If a plugin spins a nested glib event loop in response to a // synchronous IPC message from the browser, the loop might break // only after the browser responds to a request sent by the // plugin. This can happen if a plugin uses gtk's synchronous // copy/paste, for example. But because the browser is blocked on // a condvar, it can't respond to the request. This situation
--- a/dom/plugins/ipc/PluginModuleParent.cpp +++ b/dom/plugins/ipc/PluginModuleParent.cpp @@ -10,19 +10,22 @@ #include <QtCore/QEventLoop> #include "NestedLoopTimer.h" #endif #include "mozilla/plugins/PluginModuleParent.h" #include "base/process_util.h" #include "mozilla/Attributes.h" +#include "mozilla/dom/ContentParent.h" +#include "mozilla/dom/ContentChild.h" #include "mozilla/dom/PCrashReporterParent.h" #include "mozilla/ipc/MessageChannel.h" #include "mozilla/plugins/BrowserStreamParent.h" +#include "mozilla/plugins/PluginBridge.h" #include "mozilla/plugins/PluginInstanceParent.h" #include "mozilla/Preferences.h" #include "mozilla/Services.h" #include "mozilla/unused.h" #include "nsAutoPtr.h" #include "nsCRT.h" #include "nsIFile.h" #include "nsIObserverService.h" @@ -78,26 +81,92 @@ static const char kHangUIMinDisplayPref[ template<> struct RunnableMethodTraits<mozilla::plugins::PluginModuleParent> { typedef mozilla::plugins::PluginModuleParent Class; static void RetainCallee(Class* obj) { } static void ReleaseCallee(Class* obj) { } }; +bool +mozilla::plugins::SetupBridge(uint32_t aPluginId, dom::ContentParent* aContentParent) +{ + nsRefPtr<nsPluginHost> host = nsPluginHost::GetInst(); + nsRefPtr<nsNPAPIPlugin> plugin; + nsresult rv = host->GetPluginForContentProcess(aPluginId, getter_AddRefs(plugin)); + if (NS_FAILED(rv)) { + return false; + } + PluginModuleParent* chromeParent = static_cast<PluginModuleParent*>(plugin->GetLibrary()); + return PPluginModule::Bridge(aContentParent, chromeParent); +} + +PluginModuleContentParent* PluginModuleContentParent::sSavedModuleParent; + +/* static */ PluginLibrary* +PluginModuleContentParent::LoadModule(uint32_t aPluginId) +{ + MOZ_ASSERT(!sSavedModuleParent); + MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Content); + + /* + * We send a LoadPlugin message to the chrome process using an intr + * message. Before it sends its response, it sends a message to create + * PluginModuleParent instance. That message is handled by + * PluginModuleContentParent::Create, which saves the instance in + * sSavedModuleParent. We fetch it from there after LoadPlugin finishes. + */ + dom::ContentChild* cp = dom::ContentChild::GetSingleton(); + if (!cp->CallLoadPlugin(aPluginId)) { + return nullptr; + } + + PluginModuleContentParent* parent = sSavedModuleParent; + MOZ_ASSERT(parent); + sSavedModuleParent = nullptr; + + return parent; +} + +/* static */ PluginModuleContentParent* +PluginModuleContentParent::Create(mozilla::ipc::Transport* aTransport, + base::ProcessId aOtherProcess) +{ + nsAutoPtr<PluginModuleContentParent> parent(new PluginModuleContentParent()); + ProcessHandle handle; + if (!base::OpenProcessHandle(aOtherProcess, &handle)) { + // Bug 1090578 - need to kill |aOtherProcess|, it's boned. + return nullptr; + } + + MOZ_ASSERT(!sSavedModuleParent); + sSavedModuleParent = parent; + + DebugOnly<bool> ok = parent->Open(aTransport, handle, XRE_GetIOMessageLoop(), + mozilla::ipc::ParentSide); + MOZ_ASSERT(ok); + + // Request Windows message deferral behavior on our channel. This + // applies to the top level and all sub plugin protocols since they + // all share the same channel. + parent->GetIPCChannel()->SetChannelFlags(MessageChannel::REQUIRE_DEFERRED_MESSAGE_PROTECTION); + + return parent.forget(); +} + // static PluginLibrary* -PluginModuleParent::LoadModule(const char* aFilePath) +PluginModuleChromeParent::LoadModule(const char* aFilePath, uint32_t aPluginId) { PLUGIN_LOG_DEBUG_FUNCTION; int32_t prefSecs = Preferences::GetInt(kLaunchTimeoutPref, 0); // Block on the child process being launched and initialized. - nsAutoPtr<PluginModuleParent> parent(new PluginModuleParent(aFilePath)); + nsAutoPtr<PluginModuleChromeParent> parent(new PluginModuleChromeParent(aFilePath, aPluginId)); bool launched = parent->mSubprocess->Launch(prefSecs * 1000); if (!launched) { // We never reached open parent->mShutdown = true; return nullptr; } parent->Open(parent->mSubprocess->GetChannel(), parent->mSubprocess->GetChildProcessHandle()); @@ -119,25 +188,50 @@ PluginModuleParent::LoadModule(const cha mozilla::MutexAutoLock lock(parent->mCrashReporterMutex); parent->mCrashReporter = parent->CrashReporter(); #endif #endif return parent.forget(); } - -PluginModuleParent::PluginModuleParent(const char* aFilePath) - : mSubprocess(new PluginProcessParent(aFilePath)) +PluginModuleParent::PluginModuleParent(bool aIsChrome) + : mIsChrome(aIsChrome) , mShutdown(false) , mClearSiteDataSupported(false) , mGetSitesWithDataSupported(false) , mNPNIface(nullptr) , mPlugin(nullptr) , mTaskFactory(MOZ_THIS_IN_INITIALIZER_LIST()) +{ +} + +PluginModuleParent::~PluginModuleParent() +{ + if (!OkToCleanup()) { + NS_RUNTIMEABORT("unsafe destruction"); + } + + if (!mShutdown) { + NS_WARNING("Plugin host deleted the module without shutting down."); + NPError err; + NP_Shutdown(&err); + } +} + +PluginModuleContentParent::PluginModuleContentParent() + : PluginModuleParent(false) +{ +} + +PluginModuleChromeParent::PluginModuleChromeParent(const char* aFilePath, uint32_t aPluginId) + : PluginModuleParent(true) + , mSubprocess(new PluginProcessParent(aFilePath)) + , mPluginId(aPluginId) + , mChromeTaskFactory(MOZ_THIS_IN_INITIALIZER_LIST()) , mHangAnnotationFlags(0) #ifdef XP_WIN , mPluginCpuUsageOnHang() , mHangUIParent(nullptr) , mHangUIEnabled(true) , mIsTimerReset(true) #ifdef MOZ_CRASHREPORTER , mCrashReporterMutex("PluginModuleParent::mCrashReporterMutex") @@ -160,17 +254,17 @@ PluginModuleParent::PluginModuleParent(c #ifdef MOZ_ENABLE_PROFILER_SPS InitPluginProfiling(); #endif mozilla::HangMonitor::RegisterAnnotator(*this); } -PluginModuleParent::~PluginModuleParent() +PluginModuleChromeParent::~PluginModuleChromeParent() { if (!OkToCleanup()) { NS_RUNTIMEABORT("unsafe destruction"); } #ifdef MOZ_ENABLE_PROFILER_SPS ShutdownPluginProfiling(); #endif @@ -207,17 +301,17 @@ PluginModuleParent::~PluginModuleParent( } #endif mozilla::HangMonitor::UnregisterAnnotator(*this); } #ifdef MOZ_CRASHREPORTER void -PluginModuleParent::WriteExtraDataForMinidump(AnnotationTable& notes) +PluginModuleChromeParent::WriteExtraDataForMinidump(AnnotationTable& notes) { #ifdef XP_WIN // mCrashReporterMutex is already held by the caller mCrashReporterMutex.AssertCurrentThreadOwns(); #endif typedef nsDependentCString CS; // Get the plugin filename, try to get just the file leafname @@ -252,58 +346,58 @@ PluginModuleParent::WriteExtraDataForMin #endif } #endif } } #endif // MOZ_CRASHREPORTER void -PluginModuleParent::SetChildTimeout(const int32_t aChildTimeout) +PluginModuleChromeParent::SetChildTimeout(const int32_t aChildTimeout) { int32_t timeoutMs = (aChildTimeout > 0) ? (1000 * aChildTimeout) : MessageChannel::kNoTimeout; SetReplyTimeoutMs(timeoutMs); } void -PluginModuleParent::TimeoutChanged(const char* aPref, void* aModule) +PluginModuleChromeParent::TimeoutChanged(const char* aPref, void* aModule) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); #ifndef XP_WIN if (!strcmp(aPref, kChildTimeoutPref)) { // The timeout value used by the parent for children int32_t timeoutSecs = Preferences::GetInt(kChildTimeoutPref, 0); - static_cast<PluginModuleParent*>(aModule)->SetChildTimeout(timeoutSecs); + static_cast<PluginModuleChromeParent*>(aModule)->SetChildTimeout(timeoutSecs); #else if (!strcmp(aPref, kChildTimeoutPref) || !strcmp(aPref, kHangUIMinDisplayPref) || !strcmp(aPref, kHangUITimeoutPref)) { - static_cast<PluginModuleParent*>(aModule)->EvaluateHangUIState(true); + static_cast<PluginModuleChromeParent*>(aModule)->EvaluateHangUIState(true); #endif // XP_WIN } else if (!strcmp(aPref, kParentTimeoutPref)) { // The timeout value used by the child for its parent int32_t timeoutSecs = Preferences::GetInt(kParentTimeoutPref, 0); - unused << static_cast<PluginModuleParent*>(aModule)->SendSetParentHangTimeout(timeoutSecs); + unused << static_cast<PluginModuleChromeParent*>(aModule)->SendSetParentHangTimeout(timeoutSecs); } } void -PluginModuleParent::CleanupFromTimeout(const bool aFromHangUI) +PluginModuleChromeParent::CleanupFromTimeout(const bool aFromHangUI) { if (mShutdown) { return; } if (!OkToCleanup()) { // there's still plugin code on the C++ stack, try again MessageLoop::current()->PostDelayedTask( FROM_HERE, - mTaskFactory.NewRunnableMethod( - &PluginModuleParent::CleanupFromTimeout, aFromHangUI), 10); + mChromeTaskFactory.NewRunnableMethod( + &PluginModuleChromeParent::CleanupFromTimeout, aFromHangUI), 10); return; } /* If the plugin container was terminated by the Plugin Hang UI, then either the I/O thread detects a channel error, or the main thread must set the error (whomever gets there first). OTOH, if we terminate and return false from ShouldContinueFromReplyTimeout, then the channel state has @@ -380,35 +474,35 @@ GetProcessCpuUsage(const InfallibleTArra return true; } } // anonymous namespace #endif // #ifdef XP_WIN void -PluginModuleParent::EnteredCxxStack() +PluginModuleChromeParent::EnteredCxxStack() { mHangAnnotationFlags |= kInPluginCall; } void -PluginModuleParent::ExitedCxxStack() +PluginModuleChromeParent::ExitedCxxStack() { mHangAnnotationFlags = 0; #ifdef XP_WIN FinishHangUI(); #endif } /** * This function is always called by the HangMonitor thread. */ void -PluginModuleParent::AnnotateHang(mozilla::HangMonitor::HangAnnotations& aAnnotations) +PluginModuleChromeParent::AnnotateHang(mozilla::HangMonitor::HangAnnotations& aAnnotations) { uint32_t flags = mHangAnnotationFlags; if (flags) { /* We don't actually annotate anything specifically for kInPluginCall; we use it to determine whether to annotate other things. It will be pretty obvious from the ChromeHang stack that we're in a plugin call when the hang occurred. */ if (flags & kHangUIShown) { @@ -446,32 +540,32 @@ CreateFlashMinidump(DWORD processId, Thr bool res = CreateAdditionalChildMinidump(handle, 0, parentMinidump, name); base::CloseProcessHandle(handle); return res; } #endif bool -PluginModuleParent::ShouldContinueFromReplyTimeout() +PluginModuleChromeParent::ShouldContinueFromReplyTimeout() { #ifdef XP_WIN if (LaunchHangUI()) { return true; } // If LaunchHangUI returned false then we should proceed with the // original plugin hang behaviour and kill the plugin container. FinishHangUI(); #endif // XP_WIN TerminateChildProcess(MessageLoop::current()); return false; } void -PluginModuleParent::TerminateChildProcess(MessageLoop* aMsgLoop) +PluginModuleChromeParent::TerminateChildProcess(MessageLoop* aMsgLoop) { #ifdef MOZ_CRASHREPORTER #ifdef XP_WIN mozilla::MutexAutoLock lock(mCrashReporterMutex); CrashReporterParent* crashReporter = mCrashReporter; if (!crashReporter) { // If mCrashReporter is null then the hang has ended, the plugin module // is shutting down. There's nothing to do here. @@ -551,18 +645,18 @@ PluginModuleParent::TerminateChildProces } #endif // this must run before the error notification from the channel, // or not at all bool isFromHangUI = aMsgLoop != MessageLoop::current(); aMsgLoop->PostTask( FROM_HERE, - mTaskFactory.NewRunnableMethod( - &PluginModuleParent::CleanupFromTimeout, isFromHangUI)); + mChromeTaskFactory.NewRunnableMethod( + &PluginModuleChromeParent::CleanupFromTimeout, isFromHangUI)); if (!KillProcess(OtherProcess(), 1, false)) NS_WARNING("failed to kill subprocess!"); } bool PluginModuleParent::GetPluginDetails(nsACString& aPluginName, nsACString& aPluginVersion) @@ -577,17 +671,17 @@ PluginModuleParent::GetPluginDetails(nsA } aPluginName = pluginTag->mName; aPluginVersion = pluginTag->mVersion; return true; } #ifdef XP_WIN void -PluginModuleParent::EvaluateHangUIState(const bool aReset) +PluginModuleChromeParent::EvaluateHangUIState(const bool aReset) { int32_t minDispSecs = Preferences::GetInt(kHangUIMinDisplayPref, 10); int32_t autoStopSecs = Preferences::GetInt(kChildTimeoutPref, 0); int32_t timeoutSecs = 0; if (autoStopSecs > 0 && autoStopSecs < minDispSecs) { /* If we're going to automatically terminate the plugin within a time frame shorter than minDispSecs, there's no point in showing the hang UI; it would just flash briefly on the screen. */ @@ -611,17 +705,17 @@ PluginModuleParent::EvaluateHangUIState( autoStopSecs *= 2; } } mIsTimerReset = false; SetChildTimeout(autoStopSecs); } bool -PluginModuleParent::LaunchHangUI() +PluginModuleChromeParent::LaunchHangUI() { if (!mHangUIEnabled) { return false; } if (mHangUIParent) { if (mHangUIParent->IsShowing()) { // We've already shown the UI but the timeout has expired again. return false; @@ -648,17 +742,17 @@ PluginModuleParent::LaunchHangUI() after kChildTimeoutPref seconds if the user doesn't respond to the hang UI. */ EvaluateHangUIState(false); } return retval; } void -PluginModuleParent::FinishHangUI() +PluginModuleChromeParent::FinishHangUI() { if (mHangUIEnabled && mHangUIParent) { bool needsCancel = mHangUIParent->IsShowing(); // If we're still showing, send a Cancel notification if (needsCancel) { mHangUIParent->Cancel(); } /* If we cancelled the UI or if the user issued a response, @@ -669,25 +763,25 @@ PluginModuleParent::FinishHangUI() UI was displayed. Now that we're finishing the UI, we need to switch it back to kHangUITimeoutPref. */ EvaluateHangUIState(true); } } } void -PluginModuleParent::OnHangUIContinue() +PluginModuleChromeParent::OnHangUIContinue() { mHangAnnotationFlags |= kHangUIContinued; } #endif // XP_WIN #ifdef MOZ_CRASHREPORTER CrashReporterParent* -PluginModuleParent::CrashReporter() +PluginModuleChromeParent::CrashReporter() { return static_cast<CrashReporterParent*>(ManagedPCrashReporterParent()[0]); } #ifdef MOZ_CRASHREPORTER_INJECTOR static void RemoveMinidump(nsIFile* minidump) { @@ -699,17 +793,17 @@ RemoveMinidump(nsIFile* minidump) if (GetExtraFileForMinidump(minidump, getter_AddRefs(extraFile))) { extraFile->Remove(true); } } #endif // MOZ_CRASHREPORTER_INJECTOR void -PluginModuleParent::ProcessFirstMinidump() +PluginModuleChromeParent::ProcessFirstMinidump() { #ifdef XP_WIN mozilla::MutexAutoLock lock(mCrashReporterMutex); #endif CrashReporterParent* crashReporter = CrashReporter(); if (!crashReporter) return; @@ -776,20 +870,16 @@ PluginModuleParent::ProcessFirstMinidump } #endif void PluginModuleParent::ActorDestroy(ActorDestroyReason why) { switch (why) { case AbnormalShutdown: { -#ifdef MOZ_CRASHREPORTER - ProcessFirstMinidump(); -#endif - mShutdown = true; // Defer the PluginCrashed method so that we don't re-enter // and potentially modify the actor child list while enumerating it. if (mPlugin) MessageLoop::current()->PostTask( FROM_HERE, mTaskFactory.NewRunnableMethod( &PluginModuleParent::NotifyPluginCrashed)); @@ -800,16 +890,28 @@ PluginModuleParent::ActorDestroy(ActorDe break; default: NS_RUNTIMEABORT("Unexpected shutdown reason for toplevel actor."); } } void +PluginModuleChromeParent::ActorDestroy(ActorDestroyReason why) +{ + if (why == AbnormalShutdown) { +#ifdef MOZ_CRASHREPORTER + ProcessFirstMinidump(); +#endif + } + + PluginModuleParent::ActorDestroy(why); +} + +void PluginModuleParent::NotifyPluginCrashed() { if (!OkToCleanup()) { // there's still plugin code on the C++ stack. try again MessageLoop::current()->PostDelayedTask( FROM_HERE, mTaskFactory.NewRunnableMethod( &PluginModuleParent::NotifyPluginCrashed), 10); @@ -1168,23 +1270,26 @@ PluginModuleParent::NP_Initialize(NPNets mNPNIface = bFuncs; if (mShutdown) { *error = NPERR_GENERIC_ERROR; return NS_ERROR_FAILURE; } - if (!CallNP_Initialize(error)) { - Close(); - return NS_ERROR_FAILURE; - } - else if (*error != NPERR_NO_ERROR) { - Close(); - return NS_OK; + *error = NPERR_NO_ERROR; + if (IsChrome()) { + if (!CallNP_Initialize(error)) { + Close(); + return NS_ERROR_FAILURE; + } + else if (*error != NPERR_NO_ERROR) { + Close(); + return NS_OK; + } } SetPluginFuncs(pFuncs); return NS_OK; } #else nsresult @@ -1194,16 +1299,27 @@ PluginModuleParent::NP_Initialize(NPNets mNPNIface = bFuncs; if (mShutdown) { *error = NPERR_GENERIC_ERROR; return NS_ERROR_FAILURE; } + *error = NPERR_NO_ERROR; + return NS_OK; +} + +nsresult +PluginModuleChromeParent::NP_Initialize(NPNetscapeFuncs* bFuncs, NPError* error) +{ + nsresult rv = PluginModuleParent::NP_Initialize(bFuncs, error); + if (NS_FAILED(rv)) + return rv; + if (!CallNP_Initialize(error)) { Close(); return NS_ERROR_FAILURE; } if (*error != NPERR_NO_ERROR) { Close(); return NS_OK; } @@ -1233,17 +1349,20 @@ PluginModuleParent::NP_Shutdown(NPError* { PLUGIN_LOG_DEBUG_METHOD; if (mShutdown) { *error = NPERR_GENERIC_ERROR; return NS_ERROR_FAILURE; } - bool ok = CallNP_Shutdown(error); + bool ok = true; + if (IsChrome()) { + ok = CallNP_Shutdown(error); + } // if NP_Shutdown() is nested within another interrupt call, this will // break things. but lord help us if we're doing that anyway; the // plugin dso will have been unloaded on the other side by the // CallNP_Shutdown() message Close(); return ok ? NS_OK : NS_ERROR_FAILURE; @@ -1271,26 +1390,31 @@ PluginModuleParent::NP_GetValue(void *fu } #if defined(XP_WIN) || defined(XP_MACOSX) nsresult PluginModuleParent::NP_GetEntryPoints(NPPluginFuncs* pFuncs, NPError* error) { NS_ASSERTION(pFuncs, "Null pointer!"); - // We need to have the child process update its function table - // here by actually calling NP_GetEntryPoints since the parent's - // function table can reflect nullptr entries in the child's table. - if (!CallNP_GetEntryPoints(error)) { - return NS_ERROR_FAILURE; - } - else if (*error != NPERR_NO_ERROR) { - return NS_OK; + // We need to have the plugin process update its function table here by + // actually calling NP_GetEntryPoints. The parent's function table will + // reflect nullptr entries in the child's table once SetPluginFuncs is + // called. + + if (IsChrome()) { + if (!CallNP_GetEntryPoints(error)) { + return NS_ERROR_FAILURE; + } + else if (*error != NPERR_NO_ERROR) { + return NS_OK; + } } + *error = NPERR_NO_ERROR; SetPluginFuncs(pFuncs); return NS_OK; } #endif nsresult PluginModuleParent::NPP_New(NPMIMEType pluginType, NPP instance, @@ -1342,19 +1466,25 @@ PluginModuleParent::NPP_New(NPMIMEType p return NS_ERROR_FAILURE; } if (*error != NPERR_NO_ERROR) { NPP_Destroy(instance, 0); return NS_ERROR_FAILURE; } + UpdatePluginTimeout(); + + return NS_OK; +} + +void +PluginModuleChromeParent::UpdatePluginTimeout() +{ TimeoutChanged(kParentTimeoutPref, this); - - return NS_OK; } nsresult PluginModuleParent::NPP_ClearSiteData(const char* site, uint64_t flags, uint64_t maxAge) { if (!mClearSiteDataSupported) return NS_ERROR_NOT_AVAILABLE; @@ -1523,25 +1653,38 @@ PluginModuleParent::RecvPluginHideWindow return false; #endif } PCrashReporterParent* PluginModuleParent::AllocPCrashReporterParent(mozilla::dom::NativeThreadId* id, uint32_t* processType) { + MOZ_CRASH("unreachable"); +} + +bool +PluginModuleParent::DeallocPCrashReporterParent(PCrashReporterParent* actor) +{ + MOZ_CRASH("unreachable"); +} + +PCrashReporterParent* +PluginModuleChromeParent::AllocPCrashReporterParent(mozilla::dom::NativeThreadId* id, + uint32_t* processType) +{ #ifdef MOZ_CRASHREPORTER return new CrashReporterParent(); #else return nullptr; #endif } bool -PluginModuleParent::DeallocPCrashReporterParent(PCrashReporterParent* actor) +PluginModuleChromeParent::DeallocPCrashReporterParent(PCrashReporterParent* actor) { #ifdef MOZ_CRASHREPORTER #ifdef XP_WIN mozilla::MutexAutoLock lock(mCrashReporterMutex); if (actor == static_cast<PCrashReporterParent*>(mCrashReporter)) { mCrashReporter = nullptr; } #endif @@ -1617,42 +1760,44 @@ PluginModuleParent::RecvGetNativeCursors #else NS_NOTREACHED( "PluginInstanceParent::RecvGetNativeCursorSupportLevel not implemented!"); return false; #endif } bool -PluginModuleParent::RecvNPN_SetException(PPluginScriptableObjectParent* aActor, - const nsCString& aMessage) +PluginModuleParent::RecvNPN_SetException(const nsCString& aMessage) { PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION)); - NPObject* aNPObj = nullptr; - if (aActor) { - aNPObj = static_cast<PluginScriptableObjectParent*>(aActor)->GetObject(true); - if (!aNPObj) { - NS_ERROR("Failed to get object!"); - return false; - } - } - mozilla::plugins::parent::_setexception(aNPObj, NullableStringGet(aMessage)); + // This function ignores its first argument. + mozilla::plugins::parent::_setexception(nullptr, NullableStringGet(aMessage)); return true; } bool PluginModuleParent::RecvNPN_ReloadPlugins(const bool& aReloadPages) { PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION)); mozilla::plugins::parent::_reloadplugins(aReloadPages); return true; } +bool +PluginModuleChromeParent::RecvNotifyContentModuleDestroyed() +{ + nsRefPtr<nsPluginHost> host = nsPluginHost::GetInst(); + if (host) { + host->NotifyContentModuleDestroyed(mPluginId); + } + return true; +} + #ifdef MOZ_CRASHREPORTER_INJECTOR // We only add the crash reporter to subprocess which have the filename // FlashPlayerPlugin* #define FLASH_PROCESS_PREFIX "FLASHPLAYERPLUGIN" static DWORD GetFlashChildOfPID(DWORD pid, HANDLE snapshot) @@ -1673,17 +1818,17 @@ GetFlashChildOfPID(DWORD pid, HANDLE sna } return 0; } // We only look for child processes of the Flash plugin, NPSWF* #define FLASH_PLUGIN_PREFIX "NPSWF" void -PluginModuleParent::InitializeInjector() +PluginModuleChromeParent::InitializeInjector() { if (!Preferences::GetBool("dom.ipc.plugins.flash.subprocess.crashreporter.enabled", false)) return; nsCString path(Process()->GetPluginFilePath().c_str()); ToUpperCase(path); int32_t lastSlash = path.RFindCharInSet("\\/"); if (kNotFound == lastSlash) @@ -1705,17 +1850,17 @@ PluginModuleParent::InitializeInjector() mFlashProcess2 = GetFlashChildOfPID(mFlashProcess1, snapshot); if (mFlashProcess2) { InjectCrashReporterIntoProcess(mFlashProcess2, this); } } } void -PluginModuleParent::OnCrash(DWORD processID) +PluginModuleChromeParent::OnCrash(DWORD processID) { if (!mShutdown) { GetIPCChannel()->CloseWithError(); KillProcess(OtherProcess(), 1, false); } } #endif // MOZ_CRASHREPORTER_INJECTOR @@ -1751,26 +1896,26 @@ PluginProfilerObserver::Observe(nsISuppo if (success && !result.IsEmpty()) { pse->AddSubProfile(result.get()); } } return NS_OK; } void -PluginModuleParent::InitPluginProfiling() +PluginModuleChromeParent::InitPluginProfiling() { nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService(); if (observerService) { mProfilerObserver = new PluginProfilerObserver(this); observerService->AddObserver(mProfilerObserver, "profiler-subprocess", false); } } void -PluginModuleParent::ShutdownPluginProfiling() +PluginModuleChromeParent::ShutdownPluginProfiling() { nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService(); if (observerService) { observerService->RemoveObserver(mProfilerObserver, "profiler-subprocess"); } } #endif
--- a/dom/plugins/ipc/PluginModuleParent.h +++ b/dom/plugins/ipc/PluginModuleParent.h @@ -48,104 +48,75 @@ class PluginHangUIParent; * * This class implements the NPP API from the perspective of the rest * of Gecko, forwarding NPP calls along to the child process that is * actually running the plugin. * * This class /also/ implements a version of the NPN API, because the * child process needs to make these calls back into Gecko proper. * This class is responsible for "actually" making those function calls. + * + * If a plugin is running, there will always be one PluginModuleParent for it in + * the chrome process. In addition, any content process using the plugin will + * have its own PluginModuleParent. The subclasses PluginModuleChromeParent and + * PluginModuleContentParent implement functionality that is specific to one + * case or the other. */ class PluginModuleParent : public PPluginModuleParent , public PluginLibrary #ifdef MOZ_CRASHREPORTER_INJECTOR , public CrashReporter::InjectorCrashCallback #endif - , public mozilla::HangMonitor::Annotator { -private: +protected: typedef mozilla::PluginLibrary PluginLibrary; typedef mozilla::dom::PCrashReporterParent PCrashReporterParent; typedef mozilla::dom::CrashReporterParent CrashReporterParent; -protected: - PPluginInstanceParent* AllocPPluginInstanceParent(const nsCString& aMimeType, const uint16_t& aMode, const InfallibleTArray<nsCString>& aNames, const InfallibleTArray<nsCString>& aValues, NPError* rv) MOZ_OVERRIDE; virtual bool DeallocPPluginInstanceParent(PPluginInstanceParent* aActor) MOZ_OVERRIDE; public: - // aFilePath is UTF8, not native! - explicit PluginModuleParent(const char* aFilePath); + explicit PluginModuleParent(bool aIsChrome); virtual ~PluginModuleParent(); + bool IsChrome() const { return mIsChrome; } + virtual void SetPlugin(nsNPAPIPlugin* plugin) MOZ_OVERRIDE { mPlugin = plugin; } virtual void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE; - /** - * LoadModule - * - * This may or may not launch a plugin child process, - * and may or may not be very expensive. - */ - static PluginLibrary* LoadModule(const char* aFilePath); - const NPNetscapeFuncs* GetNetscapeFuncs() { return mNPNIface; } - PluginProcessParent* Process() const { return mSubprocess; } - base::ProcessHandle ChildProcessHandle() { return mSubprocess->GetChildProcessHandle(); } - bool OkToCleanup() const { return !IsOnCxxStack(); } void ProcessRemoteNativeEventsInInterruptCall(); - void TerminateChildProcess(MessageLoop* aMsgLoop); - - virtual void - EnteredCxxStack() MOZ_OVERRIDE; - - virtual void - ExitedCxxStack() MOZ_OVERRIDE; - - virtual void - AnnotateHang(mozilla::HangMonitor::HangAnnotations& aAnnotations) MOZ_OVERRIDE; - -#ifdef XP_WIN - /** - * Called by Plugin Hang UI to notify that the user has clicked continue. - * Used for chrome hang annotations. - */ - void - OnHangUIContinue(); -#endif // XP_WIN - protected: virtual mozilla::ipc::RacyInterruptPolicy MediateInterruptRace(const Message& parent, const Message& child) MOZ_OVERRIDE { return MediateRace(parent, child); } - virtual bool ShouldContinueFromReplyTimeout() MOZ_OVERRIDE; - virtual bool RecvBackUpXResources(const FileDescriptor& aXSocketFd) MOZ_OVERRIDE; virtual bool AnswerNPN_UserAgent(nsCString* userAgent) MOZ_OVERRIDE; virtual bool AnswerNPN_GetValue_WithBoolReturn(const NPNVariable& aVariable, @@ -182,37 +153,30 @@ protected: virtual bool RecvPopCursor() MOZ_OVERRIDE; virtual bool RecvGetNativeCursorsSupported(bool* supported) MOZ_OVERRIDE; virtual bool - RecvNPN_SetException(PPluginScriptableObjectParent* aActor, - const nsCString& aMessage) MOZ_OVERRIDE; + RecvNPN_SetException(const nsCString& aMessage) MOZ_OVERRIDE; virtual bool RecvNPN_ReloadPlugins(const bool& aReloadPages) MOZ_OVERRIDE; static PluginInstanceParent* InstCast(NPP instance); static BrowserStreamParent* StreamCast(NPP instance, NPStream* s); -private: - void SetPluginFuncs(NPPluginFuncs* aFuncs); +protected: + virtual void UpdatePluginTimeout() {} - // Implement the module-level functions from NPAPI; these are - // normally resolved directly from the DSO. -#ifdef OS_LINUX - NPError NP_Initialize(const NPNetscapeFuncs* npnIface, - NPPluginFuncs* nppIface); -#else - NPError NP_Initialize(const NPNetscapeFuncs* npnIface); - NPError NP_GetEntryPoints(NPPluginFuncs* nppIface); -#endif + virtual bool RecvNotifyContentModuleDestroyed() MOZ_OVERRIDE { return true; } + + void SetPluginFuncs(NPPluginFuncs* aFuncs); // NPP-like API that Gecko calls are trampolined into. These // messages then get forwarded along to the plugin instance, // and then eventually the child process. static NPError NPP_Destroy(NPP instance, NPSavedData** save); static NPError NPP_SetWindow(NPP instance, NPWindow* window); @@ -250,16 +214,17 @@ private: const nsIntRect& aRect) MOZ_OVERRIDE; #if defined(XP_UNIX) && !defined(XP_MACOSX) && !defined(MOZ_WIDGET_GONK) virtual nsresult NP_Initialize(NPNetscapeFuncs* bFuncs, NPPluginFuncs* pFuncs, NPError* error); #else virtual nsresult NP_Initialize(NPNetscapeFuncs* bFuncs, NPError* error); #endif virtual nsresult NP_Shutdown(NPError* error); + virtual nsresult NP_GetMIMEDescription(const char** mimeDesc); virtual nsresult NP_GetValue(void *future, NPPVariable aVariable, void *aValue, NPError* error); #if defined(XP_WIN) || defined(XP_MACOSX) virtual nsresult NP_GetEntryPoints(NPPluginFuncs* pFuncs, NPError* error); #endif virtual nsresult NPP_New(NPMIMEType pluginType, NPP instance, uint16_t mode, int16_t argc, char* argn[], @@ -269,54 +234,152 @@ private: uint64_t maxAge); virtual nsresult NPP_GetSitesWithData(InfallibleTArray<nsCString>& result); #if defined(XP_MACOSX) virtual nsresult IsRemoteDrawingCoreAnimation(NPP instance, bool *aDrawing); virtual nsresult ContentsScaleFactorChanged(NPP instance, double aContentsScaleFactor); #endif -private: - CrashReporterParent* CrashReporter(); - -#ifdef MOZ_CRASHREPORTER - void ProcessFirstMinidump(); - void WriteExtraDataForMinidump(CrashReporter::AnnotationTable& notes); -#endif - void CleanupFromTimeout(const bool aByHangUI); - void SetChildTimeout(const int32_t aChildTimeout); - static void TimeoutChanged(const char* aPref, void* aModule); +protected: void NotifyPluginCrashed(); -#ifdef MOZ_ENABLE_PROFILER_SPS - void InitPluginProfiling(); - void ShutdownPluginProfiling(); -#endif - - PluginProcessParent* mSubprocess; + bool mIsChrome; bool mShutdown; bool mClearSiteDataSupported; bool mGetSitesWithDataSupported; const NPNetscapeFuncs* mNPNIface; nsNPAPIPlugin* mPlugin; ScopedMethodFactory<PluginModuleParent> mTaskFactory; nsString mPluginDumpID; nsString mBrowserDumpID; nsString mHangID; nsRefPtr<nsIObserver> mProfilerObserver; + nsCString mPluginName; + nsCString mPluginVersion; + +#ifdef MOZ_X11 + // Dup of plugin's X socket, used to scope its resources to this + // object instead of the plugin process's lifetime + ScopedClose mPluginXSocketFdDup; +#endif + + bool + GetPluginDetails(nsACString& aPluginName, nsACString& aPluginVersion); + + friend class mozilla::dom::CrashReporterParent; +}; + +class PluginModuleContentParent : public PluginModuleParent +{ + public: + static PluginLibrary* LoadModule(uint32_t aPluginId); + + static PluginModuleContentParent* Create(mozilla::ipc::Transport* aTransport, + base::ProcessId aOtherProcess); + + private: + explicit PluginModuleContentParent(); + +#ifdef MOZ_CRASHREPORTER_INJECTOR + void OnCrash(DWORD processID) MOZ_OVERRIDE {} +#endif + + static PluginModuleContentParent* sSavedModuleParent; +}; + +class PluginModuleChromeParent + : public PluginModuleParent + , public mozilla::HangMonitor::Annotator +{ + public: + /** + * LoadModule + * + * This may or may not launch a plugin child process, + * and may or may not be very expensive. + */ + static PluginLibrary* LoadModule(const char* aFilePath, uint32_t aPluginId); + + virtual ~PluginModuleChromeParent(); + + void TerminateChildProcess(MessageLoop* aMsgLoop); + +#ifdef XP_WIN + /** + * Called by Plugin Hang UI to notify that the user has clicked continue. + * Used for chrome hang annotations. + */ + void + OnHangUIContinue(); +#endif // XP_WIN + +private: + virtual void + EnteredCxxStack() MOZ_OVERRIDE; + + void + ExitedCxxStack() MOZ_OVERRIDE; + + virtual void + AnnotateHang(mozilla::HangMonitor::HangAnnotations& aAnnotations) MOZ_OVERRIDE; + + virtual bool ShouldContinueFromReplyTimeout() MOZ_OVERRIDE; + +#ifdef MOZ_CRASHREPORTER + void ProcessFirstMinidump(); + void WriteExtraDataForMinidump(CrashReporter::AnnotationTable& notes); +#endif + + virtual PCrashReporterParent* + AllocPCrashReporterParent(mozilla::dom::NativeThreadId* id, + uint32_t* processType) MOZ_OVERRIDE; + virtual bool + DeallocPCrashReporterParent(PCrashReporterParent* actor) MOZ_OVERRIDE; + + PluginProcessParent* Process() const { return mSubprocess; } + base::ProcessHandle ChildProcessHandle() { return mSubprocess->GetChildProcessHandle(); } + +#if !defined(XP_UNIX) || defined(XP_MACOSX) || defined(MOZ_WIDGET_GONK) + virtual nsresult NP_Initialize(NPNetscapeFuncs* bFuncs, NPError* error); +#endif + + virtual void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE; + + // aFilePath is UTF8, not native! + explicit PluginModuleChromeParent(const char* aFilePath, uint32_t aPluginId); + + CrashReporterParent* CrashReporter(); + + void CleanupFromTimeout(const bool aByHangUI); + void SetChildTimeout(const int32_t aChildTimeout); + static void TimeoutChanged(const char* aPref, void* aModule); + + virtual void UpdatePluginTimeout() MOZ_OVERRIDE; + +#ifdef MOZ_ENABLE_PROFILER_SPS + void InitPluginProfiling(); + void ShutdownPluginProfiling(); +#endif + + virtual bool RecvNotifyContentModuleDestroyed() MOZ_OVERRIDE; + + PluginProcessParent* mSubprocess; + uint32_t mPluginId; + + ScopedMethodFactory<PluginModuleChromeParent> mChromeTaskFactory; + enum HangAnnotationFlags { kInPluginCall = (1u << 0), kHangUIShown = (1u << 1), kHangUIContinued = (1u << 2), kHangUIDontShow = (1u << 3) }; Atomic<uint32_t> mHangAnnotationFlags; - nsCString mPluginName; - nsCString mPluginVersion; #ifdef XP_WIN InfallibleTArray<float> mPluginCpuUsageOnHang; PluginHangUIParent *mHangUIParent; bool mHangUIEnabled; bool mIsTimerReset; #ifdef MOZ_CRASHREPORTER /** * This mutex protects the crash reporter when the Plugin Hang UI event @@ -344,25 +407,16 @@ private: /** * Finishes the Plugin Hang UI and cancels if it is being shown to the user. */ void FinishHangUI(); #endif - bool - GetPluginDetails(nsACString& aPluginName, nsACString& aPluginVersion); - -#ifdef MOZ_X11 - // Dup of plugin's X socket, used to scope its resources to this - // object instead of the plugin process's lifetime - ScopedClose mPluginXSocketFdDup; -#endif - friend class mozilla::dom::CrashReporterParent; #ifdef MOZ_CRASHREPORTER_INJECTOR void InitializeInjector(); void OnCrash(DWORD processID) MOZ_OVERRIDE; DWORD mFlashProcess1;
--- a/dom/plugins/ipc/PluginProcessChild.cpp +++ b/dom/plugins/ipc/PluginProcessChild.cpp @@ -114,19 +114,19 @@ PluginProcessChild::Init() # error Sorry #endif if (NS_FAILED(nsRegion::InitStatic())) { NS_ERROR("Could not initialize nsRegion"); return false; } - return mPlugin.Init(pluginFilename, ParentHandle(), - IOThreadChild::message_loop(), - IOThreadChild::channel()); + return mPlugin.InitForChrome(pluginFilename, ParentHandle(), + IOThreadChild::message_loop(), + IOThreadChild::channel()); } void PluginProcessChild::CleanUp() { #ifdef XP_WIN ::OleUninitialize(); #endif
--- a/dom/plugins/ipc/PluginProcessChild.h +++ b/dom/plugins/ipc/PluginProcessChild.h @@ -14,17 +14,18 @@ namespace mozilla { namespace plugins { //----------------------------------------------------------------------------- class PluginProcessChild : public mozilla::ipc::ProcessChild { protected: typedef mozilla::ipc::ProcessChild ProcessChild; public: - explicit PluginProcessChild(ProcessHandle aParentHandle) : ProcessChild(aParentHandle) + explicit PluginProcessChild(ProcessHandle aParentHandle) + : ProcessChild(aParentHandle), mPlugin(true) { } virtual ~PluginProcessChild() { } virtual bool Init() MOZ_OVERRIDE; virtual void CleanUp() MOZ_OVERRIDE;
--- a/dom/plugins/ipc/PluginScriptableObjectUtils-inl.h +++ b/dom/plugins/ipc/PluginScriptableObjectUtils-inl.h @@ -85,17 +85,17 @@ mozilla::plugins::ConvertToVariant(const npn->retainobject(object); OBJECT_TO_NPVARIANT(object, aVariant); break; } case Variant::TPPluginScriptableObjectChild: { NS_ASSERTION(!aInstance, "No instance should be given!"); - NS_ASSERTION(PluginModuleChild::current(), + NS_ASSERTION(XRE_GetProcessType() == GeckoProcessType_Plugin, "Should be running on child only!"); NPObject* object = NPObjectFromVariant(aRemoteVariant); NS_ASSERTION(object, "Null object?!"); PluginModuleChild::sBrowserFuncs.retainobject(object); OBJECT_TO_NPVARIANT(object, aVariant); break;
--- a/dom/plugins/ipc/PluginScriptableObjectUtils.h +++ b/dom/plugins/ipc/PluginScriptableObjectUtils.h @@ -107,17 +107,17 @@ ReleaseRemoteVariant(Variant& aVariant) const_cast<PluginScriptableObjectParent*>( reinterpret_cast<const PluginScriptableObjectParent*>( aVariant.get_PPluginScriptableObjectParent())); actor->Unprotect(); break; } case Variant::TPPluginScriptableObjectChild: { - NS_ASSERTION(PluginModuleChild::current(), + NS_ASSERTION(XRE_GetProcessType() == GeckoProcessType_Plugin, "Should only be running in the child!"); PluginScriptableObjectChild* actor = const_cast<PluginScriptableObjectChild*>( reinterpret_cast<const PluginScriptableObjectChild*>( aVariant.get_PPluginScriptableObjectChild())); actor->Unprotect(); break; }
--- a/dom/plugins/ipc/PluginTypes.ipdlh +++ b/dom/plugins/ipc/PluginTypes.ipdlh @@ -1,16 +1,32 @@ /* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */ /* 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/. */ namespace mozilla { namespace plugins { +struct PluginTag +{ + uint32_t id; + nsCString name; + nsCString description; + nsCString[] mimeTypes; + nsCString[] mimeDescriptions; + nsCString[] extensions; + bool isJavaPlugin; + bool isFlashPlugin; + nsCString filename; + nsCString version; + int64_t lastModifiedTime; + bool isFromExtension; +}; + union PluginIdentifier { nsCString; int32_t; }; } // namespace plugins } // namespace mozilla
--- a/dom/plugins/ipc/moz.build +++ b/dom/plugins/ipc/moz.build @@ -16,16 +16,17 @@ EXPORTS.mozilla.plugins += [ 'BrowserStreamChild.h', 'BrowserStreamParent.h', 'ChildAsyncCall.h', 'ChildTimer.h', 'NPEventAndroid.h', 'NPEventOSX.h', 'NPEventUnix.h', 'NPEventWindows.h', + 'PluginBridge.h', 'PluginInstanceChild.h', 'PluginInstanceParent.h', 'PluginMessageUtils.h', 'PluginModuleChild.h', 'PluginModuleParent.h', 'PluginProcessChild.h', 'PluginProcessParent.h', 'PluginScriptableObjectChild.h',
--- a/dom/plugins/test/mochitest/mochitest.ini +++ b/dom/plugins/test/mochitest/mochitest.ini @@ -1,10 +1,10 @@ [DEFAULT] -skip-if = ((buildapp == 'mulet' || buildapp == 'b2g') && toolkit != 'gonk') || e10s #b2g-desktop(tests that use plugins) +skip-if = ((buildapp == 'mulet' || buildapp == 'b2g') && toolkit != 'gonk') || (e10s && debug) #b2g-desktop(tests that use plugins) support-files = 307-xo-redirect.sjs crashing_subpage.html file_bug738396.html file_bug771202.html file_bug863792.html large-pic.jpg loremipsum.txt @@ -58,35 +58,35 @@ skip-if = (!crashreporter) || true # Bug [test_CrashService_crash.html] skip-if = !crashreporter || e10s [test_CrashService_hang.html] skip-if = !crashreporter || e10s [test_defaultValue.html] [test_enumerate.html] [test_fullpage.html] [test_getauthenticationinfo.html] +skip-if = e10s [test_hanging.html] -skip-if = !crashreporter +skip-if = !crashreporter || e10s [test_instance_re-parent.html] [test_instance_unparent1.html] [test_instance_unparent2.html] [test_instance_unparent3.html] [test_instantiation.html] [test_mixed_case_mime.html] [test_multipleinstanceobjects.html] [test_newstreamondestroy.html] [test_npn_asynccall.html] [test_npn_timers.html] [test_npobject_getters.html] [test_npruntime_construct.html] [test_npruntime_identifiers.html] [test_npruntime_npnevaluate.html] [test_npruntime_npninvoke.html] [test_npruntime_npninvokedefault.html] -[test_npruntime_npnsetexception.html] [test_painting.html] [test_plugin_scroll_painting.html] skip-if = true # Bug 596491 [test_pluginstream_asfile.html] [test_pluginstream_asfileonly.html] [test_pluginstream_err.html] [test_pluginstream_geturl.html] [test_pluginstream_geturlnotify.html] @@ -103,15 +103,16 @@ skip-if = true # Bug 596491 skip-if = true # disabled due to oddness, perhaps scrolling of the mochitest window? [test_propertyAndMethod.html] [test_queryContentsScaleFactor.html] skip-if = toolkit != "cocoa" [test_redirect_handling.html] [test_secondPlugin.html] [test_src_url_change.html] [test_streamNotify.html] +skip-if = e10s [test_streamatclose.html] [test_twostreams.html] [test_windowed_invalidate.html] skip-if = os != "win" [test_visibility.html] skip-if = toolkit == "cocoa" [test_zero_opacity.html]
--- a/dom/plugins/test/mochitest/test_instance_re-parent.html +++ b/dom/plugins/test/mochitest/test_instance_re-parent.html @@ -5,17 +5,17 @@ <script type="text/javascript" src="/MochiKit/packed.js"></script> <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> <script type="text/javascript" src="utils.js"></script> </head> <body onload="begin()"> <script type="application/javascript;version=1.8"> SimpleTest.waitForExplicitFinish(); - getTestPlugin().enabledState = SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED; + setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED); var exceptionThrown = false; var p = null; var d1 = null; var d2 = null; var destroyed = false;
deleted file mode 100644 --- a/dom/plugins/test/mochitest/test_npruntime_npnsetexception.html +++ /dev/null @@ -1,59 +0,0 @@ -<html> -<head> - <title>NPN_SetException Tests</title> - <script type="text/javascript" - src="/tests/SimpleTest/SimpleTest.js"></script> - <script type="text/javascript" src="utils.js"></script> - <link rel="stylesheet" type="text/css" - href="/tests/SimpleTest/test.css" /> -</head> -<body onload="runTests()"> - <script class="testbody" type="application/javascript"> - SimpleTest.waitForExplicitFinish(); - setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED); - - function runTests() { - // test a single exception thrown in scriptable invoke - var plugin = document.getElementById("plugin1"); - plugin.throwExceptionNextInvoke(); - try { - plugin.npnInvokeTest("badFunction"); - ok(false, "exception not thrown"); - } - catch (e) { - is(e, "badFunction", "wrong exception thrown"); - } - - // test multiple exceptions thrown in scriptable invokedefault - plugin.throwExceptionNextInvoke(); - try { - plugin("first exception", "second exception"); - ok(false, "exception not thrown"); - } - catch (e) { - is(e, "second exception", "wrong exception thrown"); - } - - // test calling exception with NULL message - plugin.throwExceptionNextInvoke(); - try { - plugin(); - ok(false, "exception not thrown"); - } - catch (e) { - is(e.message, "Error calling method on NPObject!", "wrong exception thrown"); - } - - SimpleTest.finish(); - } - </script> - - <p id="display"></p> - - <embed id="plugin1" type="application/x-test" width="400" height="100"> - </embed> - - <div id="verbose"> - </div> - </body> - </html>
--- a/dom/plugins/test/mochitest/test_secondPlugin.html +++ b/dom/plugins/test/mochitest/test_secondPlugin.html @@ -38,35 +38,36 @@ function run() { // Add "Test Plug-in" (but not "Second Test Plug-in") to the list of // unhidden plugins. This test must modify the "plugins.enumerable_names" // pref BEFORE accessing the navigator.plugins or navigator.mimeTypes // arrays because they only read the pref when they first initialize // their internal arrays! var prefs = SpecialPowers.Cc["@mozilla.org/preferences-service;1"].getService(SpecialPowers.Ci.nsIPrefBranch); var defaultEnumerableNamesPref = prefs.getCharPref("plugins.enumerable_names"); - prefs.setCharPref("plugins.enumerable_names", defaultEnumerableNamesPref + ",Test Plug-in"); - + SpecialPowers.pushPrefEnv( + {'set': [["plugins.enumerable_names", defaultEnumerableNamesPref + ",Test Plug-in"]]}, + finishRun + ); + } + function finishRun() { var pluginElement = document.getElementById("plugin"); is(pluginElement.identifierToStringTest("foo"), "foo", "Should be able to call a function provided by the plugin"); ok(navigator.plugins["Test Plug-in"], "Should have queried a non-hidden plugin named 'Test Plug-in'"); ok(navigator.plugins["Second Test Plug-in"], "Should have queried a hidden plugin named 'Test Plug-in'"); ok(findPlugin("Test Plug-in"), "Should have found a non-hidden plugin named 'Test Plug-in'"); ok(!findPlugin("Second Test Plug-in"), "Should NOT found a hidden plugin named 'Test Plug-in'"); ok(navigator.mimeTypes["application/x-test"], "Should have queried a non-hidden MIME type named 'application/x-test'"); ok(navigator.mimeTypes["application/x-second-test"], "Should have queried a MIME type named 'application/x-second-test'"); ok(findMimeType("application/x-test"), "Should have found a non-hidden MIME type named 'application/x-test'"); ok(!findMimeType("application/x-second-test"), "Should NOT have found a MIME type named 'application/x-second-test'"); - // Restore original pref to hide "Test Plug-in" and "Second Test Plug-in". - prefs.setCharPref("plugins.enumerable_names", defaultEnumerableNamesPref); - SimpleTest.finish(); } </script> <object id="plugin" type="application/x-second-test" width=200 height=200></object> </body> </html>