зеркало из https://github.com/mozilla/gecko-dev.git
Bug 805591 - Win32 implementation of the Plugin Hang UI r=bsmedberg,bbondy
This commit is contained in:
Родитель
3c3dcb655a
Коммит
2b91478e8a
|
@ -78,6 +78,7 @@
|
|||
@BINPATH@/@MOZ_CHILD_PROCESS_NAME@
|
||||
#endif
|
||||
#ifdef XP_WIN32
|
||||
@BINPATH@/plugin-hang-ui@BIN_SUFFIX@
|
||||
#ifndef MOZ_DEBUG
|
||||
#if MOZ_MSVC_REDIST == 1400
|
||||
@BINPATH@/Microsoft.VC80.CRT.manifest
|
||||
|
|
|
@ -78,6 +78,12 @@ PARALLEL_DIRS += \
|
|||
audiochannel \
|
||||
$(NULL)
|
||||
|
||||
ifeq (WINNT,$(OS_ARCH))
|
||||
PARALLEL_DIRS += \
|
||||
plugins/ipc/hangui \
|
||||
$(NULL)
|
||||
endif
|
||||
|
||||
ifdef MOZ_B2G_RIL
|
||||
PARALLEL_DIRS += \
|
||||
telephony \
|
||||
|
|
|
@ -114,5 +114,9 @@ MutationEventWarning=Use of Mutation Events is deprecated. Use MutationObserver
|
|||
MozSliceWarning=Use of mozSlice on the Blob object is deprecated. Use slice instead.
|
||||
# LOCALIZATION NOTE: Do not translate "Components"
|
||||
ComponentsWarning=The Components object is deprecated. It will soon be removed.
|
||||
PluginHangUITitle=Warning: Unresponsive plugin
|
||||
PluginHangUIMessage=%S may be busy, or it may have stopped responding. You can stop the plugin now, or you can continue to see if the plugin will complete.
|
||||
PluginHangUIWaitButton=Continue
|
||||
PluginHangUIStopButton=Stop plugin
|
||||
# LOCALIZATION NOTE: Do not translate "mozHidden", "mozVisibilityState", "hidden", or "visibilityState"
|
||||
PrefixedVisibilityApiWarning='mozHidden' and 'mozVisibilityState' are deprecated. Please use the unprefixed 'hidden' and 'visibilityState' instead.
|
||||
|
|
|
@ -94,6 +94,12 @@ ifeq (WINNT,$(OS_ARCH))
|
|||
CPPSRCS += \
|
||||
COMMessageFilter.cpp \
|
||||
PluginSurfaceParent.cpp \
|
||||
MiniShmParent.cpp \
|
||||
PluginHangUIParent.cpp \
|
||||
$(NULL)
|
||||
|
||||
DEFINES += \
|
||||
-DMOZ_HANGUI_PROCESS_NAME=\"plugin-hang-ui$(BIN_SUFFIX)\" \
|
||||
$(NULL)
|
||||
|
||||
EXPORTS_mozilla/plugins += \
|
||||
|
@ -121,6 +127,13 @@ LOCAL_INCLUDES = \
|
|||
-I$(topsrcdir)/xpcom/base/ \
|
||||
$(NULL)
|
||||
|
||||
ifeq (WINNT,$(OS_ARCH))
|
||||
LOCAL_INCLUDES += \
|
||||
-I$(srcdir)/hangui \
|
||||
-I$(topsrcdir)/widget/shared \
|
||||
$(NULL)
|
||||
endif
|
||||
|
||||
include $(topsrcdir)/config/config.mk
|
||||
include $(topsrcdir)/ipc/chromium/chromium-config.mk
|
||||
|
||||
|
|
|
@ -0,0 +1,219 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "MiniShmParent.h"
|
||||
|
||||
#include "base/scoped_handle.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
namespace mozilla {
|
||||
namespace plugins {
|
||||
|
||||
// static
|
||||
const unsigned int MiniShmParent::kDefaultMiniShmSectionSize = 0x1000;
|
||||
|
||||
MiniShmParent::MiniShmParent()
|
||||
: mSectionSize(0),
|
||||
mParentEvent(NULL),
|
||||
mParentGuard(NULL),
|
||||
mChildEvent(NULL),
|
||||
mChildGuard(NULL),
|
||||
mRegWait(NULL),
|
||||
mFileMapping(NULL),
|
||||
mView(nullptr),
|
||||
mIsConnected(false),
|
||||
mTimeout(INFINITE)
|
||||
{
|
||||
}
|
||||
|
||||
MiniShmParent::~MiniShmParent()
|
||||
{
|
||||
CleanUp();
|
||||
}
|
||||
|
||||
void
|
||||
MiniShmParent::CleanUp()
|
||||
{
|
||||
if (mRegWait) {
|
||||
::UnregisterWaitEx(mRegWait, INVALID_HANDLE_VALUE);
|
||||
mRegWait = NULL;
|
||||
}
|
||||
if (mParentEvent) {
|
||||
::CloseHandle(mParentEvent);
|
||||
mParentEvent = NULL;
|
||||
}
|
||||
if (mParentGuard) {
|
||||
::CloseHandle(mParentGuard);
|
||||
mParentGuard = NULL;
|
||||
}
|
||||
if (mChildEvent) {
|
||||
::CloseHandle(mChildEvent);
|
||||
mChildEvent = NULL;
|
||||
}
|
||||
if (mChildGuard) {
|
||||
::CloseHandle(mChildGuard);
|
||||
mChildGuard = NULL;
|
||||
}
|
||||
if (mView) {
|
||||
::UnmapViewOfFile(mView);
|
||||
mView = nullptr;
|
||||
}
|
||||
if (mFileMapping) {
|
||||
::CloseHandle(mFileMapping);
|
||||
mFileMapping = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
MiniShmParent::Init(MiniShmObserver* aObserver, const DWORD aTimeout,
|
||||
const unsigned int aSectionSize)
|
||||
{
|
||||
if (!aObserver || !aSectionSize || (aSectionSize % 0x1000) || !aTimeout) {
|
||||
return NS_ERROR_ILLEGAL_VALUE;
|
||||
}
|
||||
if (mFileMapping) {
|
||||
return NS_ERROR_ALREADY_INITIALIZED;
|
||||
}
|
||||
SECURITY_ATTRIBUTES securityAttributes = {sizeof(securityAttributes),
|
||||
nullptr,
|
||||
TRUE};
|
||||
ScopedHandle parentEvent(::CreateEvent(&securityAttributes,
|
||||
FALSE,
|
||||
FALSE,
|
||||
nullptr));
|
||||
if (!parentEvent.IsValid()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
ScopedHandle parentGuard(::CreateEvent(&securityAttributes,
|
||||
FALSE,
|
||||
TRUE,
|
||||
nullptr));
|
||||
if (!parentGuard.IsValid()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
ScopedHandle childEvent(::CreateEvent(&securityAttributes,
|
||||
FALSE,
|
||||
FALSE,
|
||||
nullptr));
|
||||
if (!childEvent.IsValid()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
ScopedHandle childGuard(::CreateEvent(&securityAttributes,
|
||||
FALSE,
|
||||
TRUE,
|
||||
nullptr));
|
||||
if (!childGuard.IsValid()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
ScopedHandle mapping(::CreateFileMapping(INVALID_HANDLE_VALUE,
|
||||
&securityAttributes,
|
||||
PAGE_READWRITE,
|
||||
0,
|
||||
aSectionSize,
|
||||
nullptr));
|
||||
if (!mapping.IsValid()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
ScopedMappedFileView view(::MapViewOfFile(mapping,
|
||||
FILE_MAP_WRITE,
|
||||
0, 0, 0));
|
||||
if (!view.IsValid()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
nsresult rv = SetView(view, aSectionSize, false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
MiniShmInit* initStruct = nullptr;
|
||||
rv = GetWritePtrInternal(initStruct);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
initStruct->mParentEvent = parentEvent;
|
||||
initStruct->mParentGuard = parentGuard;
|
||||
initStruct->mChildEvent = childEvent;
|
||||
initStruct->mChildGuard = childGuard;
|
||||
|
||||
if (!::RegisterWaitForSingleObject(&mRegWait,
|
||||
parentEvent,
|
||||
&SOnEvent,
|
||||
this,
|
||||
INFINITE,
|
||||
WT_EXECUTEDEFAULT)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
mParentEvent = parentEvent.Take();
|
||||
mParentGuard = parentGuard.Take();
|
||||
mChildEvent = childEvent.Take();
|
||||
mChildGuard = childGuard.Take();
|
||||
mFileMapping = mapping.Take();
|
||||
mView = view.Take();
|
||||
mSectionSize = aSectionSize;
|
||||
SetObserver(aObserver);
|
||||
mTimeout = aTimeout;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
MiniShmParent::GetCookie(std::wstring& cookie)
|
||||
{
|
||||
if (!mFileMapping) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
std::wostringstream oss;
|
||||
oss << mFileMapping;
|
||||
if (!oss) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
cookie = oss.str();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
MiniShmParent::Send()
|
||||
{
|
||||
if (!mChildEvent || !mChildGuard) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
if (::WaitForSingleObject(mChildGuard, mTimeout) != WAIT_OBJECT_0) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
if (!::SetEvent(mChildEvent)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
MiniShmParent::IsConnected() const
|
||||
{
|
||||
return mIsConnected;
|
||||
}
|
||||
|
||||
void
|
||||
MiniShmParent::OnEvent()
|
||||
{
|
||||
if (mIsConnected) {
|
||||
MiniShmBase::OnEvent();
|
||||
} else {
|
||||
FinalizeConnection();
|
||||
}
|
||||
::SetEvent(mParentGuard);
|
||||
}
|
||||
|
||||
void
|
||||
MiniShmParent::FinalizeConnection()
|
||||
{
|
||||
const MiniShmInitComplete* initCompleteStruct = nullptr;
|
||||
nsresult rv = GetReadPtr(initCompleteStruct);
|
||||
mIsConnected = NS_SUCCEEDED(rv) && initCompleteStruct->mSucceeded;
|
||||
if (mIsConnected) {
|
||||
OnConnect();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace plugins
|
||||
} // namespace mozilla
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_plugins_MiniShmParent_h
|
||||
#define mozilla_plugins_MiniShmParent_h
|
||||
|
||||
#include "MiniShmBase.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace mozilla {
|
||||
namespace plugins {
|
||||
|
||||
/**
|
||||
* This class provides a lightweight shared memory interface for a parent
|
||||
* process in Win32.
|
||||
* This code assumes that there is a parent-child relationship between
|
||||
* processes, as it creates inheritable handles.
|
||||
* Note that this class is *not* an IPDL actor.
|
||||
*
|
||||
* @see MiniShmChild
|
||||
*/
|
||||
class MiniShmParent : public MiniShmBase
|
||||
{
|
||||
public:
|
||||
MiniShmParent();
|
||||
virtual ~MiniShmParent();
|
||||
|
||||
static const unsigned int kDefaultMiniShmSectionSize;
|
||||
|
||||
/**
|
||||
* Initialize shared memory on the parent side.
|
||||
*
|
||||
* @param aObserver A MiniShmObserver object to receive event notifications.
|
||||
* @param aTimeout Timeout in milliseconds.
|
||||
* @param aSectionSize Desired size of the shared memory section. This is
|
||||
* expected to be a multiple of 0x1000 (4KiB).
|
||||
* @return nsresult error code
|
||||
*/
|
||||
nsresult
|
||||
Init(MiniShmObserver* aObserver, const DWORD aTimeout,
|
||||
const unsigned int aSectionSize = kDefaultMiniShmSectionSize);
|
||||
|
||||
/**
|
||||
* Destroys the shared memory section. Useful to explicitly release
|
||||
* resources if it is known that they won't be needed again.
|
||||
*/
|
||||
void
|
||||
CleanUp();
|
||||
|
||||
/**
|
||||
* Provides a cookie string that should be passed to MiniShmChild
|
||||
* during its initialization.
|
||||
*
|
||||
* @param aCookie A std::wstring variable to receive the cookie.
|
||||
* @return nsresult error code
|
||||
*/
|
||||
nsresult
|
||||
GetCookie(std::wstring& aCookie);
|
||||
|
||||
virtual nsresult
|
||||
Send() MOZ_OVERRIDE;
|
||||
|
||||
bool
|
||||
IsConnected() const;
|
||||
|
||||
protected:
|
||||
void
|
||||
OnEvent() MOZ_OVERRIDE;
|
||||
|
||||
private:
|
||||
void
|
||||
FinalizeConnection();
|
||||
|
||||
unsigned int mSectionSize;
|
||||
HANDLE mParentEvent;
|
||||
HANDLE mParentGuard;
|
||||
HANDLE mChildEvent;
|
||||
HANDLE mChildGuard;
|
||||
HANDLE mRegWait;
|
||||
HANDLE mFileMapping;
|
||||
LPVOID mView;
|
||||
bool mIsConnected;
|
||||
DWORD mTimeout;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(MiniShmParent);
|
||||
};
|
||||
|
||||
} // namespace plugins
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_plugins_MiniShmParent_h
|
||||
|
|
@ -0,0 +1,348 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "PluginHangUI.h"
|
||||
|
||||
#include "PluginHangUIParent.h"
|
||||
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "mozilla/plugins/PluginModuleParent.h"
|
||||
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsDirectoryServiceDefs.h"
|
||||
#include "nsIFile.h"
|
||||
#include "nsIProperties.h"
|
||||
#include "nsIWindowMediator.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
|
||||
#include "WidgetUtils.h"
|
||||
|
||||
using base::ProcessHandle;
|
||||
|
||||
using mozilla::widget::WidgetUtils;
|
||||
|
||||
using std::string;
|
||||
using std::vector;
|
||||
|
||||
namespace {
|
||||
class nsPluginHangUITelemetry : public nsRunnable
|
||||
{
|
||||
public:
|
||||
nsPluginHangUITelemetry(int aResponseCode, int aDontAskCode)
|
||||
: mResponseCode(aResponseCode),
|
||||
mDontAskCode(aDontAskCode)
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
Run()
|
||||
{
|
||||
mozilla::Telemetry::Accumulate(
|
||||
mozilla::Telemetry::PLUGIN_HANG_UI_USER_RESPONSE, mResponseCode);
|
||||
mozilla::Telemetry::Accumulate(mozilla::Telemetry::PLUGIN_HANG_UI_DONT_ASK,
|
||||
mDontAskCode);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
int mResponseCode;
|
||||
int mDontAskCode;
|
||||
};
|
||||
} // anonymous namespace
|
||||
|
||||
namespace mozilla {
|
||||
namespace plugins {
|
||||
|
||||
const DWORD PluginHangUIParent::kTimeout = 5000U;
|
||||
|
||||
PluginHangUIParent::PluginHangUIParent(PluginModuleParent* aModule)
|
||||
: mModule(aModule),
|
||||
mMainThreadMessageLoop(MessageLoop::current()),
|
||||
mIsShowing(false),
|
||||
mLastUserResponse(0),
|
||||
mHangUIProcessHandle(NULL),
|
||||
mMainWindowHandle(NULL),
|
||||
mRegWait(NULL),
|
||||
mShowEvent(NULL),
|
||||
mShowTicks(0),
|
||||
mResponseTicks(0)
|
||||
{
|
||||
}
|
||||
|
||||
PluginHangUIParent::~PluginHangUIParent()
|
||||
{
|
||||
if (mRegWait) {
|
||||
::UnregisterWaitEx(mRegWait, INVALID_HANDLE_VALUE);
|
||||
}
|
||||
if (mShowEvent) {
|
||||
::CloseHandle(mShowEvent);
|
||||
}
|
||||
if (mHangUIProcessHandle) {
|
||||
::CloseHandle(mHangUIProcessHandle);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
PluginHangUIParent::DontShowAgain() const
|
||||
{
|
||||
return (mLastUserResponse & HANGUI_USER_RESPONSE_DONT_SHOW_AGAIN);
|
||||
}
|
||||
|
||||
bool
|
||||
PluginHangUIParent::WasLastHangStopped() const
|
||||
{
|
||||
return (mLastUserResponse & HANGUI_USER_RESPONSE_STOP);
|
||||
}
|
||||
|
||||
unsigned int
|
||||
PluginHangUIParent::LastShowDurationMs() const
|
||||
{
|
||||
// We only return something if there was a user response
|
||||
if (!mLastUserResponse) {
|
||||
return 0;
|
||||
}
|
||||
return static_cast<unsigned int>(mResponseTicks - mShowTicks);
|
||||
}
|
||||
|
||||
bool
|
||||
PluginHangUIParent::Init(const nsString& aPluginName)
|
||||
{
|
||||
if (mHangUIProcessHandle) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsresult rv;
|
||||
rv = mMiniShm.Init(this, ::IsDebuggerPresent() ? INFINITE : kTimeout);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
nsCOMPtr<nsIProperties>
|
||||
directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID));
|
||||
if (!directoryService) {
|
||||
return false;
|
||||
}
|
||||
nsCOMPtr<nsIFile> greDir;
|
||||
rv = directoryService->Get(NS_GRE_DIR,
|
||||
NS_GET_IID(nsIFile),
|
||||
getter_AddRefs(greDir));
|
||||
if (NS_FAILED(rv)) {
|
||||
return false;
|
||||
}
|
||||
nsAutoString path;
|
||||
greDir->GetPath(path);
|
||||
|
||||
FilePath exePath(path.get());
|
||||
exePath = exePath.AppendASCII(MOZ_HANGUI_PROCESS_NAME);
|
||||
CommandLine commandLine(exePath.value());
|
||||
|
||||
nsXPIDLString localizedStr;
|
||||
const PRUnichar* formatParams[] = { aPluginName.get() };
|
||||
rv = nsContentUtils::FormatLocalizedString(nsContentUtils::eDOM_PROPERTIES,
|
||||
"PluginHangUIMessage",
|
||||
formatParams,
|
||||
localizedStr);
|
||||
if (NS_FAILED(rv)) {
|
||||
return false;
|
||||
}
|
||||
commandLine.AppendLooseValue(localizedStr.get());
|
||||
|
||||
const char* keys[] = { "PluginHangUITitle",
|
||||
"PluginHangUIWaitButton",
|
||||
"PluginHangUIStopButton",
|
||||
"DontAskAgain" };
|
||||
for (unsigned int i = 0; i < ArrayLength(keys); ++i) {
|
||||
rv = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
|
||||
keys[i],
|
||||
localizedStr);
|
||||
if (NS_FAILED(rv)) {
|
||||
return false;
|
||||
}
|
||||
commandLine.AppendLooseValue(localizedStr.get());
|
||||
}
|
||||
|
||||
rv = GetHangUIOwnerWindowHandle(mMainWindowHandle);
|
||||
if (NS_FAILED(rv)) {
|
||||
return false;
|
||||
}
|
||||
nsAutoString hwndStr;
|
||||
hwndStr.AppendPrintf("%p", mMainWindowHandle);
|
||||
commandLine.AppendLooseValue(hwndStr.get());
|
||||
|
||||
ScopedHandle procHandle(::OpenProcess(SYNCHRONIZE,
|
||||
TRUE,
|
||||
GetCurrentProcessId()));
|
||||
if (!procHandle.IsValid()) {
|
||||
return false;
|
||||
}
|
||||
nsAutoString procHandleStr;
|
||||
procHandleStr.AppendPrintf("%p", procHandle);
|
||||
commandLine.AppendLooseValue(procHandleStr.get());
|
||||
|
||||
std::wstring ipcCookie;
|
||||
rv = mMiniShm.GetCookie(ipcCookie);
|
||||
if (NS_FAILED(rv)) {
|
||||
return false;
|
||||
}
|
||||
commandLine.AppendLooseValue(ipcCookie);
|
||||
|
||||
mShowEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
ScopedHandle showEvent(::CreateEvent(NULL, FALSE, FALSE, NULL));
|
||||
if (!showEvent.IsValid()) {
|
||||
return false;
|
||||
}
|
||||
mShowEvent = showEvent.Get();
|
||||
|
||||
STARTUPINFO startupInfo = { sizeof(STARTUPINFO) };
|
||||
PROCESS_INFORMATION processInfo = { NULL };
|
||||
BOOL isProcessCreated = ::CreateProcess(exePath.value().c_str(),
|
||||
const_cast<wchar_t*>(commandLine.command_line_string().c_str()),
|
||||
nullptr,
|
||||
nullptr,
|
||||
TRUE,
|
||||
DETACHED_PROCESS,
|
||||
nullptr,
|
||||
nullptr,
|
||||
&startupInfo,
|
||||
&processInfo);
|
||||
if (isProcessCreated) {
|
||||
::CloseHandle(processInfo.hThread);
|
||||
mHangUIProcessHandle = processInfo.hProcess;
|
||||
::RegisterWaitForSingleObject(&mRegWait,
|
||||
processInfo.hProcess,
|
||||
&SOnHangUIProcessExit,
|
||||
this,
|
||||
INFINITE,
|
||||
WT_EXECUTEDEFAULT | WT_EXECUTEONLYONCE);
|
||||
::WaitForSingleObject(mShowEvent, kTimeout);
|
||||
}
|
||||
mShowEvent = NULL;
|
||||
return !(!isProcessCreated);
|
||||
}
|
||||
|
||||
// static
|
||||
VOID CALLBACK PluginHangUIParent::SOnHangUIProcessExit(PVOID aContext,
|
||||
BOOLEAN aIsTimer)
|
||||
{
|
||||
PluginHangUIParent* object = static_cast<PluginHangUIParent*>(aContext);
|
||||
// If the Hang UI child process died unexpectedly, act as if the UI cancelled
|
||||
if (object->IsShowing()) {
|
||||
object->RecvUserResponse(HANGUI_USER_RESPONSE_CANCEL);
|
||||
// Firefox window was disabled automatically when the Hang UI was shown.
|
||||
// If plugin-hang-ui.exe was unexpectedly terminated, we need to re-enable.
|
||||
::EnableWindow(object->mMainWindowHandle, TRUE);
|
||||
}
|
||||
object->mMiniShm.CleanUp();
|
||||
}
|
||||
|
||||
bool
|
||||
PluginHangUIParent::Cancel()
|
||||
{
|
||||
bool result = mIsShowing && SendCancel();
|
||||
if (result) {
|
||||
mIsShowing = false;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool
|
||||
PluginHangUIParent::SendCancel()
|
||||
{
|
||||
PluginHangUICommand* cmd = nullptr;
|
||||
nsresult rv = mMiniShm.GetWritePtr(cmd);
|
||||
if (NS_FAILED(rv)) {
|
||||
return false;
|
||||
}
|
||||
cmd->mCode = PluginHangUICommand::HANGUI_CMD_CANCEL;
|
||||
return NS_SUCCEEDED(mMiniShm.Send());
|
||||
}
|
||||
|
||||
bool
|
||||
PluginHangUIParent::RecvUserResponse(const unsigned int& aResponse)
|
||||
{
|
||||
mLastUserResponse = aResponse;
|
||||
mResponseTicks = GetTickCount();
|
||||
mIsShowing = false;
|
||||
// responseCode: 1 = Stop, 2 = Continue, 3 = Cancel
|
||||
int responseCode;
|
||||
if (aResponse & HANGUI_USER_RESPONSE_STOP) {
|
||||
// User clicked Stop
|
||||
mModule->TerminateChildProcess(mMainThreadMessageLoop);
|
||||
responseCode = 1;
|
||||
} else if(aResponse & HANGUI_USER_RESPONSE_CONTINUE) {
|
||||
// User clicked Continue
|
||||
responseCode = 2;
|
||||
} else {
|
||||
// Dialog was cancelled
|
||||
responseCode = 3;
|
||||
}
|
||||
int dontAskCode = (aResponse & HANGUI_USER_RESPONSE_DONT_SHOW_AGAIN) ? 1 : 0;
|
||||
nsCOMPtr<nsIRunnable> workItem = new nsPluginHangUITelemetry(responseCode,
|
||||
dontAskCode);
|
||||
NS_DispatchToMainThread(workItem, NS_DISPATCH_NORMAL);
|
||||
return true;
|
||||
}
|
||||
|
||||
nsresult
|
||||
PluginHangUIParent::GetHangUIOwnerWindowHandle(NativeWindowHandle& windowHandle)
|
||||
{
|
||||
windowHandle = NULL;
|
||||
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIWindowMediator> winMediator(do_GetService(NS_WINDOWMEDIATOR_CONTRACTID,
|
||||
&rv));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIDOMWindow> navWin;
|
||||
rv = winMediator->GetMostRecentWindow(NS_LITERAL_STRING("navigator:browser").get(),
|
||||
getter_AddRefs(navWin));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (!navWin) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIWidget> widget = WidgetUtils::DOMWindowToWidget(navWin);
|
||||
if (!widget) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
windowHandle = reinterpret_cast<NativeWindowHandle>(widget->GetNativeData(NS_NATIVE_WINDOW));
|
||||
if (!windowHandle) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
PluginHangUIParent::OnMiniShmEvent(MiniShmBase *aMiniShmObj)
|
||||
{
|
||||
const PluginHangUIResponse* response = nullptr;
|
||||
nsresult rv = aMiniShmObj->GetReadPtr(response);
|
||||
NS_ASSERTION(NS_SUCCEEDED(rv),
|
||||
"Couldn't obtain read pointer OnMiniShmEvent");
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
RecvUserResponse(response->mResponseBits);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PluginHangUIParent::OnMiniShmConnect(MiniShmBase* aMiniShmObj)
|
||||
{
|
||||
PluginHangUICommand* cmd = nullptr;
|
||||
nsresult rv = aMiniShmObj->GetWritePtr(cmd);
|
||||
NS_ASSERTION(NS_SUCCEEDED(rv),
|
||||
"Couldn't obtain write pointer OnMiniShmConnect");
|
||||
if (NS_FAILED(rv)) {
|
||||
return;
|
||||
}
|
||||
cmd->mCode = PluginHangUICommand::HANGUI_CMD_SHOW;
|
||||
mIsShowing = NS_SUCCEEDED(aMiniShmObj->Send());
|
||||
if (mIsShowing) {
|
||||
mShowTicks = GetTickCount();
|
||||
}
|
||||
::SetEvent(mShowEvent);
|
||||
}
|
||||
|
||||
} // namespace plugins
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,152 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_plugins_PluginHangUIParent_h
|
||||
#define mozilla_plugins_PluginHangUIParent_h
|
||||
|
||||
#include "nsString.h"
|
||||
|
||||
#include "base/process.h"
|
||||
#include "base/process_util.h"
|
||||
|
||||
#include "mozilla/plugins/PluginMessageUtils.h"
|
||||
|
||||
#include "MiniShmParent.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace plugins {
|
||||
|
||||
class PluginModuleParent;
|
||||
|
||||
/**
|
||||
* This class is responsible for launching and communicating with the
|
||||
* plugin-hang-ui process.
|
||||
*
|
||||
* NOTE: PluginHangUIParent is *not* an IPDL actor! In this case, "Parent"
|
||||
* is describing the fact that firefox is the parent process to the
|
||||
* plugin-hang-ui process, which is the PluginHangUIChild.
|
||||
* PluginHangUIParent and PluginHangUIChild are a matched pair.
|
||||
* @see PluginHangUIChild
|
||||
*/
|
||||
class PluginHangUIParent : public MiniShmObserver
|
||||
{
|
||||
public:
|
||||
PluginHangUIParent(PluginModuleParent* aModule);
|
||||
virtual ~PluginHangUIParent();
|
||||
|
||||
/**
|
||||
* Spawn the plugin-hang-ui.exe child process and terminate the given
|
||||
* plugin container process if the user elects to stop the hung plugin.
|
||||
*
|
||||
* @param aPluginName Human-readable name of the affected plugin.
|
||||
* @return true if the plugin hang ui process was successfully launched,
|
||||
* otherwise false.
|
||||
*/
|
||||
bool
|
||||
Init(const nsString& aPluginName);
|
||||
|
||||
/**
|
||||
* If the Plugin Hang UI is being shown, send a cancel notification to the
|
||||
* Plugin Hang UI child process.
|
||||
*
|
||||
* @return true if the UI was shown and the cancel command was successfully
|
||||
* sent to the child process, otherwise false.
|
||||
*/
|
||||
bool
|
||||
Cancel();
|
||||
|
||||
/**
|
||||
* Returns whether the Plugin Hang UI is currently being displayed.
|
||||
*
|
||||
* @return true if the Plugin Hang UI is showing, otherwise false.
|
||||
*/
|
||||
bool
|
||||
IsShowing() const { return mIsShowing; }
|
||||
|
||||
/**
|
||||
* Returns whether this Plugin Hang UI instance has been shown. Note
|
||||
* that this does not necessarily mean that the UI is showing right now.
|
||||
*
|
||||
* @return true if the Plugin Hang UI has shown, otherwise false.
|
||||
*/
|
||||
bool
|
||||
WasShown() const { return mIsShowing || mLastUserResponse != 0; }
|
||||
|
||||
/**
|
||||
* Returns whether the user checked the "Don't ask me again" checkbox.
|
||||
*
|
||||
* @return true if the user does not want to see the Hang UI again.
|
||||
*/
|
||||
bool
|
||||
DontShowAgain() const;
|
||||
|
||||
/**
|
||||
* Returns whether the user clicked stop during the last time that the
|
||||
* Plugin Hang UI was displayed, if applicable.
|
||||
*
|
||||
* @return true if the UI was shown and the user chose to stop the
|
||||
* plugin, otherwise false
|
||||
*/
|
||||
bool
|
||||
WasLastHangStopped() const;
|
||||
|
||||
/**
|
||||
* @return unsigned int containing the response bits from the last
|
||||
* time the Plugin Hang UI ran.
|
||||
*/
|
||||
unsigned int
|
||||
LastUserResponse() const { return mLastUserResponse; }
|
||||
|
||||
/**
|
||||
* @return unsigned int containing the number of milliseconds that
|
||||
* the Plugin Hang UI was displayed before the user responded.
|
||||
* Returns 0 if the Plugin Hang UI has not been shown or was cancelled.
|
||||
*/
|
||||
unsigned int
|
||||
LastShowDurationMs() const;
|
||||
|
||||
virtual void
|
||||
OnMiniShmEvent(MiniShmBase* aMiniShmObj) MOZ_OVERRIDE;
|
||||
|
||||
virtual void
|
||||
OnMiniShmConnect(MiniShmBase* aMiniShmObj) MOZ_OVERRIDE;
|
||||
|
||||
private:
|
||||
nsresult
|
||||
GetHangUIOwnerWindowHandle(NativeWindowHandle& windowHandle);
|
||||
|
||||
bool
|
||||
SendCancel();
|
||||
|
||||
bool
|
||||
RecvUserResponse(const unsigned int& aResponse);
|
||||
|
||||
static
|
||||
VOID CALLBACK SOnHangUIProcessExit(PVOID aContext, BOOLEAN aIsTimer);
|
||||
|
||||
private:
|
||||
PluginModuleParent* mModule;
|
||||
MessageLoop* mMainThreadMessageLoop;
|
||||
volatile bool mIsShowing;
|
||||
unsigned int mLastUserResponse;
|
||||
base::ProcessHandle mHangUIProcessHandle;
|
||||
NativeWindowHandle mMainWindowHandle;
|
||||
HANDLE mRegWait;
|
||||
HANDLE mShowEvent;
|
||||
DWORD mShowTicks;
|
||||
DWORD mResponseTicks;
|
||||
MiniShmParent mMiniShm;
|
||||
|
||||
static const DWORD kTimeout;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(PluginHangUIParent);
|
||||
};
|
||||
|
||||
} // namespace plugins
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_plugins_PluginHangUIParent_h
|
||||
|
|
@ -35,6 +35,7 @@
|
|||
#include "prsystem.h"
|
||||
|
||||
#ifdef XP_WIN
|
||||
#include "PluginHangUIParent.h"
|
||||
#include "mozilla/widget/AudioSession.h"
|
||||
#endif
|
||||
#include "sampler.h"
|
||||
|
@ -59,6 +60,13 @@ using namespace CrashReporter;
|
|||
static const char kChildTimeoutPref[] = "dom.ipc.plugins.timeoutSecs";
|
||||
static const char kParentTimeoutPref[] = "dom.ipc.plugins.parentTimeoutSecs";
|
||||
static const char kLaunchTimeoutPref[] = "dom.ipc.plugins.processLaunchTimeoutSecs";
|
||||
#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>
|
||||
|
@ -87,7 +95,7 @@ PluginModuleParent::LoadModule(const char* aFilePath)
|
|||
parent->Open(parent->mSubprocess->GetChannel(),
|
||||
parent->mSubprocess->GetChildProcessHandle());
|
||||
|
||||
TimeoutChanged(kChildTimeoutPref, parent);
|
||||
TimeoutChanged(CHILD_TIMEOUT_PREF, parent);
|
||||
|
||||
#ifdef MOZ_CRASHREPORTER
|
||||
// If this fails, we're having IPC troubles, and we're doomed anyways.
|
||||
|
@ -111,6 +119,9 @@ PluginModuleParent::PluginModuleParent(const char* aFilePath)
|
|||
, mTaskFactory(this)
|
||||
#ifdef XP_WIN
|
||||
, mPluginCpuUsageOnHang()
|
||||
, mHangUIParent(nullptr)
|
||||
, mHangUIEnabled(true)
|
||||
, mIsTimerReset(true)
|
||||
#endif
|
||||
#ifdef MOZ_CRASHREPORTER_INJECTOR
|
||||
, mFlashProcess1(0)
|
||||
|
@ -123,6 +134,10 @@ PluginModuleParent::PluginModuleParent(const char* aFilePath)
|
|||
|
||||
Preferences::RegisterCallback(TimeoutChanged, kChildTimeoutPref, this);
|
||||
Preferences::RegisterCallback(TimeoutChanged, kParentTimeoutPref, this);
|
||||
#ifdef XP_WIN
|
||||
Preferences::RegisterCallback(TimeoutChanged, kHangUITimeoutPref, this);
|
||||
Preferences::RegisterCallback(TimeoutChanged, kHangUIMinDisplayPref, this);
|
||||
#endif
|
||||
}
|
||||
|
||||
PluginModuleParent::~PluginModuleParent()
|
||||
|
@ -150,6 +165,15 @@ PluginModuleParent::~PluginModuleParent()
|
|||
|
||||
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
|
||||
}
|
||||
|
||||
#ifdef MOZ_CRASHREPORTER
|
||||
|
@ -206,16 +230,29 @@ PluginModuleParent::WriteExtraDataForMinidump(AnnotationTable& notes)
|
|||
}
|
||||
#endif // MOZ_CRASHREPORTER
|
||||
|
||||
void
|
||||
PluginModuleParent::SetChildTimeout(const int32_t aChildTimeout)
|
||||
{
|
||||
int32_t timeoutMs = (aChildTimeout > 0) ? (1000 * aChildTimeout) :
|
||||
SyncChannel::kNoTimeout;
|
||||
SetReplyTimeoutMs(timeoutMs);
|
||||
}
|
||||
|
||||
int
|
||||
PluginModuleParent::TimeoutChanged(const char* aPref, void* aModule)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thead!");
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
#ifndef XP_WIN
|
||||
if (!strcmp(aPref, kChildTimeoutPref)) {
|
||||
// The timeout value used by the parent for children
|
||||
int32_t timeoutSecs = Preferences::GetInt(kChildTimeoutPref, 0);
|
||||
int32_t timeoutMs = (timeoutSecs > 0) ? (1000 * timeoutSecs) :
|
||||
SyncChannel::kNoTimeout;
|
||||
static_cast<PluginModuleParent*>(aModule)->SetReplyTimeoutMs(timeoutMs);
|
||||
static_cast<PluginModuleParent*>(aModule)->SetChildTimeout(timeoutSecs);
|
||||
#else
|
||||
if (!strcmp(aPref, kChildTimeoutPref) ||
|
||||
!strcmp(aPref, kHangUIMinDisplayPref) ||
|
||||
!strcmp(aPref, kHangUITimeoutPref)) {
|
||||
static_cast<PluginModuleParent*>(aModule)->EvaluateHangUIState(true);
|
||||
#endif // XP_WIN
|
||||
} else if (!strcmp(aPref, kParentTimeoutPref)) {
|
||||
// The timeout value used by the child for its parent
|
||||
int32_t timeoutSecs = Preferences::GetInt(kParentTimeoutPref, 0);
|
||||
|
@ -308,6 +345,13 @@ GetProcessCpuUsage(const InfallibleTArray<base::ProcessHandle>& processHandles,
|
|||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
void
|
||||
PluginModuleParent::ExitedCxxStack()
|
||||
{
|
||||
FinishHangUI();
|
||||
}
|
||||
|
||||
#endif // #ifdef XP_WIN
|
||||
|
||||
#ifdef MOZ_CRASHREPORTER_INJECTOR
|
||||
|
@ -334,10 +378,36 @@ CreateFlashMinidump(DWORD processId, ThreadId childThread,
|
|||
bool
|
||||
PluginModuleParent::ShouldContinueFromReplyTimeout()
|
||||
{
|
||||
#ifdef XP_WIN
|
||||
if (LaunchHangUI()) {
|
||||
return true;
|
||||
}
|
||||
// If LaunchHangUI returned false then we should proceed with the
|
||||
// original plugin hang behaviour and kill the plugin container.
|
||||
FinishHangUI();
|
||||
#endif // XP_WIN
|
||||
TerminateChildProcess(MessageLoop::current());
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
PluginModuleParent::TerminateChildProcess(MessageLoop* aMsgLoop)
|
||||
{
|
||||
#ifdef MOZ_CRASHREPORTER
|
||||
CrashReporterParent* crashReporter = CrashReporter();
|
||||
crashReporter->AnnotateCrashReport(NS_LITERAL_CSTRING("PluginHang"),
|
||||
NS_LITERAL_CSTRING("1"));
|
||||
#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
|
||||
if (crashReporter->GeneratePairedMinidump(this)) {
|
||||
mPluginDumpID = crashReporter->ChildDumpID();
|
||||
PLUGIN_LOG_DEBUG(
|
||||
|
@ -395,17 +465,128 @@ PluginModuleParent::ShouldContinueFromReplyTimeout()
|
|||
|
||||
// this must run before the error notification from the channel,
|
||||
// or not at all
|
||||
MessageLoop::current()->PostTask(
|
||||
FROM_HERE,
|
||||
mTaskFactory.NewRunnableMethod(
|
||||
&PluginModuleParent::CleanupFromTimeout));
|
||||
if (aMsgLoop == MessageLoop::current()) {
|
||||
aMsgLoop->PostTask(
|
||||
FROM_HERE,
|
||||
mTaskFactory.NewRunnableMethod(
|
||||
&PluginModuleParent::CleanupFromTimeout));
|
||||
} else {
|
||||
// If we're posting from a different thread we can't create
|
||||
// the task via mTaskFactory
|
||||
aMsgLoop->PostTask(FROM_HERE,
|
||||
NewRunnableMethod(this,
|
||||
&PluginModuleParent::CleanupFromTimeout));
|
||||
}
|
||||
|
||||
if (!KillProcess(OtherProcess(), 1, false))
|
||||
NS_WARNING("failed to kill subprocess!");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef XP_WIN
|
||||
void
|
||||
PluginModuleParent::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
|
||||
PluginModuleParent::GetPluginName(nsAString& aPluginName)
|
||||
{
|
||||
nsPluginHost* host = nsPluginHost::GetInst();
|
||||
if (!host) {
|
||||
return false;
|
||||
}
|
||||
nsPluginTag* pluginTag = host->TagForPlugin(mPlugin);
|
||||
if (!pluginTag) {
|
||||
return false;
|
||||
}
|
||||
CopyUTF8toUTF16(pluginTag->mName, aPluginName);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
PluginModuleParent::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()) {
|
||||
return !mHangUIParent->WasLastHangStopped();
|
||||
}
|
||||
delete mHangUIParent;
|
||||
mHangUIParent = nullptr;
|
||||
}
|
||||
mHangUIParent = new PluginHangUIParent(this);
|
||||
nsAutoString pluginName;
|
||||
if (!GetPluginName(pluginName)) {
|
||||
return false;
|
||||
}
|
||||
bool retval = mHangUIParent->Init(pluginName);
|
||||
if (retval) {
|
||||
/* 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
|
||||
PluginModuleParent::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);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // XP_WIN
|
||||
|
||||
#ifdef MOZ_CRASHREPORTER
|
||||
CrashReporterParent*
|
||||
PluginModuleParent::CrashReporter()
|
||||
|
|
|
@ -45,6 +45,10 @@ namespace plugins {
|
|||
|
||||
class BrowserStreamParent;
|
||||
|
||||
#ifdef XP_WIN
|
||||
class PluginHangUIParent;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* PluginModuleParent
|
||||
*
|
||||
|
@ -130,6 +134,13 @@ public:
|
|||
|
||||
void ProcessRemoteNativeEventsInRPCCall();
|
||||
|
||||
void TerminateChildProcess(MessageLoop* aMsgLoop);
|
||||
|
||||
#ifdef XP_WIN
|
||||
void
|
||||
ExitedCxxStack() MOZ_OVERRIDE;
|
||||
#endif // XP_WIN
|
||||
|
||||
protected:
|
||||
virtual mozilla::ipc::RPCChannel::RacyRPCPolicy
|
||||
MediateRPCRace(const Message& parent, const Message& child) MOZ_OVERRIDE
|
||||
|
@ -286,6 +297,7 @@ private:
|
|||
void WriteExtraDataForMinidump(CrashReporter::AnnotationTable& notes);
|
||||
#endif
|
||||
void CleanupFromTimeout();
|
||||
void SetChildTimeout(const int32_t aChildTimeout);
|
||||
static int TimeoutChanged(const char* aPref, void* aModule);
|
||||
void NotifyPluginCrashed();
|
||||
|
||||
|
@ -304,6 +316,31 @@ private:
|
|||
nsString mHangID;
|
||||
#ifdef XP_WIN
|
||||
InfallibleTArray<float> mPluginCpuUsageOnHang;
|
||||
PluginHangUIParent *mHangUIParent;
|
||||
bool mHangUIEnabled;
|
||||
bool mIsTimerReset;
|
||||
|
||||
void
|
||||
EvaluateHangUIState(const bool aReset);
|
||||
|
||||
bool
|
||||
GetPluginName(nsAString& aPluginName);
|
||||
|
||||
/**
|
||||
* Launches the Plugin Hang UI.
|
||||
*
|
||||
* @return true if plugin-hang-ui.exe has been successfully launched.
|
||||
* false if the Plugin Hang UI is disabled, already showing,
|
||||
* or the launch failed.
|
||||
*/
|
||||
bool
|
||||
LaunchHangUI();
|
||||
|
||||
/**
|
||||
* Finishes the Plugin Hang UI and cancels if it is being shown to the user.
|
||||
*/
|
||||
void
|
||||
FinishHangUI();
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_X11
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_plugins_HangUIDlg_h
|
||||
#define mozilla_plugins_HangUIDlg_h
|
||||
|
||||
#define IDD_HANGUIDLG 102
|
||||
#define IDC_MSG 1000
|
||||
#define IDC_CONTINUE 1001
|
||||
#define IDC_STOP 1002
|
||||
#define IDC_NOFUTURE 1003
|
||||
#define IDC_DLGICON 1004
|
||||
|
||||
#endif // mozilla_plugins_HangUIDlg_h
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "HangUIDlg.h"
|
||||
#include <windows.h>
|
||||
|
||||
LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Dialog
|
||||
//
|
||||
|
||||
IDD_HANGUIDLG DIALOGEX 0, 0, 400, 75
|
||||
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU
|
||||
CAPTION "Dialog"
|
||||
FONT 8, "MS Shell Dlg", 400, 0, 0x1
|
||||
BEGIN
|
||||
DEFPUSHBUTTON "Continue",IDC_CONTINUE,283,51,50,18
|
||||
PUSHBUTTON "Stop",IDC_STOP,341,51,50,18
|
||||
CONTROL "Check1",IDC_NOFUTURE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,37,32,354,10
|
||||
LTEXT "Static",IDC_MSG,37,7,353,24
|
||||
ICON "",IDC_DLGICON,7,7,20,20
|
||||
END
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
DEPTH = @DEPTH@
|
||||
topsrcdir = @top_srcdir@
|
||||
srcdir = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
FAIL_ON_WARNINGS := 1
|
||||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
|
||||
CPPSRCS = \
|
||||
MiniShmChild.cpp \
|
||||
PluginHangUIChild.cpp \
|
||||
$(NULL)
|
||||
|
||||
PROGRAM = plugin-hang-ui$(BIN_SUFFIX)
|
||||
|
||||
OS_LIBS = comctl32.lib
|
||||
|
||||
RCINCLUDE = HangUIDlg.rc
|
||||
|
||||
include $(topsrcdir)/config/config.mk
|
||||
|
||||
DEFINES += \
|
||||
-DNS_NO_XPCOM \
|
||||
$(NULL)
|
||||
|
||||
STL_FLAGS = \
|
||||
-D_HAS_EXCEPTIONS=0 \
|
||||
$(NULL)
|
||||
|
||||
MOZ_GLUE_LDFLAGS =
|
||||
|
||||
include $(topsrcdir)/ipc/chromium/chromium-config.mk
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
|
@ -0,0 +1,315 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_plugins_MiniShmBase_h
|
||||
#define mozilla_plugins_MiniShmBase_h
|
||||
|
||||
#include "base/basictypes.h"
|
||||
|
||||
#include "nsDebug.h"
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
namespace mozilla {
|
||||
namespace plugins {
|
||||
|
||||
/**
|
||||
* This class is used to provide RAII semantics for mapped views.
|
||||
* @see ScopedHandle
|
||||
*/
|
||||
class ScopedMappedFileView
|
||||
{
|
||||
public:
|
||||
explicit
|
||||
ScopedMappedFileView(LPVOID aView)
|
||||
: mView(aView)
|
||||
{
|
||||
}
|
||||
|
||||
~ScopedMappedFileView()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
void
|
||||
Close()
|
||||
{
|
||||
if (mView) {
|
||||
::UnmapViewOfFile(mView);
|
||||
mView = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Set(LPVOID aView)
|
||||
{
|
||||
Close();
|
||||
mView = aView;
|
||||
}
|
||||
|
||||
LPVOID
|
||||
Get() const
|
||||
{
|
||||
return mView;
|
||||
}
|
||||
|
||||
LPVOID
|
||||
Take()
|
||||
{
|
||||
LPVOID result = mView;
|
||||
mView = nullptr;
|
||||
return result;
|
||||
}
|
||||
|
||||
operator LPVOID()
|
||||
{
|
||||
return mView;
|
||||
}
|
||||
|
||||
bool
|
||||
IsValid() const
|
||||
{
|
||||
return (mView);
|
||||
}
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(ScopedMappedFileView);
|
||||
|
||||
LPVOID mView;
|
||||
};
|
||||
|
||||
class MiniShmBase;
|
||||
|
||||
class MiniShmObserver
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* This function is called whenever there is a new shared memory request.
|
||||
* @param aMiniShmObj MiniShmBase object that may be used to read and
|
||||
* write from shared memory.
|
||||
*/
|
||||
virtual void OnMiniShmEvent(MiniShmBase *aMiniShmObj) = 0;
|
||||
/**
|
||||
* This function is called once when a MiniShmParent and a MiniShmChild
|
||||
* object have successfully negotiated a connection.
|
||||
*
|
||||
* @param aMiniShmObj MiniShmBase object that may be used to read and
|
||||
* write from shared memory.
|
||||
*/
|
||||
virtual void OnMiniShmConnect(MiniShmBase *aMiniShmObj) { }
|
||||
};
|
||||
|
||||
/**
|
||||
* Base class for MiniShm connections. This class defines the common
|
||||
* interfaces and code between parent and child.
|
||||
*/
|
||||
class MiniShmBase
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Obtains a writable pointer into shared memory of type T.
|
||||
* typename T must be plain-old-data and contain an unsigned integral
|
||||
* member T::identifier that uniquely identifies T with respect to
|
||||
* other types used by the protocol being implemented.
|
||||
*
|
||||
* @param aPtr Pointer to receive the shared memory address.
|
||||
* This value is set if and only if the function
|
||||
* succeeded.
|
||||
* @return NS_OK if and only if aPtr was successfully obtained.
|
||||
* NS_ERROR_ILLEGAL_VALUE if type T is not valid for MiniShm.
|
||||
* NS_ERROR_NOT_INITIALIZED if there is no valid MiniShm connection.
|
||||
*/
|
||||
template<typename T> nsresult
|
||||
GetWritePtr(T*& aPtr)
|
||||
{
|
||||
if (!mWriteHeader) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
if (sizeof(T) > mPayloadMaxLen ||
|
||||
T::identifier <= RESERVED_CODE_LAST) {
|
||||
return NS_ERROR_ILLEGAL_VALUE;
|
||||
}
|
||||
mWriteHeader->mId = T::identifier;
|
||||
mWriteHeader->mPayloadLen = sizeof(T);
|
||||
aPtr = reinterpret_cast<T*>(mWriteHeader + 1);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains a readable pointer into shared memory of type T.
|
||||
* typename T must be plain-old-data and contain an unsigned integral
|
||||
* member T::identifier that uniquely identifies T with respect to
|
||||
* other types used by the protocol being implemented.
|
||||
*
|
||||
* @param aPtr Pointer to receive the shared memory address.
|
||||
* This value is set if and only if the function
|
||||
* succeeded.
|
||||
* @return NS_OK if and only if aPtr was successfully obtained.
|
||||
* NS_ERROR_ILLEGAL_VALUE if type T is not valid for MiniShm or if
|
||||
* type T does not match the type of the data
|
||||
* stored in shared memory.
|
||||
* NS_ERROR_NOT_INITIALIZED if there is no valid MiniShm connection.
|
||||
*/
|
||||
template<typename T> nsresult
|
||||
GetReadPtr(const T*& aPtr)
|
||||
{
|
||||
if (!mReadHeader) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
if (mReadHeader->mId != T::identifier ||
|
||||
sizeof(T) != mReadHeader->mPayloadLen) {
|
||||
return NS_ERROR_ILLEGAL_VALUE;
|
||||
}
|
||||
aPtr = reinterpret_cast<const T*>(mReadHeader + 1);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires the peer's event causing its request handler to execute.
|
||||
*
|
||||
* @return Should return NS_OK if the send was successful.
|
||||
*/
|
||||
virtual nsresult
|
||||
Send() = 0;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* MiniShm reserves some identifier codes for its own use. Any
|
||||
* identifiers used by MiniShm protocol implementations must be
|
||||
* greater than RESERVED_CODE_LAST.
|
||||
*/
|
||||
enum ReservedCodes
|
||||
{
|
||||
RESERVED_CODE_INIT = 0,
|
||||
RESERVED_CODE_INIT_COMPLETE = 1,
|
||||
RESERVED_CODE_LAST = RESERVED_CODE_INIT_COMPLETE
|
||||
};
|
||||
|
||||
struct MiniShmHeader
|
||||
{
|
||||
unsigned int mId;
|
||||
unsigned int mPayloadLen;
|
||||
};
|
||||
|
||||
struct MiniShmInit
|
||||
{
|
||||
enum identifier_t
|
||||
{
|
||||
identifier = RESERVED_CODE_INIT
|
||||
};
|
||||
HANDLE mParentEvent;
|
||||
HANDLE mParentGuard;
|
||||
HANDLE mChildEvent;
|
||||
HANDLE mChildGuard;
|
||||
};
|
||||
|
||||
struct MiniShmInitComplete
|
||||
{
|
||||
enum identifier_t
|
||||
{
|
||||
identifier = RESERVED_CODE_INIT_COMPLETE
|
||||
};
|
||||
bool mSucceeded;
|
||||
};
|
||||
|
||||
MiniShmBase()
|
||||
: mObserver(nullptr),
|
||||
mWriteHeader(nullptr),
|
||||
mReadHeader(nullptr),
|
||||
mPayloadMaxLen(0)
|
||||
{
|
||||
}
|
||||
virtual ~MiniShmBase()
|
||||
{ }
|
||||
|
||||
virtual void
|
||||
OnEvent()
|
||||
{
|
||||
if (mObserver) {
|
||||
mObserver->OnMiniShmEvent(this);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void
|
||||
OnConnect()
|
||||
{
|
||||
if (mObserver) {
|
||||
mObserver->OnMiniShmConnect(this);
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
SetView(LPVOID aView, const unsigned int aSize, bool aIsChild)
|
||||
{
|
||||
if (!aView || aSize <= 2 * sizeof(MiniShmHeader)) {
|
||||
return NS_ERROR_ILLEGAL_VALUE;
|
||||
}
|
||||
// Divide the region into halves for parent and child
|
||||
if (aIsChild) {
|
||||
mReadHeader = static_cast<MiniShmHeader*>(aView);
|
||||
mWriteHeader = reinterpret_cast<MiniShmHeader*>(static_cast<char*>(aView)
|
||||
+ aSize / 2U);
|
||||
} else {
|
||||
mWriteHeader = static_cast<MiniShmHeader*>(aView);
|
||||
mReadHeader = reinterpret_cast<MiniShmHeader*>(static_cast<char*>(aView)
|
||||
+ aSize / 2U);
|
||||
}
|
||||
mPayloadMaxLen = aSize / 2U - sizeof(MiniShmHeader);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
inline void
|
||||
SetObserver(MiniShmObserver *aObserver) { mObserver = aObserver; }
|
||||
|
||||
/**
|
||||
* Obtains a writable pointer into shared memory of type T. This version
|
||||
* differs from GetWritePtr in that it allows typename T to be one of
|
||||
* the private data structures declared in MiniShmBase.
|
||||
*
|
||||
* @param aPtr Pointer to receive the shared memory address.
|
||||
* This value is set if and only if the function
|
||||
* succeeded.
|
||||
* @return NS_OK if and only if aPtr was successfully obtained.
|
||||
* NS_ERROR_ILLEGAL_VALUE if type T not an internal MiniShm struct.
|
||||
* NS_ERROR_NOT_INITIALIZED if there is no valid MiniShm connection.
|
||||
*/
|
||||
template<typename T> nsresult
|
||||
GetWritePtrInternal(T*& aPtr)
|
||||
{
|
||||
if (!mWriteHeader) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
if (sizeof(T) > mPayloadMaxLen ||
|
||||
T::identifier > RESERVED_CODE_LAST) {
|
||||
return NS_ERROR_ILLEGAL_VALUE;
|
||||
}
|
||||
mWriteHeader->mId = T::identifier;
|
||||
mWriteHeader->mPayloadLen = sizeof(T);
|
||||
aPtr = reinterpret_cast<T*>(mWriteHeader + 1);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static VOID CALLBACK
|
||||
SOnEvent(PVOID aContext, BOOLEAN aIsTimer)
|
||||
{
|
||||
MiniShmBase* object = static_cast<MiniShmBase*>(aContext);
|
||||
object->OnEvent();
|
||||
}
|
||||
|
||||
private:
|
||||
MiniShmObserver* mObserver;
|
||||
MiniShmHeader* mWriteHeader;
|
||||
MiniShmHeader* mReadHeader;
|
||||
unsigned int mPayloadMaxLen;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(MiniShmBase);
|
||||
};
|
||||
|
||||
} // namespace plugins
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_plugins_MiniShmBase_h
|
||||
|
|
@ -0,0 +1,170 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "MiniShmChild.h"
|
||||
|
||||
#include <limits>
|
||||
#include <sstream>
|
||||
|
||||
namespace mozilla {
|
||||
namespace plugins {
|
||||
|
||||
MiniShmChild::MiniShmChild()
|
||||
: mParentEvent(NULL),
|
||||
mParentGuard(NULL),
|
||||
mChildEvent(NULL),
|
||||
mChildGuard(NULL),
|
||||
mFileMapping(NULL),
|
||||
mRegWait(NULL),
|
||||
mView(nullptr),
|
||||
mTimeout(INFINITE)
|
||||
{}
|
||||
|
||||
MiniShmChild::~MiniShmChild()
|
||||
{
|
||||
if (mRegWait) {
|
||||
::UnregisterWaitEx(mRegWait, INVALID_HANDLE_VALUE);
|
||||
}
|
||||
if (mParentEvent) {
|
||||
::CloseHandle(mParentEvent);
|
||||
}
|
||||
if (mParentGuard) {
|
||||
::CloseHandle(mParentGuard);
|
||||
}
|
||||
if (mChildEvent) {
|
||||
::CloseHandle(mChildEvent);
|
||||
}
|
||||
if (mChildGuard) {
|
||||
::CloseHandle(mChildGuard);
|
||||
}
|
||||
if (mView) {
|
||||
::UnmapViewOfFile(mView);
|
||||
}
|
||||
if (mFileMapping) {
|
||||
::CloseHandle(mFileMapping);
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
MiniShmChild::Init(MiniShmObserver* aObserver, const std::wstring& aCookie,
|
||||
const DWORD aTimeout)
|
||||
{
|
||||
if (aCookie.empty() || !aTimeout) {
|
||||
return NS_ERROR_ILLEGAL_VALUE;
|
||||
}
|
||||
if (mFileMapping) {
|
||||
return NS_ERROR_ALREADY_INITIALIZED;
|
||||
}
|
||||
std::wistringstream iss(aCookie);
|
||||
HANDLE mapHandle = NULL;
|
||||
iss >> mapHandle;
|
||||
if (!iss) {
|
||||
return NS_ERROR_ILLEGAL_VALUE;
|
||||
}
|
||||
ScopedMappedFileView view(::MapViewOfFile(mapHandle,
|
||||
FILE_MAP_WRITE,
|
||||
0, 0, 0));
|
||||
if (!view.IsValid()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
MEMORY_BASIC_INFORMATION memInfo = {0};
|
||||
SIZE_T querySize = ::VirtualQuery(view, &memInfo, sizeof(memInfo));
|
||||
unsigned int mappingSize = 0;
|
||||
if (querySize) {
|
||||
if (memInfo.RegionSize <= std::numeric_limits<unsigned int>::max()) {
|
||||
mappingSize = static_cast<unsigned int>(memInfo.RegionSize);
|
||||
}
|
||||
}
|
||||
if (!querySize || !mappingSize) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
nsresult rv = SetView(view, mappingSize, true);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
const MiniShmInit* initStruct = nullptr;
|
||||
rv = GetReadPtr(initStruct);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
if (!initStruct->mParentEvent || !initStruct->mParentGuard ||
|
||||
!initStruct->mChildEvent || !initStruct->mChildGuard) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
if (!::RegisterWaitForSingleObject(&mRegWait,
|
||||
initStruct->mChildEvent,
|
||||
&SOnEvent,
|
||||
this,
|
||||
INFINITE,
|
||||
WT_EXECUTEDEFAULT)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
MiniShmInitComplete* initCompleteStruct = nullptr;
|
||||
rv = GetWritePtrInternal(initCompleteStruct);
|
||||
if (NS_FAILED(rv)) {
|
||||
::UnregisterWaitEx(mRegWait, INVALID_HANDLE_VALUE);
|
||||
mRegWait = NULL;
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
initCompleteStruct->mSucceeded = true;
|
||||
|
||||
// We must set the member variables before we signal the event
|
||||
mFileMapping = mapHandle;
|
||||
mView = view.Take();
|
||||
mParentEvent = initStruct->mParentEvent;
|
||||
mParentGuard = initStruct->mParentGuard;
|
||||
mChildEvent = initStruct->mChildEvent;
|
||||
mChildGuard = initStruct->mChildGuard;
|
||||
SetObserver(aObserver);
|
||||
mTimeout = aTimeout;
|
||||
|
||||
rv = Send();
|
||||
if (NS_FAILED(rv)) {
|
||||
initCompleteStruct->mSucceeded = false;
|
||||
mFileMapping = NULL;
|
||||
view.Set(mView);
|
||||
mView = nullptr;
|
||||
mParentEvent = NULL;
|
||||
mParentGuard = NULL;
|
||||
mChildEvent = NULL;
|
||||
mChildGuard = NULL;
|
||||
::UnregisterWaitEx(mRegWait, INVALID_HANDLE_VALUE);
|
||||
mRegWait = NULL;
|
||||
return rv;
|
||||
}
|
||||
|
||||
OnConnect();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
MiniShmChild::Send()
|
||||
{
|
||||
if (!mParentEvent || !mParentGuard) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
if (::WaitForSingleObject(mParentGuard, mTimeout) != WAIT_OBJECT_0) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
if (!::SetEvent(mParentEvent)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
MiniShmChild::OnEvent()
|
||||
{
|
||||
MiniShmBase::OnEvent();
|
||||
::SetEvent(mChildGuard);
|
||||
}
|
||||
|
||||
} // namespace plugins
|
||||
} // namespace mozilla
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_plugins_MiniShmChild_h
|
||||
#define mozilla_plugins_MiniShmChild_h
|
||||
|
||||
#include "MiniShmBase.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace mozilla {
|
||||
namespace plugins {
|
||||
|
||||
/**
|
||||
* This class provides a lightweight shared memory interface for a child
|
||||
* process in Win32.
|
||||
* This code assumes that there is a parent-child relationship between
|
||||
* processes, as it inherits handles from the parent process.
|
||||
* Note that this class is *not* an IPDL actor.
|
||||
*
|
||||
* @see MiniShmParent
|
||||
*/
|
||||
class MiniShmChild : public MiniShmBase
|
||||
{
|
||||
public:
|
||||
MiniShmChild();
|
||||
virtual ~MiniShmChild();
|
||||
|
||||
/**
|
||||
* Initialize shared memory on the child side.
|
||||
*
|
||||
* @param aObserver A MiniShmObserver object to receive event notifications.
|
||||
* @param aCookie Cookie obtained from MiniShmParent::GetCookie
|
||||
* @param aTimeout Timeout in milliseconds.
|
||||
* @return nsresult error code
|
||||
*/
|
||||
nsresult
|
||||
Init(MiniShmObserver* aObserver, const std::wstring& aCookie,
|
||||
const DWORD aTimeout);
|
||||
|
||||
virtual nsresult
|
||||
Send() MOZ_OVERRIDE;
|
||||
|
||||
protected:
|
||||
void
|
||||
OnEvent() MOZ_OVERRIDE;
|
||||
|
||||
private:
|
||||
HANDLE mParentEvent;
|
||||
HANDLE mParentGuard;
|
||||
HANDLE mChildEvent;
|
||||
HANDLE mChildGuard;
|
||||
HANDLE mFileMapping;
|
||||
HANDLE mRegWait;
|
||||
LPVOID mView;
|
||||
DWORD mTimeout;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(MiniShmChild);
|
||||
};
|
||||
|
||||
} // namespace plugins
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_plugins_MiniShmChild_h
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_plugins_PluginHangUI_h
|
||||
#define mozilla_plugins_PluginHangUI_h
|
||||
|
||||
namespace mozilla {
|
||||
namespace plugins {
|
||||
|
||||
enum HangUIUserResponse
|
||||
{
|
||||
HANGUI_USER_RESPONSE_CANCEL = 1,
|
||||
HANGUI_USER_RESPONSE_CONTINUE = 2,
|
||||
HANGUI_USER_RESPONSE_STOP = 4,
|
||||
HANGUI_USER_RESPONSE_DONT_SHOW_AGAIN = 8
|
||||
};
|
||||
|
||||
enum PluginHangUIStructID
|
||||
{
|
||||
PLUGIN_HANGUI_COMMAND = 0x10,
|
||||
PLUGIN_HANGUI_RESULT
|
||||
};
|
||||
|
||||
struct PluginHangUICommand
|
||||
{
|
||||
enum
|
||||
{
|
||||
identifier = PLUGIN_HANGUI_COMMAND
|
||||
};
|
||||
enum CmdCode
|
||||
{
|
||||
HANGUI_CMD_SHOW = 1,
|
||||
HANGUI_CMD_CANCEL = 2
|
||||
};
|
||||
CmdCode mCode;
|
||||
};
|
||||
|
||||
struct PluginHangUIResponse
|
||||
{
|
||||
enum
|
||||
{
|
||||
identifier = PLUGIN_HANGUI_RESULT
|
||||
};
|
||||
unsigned int mResponseBits;
|
||||
};
|
||||
|
||||
} // namespace plugins
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_plugins_PluginHangUI_h
|
||||
|
|
@ -0,0 +1,289 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "PluginHangUI.h"
|
||||
|
||||
#include "PluginHangUIChild.h"
|
||||
#include "HangUIDlg.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <commctrl.h>
|
||||
#include <windowsx.h>
|
||||
#include <sstream>
|
||||
|
||||
namespace mozilla {
|
||||
namespace plugins {
|
||||
|
||||
PluginHangUIChild* PluginHangUIChild::sSelf = nullptr;
|
||||
const int PluginHangUIChild::kExpectedMinimumArgc = 9;
|
||||
const DWORD PluginHangUIChild::kProcessTimeout = 1200000U;
|
||||
const DWORD PluginHangUIChild::kShmTimeout = 5000U;
|
||||
|
||||
PluginHangUIChild::PluginHangUIChild()
|
||||
: mResponseBits(0),
|
||||
mParentWindow(NULL),
|
||||
mDlgHandle(NULL),
|
||||
mMainThread(NULL),
|
||||
mParentProcess(NULL),
|
||||
mRegWaitProcess(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
PluginHangUIChild::~PluginHangUIChild()
|
||||
{
|
||||
if (mMainThread) {
|
||||
CloseHandle(mMainThread);
|
||||
}
|
||||
if (mRegWaitProcess) {
|
||||
UnregisterWaitEx(mRegWaitProcess, INVALID_HANDLE_VALUE);
|
||||
}
|
||||
if (mParentProcess) {
|
||||
CloseHandle(mParentProcess);
|
||||
}
|
||||
sSelf = nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
PluginHangUIChild::Init(int aArgc, wchar_t* aArgv[])
|
||||
{
|
||||
if (aArgc < kExpectedMinimumArgc) {
|
||||
return false;
|
||||
}
|
||||
unsigned int i = 1;
|
||||
mMessageText = aArgv[i];
|
||||
mWindowTitle = aArgv[++i];
|
||||
mWaitBtnText = aArgv[++i];
|
||||
mKillBtnText = aArgv[++i];
|
||||
mNoFutureText = aArgv[++i];
|
||||
std::wistringstream issHwnd(aArgv[++i]);
|
||||
issHwnd >> reinterpret_cast<HANDLE&>(mParentWindow);
|
||||
if (!issHwnd) {
|
||||
return false;
|
||||
}
|
||||
std::wistringstream issProc(aArgv[++i]);
|
||||
issProc >> mParentProcess;
|
||||
if (!issProc) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsresult rv = mMiniShm.Init(this,
|
||||
std::wstring(aArgv[++i]),
|
||||
IsDebuggerPresent() ? INFINITE : kShmTimeout);
|
||||
if (NS_FAILED(rv)) {
|
||||
return false;
|
||||
}
|
||||
sSelf = this;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
PluginHangUIChild::OnMiniShmEvent(MiniShmBase* aMiniShmObj)
|
||||
{
|
||||
const PluginHangUICommand* cmd = nullptr;
|
||||
nsresult rv = aMiniShmObj->GetReadPtr(cmd);
|
||||
assert(NS_SUCCEEDED(rv));
|
||||
bool returnStatus = false;
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
switch (cmd->mCode) {
|
||||
case PluginHangUICommand::HANGUI_CMD_SHOW:
|
||||
returnStatus = RecvShow();
|
||||
break;
|
||||
case PluginHangUICommand::HANGUI_CMD_CANCEL:
|
||||
returnStatus = RecvCancel();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
INT_PTR CALLBACK
|
||||
PluginHangUIChild::SHangUIDlgProc(HWND aDlgHandle, UINT aMsgCode,
|
||||
WPARAM aWParam, LPARAM aLParam)
|
||||
{
|
||||
PluginHangUIChild *self = PluginHangUIChild::sSelf;
|
||||
if (self) {
|
||||
return self->HangUIDlgProc(aDlgHandle, aMsgCode, aWParam, aLParam);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
INT_PTR
|
||||
PluginHangUIChild::HangUIDlgProc(HWND aDlgHandle, UINT aMsgCode, WPARAM aWParam,
|
||||
LPARAM aLParam)
|
||||
{
|
||||
mDlgHandle = aDlgHandle;
|
||||
switch (aMsgCode) {
|
||||
case WM_INITDIALOG: {
|
||||
// Disentangle our input queue from the hung Firefox process
|
||||
AttachThreadInput(GetCurrentThreadId(),
|
||||
GetWindowThreadProcessId(mParentWindow, nullptr),
|
||||
FALSE);
|
||||
// Register a wait on the Firefox process so that we will be informed
|
||||
// if it dies while the dialog is showing
|
||||
RegisterWaitForSingleObject(&mRegWaitProcess,
|
||||
mParentProcess,
|
||||
&SOnParentProcessExit,
|
||||
this,
|
||||
INFINITE,
|
||||
WT_EXECUTEDEFAULT | WT_EXECUTEONLYONCE);
|
||||
SetWindowText(aDlgHandle, mWindowTitle);
|
||||
SetDlgItemText(aDlgHandle, IDC_MSG, mMessageText);
|
||||
SetDlgItemText(aDlgHandle, IDC_NOFUTURE, mNoFutureText);
|
||||
SetDlgItemText(aDlgHandle, IDC_CONTINUE, mWaitBtnText);
|
||||
SetDlgItemText(aDlgHandle, IDC_STOP, mKillBtnText);
|
||||
HANDLE icon = LoadImage(NULL, IDI_QUESTION, IMAGE_ICON, 0, 0,
|
||||
LR_DEFAULTSIZE | LR_SHARED);
|
||||
if (icon) {
|
||||
SendDlgItemMessage(aDlgHandle, IDC_DLGICON, STM_SETICON, (WPARAM)icon, 0);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
case WM_CLOSE: {
|
||||
mResponseBits |= HANGUI_USER_RESPONSE_CANCEL;
|
||||
EndDialog(aDlgHandle, 0);
|
||||
return TRUE;
|
||||
}
|
||||
case WM_COMMAND: {
|
||||
switch (LOWORD(aWParam)) {
|
||||
case IDC_CONTINUE:
|
||||
if (HIWORD(aWParam) == BN_CLICKED) {
|
||||
mResponseBits |= HANGUI_USER_RESPONSE_CONTINUE;
|
||||
EndDialog(aDlgHandle, 1);
|
||||
return TRUE;
|
||||
}
|
||||
break;
|
||||
case IDC_STOP:
|
||||
if (HIWORD(aWParam) == BN_CLICKED) {
|
||||
mResponseBits |= HANGUI_USER_RESPONSE_STOP;
|
||||
EndDialog(aDlgHandle, 1);
|
||||
return TRUE;
|
||||
}
|
||||
break;
|
||||
case IDC_NOFUTURE:
|
||||
if (HIWORD(aWParam) == BN_CLICKED) {
|
||||
if (Button_GetCheck(GetDlgItem(aDlgHandle,
|
||||
IDC_NOFUTURE)) == BST_CHECKED) {
|
||||
mResponseBits |= HANGUI_USER_RESPONSE_DONT_SHOW_AGAIN;
|
||||
} else {
|
||||
mResponseBits &=
|
||||
~static_cast<DWORD>(HANGUI_USER_RESPONSE_DONT_SHOW_AGAIN);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
VOID CALLBACK
|
||||
PluginHangUIChild::SOnParentProcessExit(PVOID aObject, BOOLEAN aIsTimer)
|
||||
{
|
||||
// Simulate a cancel if the parent process died
|
||||
PluginHangUIChild* object = static_cast<PluginHangUIChild*>(aObject);
|
||||
object->RecvCancel();
|
||||
}
|
||||
|
||||
bool
|
||||
PluginHangUIChild::RecvShow()
|
||||
{
|
||||
return (QueueUserAPC(&ShowAPC,
|
||||
mMainThread,
|
||||
reinterpret_cast<ULONG_PTR>(this)));
|
||||
}
|
||||
|
||||
bool
|
||||
PluginHangUIChild::Show()
|
||||
{
|
||||
INT_PTR dlgResult = DialogBox(GetModuleHandle(NULL),
|
||||
MAKEINTRESOURCE(IDD_HANGUIDLG),
|
||||
mParentWindow,
|
||||
&SHangUIDlgProc);
|
||||
mDlgHandle = NULL;
|
||||
assert(dlgResult != -1);
|
||||
bool result = false;
|
||||
if (dlgResult != -1) {
|
||||
PluginHangUIResponse* response = nullptr;
|
||||
nsresult rv = mMiniShm.GetWritePtr(response);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
response->mResponseBits = mResponseBits;
|
||||
result = NS_SUCCEEDED(mMiniShm.Send());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// static
|
||||
VOID CALLBACK
|
||||
PluginHangUIChild::ShowAPC(ULONG_PTR aContext)
|
||||
{
|
||||
PluginHangUIChild* object = reinterpret_cast<PluginHangUIChild*>(aContext);
|
||||
object->Show();
|
||||
}
|
||||
|
||||
bool
|
||||
PluginHangUIChild::RecvCancel()
|
||||
{
|
||||
if (mDlgHandle) {
|
||||
PostMessage(mDlgHandle, WM_CLOSE, 0, 0);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
PluginHangUIChild::WaitForDismissal()
|
||||
{
|
||||
if (!SetMainThread()) {
|
||||
return false;
|
||||
}
|
||||
DWORD waitResult = WaitForSingleObjectEx(mParentProcess,
|
||||
kProcessTimeout,
|
||||
TRUE);
|
||||
return waitResult == WAIT_OBJECT_0 ||
|
||||
waitResult == WAIT_IO_COMPLETION;
|
||||
}
|
||||
|
||||
bool
|
||||
PluginHangUIChild::SetMainThread()
|
||||
{
|
||||
if (mMainThread) {
|
||||
CloseHandle(mMainThread);
|
||||
mMainThread = NULL;
|
||||
}
|
||||
mMainThread = OpenThread(THREAD_SET_CONTEXT,
|
||||
FALSE,
|
||||
GetCurrentThreadId());
|
||||
return !(!mMainThread);
|
||||
}
|
||||
|
||||
} // namespace plugins
|
||||
} // namespace mozilla
|
||||
|
||||
int
|
||||
wmain(int argc, wchar_t *argv[])
|
||||
{
|
||||
INITCOMMONCONTROLSEX icc = { sizeof(INITCOMMONCONTROLSEX),
|
||||
ICC_STANDARD_CLASSES };
|
||||
if (!InitCommonControlsEx(&icc)) {
|
||||
return 1;
|
||||
}
|
||||
mozilla::plugins::PluginHangUIChild hangui;
|
||||
if (!hangui.Init(argc, argv)) {
|
||||
return 1;
|
||||
}
|
||||
if (!hangui.WaitForDismissal()) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_plugins_PluginHangUIChild_h
|
||||
#define mozilla_plugins_PluginHangUIChild_h
|
||||
|
||||
#include "MiniShmChild.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
namespace mozilla {
|
||||
namespace plugins {
|
||||
|
||||
/**
|
||||
* This class implements the plugin-hang-ui.
|
||||
*
|
||||
* NOTE: PluginHangUIChild is *not* an IPDL actor! In this case, "Child"
|
||||
* is describing the fact that plugin-hang-ui is a child process to the
|
||||
* firefox process, which is the PluginHangUIParent.
|
||||
* PluginHangUIParent and PluginHangUIChild are a matched pair.
|
||||
* @see PluginHangUIParent
|
||||
*/
|
||||
class PluginHangUIChild : public MiniShmObserver
|
||||
{
|
||||
public:
|
||||
PluginHangUIChild();
|
||||
virtual ~PluginHangUIChild();
|
||||
|
||||
bool
|
||||
Init(int aArgc, wchar_t* aArgv[]);
|
||||
|
||||
/**
|
||||
* Displays the Plugin Hang UI and does not return until the UI has
|
||||
* been dismissed.
|
||||
*
|
||||
* @return true if the UI was displayed and the user response was
|
||||
* successfully sent back to the parent. Otherwise false.
|
||||
*/
|
||||
bool
|
||||
Show();
|
||||
|
||||
/**
|
||||
* Causes the calling thread to wait either for the Hang UI to be
|
||||
* dismissed or for the parent process to terminate. This should
|
||||
* be called by the main thread.
|
||||
*
|
||||
* @return true unless there was an error initiating the wait
|
||||
*/
|
||||
bool
|
||||
WaitForDismissal();
|
||||
|
||||
virtual void
|
||||
OnMiniShmEvent(MiniShmBase* aMiniShmObj) MOZ_OVERRIDE;
|
||||
|
||||
private:
|
||||
bool
|
||||
RecvShow();
|
||||
|
||||
bool
|
||||
RecvCancel();
|
||||
|
||||
bool
|
||||
SetMainThread();
|
||||
|
||||
INT_PTR
|
||||
HangUIDlgProc(HWND aDlgHandle, UINT aMsgCode, WPARAM aWParam, LPARAM aLParam);
|
||||
|
||||
static VOID CALLBACK
|
||||
ShowAPC(ULONG_PTR aContext);
|
||||
|
||||
static INT_PTR CALLBACK
|
||||
SHangUIDlgProc(HWND aDlgHandle, UINT aMsgCode, WPARAM aWParam,
|
||||
LPARAM aLParam);
|
||||
|
||||
static VOID CALLBACK
|
||||
SOnParentProcessExit(PVOID aObject, BOOLEAN aIsTimer);
|
||||
|
||||
static PluginHangUIChild *sSelf;
|
||||
|
||||
const wchar_t* mMessageText;
|
||||
const wchar_t* mWindowTitle;
|
||||
const wchar_t* mWaitBtnText;
|
||||
const wchar_t* mKillBtnText;
|
||||
const wchar_t* mNoFutureText;
|
||||
unsigned int mResponseBits;
|
||||
HWND mParentWindow;
|
||||
HWND mDlgHandle;
|
||||
HANDLE mMainThread;
|
||||
HANDLE mParentProcess;
|
||||
HANDLE mRegWaitProcess;
|
||||
MiniShmChild mMiniShm;
|
||||
|
||||
static const int kExpectedMinimumArgc;
|
||||
static const DWORD kProcessTimeout;
|
||||
static const DWORD kShmTimeout;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(PluginHangUIChild);
|
||||
};
|
||||
|
||||
} // namespace plugins
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_plugins_PluginHangUIChild_h
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
WIN32_MODULE_COMPANYNAME=Mozilla Corporation
|
||||
WIN32_MODULE_PRODUCTVERSION=@MOZ_APP_WINVERSION@
|
||||
WIN32_MODULE_PRODUCTVERSION_STRING=@MOZ_APP_VERSION@
|
||||
WIN32_MODULE_DESCRIPTION=Plugin Hang UI for @MOZ_APP_DISPLAYNAME@
|
||||
WIN32_MODULE_PRODUCTNAME=@MOZ_APP_DISPLAYNAME@
|
||||
WIN32_MODULE_NAME=@MOZ_APP_DISPLAYNAME@
|
|
@ -0,0 +1,35 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||
<assemblyIdentity
|
||||
version="1.0.0.0"
|
||||
processorArchitecture="*"
|
||||
name="plugin-hang-ui"
|
||||
type="win32"
|
||||
/>
|
||||
<description>Firefox Plugin Hang User Interface</description>
|
||||
<dependency>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity
|
||||
type="win32"
|
||||
name="Microsoft.Windows.Common-Controls"
|
||||
version="6.0.0.0"
|
||||
processorArchitecture="*"
|
||||
publicKeyToken="6595b64144ccf1df"
|
||||
language="*"
|
||||
/>
|
||||
</dependentAssembly>
|
||||
</dependency>
|
||||
<ms_asmv3:trustInfo xmlns:ms_asmv3="urn:schemas-microsoft-com:asm.v3">
|
||||
<ms_asmv3:security>
|
||||
<ms_asmv3:requestedPrivileges>
|
||||
<ms_asmv3:requestedExecutionLevel level="asInvoker" uiAccess="false" />
|
||||
</ms_asmv3:requestedPrivileges>
|
||||
</ms_asmv3:security>
|
||||
</ms_asmv3:trustInfo>
|
||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||
<application>
|
||||
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />
|
||||
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />
|
||||
</application>
|
||||
</compatibility>
|
||||
</assembly>
|
|
@ -1755,11 +1755,22 @@ pref("dom.ipc.plugins.parentTimeoutSecs", 0);
|
|||
// How long a plugin launch is allowed to take before
|
||||
// we consider it failed.
|
||||
pref("dom.ipc.plugins.processLaunchTimeoutSecs", 45);
|
||||
#ifdef XP_WIN
|
||||
// How long a plugin is allowed to process a synchronous IPC message
|
||||
// before we display the plugin hang UI
|
||||
pref("dom.ipc.plugins.hangUITimeoutSecs", 5);
|
||||
// Minimum time that the plugin hang UI will be displayed
|
||||
pref("dom.ipc.plugins.hangUIMinDisplaySecs", 10);
|
||||
#endif
|
||||
#else
|
||||
// No timeout in DEBUG builds
|
||||
pref("dom.ipc.plugins.timeoutSecs", 0);
|
||||
pref("dom.ipc.plugins.processLaunchTimeoutSecs", 0);
|
||||
pref("dom.ipc.plugins.parentTimeoutSecs", 0);
|
||||
#ifdef XP_WIN
|
||||
pref("dom.ipc.plugins.hangUITimeoutSecs", 0);
|
||||
pref("dom.ipc.plugins.hangUIMinDisplaySecs", 0);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef XP_WIN
|
||||
|
|
|
@ -1463,6 +1463,15 @@
|
|||
"n_buckets": 10,
|
||||
"description": "Time spent checking if Java is enabled (ms)"
|
||||
},
|
||||
"PLUGIN_HANG_UI_USER_RESPONSE": {
|
||||
"kind": "enumerated",
|
||||
"n_values": 3,
|
||||
"description": "User response to Plugin Hang UI"
|
||||
},
|
||||
"PLUGIN_HANG_UI_DONT_ASK": {
|
||||
"kind": "boolean",
|
||||
"description": "Whether the user has requested not to see the Plugin Hang UI again"
|
||||
},
|
||||
"PLUGIN_SHUTDOWN_MS": {
|
||||
"kind": "exponential",
|
||||
"high": "5000",
|
||||
|
|
Загрузка…
Ссылка в новой задаче