mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge mozilla-central to fx-team
This commit is contained in:
commit
ca657b39c3
@ -1,4 +1,4 @@
|
||||
{
|
||||
"revision": "2cc6e0988688b33fa46e3a05a726fb7919d7420e",
|
||||
"revision": "7412c36923b59f6e4d7076de5be7e6ded6ddc586",
|
||||
"repo_path": "/integration/gaia-central"
|
||||
}
|
||||
|
@ -561,23 +561,6 @@ function waitForObserver(aObsEvent, aTimeoutMs) {
|
||||
* generating os level input events that get processed by widget and
|
||||
* apzc logic.
|
||||
*===========================================================================*/
|
||||
|
||||
// Keyboard layouts for use with synthesizeNativeKey
|
||||
const usEnglish = 0x409;
|
||||
const arSpanish = 0x2C0A;
|
||||
|
||||
// Modifiers for use with synthesizeNativeKey
|
||||
const leftShift = 0x100;
|
||||
const rightShift = 0x200;
|
||||
const leftControl = 0x400;
|
||||
const rightControl = 0x800;
|
||||
const leftAlt = 0x1000;
|
||||
const rightAlt = 0x2000;
|
||||
|
||||
function synthesizeNativeKey(aKbLayout, aVKey, aModifiers) {
|
||||
Browser.windowUtils.sendNativeKeyEvent(aKbLayout, aVKey, aModifiers, '', '');
|
||||
}
|
||||
|
||||
function synthesizeNativeMouse(aElement, aOffsetX, aOffsetY, aMsg) {
|
||||
let x = aOffsetX;
|
||||
let y = aOffsetY;
|
||||
|
@ -3914,6 +3914,7 @@ MOZ_ARG_WITH_BOOL(system-icu,
|
||||
if test -n "$MOZ_NATIVE_ICU"; then
|
||||
PKG_CHECK_MODULES(MOZ_ICU, icu-i18n >= 50.1)
|
||||
MOZ_JS_STATIC_LIBS="$MOZ_JS_STATIC_LIBS $MOZ_ICU_LIBS"
|
||||
MOZ_SHARED_ICU=1
|
||||
fi
|
||||
|
||||
AC_SUBST(MOZ_NATIVE_ICU)
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
#include "nsContentEventHandler.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsFocusManager.h"
|
||||
#include "nsPresContext.h"
|
||||
#include "nsIPresShell.h"
|
||||
#include "nsISelection.h"
|
||||
@ -31,6 +32,7 @@
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
using namespace mozilla::widget;
|
||||
|
||||
/******************************************************************/
|
||||
/* nsContentEventHandler */
|
||||
@ -127,6 +129,55 @@ nsContentEventHandler::Init(WidgetSelectionEvent* aEvent)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsIContent*
|
||||
nsContentEventHandler::GetFocusedContent()
|
||||
{
|
||||
nsIDocument* doc = mPresShell->GetDocument();
|
||||
if (!doc) {
|
||||
return nullptr;
|
||||
}
|
||||
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(doc->GetWindow());
|
||||
nsCOMPtr<nsPIDOMWindow> focusedWindow;
|
||||
return nsFocusManager::GetFocusedDescendant(window, true,
|
||||
getter_AddRefs(focusedWindow));
|
||||
}
|
||||
|
||||
bool
|
||||
nsContentEventHandler::IsPlugin(nsIContent* aContent)
|
||||
{
|
||||
return aContent &&
|
||||
aContent->GetDesiredIMEState().mEnabled == IMEState::PLUGIN;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsContentEventHandler::QueryContentRect(nsIContent* aContent,
|
||||
WidgetQueryContentEvent* aEvent)
|
||||
{
|
||||
NS_PRECONDITION(aContent, "aContent must not be null");
|
||||
|
||||
nsIFrame* frame = aContent->GetPrimaryFrame();
|
||||
NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
|
||||
|
||||
// get rect for first frame
|
||||
nsRect resultRect(nsPoint(0, 0), frame->GetRect().Size());
|
||||
nsresult rv = ConvertToRootViewRelativeOffset(frame, resultRect);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// account for any additional frames
|
||||
while ((frame = frame->GetNextContinuation()) != nullptr) {
|
||||
nsRect frameRect(nsPoint(0, 0), frame->GetRect().Size());
|
||||
rv = ConvertToRootViewRelativeOffset(frame, frameRect);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
resultRect.UnionRect(resultRect, frameRect);
|
||||
}
|
||||
|
||||
aEvent->mReply.mRect =
|
||||
resultRect.ToOutsidePixels(mPresContext->AppUnitsPerDevPixel());
|
||||
aEvent->mSucceeded = true;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Editor places a bogus BR node under its root content if the editor doesn't
|
||||
// have any text. This happens even for single line editors.
|
||||
// When we get text content and when we change the selection,
|
||||
@ -676,25 +727,10 @@ nsContentEventHandler::OnQueryEditorRect(WidgetQueryContentEvent* aEvent)
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
nsIFrame* frame = mRootContent->GetPrimaryFrame();
|
||||
NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
|
||||
|
||||
// get rect for first frame
|
||||
nsRect resultRect(nsPoint(0, 0), frame->GetRect().Size());
|
||||
rv = ConvertToRootViewRelativeOffset(frame, resultRect);
|
||||
nsIContent* focusedContent = GetFocusedContent();
|
||||
rv = QueryContentRect(IsPlugin(focusedContent) ?
|
||||
focusedContent : mRootContent.get(), aEvent);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// account for any additional frames
|
||||
while ((frame = frame->GetNextContinuation()) != nullptr) {
|
||||
nsRect frameRect(nsPoint(0, 0), frame->GetRect().Size());
|
||||
rv = ConvertToRootViewRelativeOffset(frame, frameRect);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
resultRect.UnionRect(resultRect, frameRect);
|
||||
}
|
||||
|
||||
aEvent->mReply.mRect =
|
||||
resultRect.ToOutsidePixels(mPresContext->AppUnitsPerDevPixel());
|
||||
aEvent->mSucceeded = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -81,6 +81,13 @@ public:
|
||||
static uint32_t GetNativeTextLength(nsIContent* aContent,
|
||||
uint32_t aMaxLength = UINT32_MAX);
|
||||
protected:
|
||||
// Returns focused content (including its descendant documents).
|
||||
nsIContent* GetFocusedContent();
|
||||
// Returns true if the content is a plugin host.
|
||||
bool IsPlugin(nsIContent* aContent);
|
||||
// QueryContentRect() sets the rect of aContent's frame(s) to aEvent.
|
||||
nsresult QueryContentRect(nsIContent* aContent,
|
||||
mozilla::WidgetQueryContentEvent* aEvent);
|
||||
// Make the DOM range from the offset of FlatText and the text length.
|
||||
// If aExpandToClusterBoundaries is true, the start offset and the end one are
|
||||
// expanded to nearest cluster boundaries.
|
||||
|
@ -13,7 +13,6 @@
|
||||
#include "VideoUtils.h"
|
||||
#include "MediaDecoderStateMachine.h"
|
||||
#include "mozilla/dom/TimeRanges.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "ImageContainer.h"
|
||||
#include "MediaResource.h"
|
||||
#include "nsError.h"
|
||||
@ -23,6 +22,7 @@
|
||||
#include "nsComponentManagerUtils.h"
|
||||
#include "nsITimer.h"
|
||||
#include <algorithm>
|
||||
#include "MediaShutdownManager.h"
|
||||
|
||||
#ifdef MOZ_WMF
|
||||
#include "WMFDecoder.h"
|
||||
@ -444,7 +444,7 @@ bool MediaDecoder::Init(MediaDecoderOwner* aOwner)
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
mOwner = aOwner;
|
||||
mVideoFrameContainer = aOwner->GetVideoFrameContainer();
|
||||
nsContentUtils::RegisterShutdownObserver(this);
|
||||
MediaShutdownManager::Instance().Register(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -480,7 +480,7 @@ void MediaDecoder::Shutdown()
|
||||
StopProgress();
|
||||
mOwner = nullptr;
|
||||
|
||||
nsContentUtils::UnregisterShutdownObserver(this);
|
||||
MediaShutdownManager::Instance().Unregister(this);
|
||||
}
|
||||
|
||||
MediaDecoder::~MediaDecoder()
|
||||
|
@ -26,6 +26,8 @@
|
||||
#include "ImageContainer.h"
|
||||
#include "nsComponentManagerUtils.h"
|
||||
#include "nsITimer.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "MediaShutdownManager.h"
|
||||
|
||||
#include "prenv.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
@ -178,7 +180,7 @@ public:
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mMonitor);
|
||||
NS_ASSERTION(mStateMachineThread, "Should have non-null state machine thread!");
|
||||
return mStateMachineThread;
|
||||
return mStateMachineThread->GetThread();
|
||||
}
|
||||
|
||||
// Requests that a decode thread be created for aStateMachine. The thread
|
||||
@ -234,7 +236,7 @@ private:
|
||||
// Global state machine thread. Write on the main thread
|
||||
// only, read from the decoder threads. Synchronized via
|
||||
// the mMonitor.
|
||||
nsIThread* mStateMachineThread;
|
||||
nsRefPtr<StateMachineThread> mStateMachineThread;
|
||||
|
||||
// Queue of state machines waiting for decode threads. Entries at the front
|
||||
// get their threads first.
|
||||
@ -258,7 +260,8 @@ void StateMachineTracker::EnsureGlobalStateMachine()
|
||||
ReentrantMonitorAutoEnter mon(mMonitor);
|
||||
if (mStateMachineCount == 0) {
|
||||
NS_ASSERTION(!mStateMachineThread, "Should have null state machine thread!");
|
||||
DebugOnly<nsresult> rv = NS_NewNamedThread("Media State", &mStateMachineThread);
|
||||
mStateMachineThread = new StateMachineThread();
|
||||
DebugOnly<nsresult> rv = mStateMachineThread->Init();
|
||||
NS_ABORT_IF_FALSE(NS_SUCCEEDED(rv), "Can't create media state machine thread");
|
||||
}
|
||||
mStateMachineCount++;
|
||||
@ -291,11 +294,8 @@ void StateMachineTracker::CleanupGlobalStateMachine()
|
||||
NS_ASSERTION(mPending.GetSize() == 0, "Shouldn't all requests be handled by now?");
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mMonitor);
|
||||
nsCOMPtr<nsIRunnable> event = new ShutdownThreadEvent(mStateMachineThread);
|
||||
NS_RELEASE(mStateMachineThread);
|
||||
mStateMachineThread->Shutdown();
|
||||
mStateMachineThread = nullptr;
|
||||
NS_DispatchToMainThread(event);
|
||||
|
||||
NS_ASSERTION(mDecodeThreadCount == 0, "Decode thread count must be zero.");
|
||||
sInstance = nullptr;
|
||||
}
|
||||
|
238
content/media/MediaShutdownManager.cpp
Normal file
238
content/media/MediaShutdownManager.cpp
Normal file
@ -0,0 +1,238 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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 "MediaShutdownManager.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "mozilla/ClearOnShutdown.h"
|
||||
#include "MediaDecoder.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
StateMachineThread::StateMachineThread()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_COUNT_CTOR(StateMachineThread);
|
||||
}
|
||||
|
||||
StateMachineThread::~StateMachineThread()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_COUNT_DTOR(StateMachineThread);
|
||||
}
|
||||
|
||||
void
|
||||
StateMachineThread::Shutdown()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mThread);
|
||||
if (mThread) {
|
||||
nsCOMPtr<nsIRunnable> event =
|
||||
NS_NewRunnableMethod(this, &StateMachineThread::ShutdownThread);
|
||||
NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
StateMachineThread::ShutdownThread()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mThread);
|
||||
mThread->Shutdown();
|
||||
mThread = nullptr;
|
||||
MediaShutdownManager::Instance().Unregister(this);
|
||||
}
|
||||
|
||||
nsresult
|
||||
StateMachineThread::Init()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
nsresult rv = NS_NewNamedThread("Media State", getter_AddRefs(mThread));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
MediaShutdownManager::Instance().Register(this);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsIThread*
|
||||
StateMachineThread::GetThread()
|
||||
{
|
||||
MOZ_ASSERT(mThread);
|
||||
return mThread;
|
||||
}
|
||||
|
||||
void
|
||||
StateMachineThread::SpinUntilShutdownComplete()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
while (mThread) {
|
||||
bool processed = false;
|
||||
nsresult rv = NS_GetCurrentThread()->ProcessNextEvent(true, &processed);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Failed to spin main thread while awaiting media shutdown");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS1(MediaShutdownManager, nsIObserver)
|
||||
|
||||
MediaShutdownManager::MediaShutdownManager()
|
||||
: mIsObservingShutdown(false),
|
||||
mIsDoingXPCOMShutDown(false)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_COUNT_CTOR(MediaShutdownManager);
|
||||
}
|
||||
|
||||
MediaShutdownManager::~MediaShutdownManager()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_COUNT_DTOR(MediaShutdownManager);
|
||||
}
|
||||
|
||||
// Note that we don't use ClearOnShutdown() on this StaticRefPtr, as that
|
||||
// may interfere with our shutdown listener.
|
||||
StaticRefPtr<MediaShutdownManager> MediaShutdownManager::sInstance;
|
||||
|
||||
MediaShutdownManager&
|
||||
MediaShutdownManager::Instance()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (!sInstance) {
|
||||
sInstance = new MediaShutdownManager();
|
||||
}
|
||||
return *sInstance;
|
||||
}
|
||||
|
||||
void
|
||||
MediaShutdownManager::EnsureCorrectShutdownObserverState()
|
||||
{
|
||||
MOZ_ASSERT(!mIsDoingXPCOMShutDown);
|
||||
bool needShutdownObserver = (mDecoders.Count() > 0) ||
|
||||
(mStateMachineThreads.Count() > 0);
|
||||
if (needShutdownObserver != mIsObservingShutdown) {
|
||||
mIsObservingShutdown = needShutdownObserver;
|
||||
if (mIsObservingShutdown) {
|
||||
nsContentUtils::RegisterShutdownObserver(this);
|
||||
} else {
|
||||
nsContentUtils::UnregisterShutdownObserver(this);
|
||||
// Clear our singleton reference. This will probably delete
|
||||
// this instance, so don't deref |this| clearing sInstance.
|
||||
sInstance = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MediaShutdownManager::Register(MediaDecoder* aDecoder)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
// Don't call Register() after you've Unregistered() all the decoders,
|
||||
// that's not going to work.
|
||||
MOZ_ASSERT(!mDecoders.Contains(aDecoder));
|
||||
mDecoders.PutEntry(aDecoder);
|
||||
MOZ_ASSERT(mDecoders.Contains(aDecoder));
|
||||
MOZ_ASSERT(mDecoders.Count() > 0);
|
||||
EnsureCorrectShutdownObserverState();
|
||||
}
|
||||
|
||||
void
|
||||
MediaShutdownManager::Unregister(MediaDecoder* aDecoder)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mDecoders.Contains(aDecoder));
|
||||
if (!mIsDoingXPCOMShutDown) {
|
||||
mDecoders.RemoveEntry(aDecoder);
|
||||
EnsureCorrectShutdownObserverState();
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
MediaShutdownManager::Observe(nsISupports *aSubjet,
|
||||
const char *aTopic,
|
||||
const PRUnichar *someData)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
|
||||
Shutdown();
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
MediaShutdownManager::Register(StateMachineThread* aThread)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(!mStateMachineThreads.Contains(aThread));
|
||||
mStateMachineThreads.PutEntry(aThread);
|
||||
MOZ_ASSERT(mStateMachineThreads.Contains(aThread));
|
||||
MOZ_ASSERT(mStateMachineThreads.Count() > 0);
|
||||
EnsureCorrectShutdownObserverState();
|
||||
}
|
||||
|
||||
void
|
||||
MediaShutdownManager::Unregister(StateMachineThread* aThread)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mStateMachineThreads.Contains(aThread));
|
||||
if (!mIsDoingXPCOMShutDown) {
|
||||
mStateMachineThreads.RemoveEntry(aThread);
|
||||
EnsureCorrectShutdownObserverState();
|
||||
}
|
||||
}
|
||||
|
||||
static PLDHashOperator
|
||||
ShutdownMediaDecoder(nsRefPtrHashKey<MediaDecoder>* aEntry, void*)
|
||||
{
|
||||
aEntry->GetKey()->Shutdown();
|
||||
return PL_DHASH_REMOVE;
|
||||
}
|
||||
|
||||
static PLDHashOperator
|
||||
JoinStateMachineThreads(nsRefPtrHashKey<StateMachineThread>* aEntry, void*)
|
||||
{
|
||||
// Hold a strong reference to the StateMachineThread, so that if it
|
||||
// is Unregistered() and the hashtable's owning reference is cleared,
|
||||
// it won't be destroyed while we're spinning here.
|
||||
RefPtr<StateMachineThread> thread = aEntry->GetKey();
|
||||
thread->SpinUntilShutdownComplete();
|
||||
return PL_DHASH_REMOVE;
|
||||
}
|
||||
|
||||
void
|
||||
MediaShutdownManager::Shutdown()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(sInstance);
|
||||
|
||||
// Mark that we're shutting down, so that Unregister(*) calls don't remove
|
||||
// hashtable entries. If Unregsiter(*) was to remove from the hash table,
|
||||
// the iterations over the hashtables below would be disrupted.
|
||||
mIsDoingXPCOMShutDown = true;
|
||||
|
||||
// Iterate over the decoders and shut them down, and remove them from the
|
||||
// hashtable.
|
||||
mDecoders.EnumerateEntries(ShutdownMediaDecoder, nullptr);
|
||||
|
||||
// Iterate over the StateMachineThreads and wait for them to have finished
|
||||
// shutting down, and remove them from the hashtable. Once all the decoders
|
||||
// have shutdown the active state machine thread will naturally shutdown
|
||||
// asynchronously. We may also have another state machine thread active,
|
||||
// if construction and shutdown of the state machine thread has interleaved.
|
||||
mStateMachineThreads.EnumerateEntries(JoinStateMachineThreads, nullptr);
|
||||
|
||||
// Remove the MediaShutdownManager instance from the shutdown observer
|
||||
// list.
|
||||
nsContentUtils::UnregisterShutdownObserver(this);
|
||||
|
||||
// Clear the singleton instance. The only remaining reference should be the
|
||||
// reference that the observer service used to call us with. The
|
||||
// MediaShutdownManager will be deleted once the observer service cleans
|
||||
// up after it finishes its notifications.
|
||||
sInstance = nullptr;
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
169
content/media/MediaShutdownManager.h
Normal file
169
content/media/MediaShutdownManager.h
Normal file
@ -0,0 +1,169 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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/. */
|
||||
|
||||
#if !defined(MediaShutdownManager_h_)
|
||||
#define MediaShutdownManager_h_
|
||||
|
||||
#include "nsIObserver.h"
|
||||
#include "mozilla/Monitor.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "nsIThread.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsTHashtable.h"
|
||||
#include "nsHashKeys.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class MediaDecoder;
|
||||
class StateMachineThread;
|
||||
|
||||
// The MediaShutdownManager manages shutting down the MediaDecoder
|
||||
// infrastructure in response to an xpcom-shutdown notification. This happens
|
||||
// when Gecko is shutting down in the middle of operation. This is tricky, as
|
||||
// there are a number of moving parts that must be shutdown in a particular
|
||||
// order. Additionally the xpcom-shutdown observer *must* block until all
|
||||
// threads are shutdown, which is tricky since we have a number of threads
|
||||
// here and their shutdown is asynchronous. We can't have each element of
|
||||
// our pipeline listening for xpcom-shutdown, as if each observer blocks
|
||||
// waiting for its threads to shutdown it will block other xpcom-shutdown
|
||||
// notifications from firing, and shutdown of one part of the media pipeline
|
||||
// (say the State Machine thread) may depend another part to be shutdown
|
||||
// first (the MediaDecoder threads). Additionally we need to not interfere
|
||||
// with shutdown in the the non-xpcom-shutdown case, where we need to be able
|
||||
// to recreate the State Machine thread after it's been destroyed without
|
||||
// affecting the shutdown of the old State Machine thread. The
|
||||
// MediaShutdownManager encapsulates all these dependencies, and provides
|
||||
// a single xpcom-shutdown listener for the MediaDecoder infrastructure, to
|
||||
// ensure that no shutdown order dependencies leak out of the MediaDecoder
|
||||
// stack. The MediaShutdownManager is a singleton.
|
||||
//
|
||||
// The MediaShutdownManager ensures that the MediaDecoder stack is shutdown
|
||||
// before returning from its xpcom-shutdown observer by keeping track of all
|
||||
// the active MediaDecoders, and upon xpcom-shutdown calling Shutdown() on
|
||||
// every MediaDecoder and then spinning the main thread event loop until the
|
||||
// State Machine thread has shutdown. Once the State Machine thread has been
|
||||
// shutdown, the xpcom-shutdown observer returns.
|
||||
//
|
||||
// Note that calling the Unregister() functions may result in the singleton
|
||||
// being deleted, so don't store references to the singleton, always use the
|
||||
// singleton by derefing the referenced returned by
|
||||
// MediaShutdownManager::Instance(), which ensures that the singleton is
|
||||
// created when needed.
|
||||
// i.e. like this:
|
||||
// MediaShutdownManager::Instance()::Unregister(someDecoder);
|
||||
// MediaShutdownManager::Instance()::Register(someOtherDecoder);
|
||||
// Not like this:
|
||||
// MediaShutdownManager& instance = MediaShutdownManager::Instance();
|
||||
// instance.Unregister(someDecoder); // Warning! May delete instance!
|
||||
// instance.Register(someOtherDecoder); // BAD! instance may be dangling!
|
||||
class MediaShutdownManager : public nsIObserver {
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIOBSERVER
|
||||
|
||||
// The MediaShutdownManager is a singleton, access its instance with
|
||||
// this accessor.
|
||||
static MediaShutdownManager& Instance();
|
||||
|
||||
// Notifies the MediaShutdownManager that it needs to track the shutdown
|
||||
// of this MediaDecoder.
|
||||
void Register(MediaDecoder* aDecoder);
|
||||
|
||||
// Notifies the MediaShutdownManager that a MediaDecoder that it was
|
||||
// tracking has shutdown, and it no longer needs to be shutdown in the
|
||||
// xpcom-shutdown listener.
|
||||
void Unregister(MediaDecoder* aDecoder);
|
||||
|
||||
// Notifies the MediaShutdownManager of a state machine thread that
|
||||
// must be tracked. Note that we track State Machine threads individually
|
||||
// as their shutdown and the construction of a new state machine thread
|
||||
// can interleave. This stores a strong ref to the state machine.
|
||||
void Register(StateMachineThread* aThread);
|
||||
|
||||
// Notifies the MediaShutdownManager that a StateMachineThread that it was
|
||||
// tracking has shutdown, and it no longer needs to be shutdown in the
|
||||
// xpcom-shutdown listener. This drops the strong reference to the
|
||||
// StateMachineThread, which may destroy it.
|
||||
void Unregister(StateMachineThread* aThread);
|
||||
|
||||
private:
|
||||
|
||||
MediaShutdownManager();
|
||||
virtual ~MediaShutdownManager();
|
||||
|
||||
void Shutdown();
|
||||
|
||||
// Ensures we have a shutdown listener if we need one, and removes the
|
||||
// listener and destroys the singleton if we don't.
|
||||
void EnsureCorrectShutdownObserverState();
|
||||
|
||||
static StaticRefPtr<MediaShutdownManager> sInstance;
|
||||
|
||||
// References to the MediaDecoder. The decoders unregister themselves
|
||||
// in their Shutdown() method, so we'll drop the reference naturally when
|
||||
// we're shutting down (in the non xpcom-shutdown case).
|
||||
nsTHashtable<nsRefPtrHashKey<MediaDecoder>> mDecoders;
|
||||
|
||||
// References to the state machine threads that we're tracking shutdown
|
||||
// of. Note that although there is supposed to be a single state machine,
|
||||
// the construction and shutdown of these can interleave, so we must track
|
||||
// individual instances of the state machine threads.
|
||||
// These are strong references.
|
||||
nsTHashtable<nsRefPtrHashKey<StateMachineThread>> mStateMachineThreads;
|
||||
|
||||
// True if we have an XPCOM shutdown observer.
|
||||
bool mIsObservingShutdown;
|
||||
|
||||
bool mIsDoingXPCOMShutDown;
|
||||
};
|
||||
|
||||
// A wrapper for the state machine thread. We must wrap this so that the
|
||||
// state machine threads can shutdown independently from the
|
||||
// StateMachineTracker, under the control of the MediaShutdownManager.
|
||||
// The state machine thread is shutdown naturally when all decoders
|
||||
// complete their shutdown. So if a new decoder is created just as the
|
||||
// old state machine thread has shutdown, we need to be able to shutdown
|
||||
// the old state machine thread independently of the StateMachineTracker
|
||||
// creating a new state machine thread. Also if this happens we need to
|
||||
// be able to force both state machine threads to shutdown in the
|
||||
// MediaShutdownManager, which is why we maintain a set of state machine
|
||||
// threads, even though there's supposed to only be one alive at once.
|
||||
// This class does not enforce its own thread safety, the StateMachineTracker
|
||||
// ensures thread safety when it uses the StateMachineThread.
|
||||
class StateMachineThread {
|
||||
public:
|
||||
StateMachineThread();
|
||||
~StateMachineThread();
|
||||
|
||||
NS_INLINE_DECL_REFCOUNTING(StateMachineThread);
|
||||
|
||||
// Creates the wrapped thread.
|
||||
nsresult Init();
|
||||
|
||||
// Returns a reference to the underlying thread. Don't shut this down
|
||||
// directly, use StateMachineThread::Shutdown() instead.
|
||||
nsIThread* GetThread();
|
||||
|
||||
// Dispatches an event to the main thread to shutdown the wrapped thread.
|
||||
// The owner's (StateMachineTracker's) reference to the StateMachineThread
|
||||
// can be dropped, the StateMachineThread will shutdown itself
|
||||
// asynchronously.
|
||||
void Shutdown();
|
||||
|
||||
// Processes events on the main thread event loop until this thread
|
||||
// has been shutdown. Use this to block until the asynchronous shutdown
|
||||
// has complete.
|
||||
void SpinUntilShutdownComplete();
|
||||
|
||||
private:
|
||||
void ShutdownThread();
|
||||
nsCOMPtr<nsIThread> mThread;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
@ -123,6 +123,7 @@ UNIFIED_SOURCES += [
|
||||
'MediaDecoderStateMachine.cpp',
|
||||
'MediaRecorder.cpp',
|
||||
'MediaResource.cpp',
|
||||
'MediaShutdownManager.cpp',
|
||||
'MediaStreamGraph.cpp',
|
||||
'MediaStreamTrack.cpp',
|
||||
'MP3FrameParser.cpp',
|
||||
|
@ -38,8 +38,7 @@ function runNextTest() {
|
||||
win.onunload = function() {
|
||||
didClose = true;
|
||||
}
|
||||
var utils = SpecialPowers.getDOMWindowUtils(win);
|
||||
utils.sendNativeKeyEvent(0, MAC_VK_ANSI_W, 0x4000 /* cmd */, "w", "w");
|
||||
synthesizeNativeKey(KEYBOARD_LAYOUT_EN_US, MAC_VK_ANSI_W, {metaKey:1}, "w", "w");
|
||||
|
||||
setTimeout(function () {
|
||||
ok(didClose, "Cmd+W should have closed the tab");
|
||||
|
@ -2134,18 +2134,9 @@ class CGConstructorEnabledViaFunc(CGAbstractMethod):
|
||||
return " return %s(cx, obj);" % func[0]
|
||||
|
||||
def CreateBindingJSObject(descriptor, properties, parent):
|
||||
# When we have unforgeable properties, we're going to define them
|
||||
# on our object, so we have to root it when we create it, so it
|
||||
# won't suddenly die while defining the unforgeables. Similarly,
|
||||
# if we have members in slots we'll have to call the getters which
|
||||
# could also GC.
|
||||
needRoot = (properties.unforgeableAttrs.hasNonChromeOnly() or
|
||||
properties.unforgeableAttrs.hasChromeOnly() or
|
||||
descriptor.interface.hasMembersInSlots())
|
||||
if needRoot:
|
||||
# We don't always need to root obj, but there are a variety
|
||||
# of cases where we do, so for simplicity, just always root it.
|
||||
objDecl = " JS::Rooted<JSObject*> obj(aCx);\n"
|
||||
else:
|
||||
objDecl = " JSObject *obj;\n"
|
||||
if descriptor.proxy:
|
||||
create = """ JS::Rooted<JS::Value> proxyPrivateVal(aCx, JS::PrivateValue(aObject));
|
||||
obj = NewProxyObject(aCx, DOMProxyHandler::getInstance(),
|
||||
|
@ -246,7 +246,8 @@ void
|
||||
StringToBdAddressType(const nsAString& aBdAddress,
|
||||
bt_bdaddr_t *aRetBdAddressType)
|
||||
{
|
||||
const char* str = NS_ConvertUTF16toUTF8(aBdAddress).get();
|
||||
NS_ConvertUTF16toUTF8 bdAddressUTF8(aBdAddress);
|
||||
const char* str = bdAddressUTF8.get();
|
||||
|
||||
for (int i = 0; i < 6; i++) {
|
||||
aRetBdAddressType->address[i] = (uint8_t) strtoul(str, (char **)&str, 16);
|
||||
@ -982,7 +983,7 @@ BluetoothServiceBluedroid::SetProperty(BluetoothObjectType aType,
|
||||
const nsString propName = aValue.name();
|
||||
bt_property_t prop;
|
||||
bt_scan_mode_t scanMode;
|
||||
nsString str;
|
||||
nsCString str;
|
||||
|
||||
// For Bluedroid, it's necessary to check property name for SetProperty
|
||||
if (propName.EqualsLiteral("Name")) {
|
||||
@ -1000,8 +1001,8 @@ BluetoothServiceBluedroid::SetProperty(BluetoothObjectType aType,
|
||||
prop.val = (void*)aValue.value().get_uint32_t();
|
||||
} else if (aValue.value().type() == BluetoothValue::TnsString) {
|
||||
// Set name
|
||||
str = aValue.value().get_nsString();
|
||||
const char* name = NS_ConvertUTF16toUTF8(str).get();
|
||||
str = NS_ConvertUTF16toUTF8(aValue.value().get_nsString());
|
||||
const char* name = str.get();
|
||||
prop.val = (void*)name;
|
||||
prop.len = strlen(name);
|
||||
} else if (aValue.value().type() == BluetoothValue::Tbool) {
|
||||
|
@ -208,6 +208,13 @@ public:
|
||||
|
||||
NS_IMPL_ISUPPORTS1(AppClearDataObserver, nsIObserver)
|
||||
|
||||
static bool
|
||||
IsExpandedPrincipal(nsIPrincipal* aPrincipal)
|
||||
{
|
||||
nsCOMPtr<nsIExpandedPrincipal> ep = do_QueryInterface(aPrincipal);
|
||||
return !!ep;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@ -644,6 +651,11 @@ nsPermissionManager::AddFromPrincipal(nsIPrincipal* aPrincipal,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Permissions may not be added to expanded principals.
|
||||
if (IsExpandedPrincipal(aPrincipal)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
return AddInternal(aPrincipal, nsDependentCString(aType), aPermission, 0,
|
||||
aExpireType, aExpireTime, eNotify, eWriteToDB);
|
||||
}
|
||||
@ -874,6 +886,11 @@ nsPermissionManager::RemoveFromPrincipal(nsIPrincipal* aPrincipal,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Permissions may not be added to expanded principals.
|
||||
if (IsExpandedPrincipal(aPrincipal)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
// AddInternal() handles removal, just let it do the work
|
||||
return AddInternal(aPrincipal,
|
||||
nsDependentCString(aType),
|
||||
@ -958,13 +975,6 @@ nsPermissionManager::TestExactPermissionFromPrincipal(nsIPrincipal* aPrincipal,
|
||||
const char* aType,
|
||||
uint32_t* aPermission)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aPrincipal);
|
||||
|
||||
if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
|
||||
*aPermission = nsIPermissionManager::ALLOW_ACTION;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
return CommonTestPermission(aPrincipal, aType, aPermission, true, true);
|
||||
}
|
||||
|
||||
@ -973,15 +983,6 @@ nsPermissionManager::TestExactPermanentPermission(nsIPrincipal* aPrincipal,
|
||||
const char* aType,
|
||||
uint32_t* aPermission)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aPrincipal);
|
||||
|
||||
// System principals do not have URI so we can't try to get
|
||||
// retro-compatibility here.
|
||||
if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
|
||||
*aPermission = nsIPermissionManager::ALLOW_ACTION;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
return CommonTestPermission(aPrincipal, aType, aPermission, true, false);
|
||||
}
|
||||
|
||||
@ -1022,15 +1023,6 @@ nsPermissionManager::TestPermissionFromPrincipal(nsIPrincipal* aPrincipal,
|
||||
const char* aType,
|
||||
uint32_t* aPermission)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aPrincipal);
|
||||
|
||||
// System principals do not have URI so we can't try to get
|
||||
// retro-compatibility here.
|
||||
if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
|
||||
*aPermission = nsIPermissionManager::ALLOW_ACTION;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
return CommonTestPermission(aPrincipal, aType, aPermission, false, true);
|
||||
}
|
||||
|
||||
@ -1049,6 +1041,11 @@ nsPermissionManager::GetPermissionObject(nsIPrincipal* aPrincipal,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Querying the permission object of an nsEP is non-sensical.
|
||||
if (IsExpandedPrincipal(aPrincipal)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
nsAutoCString host;
|
||||
nsresult rv = GetHostForPrincipal(aPrincipal, host);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
@ -1101,6 +1098,39 @@ nsPermissionManager::CommonTestPermission(nsIPrincipal* aPrincipal,
|
||||
NS_ENSURE_ARG_POINTER(aPrincipal);
|
||||
NS_ENSURE_ARG_POINTER(aType);
|
||||
|
||||
if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
|
||||
*aPermission = nsIPermissionManager::ALLOW_ACTION;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// For expanded principals, we want to iterate over the whitelist and see
|
||||
// if the permission is granted for any of them.
|
||||
nsCOMPtr<nsIExpandedPrincipal> ep = do_QueryInterface(aPrincipal);
|
||||
if (ep) {
|
||||
nsTArray<nsCOMPtr<nsIPrincipal>>* whitelist;
|
||||
nsresult rv = ep->GetWhiteList(&whitelist);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Start with DENY_ACTION. If we get PROMPT_ACTION, keep going to see if
|
||||
// we get ALLOW_ACTION from another principal.
|
||||
*aPermission = nsIPermissionManager::DENY_ACTION;
|
||||
for (size_t i = 0; i < whitelist->Length(); ++i) {
|
||||
uint32_t perm;
|
||||
rv = CommonTestPermission(whitelist->ElementAt(i), aType, &perm, aExactHostMatch,
|
||||
aIncludingSession);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (perm == nsIPermissionManager::ALLOW_ACTION) {
|
||||
*aPermission = perm;
|
||||
return NS_OK;
|
||||
} else if (perm == nsIPermissionManager::PROMPT_ACTION) {
|
||||
// Store it, but keep going to see if we can do better.
|
||||
*aPermission = perm;
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// set the default
|
||||
*aPermission = nsIPermissionManager::UNKNOWN_ACTION;
|
||||
|
||||
@ -1838,6 +1868,11 @@ nsPermissionManager::UpdateExpireTime(nsIPrincipal* aPrincipal,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Setting the expire time of an nsEP is non-sensical.
|
||||
if (IsExpandedPrincipal(aPrincipal)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
nsAutoCString host;
|
||||
nsresult rv = GetHostForPrincipal(aPrincipal, host);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
@ -36,7 +36,6 @@ export:: \
|
||||
$(call mkdir_deps,system_wrappers_js) \
|
||||
$(NULL)
|
||||
$(PYTHON) -m mozbuild.action.preprocessor $(DEFINES) $(ACDEFINES) \
|
||||
-DMOZ_NATIVE_ICU=$(MOZ_NATIVE_ICU) \
|
||||
$(srcdir)/system-headers | $(PERL) $(srcdir)/make-system-wrappers.pl system_wrappers_js
|
||||
$(INSTALL) system_wrappers_js $(DIST)
|
||||
|
||||
|
@ -4165,6 +4165,7 @@ MOZ_ARG_WITH_BOOL(system-icu,
|
||||
|
||||
if test -n "$MOZ_NATIVE_ICU"; then
|
||||
PKG_CHECK_MODULES(MOZ_ICU, icu-i18n >= 50.1)
|
||||
MOZ_SHARED_ICU=1
|
||||
fi
|
||||
|
||||
MOZ_ARG_WITH_STRING(intl-api,
|
||||
|
@ -647,6 +647,17 @@ js::AreGCGrayBitsValid(JSRuntime *rt)
|
||||
return rt->gcGrayBitsValid;
|
||||
}
|
||||
|
||||
JS_FRIEND_API(bool)
|
||||
js::ZoneGlobalsAreAllGray(JS::Zone *zone)
|
||||
{
|
||||
for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next()) {
|
||||
JSObject *obj = comp->maybeGlobal();
|
||||
if (!obj || !JS::GCThingIsMarkedGray(obj))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
JS_FRIEND_API(JSGCTraceKind)
|
||||
js::GCThingTraceKind(void *thing)
|
||||
{
|
||||
|
@ -339,6 +339,9 @@ TraceWeakMaps(WeakMapTracer *trc);
|
||||
extern JS_FRIEND_API(bool)
|
||||
AreGCGrayBitsValid(JSRuntime *rt);
|
||||
|
||||
extern JS_FRIEND_API(bool)
|
||||
ZoneGlobalsAreAllGray(JS::Zone *zone);
|
||||
|
||||
typedef void
|
||||
(*GCThingCallback)(void *closure, void *gcthing);
|
||||
|
||||
|
@ -1638,7 +1638,7 @@ TemporaryTypeSet::isDOMClass()
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return count > 0;
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -225,7 +225,8 @@ nsXPCWrappedJS::AddRef(void)
|
||||
MOZ_CRASH();
|
||||
|
||||
MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt");
|
||||
nsrefcnt cnt = mRefCnt.incr();
|
||||
nsISupports *base = NS_CYCLE_COLLECTION_CLASSNAME(nsXPCWrappedJS)::Upcast(this);
|
||||
nsrefcnt cnt = mRefCnt.incr(base);
|
||||
NS_LOG_ADDREF(this, cnt, "nsXPCWrappedJS", sizeof(*this));
|
||||
|
||||
if (2 == cnt && IsValid()) {
|
||||
@ -254,7 +255,7 @@ nsXPCWrappedJS::Release(void)
|
||||
mRefCnt.stabilizeForDeletion();
|
||||
DeleteCycleCollectable();
|
||||
} else {
|
||||
mRefCnt.incr();
|
||||
mRefCnt.incr(base);
|
||||
Destroy();
|
||||
mRefCnt.decr(base);
|
||||
}
|
||||
|
@ -2261,11 +2261,12 @@ ContainerState::ProcessDisplayItems(const nsDisplayList& aList,
|
||||
|
||||
|
||||
nsDisplayItem::Type type = item->GetType();
|
||||
bool setVisibleRegion = type != nsDisplayItem::TYPE_TRANSFORM;
|
||||
if (setVisibleRegion) {
|
||||
mParameters.mAncestorClipRect = nullptr;
|
||||
} else {
|
||||
bool setVisibleRegion = (type != nsDisplayItem::TYPE_TRANSFORM) &&
|
||||
(type != nsDisplayItem::TYPE_SCROLL_LAYER);
|
||||
if (type == nsDisplayItem::TYPE_TRANSFORM) {
|
||||
mParameters.mAncestorClipRect = itemClip.HasClip() ? &clipRect : nullptr;
|
||||
} else {
|
||||
mParameters.mAncestorClipRect = nullptr;
|
||||
}
|
||||
|
||||
// Just use its layer.
|
||||
|
@ -4862,6 +4862,8 @@ nsContextBoxBlur::BlurRectangle(gfxContext* aDestinationCtx,
|
||||
scaleX = transform.xx;
|
||||
scaleY = transform.yy;
|
||||
aDestinationCtx->IdentityMatrix();
|
||||
} else {
|
||||
transform = gfxMatrix();
|
||||
}
|
||||
|
||||
gfxPoint blurStdDev = ComputeBlurStdDev(aBlurRadius, aAppUnitsPerDevPixel, scaleX, scaleY);
|
||||
|
@ -3484,6 +3484,17 @@ nsDisplayScrollLayer::~nsDisplayScrollLayer()
|
||||
}
|
||||
#endif
|
||||
|
||||
nsRect
|
||||
nsDisplayScrollLayer::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap)
|
||||
{
|
||||
nsIScrollableFrame* sf = do_QueryFrame(mScrollFrame);
|
||||
if (sf) {
|
||||
*aSnap = false;
|
||||
return sf->GetScrollPortRect() + aBuilder->ToReferenceFrame(mScrollFrame);
|
||||
}
|
||||
return nsDisplayWrapList::GetBounds(aBuilder, aSnap);
|
||||
}
|
||||
|
||||
already_AddRefed<Layer>
|
||||
nsDisplayScrollLayer::BuildLayer(nsDisplayListBuilder* aBuilder,
|
||||
LayerManager* aManager,
|
||||
@ -3510,7 +3521,7 @@ nsDisplayScrollLayer::BuildLayer(nsDisplayListBuilder* aBuilder,
|
||||
nsLayoutUtils::GetCriticalDisplayPort(content, &criticalDisplayport);
|
||||
}
|
||||
RecordFrameMetrics(mScrolledFrame, mScrollFrame, ReferenceFrame(), layer,
|
||||
mVisibleRect, viewport,
|
||||
mList.GetVisibleRect(), viewport,
|
||||
(usingDisplayport ? &displayport : nullptr),
|
||||
(usingCriticalDisplayport ? &criticalDisplayport : nullptr),
|
||||
scrollId, false, aContainerParameters);
|
||||
@ -3534,12 +3545,18 @@ nsDisplayScrollLayer::ComputeVisibility(nsDisplayListBuilder* aBuilder,
|
||||
const nsRect& aAllowVisibleRegionExpansion)
|
||||
{
|
||||
nsRect displayport;
|
||||
if (nsLayoutUtils::GetDisplayPort(mScrolledFrame->GetContent(), &displayport)) {
|
||||
bool usingDisplayPort =
|
||||
nsLayoutUtils::GetDisplayPort(mScrolledFrame->GetContent(), &displayport);
|
||||
nsRegion childVisibleRegion;
|
||||
if (usingDisplayPort) {
|
||||
// The visible region for the children may be much bigger than the hole we
|
||||
// are viewing the children from, so that the compositor process has enough
|
||||
// content to asynchronously pan while content is being refreshed.
|
||||
|
||||
nsRegion childVisibleRegion = displayport + mScrollFrame->GetOffsetToCrossDoc(ReferenceFrame());
|
||||
childVisibleRegion = displayport + mScrollFrame->GetOffsetToCrossDoc(ReferenceFrame());
|
||||
} else {
|
||||
bool snap;
|
||||
childVisibleRegion = GetBounds(aBuilder, &snap);
|
||||
}
|
||||
|
||||
nsRect boundedRect =
|
||||
childVisibleRegion.GetBounds().Intersect(mList.GetBounds(aBuilder));
|
||||
@ -3549,13 +3566,8 @@ nsDisplayScrollLayer::ComputeVisibility(nsDisplayListBuilder* aBuilder,
|
||||
// We don't allow this computation to influence aVisibleRegion, on the
|
||||
// assumption that the layer can be asynchronously scrolled so we'll
|
||||
// definitely need all the content under it.
|
||||
mVisibleRect = boundedRect;
|
||||
|
||||
return visible;
|
||||
} else {
|
||||
return nsDisplayWrapList::ComputeVisibility(aBuilder, aVisibleRegion,
|
||||
aAllowVisibleRegionExpansion);
|
||||
}
|
||||
}
|
||||
|
||||
LayerState
|
||||
@ -3668,6 +3680,12 @@ nsDisplayScrollInfoLayer::~nsDisplayScrollInfoLayer()
|
||||
MOZ_COUNT_DTOR(nsDisplayScrollInfoLayer);
|
||||
}
|
||||
|
||||
nsRect
|
||||
nsDisplayScrollInfoLayer::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap)
|
||||
{
|
||||
return nsDisplayWrapList::GetBounds(aBuilder, aSnap);
|
||||
}
|
||||
|
||||
LayerState
|
||||
nsDisplayScrollInfoLayer::GetLayerState(nsDisplayListBuilder* aBuilder,
|
||||
LayerManager* aManager,
|
||||
|
@ -1512,6 +1512,8 @@ public:
|
||||
bool DidComputeVisibility() const { return mDidComputeVisibility; }
|
||||
#endif
|
||||
|
||||
nsRect GetVisibleRect() const { return mVisibleRect; }
|
||||
|
||||
private:
|
||||
// This class is only used on stack, so we don't have to worry about leaking
|
||||
// it. Don't let us be heap-allocated!
|
||||
@ -2709,6 +2711,8 @@ public:
|
||||
virtual ~nsDisplayScrollLayer();
|
||||
#endif
|
||||
|
||||
virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) MOZ_OVERRIDE;
|
||||
|
||||
virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
|
||||
LayerManager* aManager,
|
||||
const ContainerLayerParameters& aContainerParameters) MOZ_OVERRIDE;
|
||||
@ -2760,6 +2764,8 @@ public:
|
||||
|
||||
virtual ~nsDisplayScrollInfoLayer();
|
||||
|
||||
virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) MOZ_OVERRIDE;
|
||||
|
||||
virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
|
||||
LayerManager* aManager,
|
||||
const ContainerLayerParameters& aParameters) MOZ_OVERRIDE;
|
||||
|
@ -123,12 +123,48 @@ nsPresContext::MakeColorPref(const nsString& aColor)
|
||||
: NS_RGB(0, 0, 0);
|
||||
}
|
||||
|
||||
static void DumpPresContextState(nsPresContext* aPC)
|
||||
{
|
||||
printf_stderr("PresContext(%p) ", aPC);
|
||||
nsIURI* uri = aPC->Document()->GetDocumentURI();
|
||||
if (uri) {
|
||||
nsAutoCString uriSpec;
|
||||
nsresult rv = uri->GetSpec(uriSpec);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
printf_stderr("%s ", uriSpec.get());
|
||||
}
|
||||
}
|
||||
nsIPresShell* shell = aPC->GetPresShell();
|
||||
if (shell) {
|
||||
printf_stderr("PresShell(%p) - IsDestroying(%i) IsFrozen(%i) IsActive(%i) IsVisible(%i) IsNeverPainting(%i) GetRootFrame(%p)",
|
||||
shell->IsDestroying(),
|
||||
shell->IsFrozen(),
|
||||
shell->IsActive(),
|
||||
shell->IsVisible(),
|
||||
shell->IsNeverPainting(),
|
||||
shell->GetRootFrame());
|
||||
}
|
||||
printf_stderr("\n");
|
||||
}
|
||||
|
||||
bool
|
||||
nsPresContext::IsDOMPaintEventPending()
|
||||
{
|
||||
if (mFireAfterPaintEvents) {
|
||||
return true;
|
||||
}
|
||||
if (!GetDisplayRootPresContext() ||
|
||||
!GetDisplayRootPresContext()->GetRootPresContext()) {
|
||||
printf_stderr("Failed to find root pres context, dumping pres context and ancestors\n");
|
||||
nsPresContext* pc = this;
|
||||
for (;;) {
|
||||
DumpPresContextState(pc);
|
||||
nsPresContext* parent = pc->GetParentPresContext();
|
||||
if (!parent)
|
||||
break;
|
||||
pc = parent;
|
||||
}
|
||||
}
|
||||
if (GetDisplayRootPresContext()->GetRootPresContext()->mRefreshDriver->ViewManagerFlushIsPending()) {
|
||||
// Since we're promising that there will be a MozAfterPaint event
|
||||
// fired, we record an empty invalidation in case display list
|
||||
|
@ -620,10 +620,47 @@ function _parseNativeModifiers(aModifiers)
|
||||
modifiers |=
|
||||
(navigator.platform.indexOf("Mac") == 0) ? 0x00008000 : 0x00000800;
|
||||
}
|
||||
if (aModifiers.altGrKey) {
|
||||
modifiers |=
|
||||
(navigator.platform.indexOf("Win") == 0) ? 0x00002800 : 0x00001000;
|
||||
}
|
||||
return modifiers;
|
||||
}
|
||||
|
||||
const KEYBOARD_LAYOUT_EN_US = 0;
|
||||
// Mac: Any unused number is okay for adding new keyboard layout.
|
||||
// When you add new keyboard layout here, you need to modify
|
||||
// TISInputSourceWrapper::InitByLayoutID().
|
||||
// Win: These constants can be found by inspecting registry keys under
|
||||
// HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Keyboard Layouts
|
||||
|
||||
const KEYBOARD_LAYOUT_ARABIC =
|
||||
{ name: "Arabic", Mac: 6, Win: 0x00000401 };
|
||||
const KEYBOARD_LAYOUT_BRAZILIAN_ABNT =
|
||||
{ name: "Brazilian ABNT", Mac: null, Win: 0x00000416 };
|
||||
const KEYBOARD_LAYOUT_DVORAK_QWERTY =
|
||||
{ name: "Dvorak-QWERTY", Mac: 4, Win: null };
|
||||
const KEYBOARD_LAYOUT_EN_US =
|
||||
{ name: "US", Mac: 0, Win: 0x00000409 };
|
||||
const KEYBOARD_LAYOUT_FRENCH =
|
||||
{ name: "French", Mac: 7, Win: 0x0000040C };
|
||||
const KEYBOARD_LAYOUT_GREEK =
|
||||
{ name: "Greek", Mac: 1, Win: 0x00000408 };
|
||||
const KEYBOARD_LAYOUT_GERMAN =
|
||||
{ name: "German", Mac: 2, Win: 0x00000407 };
|
||||
const KEYBOARD_LAYOUT_HEBREW =
|
||||
{ name: "Hebrew", Mac: 8, Win: 0x0000040D };
|
||||
const KEYBOARD_LAYOUT_JAPANESE =
|
||||
{ name: "Japanese", Mac: null, Win: 0x00000411 };
|
||||
const KEYBOARD_LAYOUT_LITHUANIAN =
|
||||
{ name: "Lithuanian", Mac: 9, Win: 0x00010427 };
|
||||
const KEYBOARD_LAYOUT_NORWEGIAN =
|
||||
{ name: "Norwegian", Mac: 10, Win: 0x00000414 };
|
||||
const KEYBOARD_LAYOUT_SPANISH =
|
||||
{ name: "Spanish", Mac: 11, Win: 0x0000040A };
|
||||
const KEYBOARD_LAYOUT_SWEDISH =
|
||||
{ name: "Swedish", Mac: 3, Win: 0x0000041D };
|
||||
const KEYBOARD_LAYOUT_THAI =
|
||||
{ name: "Thai", Mac: 5, Win: 0x0002041E };
|
||||
|
||||
/**
|
||||
* synthesizeNativeKey() dispatches native key event on active window.
|
||||
@ -651,24 +688,13 @@ function synthesizeNativeKey(aKeyboardLayout, aNativeKeyCode, aModifiers,
|
||||
if (!utils) {
|
||||
return false;
|
||||
}
|
||||
var nativeKeyboardLayout;
|
||||
var nativeKeyboardLayout = null;
|
||||
if (navigator.platform.indexOf("Mac") == 0) {
|
||||
switch (aKeyboardLayout) {
|
||||
case KEYBOARD_LAYOUT_EN_US:
|
||||
nativeKeyboardLayout = 0;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
nativeKeyboardLayout = aKeyboardLayout.Mac;
|
||||
} else if (navigator.platform.indexOf("Win") == 0) {
|
||||
switch (aKeyboardLayout) {
|
||||
case KEYBOARD_LAYOUT_EN_US:
|
||||
nativeKeyboardLayout = 0x409;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
nativeKeyboardLayout = aKeyboardLayout.Win;
|
||||
}
|
||||
} else {
|
||||
if (nativeKeyboardLayout === null) {
|
||||
return false;
|
||||
}
|
||||
utils.sendNativeKeyEvent(nativeKeyboardLayout, aNativeKeyCode,
|
||||
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* This file defines all virtual keycodes for
|
||||
* nsIDOMWindowUtils.sendNativeKeyEvent()
|
||||
* This file defines all virtual keycodes for synthesizeNativeKey() of
|
||||
* EventUtils.js and nsIDOMWindowUtils.sendNativeKeyEvent().
|
||||
* These values are defined in each platform's SDK or documents.
|
||||
*/
|
||||
|
||||
|
@ -5,6 +5,7 @@
|
||||
[browser_bug594509.js]
|
||||
[browser_default_image_filename.js]
|
||||
[browser_findbar.js]
|
||||
skip-if = os == "linux" # Bug 945667
|
||||
[browser_input_file_tooltips.js]
|
||||
[browser_keyevents_during_autoscrolling.js]
|
||||
[browser_save_resend_postdata.js]
|
||||
|
@ -667,12 +667,8 @@ AndroidGeckoEvent::MakeTouchEvent(nsIWidget* widget)
|
||||
return event;
|
||||
}
|
||||
|
||||
event.modifiers = 0;
|
||||
event.modifiers = DOMModifiers();
|
||||
event.time = Time();
|
||||
event.InitBasicModifiers(IsCtrlPressed(),
|
||||
IsAltPressed(),
|
||||
IsShiftPressed(),
|
||||
IsMetaPressed());
|
||||
|
||||
const nsIntPoint& offset = widget->WidgetToScreenOffset();
|
||||
event.touches.SetCapacity(endIndex - startIndex);
|
||||
@ -733,18 +729,7 @@ AndroidGeckoEvent::MakeMultiTouchInput(nsIWidget* widget)
|
||||
}
|
||||
|
||||
MultiTouchInput event(type, Time(), 0);
|
||||
if (IsCtrlPressed()) {
|
||||
event.modifiers |= MODIFIER_CONTROL;
|
||||
}
|
||||
if (IsAltPressed()) {
|
||||
event.modifiers |= MODIFIER_ALT;
|
||||
}
|
||||
if (IsShiftPressed()) {
|
||||
event.modifiers |= MODIFIER_SHIFT;
|
||||
}
|
||||
if (IsMetaPressed()) {
|
||||
event.modifiers |= MODIFIER_META;
|
||||
}
|
||||
event.modifiers = DOMModifiers();
|
||||
|
||||
if (type < 0) {
|
||||
// An event we don't know about
|
||||
@ -802,7 +787,7 @@ AndroidGeckoEvent::MakeMouseEvent(nsIWidget* widget)
|
||||
if (msg != NS_MOUSE_MOVE) {
|
||||
event.clickCount = 1;
|
||||
}
|
||||
event.modifiers = 0;
|
||||
event.modifiers = DOMModifiers();
|
||||
event.time = Time();
|
||||
|
||||
// We are dispatching this event directly into Gecko (as opposed to going
|
||||
@ -816,6 +801,37 @@ AndroidGeckoEvent::MakeMouseEvent(nsIWidget* widget)
|
||||
return event;
|
||||
}
|
||||
|
||||
Modifiers
|
||||
AndroidGeckoEvent::DOMModifiers() const
|
||||
{
|
||||
Modifiers result = 0;
|
||||
if (mMetaState & AMETA_ALT_MASK) {
|
||||
result |= MODIFIER_ALT;
|
||||
}
|
||||
if (mMetaState & AMETA_SHIFT_MASK) {
|
||||
result |= MODIFIER_SHIFT;
|
||||
}
|
||||
if (mMetaState & AMETA_CTRL_MASK) {
|
||||
result |= MODIFIER_CONTROL;
|
||||
}
|
||||
if (mMetaState & AMETA_META_MASK) {
|
||||
result |= MODIFIER_META;
|
||||
}
|
||||
if (mMetaState & AMETA_FUNCTION_ON) {
|
||||
result |= MODIFIER_FN;
|
||||
}
|
||||
if (mMetaState & AMETA_CAPS_LOCK_ON) {
|
||||
result |= MODIFIER_CAPSLOCK;
|
||||
}
|
||||
if (mMetaState & AMETA_NUM_LOCK_ON) {
|
||||
result |= MODIFIER_NUMLOCK;
|
||||
}
|
||||
if (mMetaState & AMETA_SCROLL_LOCK_ON) {
|
||||
result |= MODIFIER_SCROLLLOCK;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
AndroidPoint::Init(JNIEnv *jenv, jobject jobj)
|
||||
{
|
||||
|
@ -342,12 +342,16 @@ enum {
|
||||
AKEYCODE_KANA = 218,
|
||||
AKEYCODE_ASSIST = 219,
|
||||
|
||||
AMETA_FUNCTION_ON = 0x00000008,
|
||||
AMETA_CTRL_ON = 0x00001000,
|
||||
AMETA_CTRL_LEFT_ON = 0x00002000,
|
||||
AMETA_CTRL_RIGHT_ON = 0x00004000,
|
||||
AMETA_META_ON = 0x00010000,
|
||||
AMETA_META_LEFT_ON = 0x00020000,
|
||||
AMETA_META_RIGHT_ON = 0x00040000,
|
||||
AMETA_CAPS_LOCK_ON = 0x00100000,
|
||||
AMETA_NUM_LOCK_ON = 0x00200000,
|
||||
AMETA_SCROLL_LOCK_ON = 0x00400000,
|
||||
|
||||
AMETA_ALT_MASK = AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON | AMETA_ALT_ON,
|
||||
AMETA_CTRL_MASK = AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON | AMETA_CTRL_ON,
|
||||
@ -504,6 +508,7 @@ public:
|
||||
int KeyCode() { return mKeyCode; }
|
||||
int MetaState() { return mMetaState; }
|
||||
uint32_t DomKeyLocation() { return mDomKeyLocation; }
|
||||
Modifiers DOMModifiers() const;
|
||||
bool IsAltPressed() const { return (mMetaState & AMETA_ALT_MASK) != 0; }
|
||||
bool IsShiftPressed() const { return (mMetaState & AMETA_SHIFT_MASK) != 0; }
|
||||
bool IsCtrlPressed() const { return (mMetaState & AMETA_CTRL_MASK) != 0; }
|
||||
|
@ -1263,7 +1263,7 @@ nsWindow::DispatchMotionEvent(WidgetInputEvent &event, AndroidGeckoEvent *ae,
|
||||
{
|
||||
nsIntPoint offset = WidgetToScreenOffset();
|
||||
|
||||
event.modifiers = 0;
|
||||
event.modifiers = ae->DOMModifiers();
|
||||
event.time = ae->Time();
|
||||
|
||||
// XXX possibly bound the range of event.refPoint here.
|
||||
@ -1609,16 +1609,19 @@ nsWindow::InitKeyEvent(WidgetKeyboardEvent& event, AndroidGeckoEvent& key,
|
||||
event.pluginEvent = pluginEvent;
|
||||
}
|
||||
|
||||
if (event.message != NS_KEY_PRESS ||
|
||||
!key.UnicodeChar() || !key.BaseUnicodeChar() ||
|
||||
key.UnicodeChar() == key.BaseUnicodeChar()) {
|
||||
event.modifiers = key.DOMModifiers();
|
||||
if (gMenu) {
|
||||
event.modifiers |= MODIFIER_CONTROL;
|
||||
}
|
||||
// For keypress, if the unicode char already has modifiers applied, we
|
||||
// don't specify extra modifiers. If UnicodeChar() != BaseUnicodeChar()
|
||||
// it means UnicodeChar() already has modifiers applied.
|
||||
event.InitBasicModifiers(gMenu || key.IsCtrlPressed(),
|
||||
key.IsAltPressed(),
|
||||
key.IsShiftPressed(),
|
||||
key.IsMetaPressed());
|
||||
// Note that on Android 4.x, Alt modifier isn't set when the key input
|
||||
// causes text input even while right Alt key is pressed. However, this
|
||||
// is necessary for Android 2.3 compatibility.
|
||||
if (event.message == NS_KEY_PRESS &&
|
||||
key.UnicodeChar() && key.UnicodeChar() != key.BaseUnicodeChar()) {
|
||||
event.modifiers &= ~(MODIFIER_ALT | MODIFIER_CONTROL | MODIFIER_META);
|
||||
}
|
||||
|
||||
event.mIsRepeat =
|
||||
|
@ -94,6 +94,12 @@ public:
|
||||
* 3: Swedish-Pro
|
||||
* 4: Dvorak-Qwerty Cmd
|
||||
* 5: Thai
|
||||
* 6: Arabic
|
||||
* 7: French
|
||||
* 8: Hebrew
|
||||
* 9: Lithuanian
|
||||
* 10: Norwegian
|
||||
* 11: Spanish
|
||||
* @param aOverrideKeyboard When testing set to TRUE, otherwise, set to
|
||||
* FALSE. When TRUE, we use an ANSI keyboard
|
||||
* instead of the actual keyboard.
|
||||
|
@ -527,6 +527,24 @@ TISInputSourceWrapper::InitByLayoutID(SInt32 aLayoutID,
|
||||
case 5:
|
||||
InitByInputSourceID("com.apple.keylayout.Thai");
|
||||
break;
|
||||
case 6:
|
||||
InitByInputSourceID("com.apple.keylayout.Arabic");
|
||||
break;
|
||||
case 7:
|
||||
InitByInputSourceID("com.apple.keylayout.French");
|
||||
break;
|
||||
case 8:
|
||||
InitByInputSourceID("com.apple.keylayout.Hebrew");
|
||||
break;
|
||||
case 9:
|
||||
InitByInputSourceID("com.apple.keylayout.Lithuanian");
|
||||
break;
|
||||
case 10:
|
||||
InitByInputSourceID("com.apple.keylayout.Norwegian");
|
||||
break;
|
||||
case 11:
|
||||
InitByInputSourceID("com.apple.keylayout.Spanish");
|
||||
break;
|
||||
default:
|
||||
Clear();
|
||||
break;
|
||||
|
@ -26,8 +26,6 @@
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
var gUtils;
|
||||
|
||||
var gCmdOptYReceived = false;
|
||||
|
||||
// Look for a cmd-opt-y event.
|
||||
@ -41,8 +39,6 @@
|
||||
}
|
||||
|
||||
function setGlobals() {
|
||||
gUtils = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
|
||||
getInterface(Components.interfaces.nsIDOMWindowUtils);
|
||||
var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"].
|
||||
getService(Components.interfaces.nsIWindowMediator);
|
||||
gChromeWindow = wm.getMostRecentWindow("navigator:browser");
|
||||
@ -147,11 +143,11 @@
|
||||
// DebugEventsPlugin v1.01 from bmo bug 441880.
|
||||
|
||||
function synthesizeNativeReturnKey() {
|
||||
gUtils.sendNativeKeyEvent(0, MAC_VK_Return, 0, "\u000a", "\u000a");
|
||||
synthesizeNativeKey(KEYBOARD_LAYOUT_EN_US, MAC_VK_Return, {}, "\u000a", "\u000a");
|
||||
}
|
||||
|
||||
function synthesizeNativeCmdOptY() {
|
||||
gUtils.sendNativeKeyEvent(0, MAC_VK_ANSI_Y, 0x5000, "y", "y");
|
||||
synthesizeNativeKey(KEYBOARD_LAYOUT_EN_US, MAC_VK_ANSI_Y, {metaKey:1, altKey:1}, "y", "y");
|
||||
}
|
||||
|
||||
]]></script>
|
||||
|
@ -31,24 +31,21 @@
|
||||
|
||||
function runTest()
|
||||
{
|
||||
var domWindowUtils = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
|
||||
getInterface(Components.interfaces.nsIDOMWindowUtils);
|
||||
|
||||
window.addEventListener("keypress", onKeyPress, false);
|
||||
|
||||
// Test ctrl-tab
|
||||
gKeyPressEventCount = 0;
|
||||
domWindowUtils.sendNativeKeyEvent(0, MAC_VK_Tab, 0x0400, "\t", "\t");
|
||||
synthesizeNativeKey(KEYBOARD_LAYOUT_EN_US, MAC_VK_Tab, {ctrlKey:1}, "\t", "\t");
|
||||
is(gKeyPressEventCount, 1);
|
||||
|
||||
// Test cmd+shift+a
|
||||
gKeyPressEventCount = 0;
|
||||
domWindowUtils.sendNativeKeyEvent(0, MAC_VK_ANSI_A, 0x4000 | 0x0100, "a", "A");
|
||||
synthesizeNativeKey(KEYBOARD_LAYOUT_EN_US, MAC_VK_ANSI_A, {metaKey:1, shiftKey:1}, "a", "A");
|
||||
is(gKeyPressEventCount, 1);
|
||||
|
||||
// Test cmd-;
|
||||
gKeyPressEventCount = 0;
|
||||
domWindowUtils.sendNativeKeyEvent(0, MAC_VK_ANSI_Semicolon, 0x4000, ";", ";");
|
||||
synthesizeNativeKey(KEYBOARD_LAYOUT_EN_US, MAC_VK_ANSI_Semicolon, {metaKey:1}, ";", ";");
|
||||
is(gKeyPressEventCount, 1);
|
||||
|
||||
window.removeEventListener("keypress", onKeyPress, false);
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -96,24 +96,12 @@
|
||||
<script type="text/javascript;version=1.8">
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
let utils = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
|
||||
getInterface(Components.interfaces.nsIDOMWindowUtils);
|
||||
|
||||
let layouts = {
|
||||
"US": 0,
|
||||
"Greek": 1,
|
||||
"German": 2,
|
||||
"Swedish": 3,
|
||||
"Dvorak-Qwerty": 4,
|
||||
"Thai": 5
|
||||
};
|
||||
|
||||
let synthesizedKeys = [];
|
||||
let expectations = [];
|
||||
|
||||
// Move to beginning of line
|
||||
synthesizedKeys.push([layouts.US, MAC_VK_LeftArrow,
|
||||
{ctrl: true}, "\uf702", "\uf702"]);
|
||||
synthesizedKeys.push([KEYBOARD_LAYOUT_EN_US, MAC_VK_LeftArrow,
|
||||
{ctrlKey: true}, "\uf702", "\uf702"]);
|
||||
expectations.push({
|
||||
editable: [0, 0],
|
||||
textarea: [0, 0],
|
||||
@ -121,8 +109,8 @@
|
||||
});
|
||||
|
||||
// Move to end of line
|
||||
synthesizedKeys.push([layouts.US, MAC_VK_RightArrow,
|
||||
{ctrl: true}, "\uf703", "\uf703"]);
|
||||
synthesizedKeys.push([KEYBOARD_LAYOUT_EN_US, MAC_VK_RightArrow,
|
||||
{ctrlKey: true}, "\uf703", "\uf703"]);
|
||||
expectations.push({
|
||||
editable: [73, 73],
|
||||
textarea: [72, 72],
|
||||
@ -130,8 +118,8 @@
|
||||
});
|
||||
|
||||
// Move down
|
||||
synthesizedKeys.push([layouts.US, MAC_VK_ANSI_N,
|
||||
{ctrl: true}, "\u000e", "n"]);
|
||||
synthesizedKeys.push([KEYBOARD_LAYOUT_EN_US, MAC_VK_ANSI_N,
|
||||
{ctrlKey: true}, "\u000e", "n"]);
|
||||
expectations.push({
|
||||
editable: [140, 140],
|
||||
textarea: [145, 145],
|
||||
@ -139,8 +127,8 @@
|
||||
});
|
||||
|
||||
// Move to beginning of line
|
||||
synthesizedKeys.push([layouts.US, MAC_VK_LeftArrow,
|
||||
{ctrl: true}, "\uf702", "\uf702"]);
|
||||
synthesizedKeys.push([KEYBOARD_LAYOUT_EN_US, MAC_VK_LeftArrow,
|
||||
{ctrlKey: true}, "\uf702", "\uf702"]);
|
||||
expectations.push({
|
||||
editable: [73, 73],
|
||||
textarea: [73, 73],
|
||||
@ -148,8 +136,8 @@
|
||||
});
|
||||
|
||||
// Move word right and modify selection
|
||||
synthesizedKeys.push([layouts.US, MAC_VK_RightArrow,
|
||||
{alt: true, shift: true}, "\uf703", "\uf703"]);
|
||||
synthesizedKeys.push([KEYBOARD_LAYOUT_EN_US, MAC_VK_RightArrow,
|
||||
{altKey: true, shiftKey: true}, "\uf703", "\uf703"]);
|
||||
expectations.push({
|
||||
editable: [73, 84],
|
||||
textarea: [73, 90],
|
||||
@ -157,8 +145,8 @@
|
||||
});
|
||||
|
||||
// Move word right
|
||||
synthesizedKeys.push([layouts.US, MAC_VK_RightArrow,
|
||||
{alt: true}, "\uf703", "\uf703"]);
|
||||
synthesizedKeys.push([KEYBOARD_LAYOUT_EN_US, MAC_VK_RightArrow,
|
||||
{altKey: true}, "\uf703", "\uf703"]);
|
||||
expectations.push({
|
||||
editable: [84, 84],
|
||||
textarea: [90, 90],
|
||||
@ -166,8 +154,8 @@
|
||||
});
|
||||
|
||||
// Move word right
|
||||
synthesizedKeys.push([layouts.US, MAC_VK_RightArrow,
|
||||
{alt: true}, "\uf703", "\uf703"]);
|
||||
synthesizedKeys.push([KEYBOARD_LAYOUT_EN_US, MAC_VK_RightArrow,
|
||||
{altKey: true}, "\uf703", "\uf703"]);
|
||||
expectations.push({
|
||||
editable: [89, 89],
|
||||
textarea: [95, 95],
|
||||
@ -175,8 +163,8 @@
|
||||
});
|
||||
|
||||
// Move down and modify selection
|
||||
synthesizedKeys.push([layouts.US, MAC_VK_DownArrow,
|
||||
{shift: true}, "\uf701", "\uf701"]);
|
||||
synthesizedKeys.push([KEYBOARD_LAYOUT_EN_US, MAC_VK_DownArrow,
|
||||
{shiftKey: true}, "\uf701", "\uf701"]);
|
||||
expectations.push({
|
||||
editable: [89, 171],
|
||||
textarea: [95, 175],
|
||||
@ -184,8 +172,8 @@
|
||||
});
|
||||
|
||||
// Move backward and modify selection
|
||||
synthesizedKeys.push([layouts.US, MAC_VK_ANSI_B,
|
||||
{ctrl: true, shift: true}, "\u0002", "B"]);
|
||||
synthesizedKeys.push([KEYBOARD_LAYOUT_EN_US, MAC_VK_ANSI_B,
|
||||
{ctrlKey: true, shiftKey: true}, "\u0002", "B"]);
|
||||
expectations.push({
|
||||
editable: [89, 170],
|
||||
textarea: [95, 174],
|
||||
@ -193,8 +181,8 @@
|
||||
});
|
||||
|
||||
// Delete forward
|
||||
synthesizedKeys.push([layouts.US, MAC_VK_ANSI_D,
|
||||
{ctrl: true}, "\u0004", "d"]);
|
||||
synthesizedKeys.push([KEYBOARD_LAYOUT_EN_US, MAC_VK_ANSI_D,
|
||||
{ctrlKey: true}, "\u0004", "d"]);
|
||||
expectations.push({
|
||||
editable: [89, 89],
|
||||
textarea: [95, 95],
|
||||
@ -202,8 +190,8 @@
|
||||
});
|
||||
|
||||
// Delete backward
|
||||
synthesizedKeys.push([layouts.US, MAC_VK_ANSI_H,
|
||||
{ctrl: true}, "\u0008", "h"]);
|
||||
synthesizedKeys.push([KEYBOARD_LAYOUT_EN_US, MAC_VK_ANSI_H,
|
||||
{ctrlKey: true}, "\u0008", "h"]);
|
||||
expectations.push({
|
||||
editable: [88, 88],
|
||||
textarea: [94, 94],
|
||||
@ -211,8 +199,8 @@
|
||||
});
|
||||
|
||||
// Move backward
|
||||
synthesizedKeys.push([layouts.US, MAC_VK_ANSI_B,
|
||||
{ctrl: true}, "\u0002", "b"]);
|
||||
synthesizedKeys.push([KEYBOARD_LAYOUT_EN_US, MAC_VK_ANSI_B,
|
||||
{ctrlKey: true}, "\u0002", "b"]);
|
||||
expectations.push({
|
||||
editable: [87, 87],
|
||||
textarea: [93, 93],
|
||||
@ -220,8 +208,8 @@
|
||||
});
|
||||
|
||||
// Move to beginning of paragraph (line for now)
|
||||
synthesizedKeys.push([layouts.US, MAC_VK_ANSI_A,
|
||||
{ctrl: true}, "\u0001", "a"]);
|
||||
synthesizedKeys.push([KEYBOARD_LAYOUT_EN_US, MAC_VK_ANSI_A,
|
||||
{ctrlKey: true}, "\u0001", "a"]);
|
||||
expectations.push({
|
||||
editable: [73, 73],
|
||||
textarea: [73, 73],
|
||||
@ -229,8 +217,8 @@
|
||||
});
|
||||
|
||||
// Move forward
|
||||
synthesizedKeys.push([layouts.US, MAC_VK_ANSI_F,
|
||||
{ctrl: true}, "\u0006", "f"]);
|
||||
synthesizedKeys.push([KEYBOARD_LAYOUT_EN_US, MAC_VK_ANSI_F,
|
||||
{ctrlKey: true}, "\u0006", "f"]);
|
||||
expectations.push({
|
||||
editable: [74, 74],
|
||||
textarea: [74, 74],
|
||||
@ -238,8 +226,8 @@
|
||||
});
|
||||
|
||||
// Move word right
|
||||
synthesizedKeys.push([layouts.US, MAC_VK_RightArrow,
|
||||
{alt: true}, "\uf703", "\uf703"]);
|
||||
synthesizedKeys.push([KEYBOARD_LAYOUT_EN_US, MAC_VK_RightArrow,
|
||||
{altKey: true}, "\uf703", "\uf703"]);
|
||||
expectations.push({
|
||||
editable: [84, 84],
|
||||
textarea: [90, 90],
|
||||
@ -247,8 +235,8 @@
|
||||
});
|
||||
|
||||
// Move word right
|
||||
synthesizedKeys.push([layouts.US, MAC_VK_RightArrow,
|
||||
{alt: true}, "\uf703", "\uf703"]);
|
||||
synthesizedKeys.push([KEYBOARD_LAYOUT_EN_US, MAC_VK_RightArrow,
|
||||
{altKey: true}, "\uf703", "\uf703"]);
|
||||
expectations.push({
|
||||
editable: [88, 88],
|
||||
textarea: [94, 94],
|
||||
@ -256,8 +244,8 @@
|
||||
});
|
||||
|
||||
// Delete to end of paragraph (line for now)
|
||||
synthesizedKeys.push([layouts.US, MAC_VK_ANSI_K,
|
||||
{ctrl: true}, "\u000b", "k"]);
|
||||
synthesizedKeys.push([KEYBOARD_LAYOUT_EN_US, MAC_VK_ANSI_K,
|
||||
{ctrlKey: true}, "\u000b", "k"]);
|
||||
expectations.push({
|
||||
editable: [88, 88],
|
||||
textarea: [94, 94],
|
||||
@ -265,8 +253,8 @@
|
||||
});
|
||||
|
||||
// Move backward and modify selection
|
||||
synthesizedKeys.push([layouts.US, MAC_VK_ANSI_B,
|
||||
{ctrl: true, shift: true}, "\u0002", "B"]);
|
||||
synthesizedKeys.push([KEYBOARD_LAYOUT_EN_US, MAC_VK_ANSI_B,
|
||||
{ctrlKey: true, shiftKey: true}, "\u0002", "B"]);
|
||||
expectations.push({
|
||||
editable: [88, 87],
|
||||
textarea: [93, 94],
|
||||
@ -274,8 +262,8 @@
|
||||
});
|
||||
|
||||
// Move to end of paragraph (line for now)
|
||||
synthesizedKeys.push([layouts.US, MAC_VK_ANSI_E,
|
||||
{ctrl: true}, "\u0005", "e"]);
|
||||
synthesizedKeys.push([KEYBOARD_LAYOUT_EN_US, MAC_VK_ANSI_E,
|
||||
{ctrlKey: true}, "\u0005", "e"]);
|
||||
expectations.push({
|
||||
editable: [144, 144],
|
||||
textarea: [94, 94],
|
||||
@ -283,36 +271,14 @@
|
||||
});
|
||||
|
||||
// Move up
|
||||
synthesizedKeys.push([layouts.US, MAC_VK_ANSI_P,
|
||||
{ctrl: true}, "\u0010", "p"]);
|
||||
synthesizedKeys.push([KEYBOARD_LAYOUT_EN_US, MAC_VK_ANSI_P,
|
||||
{ctrlKey: true}, "\u0010", "p"]);
|
||||
expectations.push({
|
||||
editable: [73, 73],
|
||||
textarea: [21, 21],
|
||||
input: [0, 0]
|
||||
});
|
||||
|
||||
function synthesizeNativeKey(aLayout, aKeyCode, aModifiers, aSystemChars,
|
||||
aSystemUnmodifiedChars)
|
||||
{
|
||||
let modifiers = 0;
|
||||
if (aModifiers.capsLock) modifiers |= 0x01;
|
||||
if (aModifiers.numLock) modifiers |= 0x02;
|
||||
if (aModifiers.shift) modifiers |= 0x0100;
|
||||
if (aModifiers.shiftRight) modifiers |= 0x0200;
|
||||
if (aModifiers.ctrl) modifiers |= 0x0400;
|
||||
if (aModifiers.ctrlRight) modifiers |= 0x0800;
|
||||
if (aModifiers.alt) modifiers |= 0x1000;
|
||||
if (aModifiers.altRight) modifiers |= 0x2000;
|
||||
if (aModifiers.command) modifiers |= 0x4000;
|
||||
if (aModifiers.commandRight) modifiers |= 0x8000;
|
||||
if (aModifiers.help) modifiers |= 0x10000;
|
||||
if (aModifiers.function) modifiers |= 0x100000;
|
||||
if (aModifiers.numericKeyPad) modifiers |= 0x01000000;
|
||||
|
||||
utils.sendNativeKeyEvent(aLayout, aKeyCode, modifiers,
|
||||
aSystemChars, aSystemUnmodifiedChars);
|
||||
}
|
||||
|
||||
function checkWindowSelection(aElement, aSelection)
|
||||
{
|
||||
let selection = window.getSelection();
|
||||
|
@ -49,19 +49,13 @@ function doTest() {
|
||||
|
||||
is(gPlugin.getLastKeyText(), "", "Must be empty before first key test");
|
||||
|
||||
gUtils.sendNativeKeyEvent(0x409 /* US */,
|
||||
WIN_VK_A, 0,
|
||||
"a", "a");
|
||||
synthesizeNativeKey(KEYBOARD_LAYOUT_EN_US, WIN_VK_A, {}, "a", "a");
|
||||
is(gPlugin.getLastKeyText(), "a", "Invalid character was inputted");
|
||||
|
||||
gUtils.sendNativeKeyEvent(0x407 /* German */,
|
||||
WIN_VK_OEM_PLUS, 0,
|
||||
"+", "+");
|
||||
synthesizeNativeKey(KEYBOARD_LAYOUT_GERMAN, WIN_VK_OEM_PLUS, {}, "+", "+");
|
||||
is(gPlugin.getLastKeyText(), "+", "Invalid character was inputted");
|
||||
|
||||
gUtils.sendNativeKeyEvent(0x407 /* German */,
|
||||
WIN_VK_OEM_PLUS, 0x1400 /* Ctrl + Alt (AltGr) */,
|
||||
"~", "+");
|
||||
synthesizeNativeKey(KEYBOARD_LAYOUT_GERMAN, WIN_VK_OEM_PLUS, {altGrKey:1}, "~", "+");
|
||||
is(gPlugin.getLastKeyText(), "~", "Invalid character was inputted");
|
||||
|
||||
SimpleTest.finish();
|
||||
|
@ -36,8 +36,7 @@
|
||||
|
||||
function sendAKeyEvent()
|
||||
{
|
||||
SpecialPowers.getDOMWindowUtils(window)
|
||||
.sendNativeKeyEvent(0, MAC_VK_ANSI_A, 0, "a", "a");
|
||||
synthesizeNativeKey(KEYBOARD_LAYOUT_EN_US, MAC_VK_ANSI_A, {}, "a", "a");
|
||||
}
|
||||
|
||||
function isFocused(aElement)
|
||||
|
@ -104,6 +104,18 @@ IMEHandler::ProcessMessage(nsWindow* aWindow, UINT aMessage,
|
||||
{
|
||||
#ifdef NS_ENABLE_TSF
|
||||
if (IsTSFAvailable()) {
|
||||
if (aMessage == WM_IME_SETCONTEXT) {
|
||||
// If a windowless plugin had focus and IME was handled on it, composition
|
||||
// window was set the position. After that, even in TSF mode, WinXP keeps
|
||||
// to use composition window at the position if the active IME is not
|
||||
// aware TSF. For avoiding this issue, we need to hide the composition
|
||||
// window here.
|
||||
if (aWParam) {
|
||||
aLParam &= ~ISC_SHOWUICOMPOSITIONWINDOW;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (aMessage == WM_USER_TSF_TEXTCHANGE) {
|
||||
nsTextStore::OnTextChangeMsg();
|
||||
aResult.mConsumed = true;
|
||||
|
@ -763,6 +763,8 @@ nsIMM32Handler::OnIMEStartCompositionOnPlugin(nsWindow* aWindow,
|
||||
aWindow->GetWindowHandle(), mIsComposingOnPlugin ? "TRUE" : "FALSE"));
|
||||
mIsComposingOnPlugin = true;
|
||||
mComposingWindow = aWindow;
|
||||
nsIMEContext IMEContext(aWindow->GetWindowHandle());
|
||||
SetIMERelatedWindowsPosOnPlugin(aWindow, IMEContext);
|
||||
aResult.mConsumed =
|
||||
aWindow->DispatchPluginEvent(WM_IME_STARTCOMPOSITION, wParam, lParam,
|
||||
false);
|
||||
@ -795,6 +797,8 @@ nsIMM32Handler::OnIMECompositionOnPlugin(nsWindow* aWindow,
|
||||
if (IS_COMPOSING_LPARAM(lParam)) {
|
||||
mIsComposingOnPlugin = true;
|
||||
mComposingWindow = aWindow;
|
||||
nsIMEContext IMEContext(aWindow->GetWindowHandle());
|
||||
SetIMERelatedWindowsPosOnPlugin(aWindow, IMEContext);
|
||||
}
|
||||
aResult.mConsumed =
|
||||
aWindow->DispatchPluginEvent(WM_IME_COMPOSITION, wParam, lParam, true);
|
||||
@ -813,6 +817,12 @@ nsIMM32Handler::OnIMEEndCompositionOnPlugin(nsWindow* aWindow,
|
||||
|
||||
mIsComposingOnPlugin = false;
|
||||
mComposingWindow = nullptr;
|
||||
|
||||
if (mNativeCaretIsCreated) {
|
||||
::DestroyCaret();
|
||||
mNativeCaretIsCreated = false;
|
||||
}
|
||||
|
||||
aResult.mConsumed =
|
||||
aWindow->DispatchPluginEvent(WM_IME_ENDCOMPOSITION, wParam, lParam,
|
||||
false);
|
||||
@ -1930,6 +1940,68 @@ nsIMM32Handler::SetIMERelatedWindowsPos(nsWindow* aWindow,
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
nsIMM32Handler::SetIMERelatedWindowsPosOnPlugin(nsWindow* aWindow,
|
||||
const nsIMEContext& aIMEContext)
|
||||
{
|
||||
WidgetQueryContentEvent editorRectEvent(true, NS_QUERY_EDITOR_RECT, aWindow);
|
||||
aWindow->InitEvent(editorRectEvent);
|
||||
aWindow->DispatchWindowEvent(&editorRectEvent);
|
||||
if (!editorRectEvent.mSucceeded) {
|
||||
PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
|
||||
("IMM32: SetIMERelatedWindowsPosOnPlugin, "
|
||||
"FAILED (NS_QUERY_EDITOR_RECT)"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Clip the plugin rect by the client rect of the window because composition
|
||||
// window needs to be specified the position in the client area.
|
||||
nsWindow* toplevelWindow = aWindow->GetTopLevelWindow(false);
|
||||
nsIntRect pluginRectInScreen =
|
||||
editorRectEvent.mReply.mRect + toplevelWindow->WidgetToScreenOffset();
|
||||
nsIntRect winRectInScreen;
|
||||
aWindow->GetClientBounds(winRectInScreen);
|
||||
// composition window cannot be positioned on the edge of client area.
|
||||
winRectInScreen.width--;
|
||||
winRectInScreen.height--;
|
||||
nsIntRect clippedPluginRect;
|
||||
clippedPluginRect.x =
|
||||
std::min(std::max(pluginRectInScreen.x, winRectInScreen.x),
|
||||
winRectInScreen.XMost());
|
||||
clippedPluginRect.y =
|
||||
std::min(std::max(pluginRectInScreen.y, winRectInScreen.y),
|
||||
winRectInScreen.YMost());
|
||||
int32_t xMost = std::min(pluginRectInScreen.XMost(), winRectInScreen.XMost());
|
||||
int32_t yMost = std::min(pluginRectInScreen.YMost(), winRectInScreen.YMost());
|
||||
clippedPluginRect.width = std::max(0, xMost - clippedPluginRect.x);
|
||||
clippedPluginRect.height = std::max(0, yMost - clippedPluginRect.y);
|
||||
clippedPluginRect -= aWindow->WidgetToScreenOffset();
|
||||
|
||||
// Cover the plugin with native caret. This prevents IME's window and plugin
|
||||
// overlap.
|
||||
if (mNativeCaretIsCreated) {
|
||||
::DestroyCaret();
|
||||
}
|
||||
mNativeCaretIsCreated =
|
||||
::CreateCaret(aWindow->GetWindowHandle(), nullptr,
|
||||
clippedPluginRect.width, clippedPluginRect.height);
|
||||
::SetCaretPos(clippedPluginRect.x, clippedPluginRect.y);
|
||||
|
||||
// Set the composition window to bottom-left of the clipped plugin.
|
||||
// As far as we know, there is no IME for RTL language. Therefore, this code
|
||||
// must not need to take care of RTL environment.
|
||||
COMPOSITIONFORM compForm;
|
||||
compForm.dwStyle = CFS_POINT;
|
||||
compForm.ptCurrentPos.x = clippedPluginRect.BottomLeft().x;
|
||||
compForm.ptCurrentPos.y = clippedPluginRect.BottomLeft().y;
|
||||
if (!::ImmSetCompositionWindow(aIMEContext.get(), &compForm)) {
|
||||
PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
|
||||
("IMM32: SetIMERelatedWindowsPosOnPlugin, "
|
||||
"FAILED to set composition window"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsIMM32Handler::ResolveIMECaretPos(nsIWidget* aReferenceWidget,
|
||||
nsIntRect& aCursorRect,
|
||||
|
@ -262,6 +262,8 @@ protected:
|
||||
|
||||
bool SetIMERelatedWindowsPos(nsWindow* aWindow,
|
||||
const nsIMEContext& aIMEContext);
|
||||
void SetIMERelatedWindowsPosOnPlugin(nsWindow* aWindow,
|
||||
const nsIMEContext& aIMEContext);
|
||||
bool GetCharacterRectOfSelectedTextAt(nsWindow* aWindow,
|
||||
uint32_t aOffset,
|
||||
nsIntRect &aCharRect);
|
||||
|
@ -144,7 +144,7 @@ _class::Internal::AddRef(void) \
|
||||
_class* agg = NS_CYCLE_COLLECTION_CLASSNAME(_class)::Downcast(this); \
|
||||
MOZ_ASSERT(int32_t(agg->mRefCnt) >= 0, "illegal refcnt"); \
|
||||
NS_ASSERT_OWNINGTHREAD_AGGREGATE(agg, _class); \
|
||||
nsrefcnt count = agg->mRefCnt.incr(); \
|
||||
nsrefcnt count = agg->mRefCnt.incr(this); \
|
||||
NS_LOG_ADDREF(this, count, #_class, sizeof(*agg)); \
|
||||
return count; \
|
||||
} \
|
||||
|
@ -71,7 +71,30 @@
|
||||
//
|
||||
// The second problem is that objects in the graph can be changed, say by
|
||||
// being addrefed or released, or by having a field updated, after the object
|
||||
// has been added to the graph. This will be addressed in bug 937818.
|
||||
// has been added to the graph. The problem is that ICC can miss a newly
|
||||
// created reference to an object, and end up unlinking an object that is
|
||||
// actually alive.
|
||||
//
|
||||
// The basic idea of the solution, from "An on-the-fly Reference Counting
|
||||
// Garbage Collector for Java" by Levanoni and Petrank, is to notice if an
|
||||
// object has had an additional reference to it created during the collection,
|
||||
// and if so, don't collect it during the current collection. This avoids having
|
||||
// to rerun the scan as in Bacon & Rajan 2001.
|
||||
//
|
||||
// For cycle collected C++ objects, we modify AddRef to place the object in
|
||||
// the purple buffer, in addition to Release. Then, in the CC, we treat any
|
||||
// objects in the purple buffer as being alive, after graph building has
|
||||
// completed. Because they are in the purple buffer, they will be suspected
|
||||
// in the next CC, so there's no danger of leaks. This is imprecise, because
|
||||
// we will treat as live an object that has been Released but not AddRefed
|
||||
// during graph building, but that's probably rare enough that the additional
|
||||
// bookkeeping overhead is not worthwhile.
|
||||
//
|
||||
// For JS objects, the cycle collector is only looking at gray objects. If a
|
||||
// gray object is touched during ICC, it will be made black by UnmarkGray.
|
||||
// Thus, if a JS object has become black during the ICC, we treat it as live.
|
||||
// Merged JS zones have to be handled specially: we scan all zone globals.
|
||||
// If any are black, we treat the zone as being black.
|
||||
|
||||
|
||||
// Safety
|
||||
@ -1155,7 +1178,8 @@ private:
|
||||
|
||||
void BeginCollection(ccType aCCType, nsICycleCollectorListener *aManualListener);
|
||||
void MarkRoots(SliceBudget &aBudget);
|
||||
void ScanRoots();
|
||||
void ScanRoots(bool aFullySynchGraphBuild);
|
||||
void ScanIncrementalRoots();
|
||||
void ScanWeakMaps();
|
||||
|
||||
// returns whether anything was collected
|
||||
@ -2280,6 +2304,16 @@ nsCycleCollector::ForgetSkippable(bool aRemoveChildlessNodes,
|
||||
bool aAsyncSnowWhiteFreeing)
|
||||
{
|
||||
CheckThreadSafety();
|
||||
|
||||
// If we remove things from the purple buffer during graph building, we may
|
||||
// lose track of an object that was mutated during graph building. This should
|
||||
// only happen when somebody calls nsJSContext::CycleCollectNow explicitly
|
||||
// requesting extra forget skippable calls, during an incremental collection.
|
||||
// See bug 950949 for fixing this so we actually run the forgetSkippable calls.
|
||||
if (mIncrementalPhase != IdlePhase) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mJSRuntime) {
|
||||
mJSRuntime->PrepareForForgetSkippable();
|
||||
}
|
||||
@ -2372,8 +2406,9 @@ private:
|
||||
|
||||
struct scanVisitor
|
||||
{
|
||||
scanVisitor(uint32_t &aWhiteNodeCount, bool &aFailed)
|
||||
: mWhiteNodeCount(aWhiteNodeCount), mFailed(aFailed)
|
||||
scanVisitor(uint32_t &aWhiteNodeCount, bool &aFailed, bool aWasIncremental)
|
||||
: mWhiteNodeCount(aWhiteNodeCount), mFailed(aFailed),
|
||||
mWasIncremental(aWasIncremental)
|
||||
{
|
||||
}
|
||||
|
||||
@ -2384,8 +2419,16 @@ struct scanVisitor
|
||||
|
||||
MOZ_NEVER_INLINE void VisitNode(PtrInfo *pi)
|
||||
{
|
||||
if (pi->mInternalRefs > pi->mRefCount && pi->mRefCount > 0)
|
||||
if (pi->mInternalRefs > pi->mRefCount && pi->mRefCount > 0) {
|
||||
// If we found more references to an object than its ref count, then
|
||||
// the object should have already been marked as an incremental
|
||||
// root. Note that this is imprecise, because pi could have been
|
||||
// marked black for other reasons. Always fault if we weren't
|
||||
// incremental, as there were no incremental roots in that case.
|
||||
if (!mWasIncremental || pi->mColor != black) {
|
||||
Fault("traversed refs exceed refcount", pi);
|
||||
}
|
||||
}
|
||||
|
||||
if (pi->mInternalRefs == pi->mRefCount || pi->mRefCount == 0) {
|
||||
pi->mColor = white;
|
||||
@ -2404,6 +2447,7 @@ struct scanVisitor
|
||||
private:
|
||||
uint32_t &mWhiteNodeCount;
|
||||
bool &mFailed;
|
||||
bool mWasIncremental;
|
||||
};
|
||||
|
||||
// Iterate over the WeakMaps. If we mark anything while iterating
|
||||
@ -2451,28 +2495,139 @@ nsCycleCollector::ScanWeakMaps()
|
||||
}
|
||||
}
|
||||
|
||||
// Flood black from any objects in the purple buffer that are in the CC graph.
|
||||
class PurpleScanBlackVisitor
|
||||
{
|
||||
public:
|
||||
PurpleScanBlackVisitor(GCGraph &aGraph, uint32_t &aCount, bool &aFailed)
|
||||
: mGraph(aGraph), mCount(aCount), mFailed(aFailed)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
nsCycleCollector::ScanRoots()
|
||||
Visit(nsPurpleBuffer &aBuffer, nsPurpleBufferEntry *aEntry)
|
||||
{
|
||||
MOZ_ASSERT(aEntry->mObject, "Entries with null mObject shouldn't be in the purple buffer.");
|
||||
MOZ_ASSERT(aEntry->mRefCnt->get() != 0, "Snow-white objects shouldn't be in the purple buffer.");
|
||||
|
||||
void *obj = aEntry->mObject;
|
||||
if (!aEntry->mParticipant) {
|
||||
obj = CanonicalizeXPCOMParticipant(static_cast<nsISupports*>(obj));
|
||||
MOZ_ASSERT(obj, "Don't add objects that don't participate in collection!");
|
||||
}
|
||||
|
||||
PtrInfo *pi = mGraph.FindNode(obj);
|
||||
if (!pi) {
|
||||
return;
|
||||
}
|
||||
MOZ_ASSERT(pi->mParticipant, "No dead objects should be in the purple buffer.");
|
||||
if (pi->mColor == black) {
|
||||
return;
|
||||
}
|
||||
GraphWalker<ScanBlackVisitor>(ScanBlackVisitor(mCount, mFailed)).Walk(pi);
|
||||
}
|
||||
|
||||
private:
|
||||
GCGraph &mGraph;
|
||||
uint32_t &mCount;
|
||||
bool &mFailed;
|
||||
};
|
||||
|
||||
// Objects that have been stored somewhere since the start of incremental graph building must
|
||||
// be treated as live for this cycle collection, because we may not have accurate information
|
||||
// about who holds references to them.
|
||||
void
|
||||
nsCycleCollector::ScanIncrementalRoots()
|
||||
{
|
||||
TimeLog timeLog;
|
||||
|
||||
// Reference counted objects:
|
||||
// We cleared the purple buffer at the start of the current ICC, so if a
|
||||
// refcounted object is purple, it may have been AddRef'd during the current
|
||||
// ICC. (It may also have only been released.) If that is the case, we cannot
|
||||
// be sure that the set of things pointing to the object in the CC graph
|
||||
// is accurate. Therefore, for safety, we treat any purple objects as being
|
||||
// live during the current CC. We don't remove anything from the purple
|
||||
// buffer here, so these objects will be suspected and freed in the next CC
|
||||
// if they are garbage.
|
||||
bool failed = false;
|
||||
PurpleScanBlackVisitor purpleScanBlackVisitor(mGraph, mWhiteNodeCount, failed);
|
||||
mPurpleBuf.VisitEntries(purpleScanBlackVisitor);
|
||||
timeLog.Checkpoint("ScanIncrementalRoots::fix purple");
|
||||
|
||||
// Garbage collected objects:
|
||||
// If a GCed object was added to the graph with a refcount of zero, and is
|
||||
// now marked black by the GC, it was probably gray before and was exposed
|
||||
// to active JS, so it may have been stored somewhere, so it needs to be
|
||||
// treated as live.
|
||||
if (mJSRuntime) {
|
||||
nsCycleCollectionParticipant *jsParticipant = mJSRuntime->GCThingParticipant();
|
||||
nsCycleCollectionParticipant *zoneParticipant = mJSRuntime->ZoneParticipant();
|
||||
NodePool::Enumerator etor(mGraph.mNodes);
|
||||
|
||||
while (!etor.IsDone()) {
|
||||
PtrInfo *pi = etor.GetNext();
|
||||
|
||||
if (pi->mRefCount != 0 || pi->mColor == black) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pi->mParticipant == jsParticipant) {
|
||||
if (xpc_GCThingIsGrayCCThing(pi->mPointer)) {
|
||||
continue;
|
||||
}
|
||||
} else if (pi->mParticipant == zoneParticipant) {
|
||||
JS::Zone *zone = static_cast<JS::Zone*>(pi->mPointer);
|
||||
if (js::ZoneGlobalsAreAllGray(zone)) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
MOZ_ASSERT(false, "Non-JS thing with 0 refcount? Treating as live.");
|
||||
}
|
||||
|
||||
GraphWalker<ScanBlackVisitor>(ScanBlackVisitor(mWhiteNodeCount, failed)).Walk(pi);
|
||||
}
|
||||
|
||||
timeLog.Checkpoint("ScanIncrementalRoots::fix JS");
|
||||
}
|
||||
|
||||
if (failed) {
|
||||
NS_ASSERTION(false, "Ran out of memory in ScanIncrementalRoots");
|
||||
CC_TELEMETRY(_OOM, true);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsCycleCollector::ScanRoots(bool aFullySynchGraphBuild)
|
||||
{
|
||||
AutoRestore<bool> ar(mScanInProgress);
|
||||
MOZ_ASSERT(!mScanInProgress);
|
||||
mScanInProgress = true;
|
||||
mWhiteNodeCount = 0;
|
||||
MOZ_ASSERT(mIncrementalPhase == ScanAndCollectWhitePhase);
|
||||
|
||||
if (!aFullySynchGraphBuild) {
|
||||
ScanIncrementalRoots();
|
||||
}
|
||||
|
||||
TimeLog timeLog;
|
||||
|
||||
// On the assumption that most nodes will be black, it's
|
||||
// probably faster to use a GraphWalker than a
|
||||
// NodePool::Enumerator.
|
||||
bool failed = false;
|
||||
GraphWalker<scanVisitor>(scanVisitor(mWhiteNodeCount, failed)).WalkFromRoots(mGraph);
|
||||
scanVisitor sv(mWhiteNodeCount, failed, !aFullySynchGraphBuild);
|
||||
GraphWalker<scanVisitor>(sv).WalkFromRoots(mGraph);
|
||||
timeLog.Checkpoint("ScanRoots::WalkFromRoots");
|
||||
|
||||
if (failed) {
|
||||
NS_ASSERTION(false, "Ran out of memory in ScanRoots");
|
||||
CC_TELEMETRY(_OOM, true);
|
||||
}
|
||||
|
||||
// Scanning weak maps must be done last.
|
||||
ScanWeakMaps();
|
||||
timeLog.Checkpoint("ScanRoots::ScanWeakMaps");
|
||||
|
||||
if (mListener) {
|
||||
mListener->BeginResults();
|
||||
@ -2503,9 +2658,8 @@ nsCycleCollector::ScanRoots()
|
||||
|
||||
mListener->End();
|
||||
mListener = nullptr;
|
||||
timeLog.Checkpoint("ScanRoots::listener");
|
||||
}
|
||||
|
||||
timeLog.Checkpoint("ScanRoots()");
|
||||
}
|
||||
|
||||
|
||||
@ -2868,7 +3022,7 @@ nsCycleCollector::Collect(ccType aCCType,
|
||||
// promoted to a strong reference after ScanRoots has finished.
|
||||
// See bug 926533.
|
||||
PrintPhase("ScanRoots");
|
||||
ScanRoots();
|
||||
ScanRoots(startedIdle);
|
||||
PrintPhase("CollectWhite");
|
||||
collectedAny = CollectWhite();
|
||||
break;
|
||||
|
@ -92,10 +92,23 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE uintptr_t incr()
|
||||
MOZ_ALWAYS_INLINE uintptr_t incr(nsISupports *owner)
|
||||
{
|
||||
return incr(owner, nullptr);
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE uintptr_t incr(void *owner, nsCycleCollectionParticipant *p)
|
||||
{
|
||||
mRefCntAndFlags += NS_REFCOUNT_CHANGE;
|
||||
mRefCntAndFlags &= ~NS_IS_PURPLE;
|
||||
// For incremental cycle collection, use the purple buffer to track objects
|
||||
// that have been AddRef'd.
|
||||
if (!IsInPurpleBuffer()) {
|
||||
mRefCntAndFlags |= NS_IN_PURPLE_BUFFER;
|
||||
// Refcount isn't zero, so Suspect won't delete anything.
|
||||
MOZ_ASSERT(get() > 0);
|
||||
NS_CycleCollectorSuspect3(owner, p, this, nullptr);
|
||||
}
|
||||
return NS_REFCOUNT_VALUE(mRefCntAndFlags);
|
||||
}
|
||||
|
||||
@ -265,7 +278,9 @@ public:
|
||||
#define NS_IMPL_CC_NATIVE_ADDREF_BODY(_class) \
|
||||
MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt"); \
|
||||
NS_ASSERT_OWNINGTHREAD(_class); \
|
||||
nsrefcnt count = mRefCnt.incr(); \
|
||||
nsrefcnt count = \
|
||||
mRefCnt.incr(static_cast<void*>(this), \
|
||||
_class::NS_CYCLE_COLLECTION_INNERCLASS::GetParticipant()); \
|
||||
NS_LOG_ADDREF(this, count, #_class, sizeof(*this)); \
|
||||
return count;
|
||||
|
||||
@ -296,7 +311,8 @@ NS_METHOD_(nsrefcnt) _class::Release(void)
|
||||
&shouldDelete); \
|
||||
NS_LOG_RELEASE(this, count, #_class); \
|
||||
if (count == 0) { \
|
||||
mRefCnt.incr(); \
|
||||
mRefCnt.incr(static_cast<void*>(this), \
|
||||
_class::NS_CYCLE_COLLECTION_INNERCLASS::GetParticipant()); \
|
||||
_last; \
|
||||
mRefCnt.decr(static_cast<void*>(this), \
|
||||
_class::NS_CYCLE_COLLECTION_INNERCLASS::GetParticipant()); \
|
||||
@ -502,7 +518,8 @@ NS_IMETHODIMP_(nsrefcnt) _class::AddRef(void) \
|
||||
{ \
|
||||
MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt"); \
|
||||
NS_ASSERT_OWNINGTHREAD(_class); \
|
||||
nsrefcnt count = mRefCnt.incr(); \
|
||||
nsISupports *base = NS_CYCLE_COLLECTION_CLASSNAME(_class)::Upcast(this); \
|
||||
nsrefcnt count = mRefCnt.incr(base); \
|
||||
NS_LOG_ADDREF(this, count, #_class, sizeof(*this)); \
|
||||
return count; \
|
||||
}
|
||||
@ -537,7 +554,7 @@ NS_IMETHODIMP_(nsrefcnt) _class::Release(void) \
|
||||
nsrefcnt count = mRefCnt.decr(base, &shouldDelete); \
|
||||
NS_LOG_RELEASE(this, count, #_class); \
|
||||
if (count == 0) { \
|
||||
mRefCnt.incr(); \
|
||||
mRefCnt.incr(base); \
|
||||
_last; \
|
||||
mRefCnt.decr(base); \
|
||||
if (shouldDelete) { \
|
||||
|
Loading…
Reference in New Issue
Block a user