Merge mozilla-central to fx-team

This commit is contained in:
Carsten "Tomcat" Book 2013-12-18 13:03:15 +01:00
commit ca657b39c3
45 changed files with 2896 additions and 1236 deletions

View File

@ -1,4 +1,4 @@
{
"revision": "2cc6e0988688b33fa46e3a05a726fb7919d7420e",
"revision": "7412c36923b59f6e4d7076de5be7e6ded6ddc586",
"repo_path": "/integration/gaia-central"
}

View File

@ -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;

View File

@ -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)

View File

@ -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;
}

View File

@ -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.

View File

@ -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()

View File

@ -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;
}

View 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

View 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

View File

@ -123,6 +123,7 @@ UNIFIED_SOURCES += [
'MediaDecoderStateMachine.cpp',
'MediaRecorder.cpp',
'MediaResource.cpp',
'MediaShutdownManager.cpp',
'MediaStreamGraph.cpp',
'MediaStreamTrack.cpp',
'MP3FrameParser.cpp',

View File

@ -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");

View File

@ -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:
objDecl = " JS::Rooted<JSObject*> obj(aCx);\n"
else:
objDecl = " JSObject *obj;\n"
# 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"
if descriptor.proxy:
create = """ JS::Rooted<JS::Value> proxyPrivateVal(aCx, JS::PrivateValue(aObject));
obj = NewProxyObject(aCx, DOMProxyHandler::getInstance(),

View File

@ -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) {

View File

@ -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);

View File

@ -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)

View File

@ -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,

View File

@ -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)
{

View File

@ -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);

View File

@ -1638,7 +1638,7 @@ TemporaryTypeSet::isDOMClass()
return false;
}
return true;
return count > 0;
}
bool

View File

@ -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);
}

View File

@ -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.

View File

@ -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);

View File

@ -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,28 +3545,29 @@ 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());
nsRect boundedRect =
childVisibleRegion.GetBounds().Intersect(mList.GetBounds(aBuilder));
nsRect allowExpansion = boundedRect.Intersect(aAllowVisibleRegionExpansion);
bool visible = mList.ComputeVisibilityForSublist(
aBuilder, &childVisibleRegion, boundedRect, allowExpansion);
// 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;
childVisibleRegion = displayport + mScrollFrame->GetOffsetToCrossDoc(ReferenceFrame());
} else {
return nsDisplayWrapList::ComputeVisibility(aBuilder, aVisibleRegion,
aAllowVisibleRegionExpansion);
bool snap;
childVisibleRegion = GetBounds(aBuilder, &snap);
}
nsRect boundedRect =
childVisibleRegion.GetBounds().Intersect(mList.GetBounds(aBuilder));
nsRect allowExpansion = boundedRect.Intersect(aAllowVisibleRegionExpansion);
bool visible = mList.ComputeVisibilityForSublist(
aBuilder, &childVisibleRegion, boundedRect, allowExpansion);
// 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.
return visible;
}
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,

View File

@ -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;

View File

@ -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

View File

@ -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;
}
} else {
nativeKeyboardLayout = aKeyboardLayout.Win;
}
if (nativeKeyboardLayout === null) {
return false;
}
utils.sendNativeKeyEvent(nativeKeyboardLayout, aNativeKeyCode,

View File

@ -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.
*/

View File

@ -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]

View File

@ -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)
{

View File

@ -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; }

View File

@ -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()) {
// 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());
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.
// 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 =

View File

@ -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.

View File

@ -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;

View File

@ -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>

View File

@ -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

View File

@ -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();

View File

@ -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();

View File

@ -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)

View File

@ -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;

View File

@ -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,

View File

@ -261,7 +261,9 @@ protected:
nsACString& aANSIStr);
bool SetIMERelatedWindowsPos(nsWindow* aWindow,
const nsIMEContext &aIMEContext);
const nsIMEContext& aIMEContext);
void SetIMERelatedWindowsPosOnPlugin(nsWindow* aWindow,
const nsIMEContext& aIMEContext);
bool GetCharacterRectOfSelectedTextAt(nsWindow* aWindow,
uint32_t aOffset,
nsIntRect &aCharRect);

View File

@ -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; \
} \

View File

@ -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)
Fault("traversed refs exceed refcount", pi);
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
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::ScanRoots()
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;

View File

@ -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) { \