mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 14:30:27 +00:00
e974f749ca
- Bug 1248105 - Move CSSPseudoElement::GetParentObject out of header, r=boris (b851c3806d) - Bug 1248581 - Fix build bustage on WakeLock.h and CSSPseudoElement.h. r=khuey (fb47a96889) - Bug 1249230 - Prepend an extra colon to the pseudo type string. r=birtles (6af63f00de) - fix build on Mac > 10.5 (1ae9402eaf) - Bug 1241118 - Add gc = GetGeneralCategory(ch) when sc == MOZ_SCRIPT_COMMON. r=jfkthame (51dfbede47) - minor crash rep (1e7e5bc97e) - Bug 1240904 - Remove ParamTraits for NPString and NPVariant. r=bsmedberg (1850d10374) - bug 1170584 - fix PluginMessageUtils on iOS. r=jimm (c624cbe92d) - Bug 1170343 - Use release-mode asserts when plugins making NPAPI calls on the wrong thread, r=mccr8 (15c71250f4) - Bug 1239525 - Make sure async plugin initialization is off if e10s is on. r=aklotz (07e73ce74d) - Bug 1128454 - When plugin bridging fails, propagate the error back to the content process without aborting tabs. r=billm (e1547c00ab) - Bug 1222169 - remove unused function from PluginProcessChild.cpp; r=aklotz (11a8fc32e1) - Bug 1239749 - Remove extra space from PermissionSettings.js debug message. r=gwagner (abe2ac7574) - Bug 1221104 - Revoke 'midi' permission queries with TypeError. r=baku (cce673498f) - Bug 1221104 - Throw NS_ERROR_NOT_IMPLEMENTED instead of NS_ERROR_UNEXPECTED for PushPermissionDescriptor.userVisible. r=baku (face32ed7b) - Bug 1228723 - Avoid a promise worker proxy deadlock caused by synchronous Push callbacks. r=catalinb (bd564a0483) - Bug 1191931, Part 1 - Use tasks in the Push subscription tests. r=mt (3109fdf2af) - Bug 1191931, Part 2 - Test resubscribing from a worker. r=m (9527fa2672) - Bug 1243781 - Push methods called from a worker should reject with a `DOMException`. r=mt (f66818b161) - Bug 1219064 - Add test for extendable pushsubscriptionchange event. r=mt (12af4b59dd) - Bug 1176449 - Enter an update here to avoid assertions on startup. r=dbaron (1109cce81d) - Bug 1239743: Do not allow windows to be resized to sizes above the maximum texture size. We don't know how to draw to these anyway. r=jimm (694dafd544) - Remove some static_casting in CompositorParent. (bug 1245765 part 5, r=mattwoodrow) (4c7f39b18e) - Bug 1133615 - Don't assert about the contents of RestyleData::mDescendants when in a full style rebuild. r=dbaron (2b071b90d2) - Bug 1237902 (part 1) - Remove unneeded gfxContext ops in DrawTableBorderSegment(). r=roc. (8490ab67ca) - Bug 1237902 (part 2) - Pass a DrawTarget to DrawTableBorderSegment(). r=roc. (c602535af0) - Bug 1237902 (part 3) - Change the gfxContextMatrixAutoSaveRestore in PaintTableBorderBackground() to an AutoRestoreTransform. r=roc. (0b2412e7d6) - Bug 1242164 - Remove the implementation of colspan=0 (which is now dead code). r=dbaron (88555a04a4) - Bug 1235478 - Part 1: Rename eAdjustingTimer to eForceAdjustTimer. r=mchang (113a98fa28) - Bug 1235478 - Part 2: Don't update mMostRecentRefresh when nsRefreshDriver::ScheduleViewManagerFlush is called. r=mchang (7fd8599b7b) - Bug 1235478 - Part 3: Make weightmapping-12579.html fuzzy on MacOS 10.10. r=jdaggett (559683f95d) - Bug 1234049 - Ensure we always invalidate new PresShells that are created for an inactive DocShell upon reactivating them. r=smaug (aeca3e08d9) - Re-backout c216ff19d690 (bug 1059014 part 3) because the removed code is less dead than it first appears. (2cce434c50) - Bug 1191451 - Adding color to the top of tabs to indicate user context of tab. r=paolo (5b6af5eeff) - missing bit of Bug 1049551 - Add a tab delay spinner (dc36f8ae80) - Bug 1108047 - Use instanceof instead of QueryInterface to reduce debugging exception noise. r=gavin (924efeb129) - bring to level of ug B1213438 - Don't change tab title to Connecting# henever a page issues a network request. (21b2f70480) - missing bits of Bug 1171245 - Include changes attributes in TabAttrModified event (r=ttaubert) (1292a34bed) - Bug 894381 - FindBar buttons should be enabled when moving tab between windows. r=dao (e826cb3884) - Bug 1194311 - Reusing dnd panel and canvas. r=jimm (8c63a187ba) - Bug 1125532 - Correctly clear out removing tabs in tab container's "underflow" event handler. r=dao (99e22ec9c3) - Bug 1156592 - Record tab switch times under e10s and add support for debug console logging tied to a hidden pref. r=billm (face91fb63) - Bug 1192720 - prevent re-entrancy in event handling in tabbrowser.xml's _switcher, r=billm (12c21d7314) - Bug 980231 - MRU tab order is broken in some cases. r=dao (fc1f6056f2) - Bug 1238636 - run visual selection code in e10s when the selection is the same as the previous selection (so the tab switcher wouldn't otherwise run), r=mconley (8530b9f125) - Bug 1014313 - part 2: add undo close tab item to alltabs-button menu, r=dao (14de7095bd) - Bug 1244496: Add more detail to TabOpen/TabClose events for tabs moved between windows. r=Gijs (db95c45b52) - Bug 1214624 - tabBrowser should remove the audio icon when location changes, r=jaws, r=mak77 (fc25fbc5aa) - Bug 1231364 - the audio icon in tabs should be removed only if the location changes for real, r=ehsan (ff8c7f15a1) - Bug 1231364 - tabBrowser should not remove the audio icon if the URI doesn't change in onLocationChange, r=ehsan (4dc9b38452) - Bug 408415 - fix favicon disappearance for hash changes during load, r=jaws (d2685508eb) - Bug 1242013 - Record tab cache position in telemetry (r=mconley) (38e798f9a7) - bits of Bug 691610 - e10s support for useDefaultIcon. (fdbaf6cc9a) - Bug 1215659. Use destination canvas for thumbnail size if provided one. r=adw (f538b0cb20) - Bug 1231518. Load about:blank if thumbnail service fails. r=adw (1ad0d3dacf) - Bug 1096804 - Add support to PageThumbs for requesting non-downsampled content screenshots. r=dao (ef74661c71) - Bug 1148961 - Add missing QI in thumbnail code (r=markh) (6368229671) - Bug 1246115 - Make gSafeBrowsing set the phishing menu item correctly. r=Gijs (3b8a66c578) - Bug 1014313 - part 1: add tabs options to tabstrip context menu, r=dao (32688846e1) - Bug 1014313 - part 3: add labels specifically for this context menu, r=dao (5e4d9b5678) - fix (d37b662751) - Bug 1233803 - Register RemoteWebNavigation as a standard js-implemented XPCOM component. r=felipe (b6eee3873a) - Bug 1249439 - xul:browser adds itself as a strong observer to observer service, r=gijs (dde3e5f001) - misspatch (3d4f472402) - Bug 1233803 - Outlaw usage of sessionHistory CPOW in browser code. r=felipe (bc7c0aedad) - Bug 1123422, don't adjust the focus when opening the autoscroll popup, r=dao (4c7845552b) - Bug 1245813: add documentation to explain more about what swapDocShells does and, most importantly, does not do with respect to messageManagers. r=smaug (d1ed6116c1) - Bug 1109875 - Make browser.permanentKey a property of the XUL element instead of a property of the XUL binding r=billm (1b2561efd6) - Bug 947353 - Use proper plural form for tabs.closeWarningMultipleTabs. r=dao (4737d852a0) - Bug 879597: fix test failures. r=dao (5eeeeefc3e) - Bug 1190903 - Make swapped browser docshell active (r=Gijs) (0fb743fcfd) - Bug 1241837 - Use proxy for browsers property in tabbrowser instead of explicit array. r=daxo (32b662f5de) - Bug 1166351 - Add instrumentation to try to figure out why tabbrowser-remote-browser binding is sometimes removed. r=felipex. (58016f82c6) - Bug 919030 - Status panel should track the mouse position over the content area, not across the whole window width (e.g. it shouldn't move when moving the mouse over the bottom of the bookmarks or history sidebar). r=mdeboer (4f5aac63b0)
3299 lines
99 KiB
C++
Executable File
3299 lines
99 KiB
C++
Executable File
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
* vim: sw=4 ts=4 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/. */
|
|
|
|
#ifdef MOZ_WIDGET_QT
|
|
#include "PluginHelperQt.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/GeckoChildProcessHost.h"
|
|
#include "mozilla/ipc/MessageChannel.h"
|
|
#include "mozilla/plugins/BrowserStreamParent.h"
|
|
#include "mozilla/plugins/PluginAsyncSurrogate.h"
|
|
#include "mozilla/plugins/PluginBridge.h"
|
|
#include "mozilla/plugins/PluginInstanceParent.h"
|
|
#include "mozilla/Preferences.h"
|
|
#ifdef MOZ_ENABLE_PROFILER_SPS
|
|
#include "mozilla/ProfileGatherer.h"
|
|
#endif
|
|
#include "mozilla/ProcessHangMonitor.h"
|
|
#include "mozilla/Services.h"
|
|
#include "mozilla/Telemetry.h"
|
|
#include "mozilla/unused.h"
|
|
#include "nsAutoPtr.h"
|
|
#include "nsCRT.h"
|
|
#include "nsIFile.h"
|
|
#include "nsIObserverService.h"
|
|
#include "nsIXULRuntime.h"
|
|
#include "nsNPAPIPlugin.h"
|
|
#include "nsPrintfCString.h"
|
|
#include "prsystem.h"
|
|
#include "PluginQuirks.h"
|
|
#include "GeckoProfiler.h"
|
|
#include "nsPluginTags.h"
|
|
#include "nsUnicharUtils.h"
|
|
#include "mozilla/layers/TextureClientRecycleAllocator.h"
|
|
|
|
#ifdef XP_WIN
|
|
#include "mozilla/plugins/PluginSurfaceParent.h"
|
|
#include "mozilla/widget/AudioSession.h"
|
|
#include "PluginHangUIParent.h"
|
|
#endif
|
|
|
|
#ifdef MOZ_ENABLE_PROFILER_SPS
|
|
#include "nsIProfiler.h"
|
|
#include "nsIProfileSaveEvent.h"
|
|
#endif
|
|
|
|
#ifdef MOZ_WIDGET_GTK
|
|
#include <glib.h>
|
|
#elif XP_MACOSX
|
|
#include "PluginInterposeOSX.h"
|
|
#include "PluginUtilsOSX.h"
|
|
#endif
|
|
|
|
using base::KillProcess;
|
|
|
|
using mozilla::PluginLibrary;
|
|
#ifdef MOZ_ENABLE_PROFILER_SPS
|
|
using mozilla::ProfileGatherer;
|
|
#endif
|
|
using mozilla::ipc::MessageChannel;
|
|
using mozilla::ipc::GeckoChildProcessHost;
|
|
using mozilla::dom::PCrashReporterParent;
|
|
using mozilla::dom::CrashReporterParent;
|
|
|
|
using namespace mozilla;
|
|
using namespace mozilla::plugins;
|
|
using namespace mozilla::plugins::parent;
|
|
|
|
#ifdef MOZ_CRASHREPORTER
|
|
#include "mozilla/dom/CrashReporterParent.h"
|
|
|
|
using namespace CrashReporter;
|
|
#endif
|
|
|
|
static const char kContentTimeoutPref[] = "dom.ipc.plugins.contentTimeoutSecs";
|
|
static const char kChildTimeoutPref[] = "dom.ipc.plugins.timeoutSecs";
|
|
static const char kParentTimeoutPref[] = "dom.ipc.plugins.parentTimeoutSecs";
|
|
static const char kLaunchTimeoutPref[] = "dom.ipc.plugins.processLaunchTimeoutSecs";
|
|
static const char kAsyncInitPref[] = "dom.ipc.plugins.asyncInit.enabled";
|
|
#ifdef XP_WIN
|
|
static const char kHangUITimeoutPref[] = "dom.ipc.plugins.hangUITimeoutSecs";
|
|
static const char kHangUIMinDisplayPref[] = "dom.ipc.plugins.hangUIMinDisplaySecs";
|
|
#define CHILD_TIMEOUT_PREF kHangUITimeoutPref
|
|
#else
|
|
#define CHILD_TIMEOUT_PREF kChildTimeoutPref
|
|
#endif
|
|
|
|
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,
|
|
bool aForceBridgeNow,
|
|
nsresult* rv,
|
|
uint32_t* runID)
|
|
{
|
|
PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER);
|
|
if (NS_WARN_IF(!rv) || NS_WARN_IF(!runID)) {
|
|
return false;
|
|
}
|
|
|
|
PluginModuleChromeParent::ClearInstantiationFlag();
|
|
RefPtr<nsPluginHost> host = nsPluginHost::GetInst();
|
|
RefPtr<nsNPAPIPlugin> plugin;
|
|
*rv = host->GetPluginForContentProcess(aPluginId, getter_AddRefs(plugin));
|
|
if (NS_FAILED(*rv)) {
|
|
return true;
|
|
}
|
|
PluginModuleChromeParent* chromeParent = static_cast<PluginModuleChromeParent*>(plugin->GetLibrary());
|
|
*rv = chromeParent->GetRunID(runID);
|
|
if (NS_FAILED(*rv)) {
|
|
return true;
|
|
}
|
|
chromeParent->SetContentParent(aContentParent);
|
|
if (!aForceBridgeNow && chromeParent->IsStartingAsync() &&
|
|
PluginModuleChromeParent::DidInstantiate()) {
|
|
// We'll handle the bridging asynchronously
|
|
return true;
|
|
}
|
|
*rv = PPluginModule::Bridge(aContentParent, chromeParent);
|
|
return true;
|
|
}
|
|
|
|
#ifdef MOZ_CRASHREPORTER_INJECTOR
|
|
|
|
/**
|
|
* Use for executing CreateToolhelp32Snapshot off main thread
|
|
*/
|
|
class mozilla::plugins::FinishInjectorInitTask : public CancelableTask
|
|
{
|
|
public:
|
|
FinishInjectorInitTask()
|
|
: mMutex("FlashInjectorInitTask::mMutex")
|
|
, mParent(nullptr)
|
|
, mMainThreadMsgLoop(MessageLoop::current())
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
}
|
|
|
|
void Init(PluginModuleChromeParent* aParent)
|
|
{
|
|
MOZ_ASSERT(aParent);
|
|
mParent = aParent;
|
|
}
|
|
|
|
void PostToMainThread()
|
|
{
|
|
mSnapshot.own(CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0));
|
|
bool deleteThis = false;
|
|
{ // Scope for lock
|
|
mozilla::MutexAutoLock lock(mMutex);
|
|
if (mMainThreadMsgLoop) {
|
|
mMainThreadMsgLoop->PostTask(FROM_HERE, this);
|
|
} else {
|
|
deleteThis = true;
|
|
}
|
|
}
|
|
if (deleteThis) {
|
|
delete this;
|
|
}
|
|
}
|
|
|
|
void Run() override
|
|
{
|
|
mParent->DoInjection(mSnapshot);
|
|
// We don't need to hold this lock during DoInjection, but we do need
|
|
// to obtain it before returning from Run() to ensure that
|
|
// PostToMainThread has completed before we return.
|
|
mozilla::MutexAutoLock lock(mMutex);
|
|
}
|
|
|
|
void Cancel() override
|
|
{
|
|
mozilla::MutexAutoLock lock(mMutex);
|
|
mMainThreadMsgLoop = nullptr;
|
|
}
|
|
|
|
private:
|
|
mozilla::Mutex mMutex;
|
|
nsAutoHandle mSnapshot;
|
|
PluginModuleChromeParent* mParent;
|
|
MessageLoop* mMainThreadMsgLoop;
|
|
};
|
|
|
|
#endif // MOZ_CRASHREPORTER_INJECTOR
|
|
|
|
namespace {
|
|
|
|
/**
|
|
* Objects of this class remain linked until either an error occurs in the
|
|
* plugin initialization sequence, or until
|
|
* PluginModuleContentParent::OnLoadPluginResult has completed executing.
|
|
*/
|
|
class PluginModuleMapping : public PRCList
|
|
{
|
|
public:
|
|
explicit PluginModuleMapping(uint32_t aPluginId, bool aAllowAsyncInit)
|
|
: mPluginId(aPluginId)
|
|
, mAllowAsyncInit(aAllowAsyncInit)
|
|
, mProcessIdValid(false)
|
|
, mModule(nullptr)
|
|
, mChannelOpened(false)
|
|
{
|
|
MOZ_COUNT_CTOR(PluginModuleMapping);
|
|
PR_INIT_CLIST(this);
|
|
PR_APPEND_LINK(this, &sModuleListHead);
|
|
}
|
|
|
|
~PluginModuleMapping()
|
|
{
|
|
PR_REMOVE_LINK(this);
|
|
MOZ_COUNT_DTOR(PluginModuleMapping);
|
|
}
|
|
|
|
bool
|
|
IsChannelOpened() const
|
|
{
|
|
return mChannelOpened;
|
|
}
|
|
|
|
void
|
|
SetChannelOpened()
|
|
{
|
|
mChannelOpened = true;
|
|
}
|
|
|
|
PluginModuleContentParent*
|
|
GetModule()
|
|
{
|
|
if (!mModule) {
|
|
mModule = new PluginModuleContentParent(mAllowAsyncInit);
|
|
}
|
|
return mModule;
|
|
}
|
|
|
|
static PluginModuleMapping*
|
|
AssociateWithProcessId(uint32_t aPluginId, base::ProcessId aProcessId)
|
|
{
|
|
PluginModuleMapping* mapping =
|
|
static_cast<PluginModuleMapping*>(PR_NEXT_LINK(&sModuleListHead));
|
|
while (mapping != &sModuleListHead) {
|
|
if (mapping->mPluginId == aPluginId) {
|
|
mapping->AssociateWithProcessId(aProcessId);
|
|
return mapping;
|
|
}
|
|
mapping = static_cast<PluginModuleMapping*>(PR_NEXT_LINK(mapping));
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
static PluginModuleMapping*
|
|
Resolve(base::ProcessId aProcessId)
|
|
{
|
|
PluginModuleMapping* mapping = nullptr;
|
|
|
|
if (sIsLoadModuleOnStack) {
|
|
// Special case: If loading synchronously, we just need to access
|
|
// the tail entry of the list.
|
|
mapping =
|
|
static_cast<PluginModuleMapping*>(PR_LIST_TAIL(&sModuleListHead));
|
|
MOZ_ASSERT(mapping);
|
|
return mapping;
|
|
}
|
|
|
|
mapping =
|
|
static_cast<PluginModuleMapping*>(PR_NEXT_LINK(&sModuleListHead));
|
|
while (mapping != &sModuleListHead) {
|
|
if (mapping->mProcessIdValid && mapping->mProcessId == aProcessId) {
|
|
return mapping;
|
|
}
|
|
mapping = static_cast<PluginModuleMapping*>(PR_NEXT_LINK(mapping));
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
static PluginModuleMapping*
|
|
FindModuleByPluginId(uint32_t aPluginId)
|
|
{
|
|
PluginModuleMapping* mapping =
|
|
static_cast<PluginModuleMapping*>(PR_NEXT_LINK(&sModuleListHead));
|
|
while (mapping != &sModuleListHead) {
|
|
if (mapping->mPluginId == aPluginId) {
|
|
return mapping;
|
|
}
|
|
mapping = static_cast<PluginModuleMapping*>(PR_NEXT_LINK(mapping));
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
static bool
|
|
IsLoadModuleOnStack()
|
|
{
|
|
return sIsLoadModuleOnStack;
|
|
}
|
|
|
|
class MOZ_RAII NotifyLoadingModule
|
|
{
|
|
public:
|
|
explicit NotifyLoadingModule(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM)
|
|
{
|
|
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
|
PluginModuleMapping::sIsLoadModuleOnStack = true;
|
|
}
|
|
|
|
~NotifyLoadingModule()
|
|
{
|
|
PluginModuleMapping::sIsLoadModuleOnStack = false;
|
|
}
|
|
|
|
private:
|
|
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
|
|
};
|
|
|
|
private:
|
|
void
|
|
AssociateWithProcessId(base::ProcessId aProcessId)
|
|
{
|
|
MOZ_ASSERT(!mProcessIdValid);
|
|
mProcessId = aProcessId;
|
|
mProcessIdValid = true;
|
|
}
|
|
|
|
uint32_t mPluginId;
|
|
bool mAllowAsyncInit;
|
|
bool mProcessIdValid;
|
|
base::ProcessId mProcessId;
|
|
PluginModuleContentParent* mModule;
|
|
bool mChannelOpened;
|
|
|
|
friend class NotifyLoadingModule;
|
|
|
|
static PRCList sModuleListHead;
|
|
static bool sIsLoadModuleOnStack;
|
|
};
|
|
|
|
PRCList PluginModuleMapping::sModuleListHead =
|
|
PR_INIT_STATIC_CLIST(&PluginModuleMapping::sModuleListHead);
|
|
|
|
bool PluginModuleMapping::sIsLoadModuleOnStack = false;
|
|
|
|
} // namespace
|
|
|
|
void
|
|
mozilla::plugins::TerminatePlugin(uint32_t aPluginId,
|
|
const nsCString& aMonitorDescription,
|
|
const nsAString& aBrowserDumpId)
|
|
{
|
|
MOZ_ASSERT(XRE_IsParentProcess());
|
|
|
|
RefPtr<nsPluginHost> host = nsPluginHost::GetInst();
|
|
nsPluginTag* pluginTag = host->PluginWithId(aPluginId);
|
|
if (!pluginTag || !pluginTag->mPlugin) {
|
|
return;
|
|
}
|
|
RefPtr<nsNPAPIPlugin> plugin = pluginTag->mPlugin;
|
|
PluginModuleChromeParent* chromeParent =
|
|
static_cast<PluginModuleChromeParent*>(plugin->GetLibrary());
|
|
chromeParent->TerminateChildProcess(MessageLoop::current(),
|
|
aMonitorDescription,
|
|
aBrowserDumpId);
|
|
}
|
|
|
|
/* static */ PluginLibrary*
|
|
PluginModuleContentParent::LoadModule(uint32_t aPluginId,
|
|
nsPluginTag* aPluginTag)
|
|
{
|
|
PluginModuleMapping::NotifyLoadingModule loadingModule;
|
|
nsAutoPtr<PluginModuleMapping> mapping(
|
|
new PluginModuleMapping(aPluginId, aPluginTag->mSupportsAsyncInit));
|
|
|
|
MOZ_ASSERT(XRE_IsContentProcess());
|
|
|
|
/*
|
|
* 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::Initialize, which saves the instance in
|
|
* its module mapping. We fetch it from there after LoadPlugin finishes.
|
|
*/
|
|
dom::ContentChild* cp = dom::ContentChild::GetSingleton();
|
|
nsresult rv;
|
|
uint32_t runID;
|
|
if (!cp->SendLoadPlugin(aPluginId, &rv, &runID) ||
|
|
NS_FAILED(rv)) {
|
|
return nullptr;
|
|
}
|
|
|
|
PluginModuleContentParent* parent = mapping->GetModule();
|
|
MOZ_ASSERT(parent);
|
|
|
|
if (!mapping->IsChannelOpened()) {
|
|
// mapping is linked into PluginModuleMapping::sModuleListHead and is
|
|
// needed later, so since this function is returning successfully we
|
|
// forget it here.
|
|
mapping.forget();
|
|
}
|
|
|
|
parent->mPluginId = aPluginId;
|
|
parent->mRunID = runID;
|
|
|
|
return parent;
|
|
}
|
|
|
|
/* static */ void
|
|
PluginModuleContentParent::AssociatePluginId(uint32_t aPluginId,
|
|
base::ProcessId aOtherPid)
|
|
{
|
|
DebugOnly<PluginModuleMapping*> mapping =
|
|
PluginModuleMapping::AssociateWithProcessId(aPluginId, aOtherPid);
|
|
MOZ_ASSERT(mapping);
|
|
}
|
|
|
|
/* static */ PluginModuleContentParent*
|
|
PluginModuleContentParent::Initialize(mozilla::ipc::Transport* aTransport,
|
|
base::ProcessId aOtherPid)
|
|
{
|
|
nsAutoPtr<PluginModuleMapping> moduleMapping(
|
|
PluginModuleMapping::Resolve(aOtherPid));
|
|
MOZ_ASSERT(moduleMapping);
|
|
PluginModuleContentParent* parent = moduleMapping->GetModule();
|
|
MOZ_ASSERT(parent);
|
|
|
|
DebugOnly<bool> ok = parent->Open(aTransport, aOtherPid,
|
|
XRE_GetIOMessageLoop(),
|
|
mozilla::ipc::ParentSide);
|
|
MOZ_ASSERT(ok);
|
|
|
|
moduleMapping->SetChannelOpened();
|
|
|
|
// 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);
|
|
|
|
TimeoutChanged(kContentTimeoutPref, parent);
|
|
|
|
// moduleMapping is linked into PluginModuleMapping::sModuleListHead and is
|
|
// needed later, so since this function is returning successfully we
|
|
// forget it here.
|
|
moduleMapping.forget();
|
|
return parent;
|
|
}
|
|
|
|
/* static */ void
|
|
PluginModuleContentParent::OnLoadPluginResult(const uint32_t& aPluginId,
|
|
const bool& aResult)
|
|
{
|
|
nsAutoPtr<PluginModuleMapping> moduleMapping(
|
|
PluginModuleMapping::FindModuleByPluginId(aPluginId));
|
|
MOZ_ASSERT(moduleMapping);
|
|
PluginModuleContentParent* parent = moduleMapping->GetModule();
|
|
MOZ_ASSERT(parent);
|
|
parent->RecvNP_InitializeResult(aResult ? NPERR_NO_ERROR
|
|
: NPERR_GENERIC_ERROR);
|
|
}
|
|
|
|
void
|
|
PluginModuleChromeParent::SetContentParent(dom::ContentParent* aContentParent)
|
|
{
|
|
MOZ_ASSERT(aContentParent);
|
|
mContentParent = aContentParent;
|
|
}
|
|
|
|
bool
|
|
PluginModuleChromeParent::SendAssociatePluginId()
|
|
{
|
|
MOZ_ASSERT(mContentParent);
|
|
return mContentParent->SendAssociatePluginId(mPluginId, OtherPid());
|
|
}
|
|
|
|
// static
|
|
PluginLibrary*
|
|
PluginModuleChromeParent::LoadModule(const char* aFilePath, uint32_t aPluginId,
|
|
nsPluginTag* aPluginTag)
|
|
{
|
|
PLUGIN_LOG_DEBUG_FUNCTION;
|
|
|
|
nsAutoPtr<PluginModuleChromeParent> parent(
|
|
new PluginModuleChromeParent(aFilePath, aPluginId,
|
|
aPluginTag->mSandboxLevel,
|
|
aPluginTag->mSupportsAsyncInit));
|
|
UniquePtr<LaunchCompleteTask> onLaunchedRunnable(new LaunchedTask(parent));
|
|
parent->mSubprocess->SetCallRunnableImmediately(!parent->mIsStartingAsync);
|
|
TimeStamp launchStart = TimeStamp::Now();
|
|
bool launched = parent->mSubprocess->Launch(Move(onLaunchedRunnable),
|
|
aPluginTag->mSandboxLevel);
|
|
if (!launched) {
|
|
// We never reached open
|
|
parent->mShutdown = true;
|
|
return nullptr;
|
|
}
|
|
parent->mIsFlashPlugin = aPluginTag->mIsFlashPlugin;
|
|
uint32_t blocklistState;
|
|
nsresult rv = aPluginTag->GetBlocklistState(&blocklistState);
|
|
parent->mIsBlocklisted = NS_FAILED(rv) || blocklistState != 0;
|
|
if (!parent->mIsStartingAsync) {
|
|
int32_t launchTimeoutSecs = Preferences::GetInt(kLaunchTimeoutPref, 0);
|
|
if (!parent->mSubprocess->WaitUntilConnected(launchTimeoutSecs * 1000)) {
|
|
parent->mShutdown = true;
|
|
return nullptr;
|
|
}
|
|
}
|
|
TimeStamp launchEnd = TimeStamp::Now();
|
|
parent->mTimeBlocked = (launchEnd - launchStart);
|
|
return parent.forget();
|
|
}
|
|
|
|
void
|
|
PluginModuleChromeParent::OnProcessLaunched(const bool aSucceeded)
|
|
{
|
|
if (!aSucceeded) {
|
|
mShutdown = true;
|
|
OnInitFailure();
|
|
return;
|
|
}
|
|
// We may have already been initialized by another call that was waiting
|
|
// for process connect. If so, this function doesn't need to run.
|
|
if (mAsyncInitRv != NS_ERROR_NOT_INITIALIZED || mShutdown) {
|
|
return;
|
|
}
|
|
|
|
Open(mSubprocess->GetChannel(),
|
|
base::GetProcId(mSubprocess->GetChildProcessHandle()));
|
|
|
|
// 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.
|
|
GetIPCChannel()->SetChannelFlags(MessageChannel::REQUIRE_DEFERRED_MESSAGE_PROTECTION);
|
|
|
|
TimeoutChanged(CHILD_TIMEOUT_PREF, this);
|
|
|
|
Preferences::RegisterCallback(TimeoutChanged, kChildTimeoutPref, this);
|
|
Preferences::RegisterCallback(TimeoutChanged, kParentTimeoutPref, this);
|
|
#ifdef XP_WIN
|
|
Preferences::RegisterCallback(TimeoutChanged, kHangUITimeoutPref, this);
|
|
Preferences::RegisterCallback(TimeoutChanged, kHangUIMinDisplayPref, this);
|
|
#endif
|
|
|
|
RegisterSettingsCallbacks();
|
|
|
|
#ifdef MOZ_CRASHREPORTER
|
|
// If this fails, we're having IPC troubles, and we're doomed anyways.
|
|
if (!CrashReporterParent::CreateCrashReporter(this)) {
|
|
mShutdown = true;
|
|
Close();
|
|
OnInitFailure();
|
|
return;
|
|
}
|
|
CrashReporterParent* crashReporter = CrashReporter();
|
|
if (crashReporter) {
|
|
crashReporter->AnnotateCrashReport(NS_LITERAL_CSTRING("AsyncPluginInit"),
|
|
mIsStartingAsync ?
|
|
NS_LITERAL_CSTRING("1") :
|
|
NS_LITERAL_CSTRING("0"));
|
|
}
|
|
#ifdef XP_WIN
|
|
{ // Scope for lock
|
|
mozilla::MutexAutoLock lock(mCrashReporterMutex);
|
|
mCrashReporter = CrashReporter();
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
#if defined(XP_WIN) && defined(_X86_)
|
|
// Protected mode only applies to Windows and only to x86.
|
|
if (!mIsBlocklisted && mIsFlashPlugin &&
|
|
(Preferences::GetBool("dom.ipc.plugins.flash.disable-protected-mode", false) ||
|
|
mSandboxLevel >= 2)) {
|
|
SendDisableFlashProtectedMode();
|
|
}
|
|
#endif
|
|
|
|
if (mInitOnAsyncConnect) {
|
|
mInitOnAsyncConnect = false;
|
|
#if defined(XP_WIN)
|
|
mAsyncInitRv = NP_GetEntryPoints(mNPPIface,
|
|
&mAsyncInitError);
|
|
if (NS_SUCCEEDED(mAsyncInitRv))
|
|
#endif
|
|
{
|
|
#if defined(XP_UNIX) && !defined(XP_MACOSX) && !defined(MOZ_WIDGET_GONK)
|
|
mAsyncInitRv = NP_Initialize(mNPNIface,
|
|
mNPPIface,
|
|
&mAsyncInitError);
|
|
#else
|
|
mAsyncInitRv = NP_Initialize(mNPNIface,
|
|
&mAsyncInitError);
|
|
#endif
|
|
}
|
|
|
|
#if defined(XP_MACOSX)
|
|
if (NS_SUCCEEDED(mAsyncInitRv)) {
|
|
mAsyncInitRv = NP_GetEntryPoints(mNPPIface,
|
|
&mAsyncInitError);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#ifdef MOZ_ENABLE_PROFILER_SPS
|
|
nsCOMPtr<nsIProfiler> profiler(do_GetService("@mozilla.org/tools/profiler;1"));
|
|
bool profilerActive = false;
|
|
DebugOnly<nsresult> rv = profiler->IsActive(&profilerActive);
|
|
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
|
if (profilerActive) {
|
|
nsCOMPtr<nsIProfilerStartParams> currentProfilerParams;
|
|
rv = profiler->GetStartParams(getter_AddRefs(currentProfilerParams));
|
|
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
|
|
|
nsCOMPtr<nsISupports> gatherer;
|
|
rv = profiler->GetProfileGatherer(getter_AddRefs(gatherer));
|
|
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
|
mGatherer = static_cast<ProfileGatherer*>(gatherer.get());
|
|
|
|
StartProfiler(currentProfilerParams);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
bool
|
|
PluginModuleChromeParent::WaitForIPCConnection()
|
|
{
|
|
PluginProcessParent* process = Process();
|
|
MOZ_ASSERT(process);
|
|
process->SetCallRunnableImmediately(true);
|
|
if (!process->WaitUntilConnected()) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
PluginModuleParent::PluginModuleParent(bool aIsChrome, bool aAllowAsyncInit)
|
|
: mQuirks(QUIRKS_NOT_INITIALIZED)
|
|
, mIsChrome(aIsChrome)
|
|
, mShutdown(false)
|
|
, mHadLocalInstance(false)
|
|
, mClearSiteDataSupported(false)
|
|
, mGetSitesWithDataSupported(false)
|
|
, mNPNIface(nullptr)
|
|
, mNPPIface(nullptr)
|
|
, mPlugin(nullptr)
|
|
, mTaskFactory(this)
|
|
, mSandboxLevel(0)
|
|
, mIsFlashPlugin(false)
|
|
, mIsStartingAsync(false)
|
|
, mNPInitialized(false)
|
|
, mIsNPShutdownPending(false)
|
|
, mAsyncNewRv(NS_ERROR_NOT_INITIALIZED)
|
|
{
|
|
#if defined(XP_WIN) || defined(XP_MACOSX) || defined(MOZ_WIDGET_GTK)
|
|
mIsStartingAsync = aAllowAsyncInit &&
|
|
Preferences::GetBool(kAsyncInitPref, false) &&
|
|
!BrowserTabsRemoteAutostart();
|
|
#if defined(MOZ_CRASHREPORTER)
|
|
CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("AsyncPluginInit"),
|
|
mIsStartingAsync ?
|
|
NS_LITERAL_CSTRING("1") :
|
|
NS_LITERAL_CSTRING("0"));
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
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(bool aAllowAsyncInit)
|
|
: PluginModuleParent(false, aAllowAsyncInit)
|
|
{
|
|
Preferences::RegisterCallback(TimeoutChanged, kContentTimeoutPref, this);
|
|
}
|
|
|
|
PluginModuleContentParent::~PluginModuleContentParent()
|
|
{
|
|
XRE_GetIOMessageLoop()->PostTask(FROM_HERE,
|
|
new DeleteTask<Transport>(GetTransport()));
|
|
|
|
Preferences::UnregisterCallback(TimeoutChanged, kContentTimeoutPref, this);
|
|
}
|
|
|
|
bool PluginModuleChromeParent::sInstantiated = false;
|
|
|
|
PluginModuleChromeParent::PluginModuleChromeParent(const char* aFilePath,
|
|
uint32_t aPluginId,
|
|
int32_t aSandboxLevel,
|
|
bool aAllowAsyncInit)
|
|
: PluginModuleParent(true, aAllowAsyncInit)
|
|
, mSubprocess(new PluginProcessParent(aFilePath))
|
|
, mPluginId(aPluginId)
|
|
, mChromeTaskFactory(this)
|
|
, mHangAnnotationFlags(0)
|
|
, mHangAnnotatorMutex("PluginModuleChromeParent::mHangAnnotatorMutex")
|
|
#ifdef XP_WIN
|
|
, mPluginCpuUsageOnHang()
|
|
, mHangUIParent(nullptr)
|
|
, mHangUIEnabled(true)
|
|
, mIsTimerReset(true)
|
|
#ifdef MOZ_CRASHREPORTER
|
|
, mCrashReporterMutex("PluginModuleChromeParent::mCrashReporterMutex")
|
|
, mCrashReporter(nullptr)
|
|
#endif
|
|
#endif
|
|
#ifdef MOZ_CRASHREPORTER_INJECTOR
|
|
, mFlashProcess1(0)
|
|
, mFlashProcess2(0)
|
|
, mFinishInitTask(nullptr)
|
|
#endif
|
|
, mInitOnAsyncConnect(false)
|
|
, mAsyncInitRv(NS_ERROR_NOT_INITIALIZED)
|
|
, mAsyncInitError(NPERR_NO_ERROR)
|
|
, mContentParent(nullptr)
|
|
{
|
|
NS_ASSERTION(mSubprocess, "Out of memory!");
|
|
sInstantiated = true;
|
|
mSandboxLevel = aSandboxLevel;
|
|
mRunID = GeckoChildProcessHost::GetUniqueID();
|
|
|
|
#ifdef MOZ_ENABLE_PROFILER_SPS
|
|
InitPluginProfiling();
|
|
#endif
|
|
|
|
mozilla::HangMonitor::RegisterAnnotator(*this);
|
|
}
|
|
|
|
PluginModuleChromeParent::~PluginModuleChromeParent()
|
|
{
|
|
if (!OkToCleanup()) {
|
|
NS_RUNTIMEABORT("unsafe destruction");
|
|
}
|
|
|
|
#ifdef MOZ_ENABLE_PROFILER_SPS
|
|
ShutdownPluginProfiling();
|
|
#endif
|
|
|
|
if (!mShutdown) {
|
|
NS_WARNING("Plugin host deleted the module without shutting down.");
|
|
NPError err;
|
|
NP_Shutdown(&err);
|
|
}
|
|
|
|
NS_ASSERTION(mShutdown, "NP_Shutdown didn't");
|
|
|
|
if (mSubprocess) {
|
|
mSubprocess->Delete();
|
|
mSubprocess = nullptr;
|
|
}
|
|
|
|
#ifdef MOZ_CRASHREPORTER_INJECTOR
|
|
if (mFlashProcess1)
|
|
UnregisterInjectorCallback(mFlashProcess1);
|
|
if (mFlashProcess2)
|
|
UnregisterInjectorCallback(mFlashProcess2);
|
|
if (mFinishInitTask) {
|
|
// mFinishInitTask will be deleted by the main thread message_loop
|
|
mFinishInitTask->Cancel();
|
|
}
|
|
#endif
|
|
|
|
UnregisterSettingsCallbacks();
|
|
|
|
Preferences::UnregisterCallback(TimeoutChanged, kChildTimeoutPref, this);
|
|
Preferences::UnregisterCallback(TimeoutChanged, kParentTimeoutPref, this);
|
|
#ifdef XP_WIN
|
|
Preferences::UnregisterCallback(TimeoutChanged, kHangUITimeoutPref, this);
|
|
Preferences::UnregisterCallback(TimeoutChanged, kHangUIMinDisplayPref, this);
|
|
|
|
if (mHangUIParent) {
|
|
delete mHangUIParent;
|
|
mHangUIParent = nullptr;
|
|
}
|
|
#endif
|
|
|
|
mozilla::HangMonitor::UnregisterAnnotator(*this);
|
|
}
|
|
|
|
#ifdef MOZ_CRASHREPORTER
|
|
void
|
|
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
|
|
const std::string& pluginFile = mSubprocess->GetPluginFilePath();
|
|
size_t filePos = pluginFile.rfind(FILE_PATH_SEPARATOR);
|
|
if (filePos == std::string::npos)
|
|
filePos = 0;
|
|
else
|
|
filePos++;
|
|
notes.Put(NS_LITERAL_CSTRING("PluginFilename"), CS(pluginFile.substr(filePos).c_str()));
|
|
|
|
notes.Put(NS_LITERAL_CSTRING("PluginName"), mPluginName);
|
|
notes.Put(NS_LITERAL_CSTRING("PluginVersion"), mPluginVersion);
|
|
|
|
CrashReporterParent* crashReporter = CrashReporter();
|
|
if (crashReporter) {
|
|
#ifdef XP_WIN
|
|
if (mPluginCpuUsageOnHang.Length() > 0) {
|
|
notes.Put(NS_LITERAL_CSTRING("NumberOfProcessors"),
|
|
nsPrintfCString("%d", PR_GetNumberOfProcessors()));
|
|
|
|
nsCString cpuUsageStr;
|
|
cpuUsageStr.AppendFloat(std::ceil(mPluginCpuUsageOnHang[0] * 100) / 100);
|
|
notes.Put(NS_LITERAL_CSTRING("PluginCpuUsage"), cpuUsageStr);
|
|
|
|
#ifdef MOZ_CRASHREPORTER_INJECTOR
|
|
for (uint32_t i=1; i<mPluginCpuUsageOnHang.Length(); ++i) {
|
|
nsCString tempStr;
|
|
tempStr.AppendFloat(std::ceil(mPluginCpuUsageOnHang[i] * 100) / 100);
|
|
notes.Put(nsPrintfCString("CpuUsageFlashProcess%d", i), tempStr);
|
|
}
|
|
#endif
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
#endif // MOZ_CRASHREPORTER
|
|
|
|
void
|
|
PluginModuleParent::SetChildTimeout(const int32_t aChildTimeout)
|
|
{
|
|
int32_t timeoutMs = (aChildTimeout > 0) ? (1000 * aChildTimeout) :
|
|
MessageChannel::kNoTimeout;
|
|
SetReplyTimeoutMs(timeoutMs);
|
|
}
|
|
|
|
void
|
|
PluginModuleParent::TimeoutChanged(const char* aPref, void* aModule)
|
|
{
|
|
PluginModuleParent* module = static_cast<PluginModuleParent*>(aModule);
|
|
|
|
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
|
#ifndef XP_WIN
|
|
if (!strcmp(aPref, kChildTimeoutPref)) {
|
|
MOZ_ASSERT(module->IsChrome());
|
|
// The timeout value used by the parent for children
|
|
int32_t timeoutSecs = Preferences::GetInt(kChildTimeoutPref, 0);
|
|
module->SetChildTimeout(timeoutSecs);
|
|
#else
|
|
if (!strcmp(aPref, kChildTimeoutPref) ||
|
|
!strcmp(aPref, kHangUIMinDisplayPref) ||
|
|
!strcmp(aPref, kHangUITimeoutPref)) {
|
|
MOZ_ASSERT(module->IsChrome());
|
|
static_cast<PluginModuleChromeParent*>(module)->EvaluateHangUIState(true);
|
|
#endif // XP_WIN
|
|
} else if (!strcmp(aPref, kParentTimeoutPref)) {
|
|
// The timeout value used by the child for its parent
|
|
MOZ_ASSERT(module->IsChrome());
|
|
int32_t timeoutSecs = Preferences::GetInt(kParentTimeoutPref, 0);
|
|
Unused << static_cast<PluginModuleChromeParent*>(module)->SendSetParentHangTimeout(timeoutSecs);
|
|
} else if (!strcmp(aPref, kContentTimeoutPref)) {
|
|
MOZ_ASSERT(!module->IsChrome());
|
|
int32_t timeoutSecs = Preferences::GetInt(kContentTimeoutPref, 0);
|
|
module->SetChildTimeout(timeoutSecs);
|
|
}
|
|
}
|
|
|
|
void
|
|
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,
|
|
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
|
|
already been set to ChannelTimeout and we should call the
|
|
regular Close function. */
|
|
if (aFromHangUI) {
|
|
GetIPCChannel()->CloseWithError();
|
|
} else {
|
|
Close();
|
|
}
|
|
}
|
|
|
|
#ifdef XP_WIN
|
|
namespace {
|
|
|
|
uint64_t
|
|
FileTimeToUTC(const FILETIME& ftime)
|
|
{
|
|
ULARGE_INTEGER li;
|
|
li.LowPart = ftime.dwLowDateTime;
|
|
li.HighPart = ftime.dwHighDateTime;
|
|
return li.QuadPart;
|
|
}
|
|
|
|
struct CpuUsageSamples
|
|
{
|
|
uint64_t sampleTimes[2];
|
|
uint64_t cpuTimes[2];
|
|
};
|
|
|
|
bool
|
|
GetProcessCpuUsage(const InfallibleTArray<base::ProcessHandle>& processHandles, InfallibleTArray<float>& cpuUsage)
|
|
{
|
|
InfallibleTArray<CpuUsageSamples> samples(processHandles.Length());
|
|
FILETIME creationTime, exitTime, kernelTime, userTime, currentTime;
|
|
BOOL res;
|
|
|
|
for (uint32_t i = 0; i < processHandles.Length(); ++i) {
|
|
::GetSystemTimeAsFileTime(¤tTime);
|
|
res = ::GetProcessTimes(processHandles[i], &creationTime, &exitTime, &kernelTime, &userTime);
|
|
if (!res) {
|
|
NS_WARNING("failed to get process times");
|
|
return false;
|
|
}
|
|
|
|
CpuUsageSamples s;
|
|
s.sampleTimes[0] = FileTimeToUTC(currentTime);
|
|
s.cpuTimes[0] = FileTimeToUTC(kernelTime) + FileTimeToUTC(userTime);
|
|
samples.AppendElement(s);
|
|
}
|
|
|
|
// we already hung for a while, a little bit longer won't matter
|
|
::Sleep(50);
|
|
|
|
const int32_t numberOfProcessors = PR_GetNumberOfProcessors();
|
|
|
|
for (uint32_t i = 0; i < processHandles.Length(); ++i) {
|
|
::GetSystemTimeAsFileTime(¤tTime);
|
|
res = ::GetProcessTimes(processHandles[i], &creationTime, &exitTime, &kernelTime, &userTime);
|
|
if (!res) {
|
|
NS_WARNING("failed to get process times");
|
|
return false;
|
|
}
|
|
|
|
samples[i].sampleTimes[1] = FileTimeToUTC(currentTime);
|
|
samples[i].cpuTimes[1] = FileTimeToUTC(kernelTime) + FileTimeToUTC(userTime);
|
|
|
|
const uint64_t deltaSampleTime = samples[i].sampleTimes[1] - samples[i].sampleTimes[0];
|
|
const uint64_t deltaCpuTime = samples[i].cpuTimes[1] - samples[i].cpuTimes[0];
|
|
const float usage = 100.f * (float(deltaCpuTime) / deltaSampleTime) / numberOfProcessors;
|
|
cpuUsage.AppendElement(usage);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
#endif // #ifdef XP_WIN
|
|
|
|
void
|
|
PluginModuleChromeParent::OnEnteredCall()
|
|
{
|
|
mozilla::ipc::IProtocol* protocol = GetInvokingProtocol();
|
|
MOZ_ASSERT(protocol);
|
|
mozilla::MutexAutoLock lock(mHangAnnotatorMutex);
|
|
mProtocolCallStack.AppendElement(protocol);
|
|
}
|
|
|
|
void
|
|
PluginModuleChromeParent::OnExitedCall()
|
|
{
|
|
mozilla::MutexAutoLock lock(mHangAnnotatorMutex);
|
|
MOZ_ASSERT(!mProtocolCallStack.IsEmpty());
|
|
mProtocolCallStack.RemoveElementAt(mProtocolCallStack.Length() - 1);
|
|
}
|
|
|
|
void
|
|
PluginModuleChromeParent::OnEnteredSyncSend()
|
|
{
|
|
mozilla::ipc::IProtocol* protocol = GetInvokingProtocol();
|
|
MOZ_ASSERT(protocol);
|
|
mozilla::MutexAutoLock lock(mHangAnnotatorMutex);
|
|
mProtocolCallStack.AppendElement(protocol);
|
|
}
|
|
|
|
void
|
|
PluginModuleChromeParent::OnExitedSyncSend()
|
|
{
|
|
mozilla::MutexAutoLock lock(mHangAnnotatorMutex);
|
|
MOZ_ASSERT(!mProtocolCallStack.IsEmpty());
|
|
mProtocolCallStack.RemoveElementAt(mProtocolCallStack.Length() - 1);
|
|
}
|
|
|
|
/**
|
|
* This function converts the topmost routing id on the call stack (as recorded
|
|
* by the MessageChannel) into a pointer to a IProtocol object.
|
|
*/
|
|
mozilla::ipc::IProtocol*
|
|
PluginModuleChromeParent::GetInvokingProtocol()
|
|
{
|
|
int32_t routingId = GetIPCChannel()->GetTopmostMessageRoutingId();
|
|
// Nothing being routed. No protocol. Just return nullptr.
|
|
if (routingId == MSG_ROUTING_NONE) {
|
|
return nullptr;
|
|
}
|
|
// If routingId is MSG_ROUTING_CONTROL then we're dealing with control
|
|
// messages that were initiated by the topmost managing protocol, ie. this.
|
|
if (routingId == MSG_ROUTING_CONTROL) {
|
|
return this;
|
|
}
|
|
// Otherwise we can look up the protocol object by the routing id.
|
|
mozilla::ipc::IProtocol* protocol = Lookup(routingId);
|
|
return protocol;
|
|
}
|
|
|
|
/**
|
|
* This function examines the IProtocol object parameter and converts it into
|
|
* the PluginInstanceParent object that is associated with that protocol, if
|
|
* any. Since PluginInstanceParent manages subprotocols, this function needs
|
|
* to determine whether |aProtocol| is a subprotocol, and if so it needs to
|
|
* obtain the protocol's manager.
|
|
*
|
|
* This function needs to be updated if the subprotocols are modified in
|
|
* PPluginInstance.ipdl.
|
|
*/
|
|
PluginInstanceParent*
|
|
PluginModuleChromeParent::GetManagingInstance(mozilla::ipc::IProtocol* aProtocol)
|
|
{
|
|
MOZ_ASSERT(aProtocol);
|
|
mozilla::ipc::MessageListener* listener =
|
|
static_cast<mozilla::ipc::MessageListener*>(aProtocol);
|
|
switch (listener->GetProtocolTypeId()) {
|
|
case PPluginInstanceMsgStart:
|
|
// In this case, aProtocol is the instance itself. Just cast it.
|
|
return static_cast<PluginInstanceParent*>(aProtocol);
|
|
case PPluginBackgroundDestroyerMsgStart: {
|
|
PPluginBackgroundDestroyerParent* actor =
|
|
static_cast<PPluginBackgroundDestroyerParent*>(aProtocol);
|
|
return static_cast<PluginInstanceParent*>(actor->Manager());
|
|
}
|
|
case PPluginScriptableObjectMsgStart: {
|
|
PPluginScriptableObjectParent* actor =
|
|
static_cast<PPluginScriptableObjectParent*>(aProtocol);
|
|
return static_cast<PluginInstanceParent*>(actor->Manager());
|
|
}
|
|
case PBrowserStreamMsgStart: {
|
|
PBrowserStreamParent* actor =
|
|
static_cast<PBrowserStreamParent*>(aProtocol);
|
|
return static_cast<PluginInstanceParent*>(actor->Manager());
|
|
}
|
|
case PPluginStreamMsgStart: {
|
|
PPluginStreamParent* actor =
|
|
static_cast<PPluginStreamParent*>(aProtocol);
|
|
return static_cast<PluginInstanceParent*>(actor->Manager());
|
|
}
|
|
case PStreamNotifyMsgStart: {
|
|
PStreamNotifyParent* actor =
|
|
static_cast<PStreamNotifyParent*>(aProtocol);
|
|
return static_cast<PluginInstanceParent*>(actor->Manager());
|
|
}
|
|
#ifdef XP_WIN
|
|
case PPluginSurfaceMsgStart: {
|
|
PPluginSurfaceParent* actor =
|
|
static_cast<PPluginSurfaceParent*>(aProtocol);
|
|
return static_cast<PluginInstanceParent*>(actor->Manager());
|
|
}
|
|
#endif
|
|
default:
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
void
|
|
PluginModuleChromeParent::EnteredCxxStack()
|
|
{
|
|
mHangAnnotationFlags |= kInPluginCall;
|
|
}
|
|
|
|
void
|
|
PluginModuleChromeParent::ExitedCxxStack()
|
|
{
|
|
mHangAnnotationFlags = 0;
|
|
#ifdef XP_WIN
|
|
FinishHangUI();
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* This function is always called by the HangMonitor thread.
|
|
*/
|
|
void
|
|
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) {
|
|
aAnnotations.AddAnnotation(NS_LITERAL_STRING("HangUIShown"),
|
|
true);
|
|
}
|
|
if (flags & kHangUIContinued) {
|
|
aAnnotations.AddAnnotation(NS_LITERAL_STRING("HangUIContinued"),
|
|
true);
|
|
}
|
|
if (flags & kHangUIDontShow) {
|
|
aAnnotations.AddAnnotation(NS_LITERAL_STRING("HangUIDontShow"),
|
|
true);
|
|
}
|
|
aAnnotations.AddAnnotation(NS_LITERAL_STRING("pluginName"), mPluginName);
|
|
aAnnotations.AddAnnotation(NS_LITERAL_STRING("pluginVersion"),
|
|
mPluginVersion);
|
|
if (mIsFlashPlugin) {
|
|
bool isWhitelistedForShumway = false;
|
|
{ // Scope for lock
|
|
mozilla::MutexAutoLock lock(mHangAnnotatorMutex);
|
|
if (!mProtocolCallStack.IsEmpty()) {
|
|
mozilla::ipc::IProtocol* topProtocol =
|
|
mProtocolCallStack.LastElement();
|
|
PluginInstanceParent* instance =
|
|
GetManagingInstance(topProtocol);
|
|
if (instance) {
|
|
isWhitelistedForShumway =
|
|
instance->IsWhitelistedForShumway();
|
|
}
|
|
}
|
|
}
|
|
aAnnotations.AddAnnotation(NS_LITERAL_STRING("pluginIsWhitelistedForShumway"),
|
|
isWhitelistedForShumway);
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef MOZ_CRASHREPORTER
|
|
static bool
|
|
CreatePluginMinidump(base::ProcessId processId, ThreadId childThread,
|
|
nsIFile* parentMinidump, const nsACString& name)
|
|
{
|
|
mozilla::ipc::ScopedProcessHandle handle;
|
|
if (processId == 0 ||
|
|
!base::OpenPrivilegedProcessHandle(processId, &handle.rwget())) {
|
|
return false;
|
|
}
|
|
return CreateAdditionalChildMinidump(handle, 0, parentMinidump, name);
|
|
}
|
|
#endif
|
|
|
|
bool
|
|
PluginModuleChromeParent::ShouldContinueFromReplyTimeout()
|
|
{
|
|
if (mIsFlashPlugin) {
|
|
MessageLoop::current()->PostTask(
|
|
FROM_HERE,
|
|
mTaskFactory.NewRunnableMethod(
|
|
&PluginModuleChromeParent::NotifyFlashHang));
|
|
}
|
|
|
|
#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(),
|
|
NS_LITERAL_CSTRING("ModalHangUI"),
|
|
EmptyString());
|
|
GetIPCChannel()->CloseWithTimeout();
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
PluginModuleContentParent::ShouldContinueFromReplyTimeout()
|
|
{
|
|
RefPtr<ProcessHangMonitor> monitor = ProcessHangMonitor::Get();
|
|
if (!monitor) {
|
|
return true;
|
|
}
|
|
monitor->NotifyPluginHang(mPluginId);
|
|
return true;
|
|
}
|
|
|
|
void
|
|
PluginModuleContentParent::OnExitedSyncSend()
|
|
{
|
|
ProcessHangMonitor::ClearHang();
|
|
}
|
|
|
|
void
|
|
PluginModuleChromeParent::TerminateChildProcess(MessageLoop* aMsgLoop,
|
|
const nsCString& aMonitorDescription,
|
|
const nsAString& aBrowserDumpId)
|
|
{
|
|
#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.
|
|
return;
|
|
}
|
|
#else
|
|
CrashReporterParent* crashReporter = CrashReporter();
|
|
#endif
|
|
crashReporter->AnnotateCrashReport(NS_LITERAL_CSTRING("PluginHang"),
|
|
NS_LITERAL_CSTRING("1"));
|
|
crashReporter->AnnotateCrashReport(NS_LITERAL_CSTRING("HangMonitorDescription"),
|
|
aMonitorDescription);
|
|
#ifdef XP_WIN
|
|
if (mHangUIParent) {
|
|
unsigned int hangUIDuration = mHangUIParent->LastShowDurationMs();
|
|
if (hangUIDuration) {
|
|
nsPrintfCString strHangUIDuration("%u", hangUIDuration);
|
|
crashReporter->AnnotateCrashReport(
|
|
NS_LITERAL_CSTRING("PluginHangUIDuration"),
|
|
strHangUIDuration);
|
|
}
|
|
}
|
|
#endif // XP_WIN
|
|
|
|
bool reportsReady = false;
|
|
|
|
// Check to see if we already have a browser dump id - with e10s plugin
|
|
// hangs we take this earlier (see ProcessHangMonitor) from a background
|
|
// thread. We do this before we message the main thread about the hang
|
|
// since the posted message will trash our browser stack state.
|
|
bool exists;
|
|
nsCOMPtr<nsIFile> browserDumpFile;
|
|
if (!aBrowserDumpId.IsEmpty() &&
|
|
CrashReporter::GetMinidumpForID(aBrowserDumpId, getter_AddRefs(browserDumpFile)) &&
|
|
browserDumpFile &&
|
|
NS_SUCCEEDED(browserDumpFile->Exists(&exists)) && exists)
|
|
{
|
|
// We have a single browser report, generate a new plugin process parent
|
|
// report and pair it up with the browser report handed in.
|
|
reportsReady = crashReporter->GenerateMinidumpAndPair(this, browserDumpFile,
|
|
NS_LITERAL_CSTRING("browser"));
|
|
if (!reportsReady) {
|
|
browserDumpFile = nullptr;
|
|
CrashReporter::DeleteMinidumpFilesForID(aBrowserDumpId);
|
|
}
|
|
}
|
|
|
|
// Generate crash report including plugin and browser process minidumps.
|
|
// The plugin process is the parent report with additional dumps including
|
|
// the browser process, content process when running under e10s, and
|
|
// various flash subprocesses if we're the flash module.
|
|
if (!reportsReady) {
|
|
reportsReady = crashReporter->GeneratePairedMinidump(this);
|
|
}
|
|
|
|
if (reportsReady) {
|
|
// Important to set this here, it tells the ActorDestroy handler
|
|
// that we have an existing crash report that needs to be finalized.
|
|
mPluginDumpID = crashReporter->ChildDumpID();
|
|
PLUGIN_LOG_DEBUG(
|
|
("generated paired browser/plugin minidumps: %s)",
|
|
NS_ConvertUTF16toUTF8(mPluginDumpID).get()));
|
|
nsAutoCString additionalDumps("browser");
|
|
nsCOMPtr<nsIFile> pluginDumpFile;
|
|
if (GetMinidumpForID(mPluginDumpID, getter_AddRefs(pluginDumpFile)) &&
|
|
pluginDumpFile) {
|
|
#ifdef MOZ_CRASHREPORTER_INJECTOR
|
|
// If we have handles to the flash sandbox processes on Windows,
|
|
// include those minidumps as well.
|
|
if (CreatePluginMinidump(mFlashProcess1, 0, pluginDumpFile,
|
|
NS_LITERAL_CSTRING("flash1"))) {
|
|
additionalDumps.AppendLiteral(",flash1");
|
|
}
|
|
if (CreatePluginMinidump(mFlashProcess2, 0, pluginDumpFile,
|
|
NS_LITERAL_CSTRING("flash2"))) {
|
|
additionalDumps.AppendLiteral(",flash2");
|
|
}
|
|
#endif
|
|
if (mContentParent) {
|
|
// Include the content process minidump
|
|
if (CreatePluginMinidump(mContentParent->OtherPid(), 0,
|
|
pluginDumpFile,
|
|
NS_LITERAL_CSTRING("content"))) {
|
|
additionalDumps.AppendLiteral(",content");
|
|
}
|
|
}
|
|
}
|
|
crashReporter->AnnotateCrashReport(
|
|
NS_LITERAL_CSTRING("additional_minidumps"),
|
|
additionalDumps);
|
|
} else {
|
|
NS_WARNING("failed to capture paired minidumps from hang");
|
|
}
|
|
#endif
|
|
|
|
mozilla::ipc::ScopedProcessHandle geckoChildProcess;
|
|
bool childOpened = base::OpenProcessHandle(OtherPid(),
|
|
&geckoChildProcess.rwget());
|
|
|
|
#ifdef XP_WIN
|
|
// collect cpu usage for plugin processes
|
|
|
|
InfallibleTArray<base::ProcessHandle> processHandles;
|
|
|
|
if (childOpened) {
|
|
processHandles.AppendElement(geckoChildProcess);
|
|
}
|
|
|
|
#ifdef MOZ_CRASHREPORTER_INJECTOR
|
|
mozilla::ipc::ScopedProcessHandle flashBrokerProcess;
|
|
if (mFlashProcess1 &&
|
|
base::OpenProcessHandle(mFlashProcess1, &flashBrokerProcess.rwget())) {
|
|
processHandles.AppendElement(flashBrokerProcess);
|
|
}
|
|
mozilla::ipc::ScopedProcessHandle flashSandboxProcess;
|
|
if (mFlashProcess2 &&
|
|
base::OpenProcessHandle(mFlashProcess2, &flashSandboxProcess.rwget())) {
|
|
processHandles.AppendElement(flashSandboxProcess);
|
|
}
|
|
#endif
|
|
|
|
if (!GetProcessCpuUsage(processHandles, mPluginCpuUsageOnHang)) {
|
|
mPluginCpuUsageOnHang.Clear();
|
|
}
|
|
#endif
|
|
|
|
// this must run before the error notification from the channel,
|
|
// or not at all
|
|
bool isFromHangUI = aMsgLoop != MessageLoop::current();
|
|
aMsgLoop->PostTask(
|
|
FROM_HERE,
|
|
mChromeTaskFactory.NewRunnableMethod(
|
|
&PluginModuleChromeParent::CleanupFromTimeout, isFromHangUI));
|
|
|
|
if (!childOpened || !KillProcess(geckoChildProcess, 1, false)) {
|
|
NS_WARNING("failed to kill subprocess!");
|
|
}
|
|
}
|
|
|
|
bool
|
|
PluginModuleParent::GetPluginDetails()
|
|
{
|
|
RefPtr<nsPluginHost> host = nsPluginHost::GetInst();
|
|
if (!host) {
|
|
return false;
|
|
}
|
|
nsPluginTag* pluginTag = host->TagForPlugin(mPlugin);
|
|
if (!pluginTag) {
|
|
return false;
|
|
}
|
|
mPluginName = pluginTag->Name();
|
|
mPluginVersion = pluginTag->Version();
|
|
mPluginFilename = pluginTag->FileName();
|
|
mIsFlashPlugin = pluginTag->mIsFlashPlugin;
|
|
mSandboxLevel = pluginTag->mSandboxLevel;
|
|
return true;
|
|
}
|
|
|
|
void
|
|
PluginModuleParent::InitQuirksModes(const nsCString& aMimeType)
|
|
{
|
|
if (mQuirks != QUIRKS_NOT_INITIALIZED) {
|
|
return;
|
|
}
|
|
|
|
mQuirks = GetQuirksFromMimeTypeAndFilename(aMimeType, mPluginFilename);
|
|
}
|
|
|
|
#ifdef XP_WIN
|
|
void
|
|
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. */
|
|
mHangUIEnabled = false;
|
|
} else {
|
|
timeoutSecs = Preferences::GetInt(kHangUITimeoutPref, 0);
|
|
mHangUIEnabled = timeoutSecs > 0;
|
|
}
|
|
if (mHangUIEnabled) {
|
|
if (aReset) {
|
|
mIsTimerReset = true;
|
|
SetChildTimeout(timeoutSecs);
|
|
return;
|
|
} else if (mIsTimerReset) {
|
|
/* The Hang UI is being shown, so now we're setting the
|
|
timeout to kChildTimeoutPref while we wait for a user
|
|
response. ShouldContinueFromReplyTimeout will fire
|
|
after (reply timeout / 2) seconds, which is not what
|
|
we want. Doubling the timeout value here so that we get
|
|
the right result. */
|
|
autoStopSecs *= 2;
|
|
}
|
|
}
|
|
mIsTimerReset = false;
|
|
SetChildTimeout(autoStopSecs);
|
|
}
|
|
|
|
bool
|
|
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;
|
|
}
|
|
if (mHangUIParent->DontShowAgain()) {
|
|
mHangAnnotationFlags |= kHangUIDontShow;
|
|
bool wasLastHangStopped = mHangUIParent->WasLastHangStopped();
|
|
if (!wasLastHangStopped) {
|
|
mHangAnnotationFlags |= kHangUIContinued;
|
|
}
|
|
return !wasLastHangStopped;
|
|
}
|
|
delete mHangUIParent;
|
|
mHangUIParent = nullptr;
|
|
}
|
|
mHangUIParent = new PluginHangUIParent(this,
|
|
Preferences::GetInt(kHangUITimeoutPref, 0),
|
|
Preferences::GetInt(kChildTimeoutPref, 0));
|
|
bool retval = mHangUIParent->Init(NS_ConvertUTF8toUTF16(mPluginName));
|
|
if (retval) {
|
|
mHangAnnotationFlags |= kHangUIShown;
|
|
/* Once the UI is shown we switch the timeout over to use
|
|
kChildTimeoutPref, allowing us to terminate a hung plugin
|
|
after kChildTimeoutPref seconds if the user doesn't respond to
|
|
the hang UI. */
|
|
EvaluateHangUIState(false);
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
void
|
|
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,
|
|
we need to reset the child process timeout. */
|
|
if (needsCancel ||
|
|
(!mIsTimerReset && mHangUIParent->WasShown())) {
|
|
/* We changed the timeout to kChildTimeoutPref when the plugin hang
|
|
UI was displayed. Now that we're finishing the UI, we need to
|
|
switch it back to kHangUITimeoutPref. */
|
|
EvaluateHangUIState(true);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
PluginModuleChromeParent::OnHangUIContinue()
|
|
{
|
|
mHangAnnotationFlags |= kHangUIContinued;
|
|
}
|
|
#endif // XP_WIN
|
|
|
|
#ifdef MOZ_CRASHREPORTER
|
|
CrashReporterParent*
|
|
PluginModuleChromeParent::CrashReporter()
|
|
{
|
|
return static_cast<CrashReporterParent*>(LoneManagedOrNullAsserts(ManagedPCrashReporterParent()));
|
|
}
|
|
|
|
#ifdef MOZ_CRASHREPORTER_INJECTOR
|
|
static void
|
|
RemoveMinidump(nsIFile* minidump)
|
|
{
|
|
if (!minidump)
|
|
return;
|
|
|
|
minidump->Remove(false);
|
|
nsCOMPtr<nsIFile> extraFile;
|
|
if (GetExtraFileForMinidump(minidump,
|
|
getter_AddRefs(extraFile))) {
|
|
extraFile->Remove(true);
|
|
}
|
|
}
|
|
#endif // MOZ_CRASHREPORTER_INJECTOR
|
|
|
|
void
|
|
PluginModuleChromeParent::ProcessFirstMinidump()
|
|
{
|
|
#ifdef XP_WIN
|
|
mozilla::MutexAutoLock lock(mCrashReporterMutex);
|
|
#endif
|
|
CrashReporterParent* crashReporter = CrashReporter();
|
|
if (!crashReporter)
|
|
return;
|
|
|
|
AnnotationTable notes(4);
|
|
WriteExtraDataForMinidump(notes);
|
|
|
|
if (!mPluginDumpID.IsEmpty()) {
|
|
// mPluginDumpID may be set in TerminateChildProcess, which means the
|
|
// process hang monitor has already collected a 3-way browser, plugin,
|
|
// content crash report. If so, update the existing report with our
|
|
// annotations and finalize it. If not, fall through for standard
|
|
// plugin crash report handling.
|
|
crashReporter->GenerateChildData(¬es);
|
|
crashReporter->FinalizeChildData();
|
|
return;
|
|
}
|
|
|
|
uint32_t sequence = UINT32_MAX;
|
|
nsCOMPtr<nsIFile> dumpFile;
|
|
nsAutoCString flashProcessType;
|
|
TakeMinidump(getter_AddRefs(dumpFile), &sequence);
|
|
|
|
#ifdef MOZ_CRASHREPORTER_INJECTOR
|
|
nsCOMPtr<nsIFile> childDumpFile;
|
|
uint32_t childSequence;
|
|
|
|
if (mFlashProcess1 &&
|
|
TakeMinidumpForChild(mFlashProcess1,
|
|
getter_AddRefs(childDumpFile),
|
|
&childSequence)) {
|
|
if (childSequence < sequence) {
|
|
RemoveMinidump(dumpFile);
|
|
dumpFile = childDumpFile;
|
|
sequence = childSequence;
|
|
flashProcessType.AssignLiteral("Broker");
|
|
}
|
|
else {
|
|
RemoveMinidump(childDumpFile);
|
|
}
|
|
}
|
|
if (mFlashProcess2 &&
|
|
TakeMinidumpForChild(mFlashProcess2,
|
|
getter_AddRefs(childDumpFile),
|
|
&childSequence)) {
|
|
if (childSequence < sequence) {
|
|
RemoveMinidump(dumpFile);
|
|
dumpFile = childDumpFile;
|
|
sequence = childSequence;
|
|
flashProcessType.AssignLiteral("Sandbox");
|
|
}
|
|
else {
|
|
RemoveMinidump(childDumpFile);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (!dumpFile) {
|
|
NS_WARNING("[PluginModuleParent::ActorDestroy] abnormal shutdown without minidump!");
|
|
return;
|
|
}
|
|
|
|
PLUGIN_LOG_DEBUG(("got child minidump: %s",
|
|
NS_ConvertUTF16toUTF8(mPluginDumpID).get()));
|
|
|
|
GetIDFromMinidump(dumpFile, mPluginDumpID);
|
|
if (!flashProcessType.IsEmpty()) {
|
|
notes.Put(NS_LITERAL_CSTRING("FlashProcessDump"), flashProcessType);
|
|
}
|
|
crashReporter->GenerateCrashReportForMinidump(dumpFile, ¬es);
|
|
}
|
|
#endif
|
|
|
|
void
|
|
PluginModuleParent::ActorDestroy(ActorDestroyReason why)
|
|
{
|
|
switch (why) {
|
|
case AbnormalShutdown: {
|
|
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));
|
|
break;
|
|
}
|
|
case NormalShutdown:
|
|
mShutdown = true;
|
|
break;
|
|
|
|
default:
|
|
NS_RUNTIMEABORT("Unexpected shutdown reason for toplevel actor.");
|
|
}
|
|
}
|
|
|
|
nsresult
|
|
PluginModuleParent::GetRunID(uint32_t* aRunID)
|
|
{
|
|
if (NS_WARN_IF(!aRunID)) {
|
|
return NS_ERROR_INVALID_POINTER;
|
|
}
|
|
*aRunID = mRunID;
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
PluginModuleChromeParent::ActorDestroy(ActorDestroyReason why)
|
|
{
|
|
if (why == AbnormalShutdown) {
|
|
#ifdef MOZ_CRASHREPORTER
|
|
ProcessFirstMinidump();
|
|
#endif
|
|
Telemetry::Accumulate(Telemetry::SUBPROCESS_ABNORMAL_ABORT,
|
|
NS_LITERAL_CSTRING("plugin"), 1);
|
|
}
|
|
|
|
// We can't broadcast settings changes anymore.
|
|
UnregisterSettingsCallbacks();
|
|
|
|
PluginModuleParent::ActorDestroy(why);
|
|
}
|
|
|
|
void
|
|
PluginModuleParent::NotifyFlashHang()
|
|
{
|
|
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
|
|
if (obs) {
|
|
obs->NotifyObservers(nullptr, "flash-plugin-hang", nullptr);
|
|
}
|
|
}
|
|
|
|
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);
|
|
return;
|
|
}
|
|
|
|
if (mPlugin)
|
|
mPlugin->PluginCrashed(mPluginDumpID, mBrowserDumpID);
|
|
}
|
|
|
|
PPluginInstanceParent*
|
|
PluginModuleParent::AllocPPluginInstanceParent(const nsCString& aMimeType,
|
|
const uint16_t& aMode,
|
|
const InfallibleTArray<nsCString>& aNames,
|
|
const InfallibleTArray<nsCString>& aValues)
|
|
{
|
|
NS_ERROR("Not reachable!");
|
|
return nullptr;
|
|
}
|
|
|
|
bool
|
|
PluginModuleParent::DeallocPPluginInstanceParent(PPluginInstanceParent* aActor)
|
|
{
|
|
PLUGIN_LOG_DEBUG_METHOD;
|
|
delete aActor;
|
|
return true;
|
|
}
|
|
|
|
void
|
|
PluginModuleParent::SetPluginFuncs(NPPluginFuncs* aFuncs)
|
|
{
|
|
MOZ_ASSERT(aFuncs);
|
|
|
|
aFuncs->version = (NP_VERSION_MAJOR << 8) | NP_VERSION_MINOR;
|
|
aFuncs->javaClass = nullptr;
|
|
|
|
// Gecko should always call these functions through a PluginLibrary object.
|
|
aFuncs->newp = nullptr;
|
|
aFuncs->clearsitedata = nullptr;
|
|
aFuncs->getsiteswithdata = nullptr;
|
|
|
|
aFuncs->destroy = NPP_Destroy;
|
|
aFuncs->setwindow = NPP_SetWindow;
|
|
aFuncs->newstream = NPP_NewStream;
|
|
aFuncs->destroystream = NPP_DestroyStream;
|
|
aFuncs->asfile = NPP_StreamAsFile;
|
|
aFuncs->writeready = NPP_WriteReady;
|
|
aFuncs->write = NPP_Write;
|
|
aFuncs->print = NPP_Print;
|
|
aFuncs->event = NPP_HandleEvent;
|
|
aFuncs->urlnotify = NPP_URLNotify;
|
|
aFuncs->getvalue = NPP_GetValue;
|
|
aFuncs->setvalue = NPP_SetValue;
|
|
aFuncs->gotfocus = nullptr;
|
|
aFuncs->lostfocus = nullptr;
|
|
aFuncs->urlredirectnotify = nullptr;
|
|
|
|
// Provide 'NPP_URLRedirectNotify', 'NPP_ClearSiteData', and
|
|
// 'NPP_GetSitesWithData' functionality if it is supported by the plugin.
|
|
bool urlRedirectSupported = false;
|
|
Unused << CallOptionalFunctionsSupported(&urlRedirectSupported,
|
|
&mClearSiteDataSupported,
|
|
&mGetSitesWithDataSupported);
|
|
if (urlRedirectSupported) {
|
|
aFuncs->urlredirectnotify = NPP_URLRedirectNotify;
|
|
}
|
|
}
|
|
|
|
#define RESOLVE_AND_CALL(instance, func) \
|
|
NP_BEGIN_MACRO \
|
|
PluginAsyncSurrogate* surrogate = nullptr; \
|
|
PluginInstanceParent* i = PluginInstanceParent::Cast(instance, &surrogate);\
|
|
if (surrogate && (!i || i->UseSurrogate())) { \
|
|
return surrogate->func; \
|
|
} \
|
|
if (!i) { \
|
|
return NPERR_GENERIC_ERROR; \
|
|
} \
|
|
return i->func; \
|
|
NP_END_MACRO
|
|
|
|
NPError
|
|
PluginModuleParent::NPP_Destroy(NPP instance,
|
|
NPSavedData** saved)
|
|
{
|
|
// FIXME/cjones:
|
|
// (1) send a "destroy" message to the child
|
|
// (2) the child shuts down its instance
|
|
// (3) remove both parent and child IDs from map
|
|
// (4) free parent
|
|
|
|
PLUGIN_LOG_DEBUG_FUNCTION;
|
|
PluginAsyncSurrogate* surrogate = nullptr;
|
|
PluginInstanceParent* parentInstance =
|
|
PluginInstanceParent::Cast(instance, &surrogate);
|
|
if (surrogate && (!parentInstance || parentInstance->UseSurrogate())) {
|
|
return surrogate->NPP_Destroy(saved);
|
|
}
|
|
|
|
if (!parentInstance)
|
|
return NPERR_NO_ERROR;
|
|
|
|
NPError retval = parentInstance->Destroy();
|
|
instance->pdata = nullptr;
|
|
|
|
Unused << PluginInstanceParent::Call__delete__(parentInstance);
|
|
return retval;
|
|
}
|
|
|
|
NPError
|
|
PluginModuleParent::NPP_NewStream(NPP instance, NPMIMEType type,
|
|
NPStream* stream, NPBool seekable,
|
|
uint16_t* stype)
|
|
{
|
|
PROFILER_LABEL("PluginModuleParent", "NPP_NewStream",
|
|
js::ProfileEntry::Category::OTHER);
|
|
RESOLVE_AND_CALL(instance, NPP_NewStream(type, stream, seekable, stype));
|
|
}
|
|
|
|
NPError
|
|
PluginModuleParent::NPP_SetWindow(NPP instance, NPWindow* window)
|
|
{
|
|
RESOLVE_AND_CALL(instance, NPP_SetWindow(window));
|
|
}
|
|
|
|
NPError
|
|
PluginModuleParent::NPP_DestroyStream(NPP instance,
|
|
NPStream* stream,
|
|
NPReason reason)
|
|
{
|
|
RESOLVE_AND_CALL(instance, NPP_DestroyStream(stream, reason));
|
|
}
|
|
|
|
int32_t
|
|
PluginModuleParent::NPP_WriteReady(NPP instance,
|
|
NPStream* stream)
|
|
{
|
|
PluginAsyncSurrogate* surrogate = nullptr;
|
|
BrowserStreamParent* s = StreamCast(instance, stream, &surrogate);
|
|
if (!s) {
|
|
if (surrogate) {
|
|
return surrogate->NPP_WriteReady(stream);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
return s->WriteReady();
|
|
}
|
|
|
|
int32_t
|
|
PluginModuleParent::NPP_Write(NPP instance,
|
|
NPStream* stream,
|
|
int32_t offset,
|
|
int32_t len,
|
|
void* buffer)
|
|
{
|
|
BrowserStreamParent* s = StreamCast(instance, stream);
|
|
if (!s)
|
|
return -1;
|
|
|
|
return s->Write(offset, len, buffer);
|
|
}
|
|
|
|
void
|
|
PluginModuleParent::NPP_StreamAsFile(NPP instance,
|
|
NPStream* stream,
|
|
const char* fname)
|
|
{
|
|
BrowserStreamParent* s = StreamCast(instance, stream);
|
|
if (!s)
|
|
return;
|
|
|
|
s->StreamAsFile(fname);
|
|
}
|
|
|
|
void
|
|
PluginModuleParent::NPP_Print(NPP instance, NPPrint* platformPrint)
|
|
{
|
|
|
|
PluginInstanceParent* i = PluginInstanceParent::Cast(instance);
|
|
i->NPP_Print(platformPrint);
|
|
}
|
|
|
|
int16_t
|
|
PluginModuleParent::NPP_HandleEvent(NPP instance, void* event)
|
|
{
|
|
RESOLVE_AND_CALL(instance, NPP_HandleEvent(event));
|
|
}
|
|
|
|
void
|
|
PluginModuleParent::NPP_URLNotify(NPP instance, const char* url,
|
|
NPReason reason, void* notifyData)
|
|
{
|
|
PluginInstanceParent* i = PluginInstanceParent::Cast(instance);
|
|
if (!i)
|
|
return;
|
|
|
|
i->NPP_URLNotify(url, reason, notifyData);
|
|
}
|
|
|
|
NPError
|
|
PluginModuleParent::NPP_GetValue(NPP instance,
|
|
NPPVariable variable, void *ret_value)
|
|
{
|
|
// The rules are slightly different for this function.
|
|
// If there is a surrogate, we *always* use it.
|
|
PluginAsyncSurrogate* surrogate = nullptr;
|
|
PluginInstanceParent* i = PluginInstanceParent::Cast(instance, &surrogate);
|
|
if (surrogate) {
|
|
return surrogate->NPP_GetValue(variable, ret_value);
|
|
}
|
|
if (!i) {
|
|
return NPERR_GENERIC_ERROR;
|
|
}
|
|
return i->NPP_GetValue(variable, ret_value);
|
|
}
|
|
|
|
NPError
|
|
PluginModuleParent::NPP_SetValue(NPP instance, NPNVariable variable,
|
|
void *value)
|
|
{
|
|
RESOLVE_AND_CALL(instance, NPP_SetValue(variable, value));
|
|
}
|
|
|
|
bool
|
|
PluginModuleParent::RecvBackUpXResources(const FileDescriptor& aXSocketFd)
|
|
{
|
|
#ifndef MOZ_X11
|
|
NS_RUNTIMEABORT("This message only makes sense on X11 platforms");
|
|
#else
|
|
MOZ_ASSERT(0 > mPluginXSocketFdDup.get(),
|
|
"Already backed up X resources??");
|
|
mPluginXSocketFdDup.forget();
|
|
if (aXSocketFd.IsValid()) {
|
|
mPluginXSocketFdDup.reset(aXSocketFd.PlatformHandle());
|
|
}
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
void
|
|
PluginModuleParent::NPP_URLRedirectNotify(NPP instance, const char* url,
|
|
int32_t status, void* notifyData)
|
|
{
|
|
PluginInstanceParent* i = PluginInstanceParent::Cast(instance);
|
|
if (!i)
|
|
return;
|
|
|
|
i->NPP_URLRedirectNotify(url, status, notifyData);
|
|
}
|
|
|
|
BrowserStreamParent*
|
|
PluginModuleParent::StreamCast(NPP instance, NPStream* s,
|
|
PluginAsyncSurrogate** aSurrogate)
|
|
{
|
|
PluginInstanceParent* ip = PluginInstanceParent::Cast(instance, aSurrogate);
|
|
if (!ip || (aSurrogate && *aSurrogate && ip->UseSurrogate())) {
|
|
return nullptr;
|
|
}
|
|
|
|
BrowserStreamParent* sp =
|
|
static_cast<BrowserStreamParent*>(static_cast<AStream*>(s->pdata));
|
|
if (sp && (sp->mNPP != ip || s != sp->mStream)) {
|
|
NS_RUNTIMEABORT("Corrupted plugin stream data.");
|
|
}
|
|
return sp;
|
|
}
|
|
|
|
bool
|
|
PluginModuleParent::HasRequiredFunctions()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
nsresult
|
|
PluginModuleParent::AsyncSetWindow(NPP instance, NPWindow* window)
|
|
{
|
|
PluginAsyncSurrogate* surrogate = nullptr;
|
|
PluginInstanceParent* i = PluginInstanceParent::Cast(instance, &surrogate);
|
|
if (surrogate && (!i || i->UseSurrogate())) {
|
|
return surrogate->AsyncSetWindow(window);
|
|
} else if (!i) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
return i->AsyncSetWindow(window);
|
|
}
|
|
|
|
nsresult
|
|
PluginModuleParent::GetImageContainer(NPP instance,
|
|
mozilla::layers::ImageContainer** aContainer)
|
|
{
|
|
PluginInstanceParent* i = PluginInstanceParent::Cast(instance);
|
|
return !i ? NS_ERROR_FAILURE : i->GetImageContainer(aContainer);
|
|
}
|
|
|
|
nsresult
|
|
PluginModuleParent::GetImageSize(NPP instance,
|
|
nsIntSize* aSize)
|
|
{
|
|
PluginInstanceParent* i = PluginInstanceParent::Cast(instance);
|
|
return !i ? NS_ERROR_FAILURE : i->GetImageSize(aSize);
|
|
}
|
|
|
|
void
|
|
PluginModuleParent::DidComposite(NPP aInstance)
|
|
{
|
|
if (PluginInstanceParent* i = PluginInstanceParent::Cast(aInstance)) {
|
|
i->DidComposite();
|
|
}
|
|
}
|
|
|
|
nsresult
|
|
PluginModuleParent::SetBackgroundUnknown(NPP instance)
|
|
{
|
|
PluginInstanceParent* i = PluginInstanceParent::Cast(instance);
|
|
if (!i)
|
|
return NS_ERROR_FAILURE;
|
|
|
|
return i->SetBackgroundUnknown();
|
|
}
|
|
|
|
nsresult
|
|
PluginModuleParent::BeginUpdateBackground(NPP instance,
|
|
const nsIntRect& aRect,
|
|
DrawTarget** aDrawTarget)
|
|
{
|
|
PluginInstanceParent* i = PluginInstanceParent::Cast(instance);
|
|
if (!i)
|
|
return NS_ERROR_FAILURE;
|
|
|
|
return i->BeginUpdateBackground(aRect, aDrawTarget);
|
|
}
|
|
|
|
nsresult
|
|
PluginModuleParent::EndUpdateBackground(NPP instance, const nsIntRect& aRect)
|
|
{
|
|
PluginInstanceParent* i = PluginInstanceParent::Cast(instance);
|
|
if (!i)
|
|
return NS_ERROR_FAILURE;
|
|
|
|
return i->EndUpdateBackground(aRect);
|
|
}
|
|
|
|
void
|
|
PluginModuleParent::OnInitFailure()
|
|
{
|
|
if (GetIPCChannel()->CanSend()) {
|
|
Close();
|
|
}
|
|
|
|
mShutdown = true;
|
|
|
|
if (mIsStartingAsync) {
|
|
/* If we've failed then we need to enumerate any pending NPP_New calls
|
|
and clean them up. */
|
|
uint32_t len = mSurrogateInstances.Length();
|
|
for (uint32_t i = 0; i < len; ++i) {
|
|
mSurrogateInstances[i]->NotifyAsyncInitFailed();
|
|
}
|
|
mSurrogateInstances.Clear();
|
|
}
|
|
}
|
|
|
|
class OfflineObserver final : public nsIObserver
|
|
{
|
|
public:
|
|
NS_DECL_ISUPPORTS
|
|
NS_DECL_NSIOBSERVER
|
|
|
|
explicit OfflineObserver(PluginModuleChromeParent* pmp)
|
|
: mPmp(pmp)
|
|
{}
|
|
|
|
private:
|
|
~OfflineObserver() {}
|
|
PluginModuleChromeParent* mPmp;
|
|
};
|
|
|
|
NS_IMPL_ISUPPORTS(OfflineObserver, nsIObserver)
|
|
|
|
NS_IMETHODIMP
|
|
OfflineObserver::Observe(nsISupports *aSubject,
|
|
const char *aTopic,
|
|
const char16_t *aData)
|
|
{
|
|
MOZ_ASSERT(!strcmp(aTopic, "ipc:network:set-offline"));
|
|
mPmp->CachedSettingChanged();
|
|
return NS_OK;
|
|
}
|
|
|
|
static const char* kSettingsPrefs[] =
|
|
{"javascript.enabled",
|
|
"dom.ipc.plugins.nativeCursorSupport"};
|
|
|
|
void
|
|
PluginModuleChromeParent::RegisterSettingsCallbacks()
|
|
{
|
|
for (size_t i = 0; i < ArrayLength(kSettingsPrefs); i++) {
|
|
Preferences::RegisterCallback(CachedSettingChanged, kSettingsPrefs[i], this);
|
|
}
|
|
|
|
nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
|
|
if (observerService) {
|
|
mOfflineObserver = new OfflineObserver(this);
|
|
observerService->AddObserver(mOfflineObserver, "ipc:network:set-offline", false);
|
|
}
|
|
}
|
|
|
|
void
|
|
PluginModuleChromeParent::UnregisterSettingsCallbacks()
|
|
{
|
|
for (size_t i = 0; i < ArrayLength(kSettingsPrefs); i++) {
|
|
Preferences::UnregisterCallback(CachedSettingChanged, kSettingsPrefs[i], this);
|
|
}
|
|
|
|
nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
|
|
if (observerService) {
|
|
observerService->RemoveObserver(mOfflineObserver, "ipc:network:set-offline");
|
|
mOfflineObserver = nullptr;
|
|
}
|
|
}
|
|
|
|
bool
|
|
PluginModuleParent::GetSetting(NPNVariable aVariable)
|
|
{
|
|
NPBool boolVal = false;
|
|
mozilla::plugins::parent::_getvalue(nullptr, aVariable, &boolVal);
|
|
return boolVal;
|
|
}
|
|
|
|
void
|
|
PluginModuleParent::GetSettings(PluginSettings* aSettings)
|
|
{
|
|
aSettings->javascriptEnabled() = GetSetting(NPNVjavascriptEnabledBool);
|
|
aSettings->asdEnabled() = GetSetting(NPNVasdEnabledBool);
|
|
aSettings->isOffline() = GetSetting(NPNVisOfflineBool);
|
|
aSettings->supportsXembed() = GetSetting(NPNVSupportsXEmbedBool);
|
|
aSettings->supportsWindowless() = GetSetting(NPNVSupportsWindowless);
|
|
aSettings->userAgent() = NullableString(mNPNIface->uagent(nullptr));
|
|
|
|
#if defined(XP_MACOSX)
|
|
aSettings->nativeCursorsSupported() =
|
|
Preferences::GetBool("dom.ipc.plugins.nativeCursorSupport", false);
|
|
#else
|
|
// Need to initialize this to satisfy IPDL.
|
|
aSettings->nativeCursorsSupported() = false;
|
|
#endif
|
|
}
|
|
|
|
void
|
|
PluginModuleChromeParent::CachedSettingChanged()
|
|
{
|
|
PluginSettings settings;
|
|
GetSettings(&settings);
|
|
Unused << SendSettingChanged(settings);
|
|
}
|
|
|
|
/* static */ void
|
|
PluginModuleChromeParent::CachedSettingChanged(const char* aPref, void* aModule)
|
|
{
|
|
PluginModuleChromeParent *module = static_cast<PluginModuleChromeParent*>(aModule);
|
|
module->CachedSettingChanged();
|
|
}
|
|
|
|
#if defined(XP_UNIX) && !defined(XP_MACOSX) && !defined(MOZ_WIDGET_GONK)
|
|
nsresult
|
|
PluginModuleParent::NP_Initialize(NPNetscapeFuncs* bFuncs, NPPluginFuncs* pFuncs, NPError* error)
|
|
{
|
|
PLUGIN_LOG_DEBUG_METHOD;
|
|
|
|
mNPNIface = bFuncs;
|
|
mNPPIface = pFuncs;
|
|
|
|
if (mShutdown) {
|
|
*error = NPERR_GENERIC_ERROR;
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
*error = NPERR_NO_ERROR;
|
|
if (mIsStartingAsync) {
|
|
if (GetIPCChannel()->CanSend()) {
|
|
// We're already connected, so we may call this immediately.
|
|
RecvNP_InitializeResult(*error);
|
|
} else {
|
|
PluginAsyncSurrogate::NP_GetEntryPoints(pFuncs);
|
|
}
|
|
} else {
|
|
SetPluginFuncs(pFuncs);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
PluginModuleChromeParent::NP_Initialize(NPNetscapeFuncs* bFuncs, NPPluginFuncs* pFuncs, NPError* error)
|
|
{
|
|
PLUGIN_LOG_DEBUG_METHOD;
|
|
|
|
if (mShutdown) {
|
|
*error = NPERR_GENERIC_ERROR;
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
*error = NPERR_NO_ERROR;
|
|
|
|
mNPNIface = bFuncs;
|
|
mNPPIface = pFuncs;
|
|
|
|
// NB: This *MUST* be set prior to checking whether the subprocess has
|
|
// been connected!
|
|
if (mIsStartingAsync) {
|
|
PluginAsyncSurrogate::NP_GetEntryPoints(pFuncs);
|
|
}
|
|
|
|
if (!mSubprocess->IsConnected()) {
|
|
// The subprocess isn't connected yet. Defer NP_Initialize until
|
|
// OnProcessLaunched is invoked.
|
|
mInitOnAsyncConnect = true;
|
|
return NS_OK;
|
|
}
|
|
|
|
PluginSettings settings;
|
|
GetSettings(&settings);
|
|
|
|
TimeStamp callNpInitStart = TimeStamp::Now();
|
|
// Asynchronous case
|
|
if (mIsStartingAsync) {
|
|
if (!SendAsyncNP_Initialize(settings)) {
|
|
Close();
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
TimeStamp callNpInitEnd = TimeStamp::Now();
|
|
mTimeBlocked += (callNpInitEnd - callNpInitStart);
|
|
return NS_OK;
|
|
}
|
|
|
|
// Synchronous case
|
|
if (!CallNP_Initialize(settings, error)) {
|
|
Close();
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
else if (*error != NPERR_NO_ERROR) {
|
|
Close();
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
TimeStamp callNpInitEnd = TimeStamp::Now();
|
|
mTimeBlocked += (callNpInitEnd - callNpInitStart);
|
|
|
|
RecvNP_InitializeResult(*error);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
bool
|
|
PluginModuleParent::RecvNP_InitializeResult(const NPError& aError)
|
|
{
|
|
if (aError != NPERR_NO_ERROR) {
|
|
OnInitFailure();
|
|
return true;
|
|
}
|
|
|
|
SetPluginFuncs(mNPPIface);
|
|
if (mIsStartingAsync) {
|
|
InitAsyncSurrogates();
|
|
}
|
|
|
|
mNPInitialized = true;
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
PluginModuleChromeParent::RecvNP_InitializeResult(const NPError& aError)
|
|
{
|
|
if (!mContentParent) {
|
|
return PluginModuleParent::RecvNP_InitializeResult(aError);
|
|
}
|
|
bool initOk = aError == NPERR_NO_ERROR;
|
|
if (initOk) {
|
|
SetPluginFuncs(mNPPIface);
|
|
if (mIsStartingAsync && !SendAssociatePluginId()) {
|
|
initOk = false;
|
|
}
|
|
}
|
|
mNPInitialized = initOk;
|
|
return mContentParent->SendLoadPluginResult(mPluginId, initOk);
|
|
}
|
|
|
|
#else
|
|
|
|
nsresult
|
|
PluginModuleParent::NP_Initialize(NPNetscapeFuncs* bFuncs, NPError* error)
|
|
{
|
|
PLUGIN_LOG_DEBUG_METHOD;
|
|
|
|
mNPNIface = bFuncs;
|
|
|
|
if (mShutdown) {
|
|
*error = NPERR_GENERIC_ERROR;
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
*error = NPERR_NO_ERROR;
|
|
return NS_OK;
|
|
}
|
|
|
|
#if defined(XP_WIN) || defined(XP_MACOSX)
|
|
|
|
nsresult
|
|
PluginModuleContentParent::NP_Initialize(NPNetscapeFuncs* bFuncs, NPError* error)
|
|
{
|
|
PLUGIN_LOG_DEBUG_METHOD;
|
|
nsresult rv = PluginModuleParent::NP_Initialize(bFuncs, error);
|
|
if (mIsStartingAsync && GetIPCChannel()->CanSend()) {
|
|
// We're already connected, so we may call this immediately.
|
|
RecvNP_InitializeResult(*error);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
#endif
|
|
|
|
nsresult
|
|
PluginModuleChromeParent::NP_Initialize(NPNetscapeFuncs* bFuncs, NPError* error)
|
|
{
|
|
nsresult rv = PluginModuleParent::NP_Initialize(bFuncs, error);
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
#if defined(XP_MACOSX)
|
|
if (!mSubprocess->IsConnected()) {
|
|
// The subprocess isn't connected yet. Defer NP_Initialize until
|
|
// OnProcessLaunched is invoked.
|
|
mInitOnAsyncConnect = true;
|
|
*error = NPERR_NO_ERROR;
|
|
return NS_OK;
|
|
}
|
|
#else
|
|
if (mInitOnAsyncConnect) {
|
|
*error = NPERR_NO_ERROR;
|
|
return NS_OK;
|
|
}
|
|
#endif
|
|
|
|
PluginSettings settings;
|
|
GetSettings(&settings);
|
|
|
|
TimeStamp callNpInitStart = TimeStamp::Now();
|
|
if (mIsStartingAsync) {
|
|
if (!SendAsyncNP_Initialize(settings)) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
TimeStamp callNpInitEnd = TimeStamp::Now();
|
|
mTimeBlocked += (callNpInitEnd - callNpInitStart);
|
|
return NS_OK;
|
|
}
|
|
|
|
if (!CallNP_Initialize(settings, error)) {
|
|
Close();
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
TimeStamp callNpInitEnd = TimeStamp::Now();
|
|
mTimeBlocked += (callNpInitEnd - callNpInitStart);
|
|
RecvNP_InitializeResult(*error);
|
|
return NS_OK;
|
|
}
|
|
|
|
bool
|
|
PluginModuleParent::RecvNP_InitializeResult(const NPError& aError)
|
|
{
|
|
if (aError != NPERR_NO_ERROR) {
|
|
OnInitFailure();
|
|
return true;
|
|
}
|
|
|
|
if (mIsStartingAsync && mNPPIface) {
|
|
SetPluginFuncs(mNPPIface);
|
|
InitAsyncSurrogates();
|
|
}
|
|
|
|
mNPInitialized = true;
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
PluginModuleChromeParent::RecvNP_InitializeResult(const NPError& aError)
|
|
{
|
|
bool ok = true;
|
|
if (mContentParent) {
|
|
if ((ok = SendAssociatePluginId())) {
|
|
ok = mContentParent->SendLoadPluginResult(mPluginId,
|
|
aError == NPERR_NO_ERROR);
|
|
}
|
|
} else if (aError == NPERR_NO_ERROR) {
|
|
// Initialization steps when e10s is disabled
|
|
#if defined XP_WIN
|
|
if (mIsStartingAsync) {
|
|
SetPluginFuncs(mNPPIface);
|
|
}
|
|
|
|
// Send the info needed to join the chrome process's audio session to the
|
|
// plugin process
|
|
nsID id;
|
|
nsString sessionName;
|
|
nsString iconPath;
|
|
|
|
if (NS_SUCCEEDED(mozilla::widget::GetAudioSessionData(id, sessionName,
|
|
iconPath))) {
|
|
Unused << SendSetAudioSessionData(id, sessionName, iconPath);
|
|
}
|
|
#endif
|
|
|
|
#ifdef MOZ_CRASHREPORTER_INJECTOR
|
|
InitializeInjector();
|
|
#endif
|
|
}
|
|
|
|
return PluginModuleParent::RecvNP_InitializeResult(aError) && ok;
|
|
}
|
|
|
|
#endif
|
|
|
|
void
|
|
PluginModuleParent::InitAsyncSurrogates()
|
|
{
|
|
if (MaybeRunDeferredShutdown()) {
|
|
// We've shut down, so the surrogates are no longer valid. Clear
|
|
// mSurrogateInstances to ensure that these aren't used.
|
|
mSurrogateInstances.Clear();
|
|
return;
|
|
}
|
|
|
|
uint32_t len = mSurrogateInstances.Length();
|
|
for (uint32_t i = 0; i < len; ++i) {
|
|
NPError err;
|
|
mAsyncNewRv = mSurrogateInstances[i]->NPP_New(&err);
|
|
if (NS_FAILED(mAsyncNewRv)) {
|
|
mSurrogateInstances[i]->NotifyAsyncInitFailed();
|
|
continue;
|
|
}
|
|
}
|
|
mSurrogateInstances.Clear();
|
|
}
|
|
|
|
bool
|
|
PluginModuleParent::RemovePendingSurrogate(
|
|
const RefPtr<PluginAsyncSurrogate>& aSurrogate)
|
|
{
|
|
return mSurrogateInstances.RemoveElement(aSurrogate);
|
|
}
|
|
|
|
bool
|
|
PluginModuleParent::MaybeRunDeferredShutdown()
|
|
{
|
|
if (!mIsStartingAsync || !mIsNPShutdownPending) {
|
|
return false;
|
|
}
|
|
MOZ_ASSERT(!mShutdown);
|
|
NPError error;
|
|
if (!DoShutdown(&error)) {
|
|
return false;
|
|
}
|
|
mIsNPShutdownPending = false;
|
|
return true;
|
|
}
|
|
|
|
nsresult
|
|
PluginModuleParent::NP_Shutdown(NPError* error)
|
|
{
|
|
PLUGIN_LOG_DEBUG_METHOD;
|
|
|
|
if (mShutdown) {
|
|
*error = NPERR_GENERIC_ERROR;
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
/* If we're still running an async NP_Initialize then we need to defer
|
|
shutdown until we've received the result of the NP_Initialize call. */
|
|
if (mIsStartingAsync && !mNPInitialized) {
|
|
mIsNPShutdownPending = true;
|
|
*error = NPERR_NO_ERROR;
|
|
return NS_OK;
|
|
}
|
|
|
|
if (!DoShutdown(error)) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
bool
|
|
PluginModuleParent::DoShutdown(NPError* error)
|
|
{
|
|
bool ok = true;
|
|
if (IsChrome() && mHadLocalInstance) {
|
|
// We synchronously call NP_Shutdown if the chrome process was using
|
|
// plugins itself. That way we can service any requests the plugin
|
|
// makes. If we're in e10s, though, the content processes will have
|
|
// already shut down and there's no one to talk to. So we shut down
|
|
// asynchronously in PluginModuleChild::ActorDestroy.
|
|
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();
|
|
|
|
// mShutdown should either be initialized to false, or be transitiong from
|
|
// false to true. It is never ok to go from true to false. Using OR for
|
|
// the following assignment to ensure this.
|
|
mShutdown |= ok;
|
|
if (!ok) {
|
|
*error = NPERR_GENERIC_ERROR;
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
nsresult
|
|
PluginModuleParent::NP_GetMIMEDescription(const char** mimeDesc)
|
|
{
|
|
PLUGIN_LOG_DEBUG_METHOD;
|
|
|
|
*mimeDesc = "application/x-foobar";
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
PluginModuleParent::NP_GetValue(void *future, NPPVariable aVariable,
|
|
void *aValue, NPError* error)
|
|
{
|
|
MOZ_LOG(GetPluginLog(), LogLevel::Warning, ("%s Not implemented, requested variable %i", __FUNCTION__,
|
|
(int) aVariable));
|
|
|
|
//TODO: implement this correctly
|
|
*error = NPERR_GENERIC_ERROR;
|
|
return NS_OK;
|
|
}
|
|
|
|
#if defined(XP_WIN) || defined(XP_MACOSX)
|
|
nsresult
|
|
PluginModuleParent::NP_GetEntryPoints(NPPluginFuncs* pFuncs, NPError* error)
|
|
{
|
|
NS_ASSERTION(pFuncs, "Null pointer!");
|
|
|
|
*error = NPERR_NO_ERROR;
|
|
if (mIsStartingAsync && !IsChrome()) {
|
|
mNPPIface = pFuncs;
|
|
#if defined(XP_MACOSX)
|
|
if (mNPInitialized) {
|
|
SetPluginFuncs(pFuncs);
|
|
InitAsyncSurrogates();
|
|
} else {
|
|
PluginAsyncSurrogate::NP_GetEntryPoints(pFuncs);
|
|
}
|
|
#else
|
|
PluginAsyncSurrogate::NP_GetEntryPoints(pFuncs);
|
|
#endif
|
|
} else {
|
|
SetPluginFuncs(pFuncs);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
PluginModuleChromeParent::NP_GetEntryPoints(NPPluginFuncs* pFuncs, NPError* error)
|
|
{
|
|
#if defined(XP_MACOSX)
|
|
if (mInitOnAsyncConnect) {
|
|
PluginAsyncSurrogate::NP_GetEntryPoints(pFuncs);
|
|
mNPPIface = pFuncs;
|
|
*error = NPERR_NO_ERROR;
|
|
return NS_OK;
|
|
}
|
|
#else
|
|
if (mIsStartingAsync) {
|
|
PluginAsyncSurrogate::NP_GetEntryPoints(pFuncs);
|
|
}
|
|
if (!mSubprocess->IsConnected()) {
|
|
mNPPIface = pFuncs;
|
|
mInitOnAsyncConnect = true;
|
|
*error = NPERR_NO_ERROR;
|
|
return NS_OK;
|
|
}
|
|
#endif
|
|
|
|
// 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 (!CallNP_GetEntryPoints(error)) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
else if (*error != NPERR_NO_ERROR) {
|
|
return NS_OK;
|
|
}
|
|
|
|
return PluginModuleParent::NP_GetEntryPoints(pFuncs, error);
|
|
}
|
|
|
|
#endif
|
|
|
|
nsresult
|
|
PluginModuleParent::NPP_New(NPMIMEType pluginType, NPP instance,
|
|
uint16_t mode, int16_t argc, char* argn[],
|
|
char* argv[], NPSavedData* saved,
|
|
NPError* error)
|
|
{
|
|
PLUGIN_LOG_DEBUG_METHOD;
|
|
|
|
if (mShutdown) {
|
|
*error = NPERR_GENERIC_ERROR;
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
if (mIsStartingAsync) {
|
|
if (!PluginAsyncSurrogate::Create(this, pluginType, instance, mode,
|
|
argc, argn, argv)) {
|
|
*error = NPERR_GENERIC_ERROR;
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
if (!mNPInitialized) {
|
|
RefPtr<PluginAsyncSurrogate> surrogate =
|
|
PluginAsyncSurrogate::Cast(instance);
|
|
mSurrogateInstances.AppendElement(surrogate);
|
|
*error = NPERR_NO_ERROR;
|
|
return NS_PLUGIN_INIT_PENDING;
|
|
}
|
|
}
|
|
|
|
// create the instance on the other side
|
|
InfallibleTArray<nsCString> names;
|
|
InfallibleTArray<nsCString> values;
|
|
|
|
for (int i = 0; i < argc; ++i) {
|
|
names.AppendElement(NullableString(argn[i]));
|
|
values.AppendElement(NullableString(argv[i]));
|
|
}
|
|
|
|
nsresult rv = NPP_NewInternal(pluginType, instance, mode, names, values,
|
|
saved, error);
|
|
if (NS_FAILED(rv) || !mIsStartingAsync) {
|
|
return rv;
|
|
}
|
|
return NS_PLUGIN_INIT_PENDING;
|
|
}
|
|
|
|
class nsCaseInsensitiveUTF8StringArrayComparator
|
|
{
|
|
public:
|
|
template<class A, class B>
|
|
bool Equals(const A& a, const B& b) const {
|
|
return a.Equals(b.get(), nsCaseInsensitiveUTF8StringComparator());
|
|
}
|
|
};
|
|
|
|
nsresult
|
|
PluginModuleParent::NPP_NewInternal(NPMIMEType pluginType, NPP instance,
|
|
uint16_t mode,
|
|
InfallibleTArray<nsCString>& names,
|
|
InfallibleTArray<nsCString>& values,
|
|
NPSavedData* saved, NPError* error)
|
|
{
|
|
if (mPluginName.IsEmpty()) {
|
|
GetPluginDetails();
|
|
InitQuirksModes(nsDependentCString(pluginType));
|
|
/** mTimeBlocked measures the time that the main thread has been blocked
|
|
* on plugin module initialization. As implemented, this is the sum of
|
|
* plugin-container launch + toolhelp32 snapshot + NP_Initialize.
|
|
* We don't accumulate its value until here because the plugin info
|
|
* is not available until *after* NP_Initialize.
|
|
*/
|
|
Telemetry::Accumulate(Telemetry::BLOCKED_ON_PLUGIN_MODULE_INIT_MS,
|
|
GetHistogramKey(),
|
|
static_cast<uint32_t>(mTimeBlocked.ToMilliseconds()));
|
|
mTimeBlocked = TimeDuration();
|
|
}
|
|
|
|
nsCaseInsensitiveUTF8StringArrayComparator comparator;
|
|
NS_NAMED_LITERAL_CSTRING(srcAttributeName, "src");
|
|
auto srcAttributeIndex = names.IndexOf(srcAttributeName, 0, comparator);
|
|
nsAutoCString srcAttribute;
|
|
if (srcAttributeIndex != names.NoIndex) {
|
|
srcAttribute = values[srcAttributeIndex];
|
|
}
|
|
|
|
nsDependentCString strPluginType(pluginType);
|
|
PluginInstanceParent* parentInstance =
|
|
new PluginInstanceParent(this, instance, strPluginType, mNPNIface);
|
|
|
|
if (mIsFlashPlugin) {
|
|
parentInstance->InitMetadata(strPluginType, srcAttribute);
|
|
#ifdef XP_WIN
|
|
// Force windowless mode (bug 1201904) when sandbox level >= 2
|
|
if (mSandboxLevel >= 2) {
|
|
NS_NAMED_LITERAL_CSTRING(wmodeAttributeName, "wmode");
|
|
NS_NAMED_LITERAL_CSTRING(opaqueAttributeValue, "opaque");
|
|
auto wmodeAttributeIndex =
|
|
names.IndexOf(wmodeAttributeName, 0, comparator);
|
|
if (wmodeAttributeIndex != names.NoIndex) {
|
|
if (!values[wmodeAttributeIndex].EqualsLiteral("transparent")) {
|
|
values[wmodeAttributeIndex].Assign(opaqueAttributeValue);
|
|
}
|
|
} else {
|
|
names.AppendElement(wmodeAttributeName);
|
|
values.AppendElement(opaqueAttributeValue);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// Release the surrogate reference that was in pdata
|
|
RefPtr<PluginAsyncSurrogate> surrogate(
|
|
dont_AddRef(PluginAsyncSurrogate::Cast(instance)));
|
|
// Now replace it with the instance
|
|
instance->pdata = static_cast<PluginDataResolver*>(parentInstance);
|
|
|
|
if (!SendPPluginInstanceConstructor(parentInstance,
|
|
nsDependentCString(pluginType), mode,
|
|
names, values)) {
|
|
// |parentInstance| is automatically deleted.
|
|
instance->pdata = nullptr;
|
|
*error = NPERR_GENERIC_ERROR;
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
{ // Scope for timer
|
|
Telemetry::AutoTimer<Telemetry::BLOCKED_ON_PLUGIN_INSTANCE_INIT_MS>
|
|
timer(GetHistogramKey());
|
|
if (mIsStartingAsync) {
|
|
MOZ_ASSERT(surrogate);
|
|
surrogate->AsyncCallDeparting();
|
|
if (!SendAsyncNPP_New(parentInstance)) {
|
|
*error = NPERR_GENERIC_ERROR;
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
*error = NPERR_NO_ERROR;
|
|
} else {
|
|
if (!CallSyncNPP_New(parentInstance, error)) {
|
|
// if IPC is down, we'll get an immediate "failed" return, but
|
|
// without *error being set. So make sure that the error
|
|
// condition is signaled to nsNPAPIPluginInstance
|
|
if (NPERR_NO_ERROR == *error) {
|
|
*error = NPERR_GENERIC_ERROR;
|
|
}
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (*error != NPERR_NO_ERROR) {
|
|
if (!mIsStartingAsync) {
|
|
NPP_Destroy(instance, 0);
|
|
}
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
UpdatePluginTimeout();
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
PluginModuleChromeParent::UpdatePluginTimeout()
|
|
{
|
|
TimeoutChanged(kParentTimeoutPref, this);
|
|
}
|
|
|
|
nsresult
|
|
PluginModuleParent::NPP_ClearSiteData(const char* site, uint64_t flags, uint64_t maxAge,
|
|
nsCOMPtr<nsIClearSiteDataCallback> callback)
|
|
{
|
|
if (!mClearSiteDataSupported)
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
|
|
static uint64_t callbackId = 0;
|
|
callbackId++;
|
|
mClearSiteDataCallbacks[callbackId] = callback;
|
|
|
|
if (!SendNPP_ClearSiteData(NullableString(site), flags, maxAge, callbackId)) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
nsresult
|
|
PluginModuleParent::NPP_GetSitesWithData(nsCOMPtr<nsIGetSitesWithDataCallback> callback)
|
|
{
|
|
if (!mGetSitesWithDataSupported)
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
|
|
static uint64_t callbackId = 0;
|
|
callbackId++;
|
|
mSitesWithDataCallbacks[callbackId] = callback;
|
|
|
|
if (!SendNPP_GetSitesWithData(callbackId))
|
|
return NS_ERROR_FAILURE;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
#if defined(XP_MACOSX)
|
|
nsresult
|
|
PluginModuleParent::IsRemoteDrawingCoreAnimation(NPP instance, bool *aDrawing)
|
|
{
|
|
PluginInstanceParent* i = PluginInstanceParent::Cast(instance);
|
|
if (!i)
|
|
return NS_ERROR_FAILURE;
|
|
|
|
return i->IsRemoteDrawingCoreAnimation(aDrawing);
|
|
}
|
|
|
|
nsresult
|
|
PluginModuleParent::ContentsScaleFactorChanged(NPP instance, double aContentsScaleFactor)
|
|
{
|
|
PluginInstanceParent* i = PluginInstanceParent::Cast(instance);
|
|
if (!i)
|
|
return NS_ERROR_FAILURE;
|
|
|
|
return i->ContentsScaleFactorChanged(aContentsScaleFactor);
|
|
}
|
|
#endif // #if defined(XP_MACOSX)
|
|
|
|
#if defined(MOZ_WIDGET_QT)
|
|
bool
|
|
PluginModuleParent::AnswerProcessSomeEvents()
|
|
{
|
|
PLUGIN_LOG_DEBUG(("Spinning mini nested loop ..."));
|
|
PluginHelperQt::AnswerProcessSomeEvents();
|
|
PLUGIN_LOG_DEBUG(("... quitting mini nested loop"));
|
|
|
|
return true;
|
|
}
|
|
|
|
#elif defined(XP_MACOSX)
|
|
bool
|
|
PluginModuleParent::AnswerProcessSomeEvents()
|
|
{
|
|
mozilla::plugins::PluginUtilsOSX::InvokeNativeEventLoop();
|
|
return true;
|
|
}
|
|
|
|
#elif !defined(MOZ_WIDGET_GTK)
|
|
bool
|
|
PluginModuleParent::AnswerProcessSomeEvents()
|
|
{
|
|
NS_RUNTIMEABORT("unreached");
|
|
return false;
|
|
}
|
|
|
|
#else
|
|
static const int kMaxChancesToProcessEvents = 20;
|
|
|
|
bool
|
|
PluginModuleParent::AnswerProcessSomeEvents()
|
|
{
|
|
PLUGIN_LOG_DEBUG(("Spinning mini nested loop ..."));
|
|
|
|
int i = 0;
|
|
for (; i < kMaxChancesToProcessEvents; ++i)
|
|
if (!g_main_context_iteration(nullptr, FALSE))
|
|
break;
|
|
|
|
PLUGIN_LOG_DEBUG(("... quitting mini nested loop; processed %i tasks", i));
|
|
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
bool
|
|
PluginModuleParent::RecvProcessNativeEventsInInterruptCall()
|
|
{
|
|
PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
|
|
#if defined(OS_WIN)
|
|
ProcessNativeEventsInInterruptCall();
|
|
return true;
|
|
#else
|
|
NS_NOTREACHED(
|
|
"PluginModuleParent::RecvProcessNativeEventsInInterruptCall not implemented!");
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
void
|
|
PluginModuleParent::ProcessRemoteNativeEventsInInterruptCall()
|
|
{
|
|
#if defined(OS_WIN)
|
|
Unused << SendProcessNativeEventsInInterruptCall();
|
|
return;
|
|
#endif
|
|
NS_NOTREACHED(
|
|
"PluginModuleParent::ProcessRemoteNativeEventsInInterruptCall not implemented!");
|
|
}
|
|
|
|
bool
|
|
PluginModuleParent::RecvPluginShowWindow(const uint32_t& aWindowId, const bool& aModal,
|
|
const int32_t& aX, const int32_t& aY,
|
|
const size_t& aWidth, const size_t& aHeight)
|
|
{
|
|
PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
|
|
#if defined(XP_MACOSX)
|
|
CGRect windowBound = ::CGRectMake(aX, aY, aWidth, aHeight);
|
|
mac_plugin_interposing::parent::OnPluginShowWindow(aWindowId, windowBound, aModal);
|
|
return true;
|
|
#else
|
|
NS_NOTREACHED(
|
|
"PluginInstanceParent::RecvPluginShowWindow not implemented!");
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
bool
|
|
PluginModuleParent::RecvPluginHideWindow(const uint32_t& aWindowId)
|
|
{
|
|
PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
|
|
#if defined(XP_MACOSX)
|
|
mac_plugin_interposing::parent::OnPluginHideWindow(aWindowId, OtherPid());
|
|
return true;
|
|
#else
|
|
NS_NOTREACHED(
|
|
"PluginInstanceParent::RecvPluginHideWindow not implemented!");
|
|
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
|
|
PluginModuleChromeParent::DeallocPCrashReporterParent(PCrashReporterParent* actor)
|
|
{
|
|
#ifdef MOZ_CRASHREPORTER
|
|
#ifdef XP_WIN
|
|
mozilla::MutexAutoLock lock(mCrashReporterMutex);
|
|
if (actor == static_cast<PCrashReporterParent*>(mCrashReporter)) {
|
|
mCrashReporter = nullptr;
|
|
}
|
|
#endif
|
|
#endif
|
|
delete actor;
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
PluginModuleParent::RecvSetCursor(const NSCursorInfo& aCursorInfo)
|
|
{
|
|
PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
|
|
#if defined(XP_MACOSX)
|
|
mac_plugin_interposing::parent::OnSetCursor(aCursorInfo);
|
|
return true;
|
|
#else
|
|
NS_NOTREACHED(
|
|
"PluginInstanceParent::RecvSetCursor not implemented!");
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
bool
|
|
PluginModuleParent::RecvShowCursor(const bool& aShow)
|
|
{
|
|
PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
|
|
#if defined(XP_MACOSX)
|
|
mac_plugin_interposing::parent::OnShowCursor(aShow);
|
|
return true;
|
|
#else
|
|
NS_NOTREACHED(
|
|
"PluginInstanceParent::RecvShowCursor not implemented!");
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
bool
|
|
PluginModuleParent::RecvPushCursor(const NSCursorInfo& aCursorInfo)
|
|
{
|
|
PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
|
|
#if defined(XP_MACOSX)
|
|
mac_plugin_interposing::parent::OnPushCursor(aCursorInfo);
|
|
return true;
|
|
#else
|
|
NS_NOTREACHED(
|
|
"PluginInstanceParent::RecvPushCursor not implemented!");
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
bool
|
|
PluginModuleParent::RecvPopCursor()
|
|
{
|
|
PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
|
|
#if defined(XP_MACOSX)
|
|
mac_plugin_interposing::parent::OnPopCursor();
|
|
return true;
|
|
#else
|
|
NS_NOTREACHED(
|
|
"PluginInstanceParent::RecvPopCursor not implemented!");
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
bool
|
|
PluginModuleParent::RecvNPN_SetException(const nsCString& aMessage)
|
|
{
|
|
PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
|
|
|
|
// 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()
|
|
{
|
|
RefPtr<nsPluginHost> host = nsPluginHost::GetInst();
|
|
if (host) {
|
|
host->NotifyContentModuleDestroyed(mPluginId);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
PluginModuleParent::RecvReturnClearSiteData(const NPError& aRv,
|
|
const uint64_t& aCallbackId)
|
|
{
|
|
if (mClearSiteDataCallbacks.find(aCallbackId) == mClearSiteDataCallbacks.end()) {
|
|
return true;
|
|
}
|
|
if (!!mClearSiteDataCallbacks[aCallbackId]) {
|
|
nsresult rv;
|
|
switch (aRv) {
|
|
case NPERR_NO_ERROR:
|
|
rv = NS_OK;
|
|
break;
|
|
case NPERR_TIME_RANGE_NOT_SUPPORTED:
|
|
rv = NS_ERROR_PLUGIN_TIME_RANGE_NOT_SUPPORTED;
|
|
break;
|
|
case NPERR_MALFORMED_SITE:
|
|
rv = NS_ERROR_INVALID_ARG;
|
|
break;
|
|
default:
|
|
rv = NS_ERROR_FAILURE;
|
|
}
|
|
mClearSiteDataCallbacks[aCallbackId]->Callback(rv);
|
|
}
|
|
mClearSiteDataCallbacks.erase(aCallbackId);
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
PluginModuleParent::RecvReturnSitesWithData(nsTArray<nsCString>&& aSites,
|
|
const uint64_t& aCallbackId)
|
|
{
|
|
if (mSitesWithDataCallbacks.find(aCallbackId) == mSitesWithDataCallbacks.end()) {
|
|
return true;
|
|
}
|
|
|
|
if (!!mSitesWithDataCallbacks[aCallbackId]) {
|
|
mSitesWithDataCallbacks[aCallbackId]->SitesWithData(aSites);
|
|
}
|
|
mSitesWithDataCallbacks.erase(aCallbackId);
|
|
return true;
|
|
}
|
|
|
|
layers::TextureClientRecycleAllocator*
|
|
PluginModuleParent::EnsureTextureAllocator()
|
|
{
|
|
if (!mTextureAllocator) {
|
|
mTextureAllocator = new TextureClientRecycleAllocator(ImageBridgeChild::GetSingleton());
|
|
}
|
|
return mTextureAllocator;
|
|
}
|
|
|
|
#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)
|
|
{
|
|
PROCESSENTRY32 entry = {
|
|
sizeof(entry)
|
|
};
|
|
for (BOOL ok = Process32First(snapshot, &entry);
|
|
ok;
|
|
ok = Process32Next(snapshot, &entry)) {
|
|
if (entry.th32ParentProcessID == pid) {
|
|
nsString name(entry.szExeFile);
|
|
ToUpperCase(name);
|
|
if (StringBeginsWith(name, NS_LITERAL_STRING(FLASH_PROCESS_PREFIX))) {
|
|
return entry.th32ProcessID;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// We only look for child processes of the Flash plugin, NPSWF*
|
|
#define FLASH_PLUGIN_PREFIX "NPSWF"
|
|
|
|
void
|
|
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)
|
|
return;
|
|
|
|
if (!StringBeginsWith(Substring(path, lastSlash + 1),
|
|
NS_LITERAL_CSTRING(FLASH_PLUGIN_PREFIX)))
|
|
return;
|
|
|
|
TimeStamp th32Start = TimeStamp::Now();
|
|
mFinishInitTask = mChromeTaskFactory.NewTask<FinishInjectorInitTask>();
|
|
mFinishInitTask->Init(this);
|
|
if (!::QueueUserWorkItem(&PluginModuleChromeParent::GetToolhelpSnapshot,
|
|
mFinishInitTask, WT_EXECUTEDEFAULT)) {
|
|
delete mFinishInitTask;
|
|
mFinishInitTask = nullptr;
|
|
return;
|
|
}
|
|
TimeStamp th32End = TimeStamp::Now();
|
|
mTimeBlocked += (th32End - th32Start);
|
|
}
|
|
|
|
void
|
|
PluginModuleChromeParent::DoInjection(const nsAutoHandle& aSnapshot)
|
|
{
|
|
DWORD pluginProcessPID = GetProcessId(Process()->GetChildProcessHandle());
|
|
mFlashProcess1 = GetFlashChildOfPID(pluginProcessPID, aSnapshot);
|
|
if (mFlashProcess1) {
|
|
InjectCrashReporterIntoProcess(mFlashProcess1, this);
|
|
|
|
mFlashProcess2 = GetFlashChildOfPID(mFlashProcess1, aSnapshot);
|
|
if (mFlashProcess2) {
|
|
InjectCrashReporterIntoProcess(mFlashProcess2, this);
|
|
}
|
|
}
|
|
mFinishInitTask = nullptr;
|
|
}
|
|
|
|
DWORD WINAPI
|
|
PluginModuleChromeParent::GetToolhelpSnapshot(LPVOID aContext)
|
|
{
|
|
FinishInjectorInitTask* task = static_cast<FinishInjectorInitTask*>(aContext);
|
|
MOZ_ASSERT(task);
|
|
task->PostToMainThread();
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
PluginModuleChromeParent::OnCrash(DWORD processID)
|
|
{
|
|
if (!mShutdown) {
|
|
GetIPCChannel()->CloseWithError();
|
|
mozilla::ipc::ScopedProcessHandle geckoPluginChild;
|
|
if (base::OpenProcessHandle(OtherPid(), &geckoPluginChild.rwget())) {
|
|
if (!base::KillProcess(geckoPluginChild,
|
|
base::PROCESS_END_KILLED_BY_USER, false)) {
|
|
NS_ERROR("May have failed to kill child process.");
|
|
}
|
|
} else {
|
|
NS_ERROR("Failed to open child process when attempting kill.");
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif // MOZ_CRASHREPORTER_INJECTOR
|
|
|
|
#ifdef MOZ_ENABLE_PROFILER_SPS
|
|
class PluginProfilerObserver final : public nsIObserver,
|
|
public nsSupportsWeakReference
|
|
{
|
|
public:
|
|
NS_DECL_ISUPPORTS
|
|
NS_DECL_NSIOBSERVER
|
|
|
|
explicit PluginProfilerObserver(PluginModuleChromeParent* pmp)
|
|
: mPmp(pmp)
|
|
{}
|
|
|
|
private:
|
|
~PluginProfilerObserver() {}
|
|
PluginModuleChromeParent* mPmp;
|
|
};
|
|
|
|
NS_IMPL_ISUPPORTS(PluginProfilerObserver, nsIObserver, nsISupportsWeakReference)
|
|
|
|
NS_IMETHODIMP
|
|
PluginProfilerObserver::Observe(nsISupports *aSubject,
|
|
const char *aTopic,
|
|
const char16_t *aData)
|
|
{
|
|
if (!strcmp(aTopic, "profiler-started")) {
|
|
nsCOMPtr<nsIProfilerStartParams> params(do_QueryInterface(aSubject));
|
|
mPmp->StartProfiler(params);
|
|
} else if (!strcmp(aTopic, "profiler-stopped")) {
|
|
mPmp->StopProfiler();
|
|
} else if (!strcmp(aTopic, "profiler-subprocess-gather")) {
|
|
mPmp->GatherAsyncProfile();
|
|
} else if (!strcmp(aTopic, "profiler-subprocess")) {
|
|
nsCOMPtr<nsIProfileSaveEvent> pse = do_QueryInterface(aSubject);
|
|
mPmp->GatheredAsyncProfile(pse);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
PluginModuleChromeParent::InitPluginProfiling()
|
|
{
|
|
nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
|
|
if (observerService) {
|
|
mProfilerObserver = new PluginProfilerObserver(this);
|
|
observerService->AddObserver(mProfilerObserver, "profiler-started", false);
|
|
observerService->AddObserver(mProfilerObserver, "profiler-stopped", false);
|
|
observerService->AddObserver(mProfilerObserver, "profiler-subprocess-gather", false);
|
|
observerService->AddObserver(mProfilerObserver, "profiler-subprocess", false);
|
|
}
|
|
}
|
|
|
|
void
|
|
PluginModuleChromeParent::ShutdownPluginProfiling()
|
|
{
|
|
nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
|
|
if (observerService) {
|
|
observerService->RemoveObserver(mProfilerObserver, "profiler-started");
|
|
observerService->RemoveObserver(mProfilerObserver, "profiler-stopped");
|
|
observerService->RemoveObserver(mProfilerObserver, "profiler-subprocess-gather");
|
|
observerService->RemoveObserver(mProfilerObserver, "profiler-subprocess");
|
|
}
|
|
}
|
|
|
|
void
|
|
PluginModuleChromeParent::StartProfiler(nsIProfilerStartParams* aParams)
|
|
{
|
|
if (NS_WARN_IF(!aParams)) {
|
|
return;
|
|
}
|
|
|
|
ProfilerInitParams ipcParams;
|
|
|
|
ipcParams.enabled() = true;
|
|
aParams->GetEntries(&ipcParams.entries());
|
|
aParams->GetInterval(&ipcParams.interval());
|
|
ipcParams.features() = aParams->GetFeatures();
|
|
ipcParams.threadFilters() = aParams->GetThreadFilterNames();
|
|
|
|
Unused << SendStartProfiler(ipcParams);
|
|
|
|
nsCOMPtr<nsIProfiler> profiler(do_GetService("@mozilla.org/tools/profiler;1"));
|
|
if (NS_WARN_IF(!profiler)) {
|
|
return;
|
|
}
|
|
nsCOMPtr<nsISupports> gatherer;
|
|
profiler->GetProfileGatherer(getter_AddRefs(gatherer));
|
|
mGatherer = static_cast<ProfileGatherer*>(gatherer.get());
|
|
}
|
|
|
|
void
|
|
PluginModuleChromeParent::StopProfiler()
|
|
{
|
|
mGatherer = nullptr;
|
|
Unused << SendStopProfiler();
|
|
}
|
|
|
|
void
|
|
PluginModuleChromeParent::GatherAsyncProfile()
|
|
{
|
|
if (NS_WARN_IF(!mGatherer)) {
|
|
return;
|
|
}
|
|
mGatherer->WillGatherOOPProfile();
|
|
Unused << SendGatherProfile();
|
|
}
|
|
|
|
void
|
|
PluginModuleChromeParent::GatheredAsyncProfile(nsIProfileSaveEvent* aSaveEvent)
|
|
{
|
|
if (aSaveEvent && !mProfile.IsEmpty()) {
|
|
aSaveEvent->AddSubProfile(mProfile.get());
|
|
mProfile.Truncate();
|
|
}
|
|
}
|
|
#endif // MOZ_ENABLE_PROFILER_SPS
|
|
|
|
bool
|
|
PluginModuleChromeParent::RecvProfile(const nsCString& aProfile)
|
|
{
|
|
#ifdef MOZ_ENABLE_PROFILER_SPS
|
|
if (NS_WARN_IF(!mGatherer)) {
|
|
return true;
|
|
}
|
|
|
|
mProfile = aProfile;
|
|
mGatherer->GatheredOOPProfile();
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
|