diff --git a/accessible/atk/AccessibleWrap.cpp b/accessible/atk/AccessibleWrap.cpp index 5f98faac2cc5..dda4df911414 100644 --- a/accessible/atk/AccessibleWrap.cpp +++ b/accessible/atk/AccessibleWrap.cpp @@ -12,6 +12,7 @@ #include "nsAccUtils.h" #include "nsIAccessibleRelation.h" #include "nsIAccessibleTable.h" +#include "ProxyAccessible.h" #include "RootAccessible.h" #include "nsIAccessibleValue.h" #include "nsMai.h" @@ -20,6 +21,7 @@ #include "nsAutoPtr.h" #include "prprf.h" #include "nsStateMap.h" +#include "mozilla/a11y/Platform.h" #include "Relation.h" #include "RootAccessible.h" #include "States.h" @@ -133,9 +135,13 @@ struct MaiAtkObject * The AccessibleWrap whose properties and features are exported * via this object instance. */ - AccessibleWrap* accWrap; + uintptr_t accWrap; }; +// This is or'd with the pointer in MaiAtkObject::accWrap if the wrap-ee is a +// proxy. +static const uintptr_t IS_PROXY = 1; + struct MaiAtkObjectClass { AtkObjectClass parent_class; @@ -248,7 +254,7 @@ AccessibleWrap::ShutdownAtkObject() { if (mAtkObject) { if (IS_MAI_OBJECT(mAtkObject)) { - MAI_ATK_OBJECT(mAtkObject)->accWrap = nullptr; + MAI_ATK_OBJECT(mAtkObject)->accWrap = 0; } SetMaiHyperlink(nullptr); g_object_unref(mAtkObject); @@ -582,8 +588,7 @@ initializeCB(AtkObject *aAtkObj, gpointer aData) ATK_OBJECT_CLASS(parent_class)->initialize(aAtkObj, aData); /* initialize object */ - MAI_ATK_OBJECT(aAtkObj)->accWrap = - static_cast(aData); + MAI_ATK_OBJECT(aAtkObj)->accWrap = reinterpret_cast(aData); } void @@ -591,7 +596,7 @@ finalizeCB(GObject *aObj) { if (!IS_MAI_OBJECT(aObj)) return; - NS_ASSERTION(MAI_ATK_OBJECT(aObj)->accWrap == nullptr, "AccWrap NOT null"); + NS_ASSERTION(MAI_ATK_OBJECT(aObj)->accWrap == 0, "AccWrap NOT null"); // call parent finalize function // finalize of GObjectClass will unref the accessible parent if has @@ -663,25 +668,33 @@ getDescriptionCB(AtkObject *aAtkObj) AtkRole getRoleCB(AtkObject *aAtkObj) { - AccessibleWrap* accWrap = GetAccessibleWrap(aAtkObj); - if (!accWrap) - return ATK_ROLE_INVALID; - -#ifdef DEBUG - NS_ASSERTION(nsAccUtils::IsTextInterfaceSupportCorrect(accWrap), - "Does not support nsIAccessibleText when it should"); -#endif - if (aAtkObj->role != ATK_ROLE_INVALID) return aAtkObj->role; + AccessibleWrap* accWrap = GetAccessibleWrap(aAtkObj); + a11y::role role; + if (!accWrap) { + ProxyAccessible* proxy = GetProxy(aAtkObj); + if (!proxy) + return ATK_ROLE_INVALID; + + role = proxy->Role(); + } else { +#ifdef DEBUG + NS_ASSERTION(nsAccUtils::IsTextInterfaceSupportCorrect(accWrap), + "Does not support nsIAccessibleText when it should"); +#endif + + role = accWrap->Role(); + } + #define ROLE(geckoRole, stringRole, atkRole, macRole, \ msaaRole, ia2Role, nameRule) \ case roles::geckoRole: \ aAtkObj->role = atkRole; \ break; - switch (accWrap->Role()) { + switch (role) { #include "RoleMap.h" default: MOZ_CRASH("Unknown role."); @@ -879,19 +892,18 @@ TranslateStates(uint64_t aState, AtkStateSet* aStateSet) AtkStateSet * refStateSetCB(AtkObject *aAtkObj) { - AtkStateSet *state_set = nullptr; - state_set = ATK_OBJECT_CLASS(parent_class)->ref_state_set(aAtkObj); + AtkStateSet *state_set = nullptr; + state_set = ATK_OBJECT_CLASS(parent_class)->ref_state_set(aAtkObj); - AccessibleWrap* accWrap = GetAccessibleWrap(aAtkObj); - if (!accWrap) { - TranslateStates(states::DEFUNCT, state_set); - return state_set; - } - - // Map states + AccessibleWrap* accWrap = GetAccessibleWrap(aAtkObj); + if (accWrap) TranslateStates(accWrap->State(), state_set); + else if (ProxyAccessible* proxy = GetProxy(aAtkObj)) + TranslateStates(proxy->State(), state_set); + else + TranslateStates(states::DEFUNCT, state_set); - return state_set; + return state_set; } static void @@ -946,7 +958,13 @@ AccessibleWrap* GetAccessibleWrap(AtkObject* aAtkObj) { NS_ENSURE_TRUE(IS_MAI_OBJECT(aAtkObj), nullptr); - AccessibleWrap* accWrap = MAI_ATK_OBJECT(aAtkObj)->accWrap; + + // Make sure its native is an AccessibleWrap not a proxy. + if (MAI_ATK_OBJECT(aAtkObj)->accWrap & IS_PROXY) + return nullptr; + + AccessibleWrap* accWrap = + reinterpret_cast(MAI_ATK_OBJECT(aAtkObj)->accWrap); // Check if the accessible was deconstructed. if (!accWrap) @@ -961,6 +979,50 @@ GetAccessibleWrap(AtkObject* aAtkObj) return accWrap; } +ProxyAccessible* +GetProxy(AtkObject* aObj) +{ + if (!aObj || !(MAI_ATK_OBJECT(aObj)->accWrap & IS_PROXY)) + return nullptr; + + return reinterpret_cast(MAI_ATK_OBJECT(aObj)->accWrap + & ~IS_PROXY); +} + +static uint16_t +GetInterfacesForProxy(ProxyAccessible* aProxy) +{ + return MAI_INTERFACE_COMPONENT; +} + +void +a11y::ProxyCreated(ProxyAccessible* aProxy) +{ + GType type = GetMaiAtkType(GetInterfacesForProxy(aProxy)); + NS_ASSERTION(type, "why don't we have a type!"); + + AtkObject* obj = + reinterpret_cast + (g_object_new(type, nullptr)); + if (!obj) + return; + + uintptr_t inner = reinterpret_cast(aProxy) | IS_PROXY; + atk_object_initialize(obj, reinterpret_cast(inner)); + obj->role = ATK_ROLE_INVALID; + obj->layer = ATK_LAYER_INVALID; + aProxy->SetWrapper(reinterpret_cast(obj) | IS_PROXY); +} + +void +a11y::ProxyDestroyed(ProxyAccessible* aProxy) +{ + auto obj = reinterpret_cast(aProxy->GetWrapper() & ~IS_PROXY); + obj->accWrap = 0; + g_object_unref(obj); + aProxy->SetWrapper(0); +} + nsresult AccessibleWrap::HandleAccEvent(AccEvent* aEvent) { diff --git a/accessible/atk/AccessibleWrap.h b/accessible/atk/AccessibleWrap.h index 5518a80bd5d8..95d03c5fc16c 100644 --- a/accessible/atk/AccessibleWrap.h +++ b/accessible/atk/AccessibleWrap.h @@ -97,7 +97,7 @@ private: static EAvailableAtkSignals gAvailableAtkSignals; - uint16_t CreateMaiInterfaces(void); + uint16_t CreateMaiInterfaces(); }; } // namespace a11y diff --git a/accessible/atk/moz.build b/accessible/atk/moz.build index 37ec9e8fded5..96fc8cee15d7 100644 --- a/accessible/atk/moz.build +++ b/accessible/atk/moz.build @@ -35,6 +35,7 @@ LOCAL_INCLUDES += [ '/accessible/base', '/accessible/generic', '/accessible/html', + '/accessible/ipc', '/accessible/xpcom', '/accessible/xul', '/other-licenses/atk-1.0', diff --git a/accessible/atk/nsMai.h b/accessible/atk/nsMai.h index 5de552ac67a5..6338a3492bae 100644 --- a/accessible/atk/nsMai.h +++ b/accessible/atk/nsMai.h @@ -13,6 +13,12 @@ #include "AccessibleWrap.h" +namespace mozilla { +namespace a11y { +class ProxyAccessible; +} +} + #define MAI_TYPE_ATK_OBJECT (mai_atk_object_get_type ()) #define MAI_ATK_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ MAI_TYPE_ATK_OBJECT, MaiAtkObject)) @@ -29,6 +35,7 @@ GType mai_atk_object_get_type(void); GType mai_util_get_type(); mozilla::a11y::AccessibleWrap* GetAccessibleWrap(AtkObject* aAtkObj); +mozilla::a11y::ProxyAccessible* GetProxy(AtkObject* aAtkObj); extern int atkMajorVersion, atkMinorVersion; diff --git a/accessible/base/AccEvent.h b/accessible/base/AccEvent.h index f97b1fc4e01b..ee28efa511b6 100644 --- a/accessible/base/AccEvent.h +++ b/accessible/base/AccEvent.h @@ -232,6 +232,8 @@ public: bool IsShow() const { return mEventType == nsIAccessibleEvent::EVENT_SHOW; } bool IsHide() const { return mEventType == nsIAccessibleEvent::EVENT_HIDE; } + Accessible* Parent() const { return mParent; } + protected: nsCOMPtr mNode; nsRefPtr mParent; diff --git a/accessible/base/DocManager.cpp b/accessible/base/DocManager.cpp index eed51d5ca01a..5ff0f3350459 100644 --- a/accessible/base/DocManager.cpp +++ b/accessible/base/DocManager.cpp @@ -8,6 +8,7 @@ #include "ApplicationAccessible.h" #include "ARIAMap.h" #include "DocAccessible-inl.h" +#include "DocAccessibleChild.h" #include "nsAccessibilityService.h" #include "RootAccessibleWrap.h" @@ -27,6 +28,8 @@ #include "nsServiceManagerUtils.h" #include "nsIWebProgress.h" #include "nsCoreUtils.h" +#include "nsXULAppAPI.h" +#include "mozilla/dom/ContentChild.h" using namespace mozilla; using namespace mozilla::a11y; @@ -418,6 +421,12 @@ DocManager::CreateDocOrRootAccessible(nsIDocument* aDocument) docAcc->FireDelayedEvent(nsIAccessibleEvent::EVENT_REORDER, ApplicationAcc()); + if (IPCAccessibilityActive()) { + DocAccessibleChild* ipcDoc = new DocAccessibleChild(docAcc); + docAcc->SetIPCDoc(ipcDoc); + auto contentChild = dom::ContentChild::GetSingleton(); + contentChild->SendPDocAccessibleConstructor(ipcDoc, nullptr, 0); + } } else { parentDocAcc->BindChildDocument(docAcc); } diff --git a/accessible/base/DocManager.h b/accessible/base/DocManager.h index 13e80a602372..69193820eb67 100644 --- a/accessible/base/DocManager.h +++ b/accessible/base/DocManager.h @@ -17,6 +17,7 @@ namespace a11y { class Accessible; class DocAccessible; +class DocAccessibleParent; /** * Manage the document accessible life cycle. @@ -65,6 +66,25 @@ public: RemoveListeners(aDocument); } + /* + * Notification that a top level document in a content process has gone away. + */ + void RemoteDocShutdown(DocAccessibleParent* aDoc) + { + DebugOnly result = mRemoteDocuments.RemoveElement(aDoc); + MOZ_ASSERT(result, "Why didn't we find the document!"); + } + + /* + * Notify of a new top level document in a content process. + */ + void RemoteDocAdded(DocAccessibleParent* aDoc) + { + MOZ_ASSERT(!mRemoteDocuments.Contains(aDoc), + "How did we already have the doc!"); + mRemoteDocuments.AppendElement(aDoc); + } + #ifdef DEBUG bool IsProcessingRefreshDriverNotification() const; #endif @@ -144,6 +164,11 @@ private: #endif DocAccessibleHashtable mDocAccessibleCache; + + /* + * The list of remote top level documents. + */ + nsTArray mRemoteDocuments; }; /** diff --git a/accessible/base/EventQueue.cpp b/accessible/base/EventQueue.cpp index 6338785e49c9..31c4687ffdfb 100644 --- a/accessible/base/EventQueue.cpp +++ b/accessible/base/EventQueue.cpp @@ -8,6 +8,7 @@ #include "Accessible-inl.h" #include "nsEventShell.h" #include "DocAccessible.h" +#include "DocAccessibleChild.h" #include "nsAccessibilityService.h" #include "nsTextEquivUtils.h" #ifdef A11Y_LOG @@ -555,5 +556,15 @@ EventQueue::ProcessEventQueue() if (!mDocument) return; + + if (IPCAccessibilityActive()) { + DocAccessibleChild* ipcDoc = mDocument->IPCDoc(); + if (event->mEventType == nsIAccessibleEvent::EVENT_SHOW) + ipcDoc->ShowEvent(downcast_accEvent(event)); + else if (event->mEventType == nsIAccessibleEvent::EVENT_HIDE) + ipcDoc->SendHideEvent(reinterpret_cast(event->GetAccessible())); + else + ipcDoc->SendEvent(event->GetEventType()); + } } } diff --git a/accessible/base/NotificationController.cpp b/accessible/base/NotificationController.cpp index 82a591ab72c1..8dee12548eae 100644 --- a/accessible/base/NotificationController.cpp +++ b/accessible/base/NotificationController.cpp @@ -6,9 +6,11 @@ #include "NotificationController.h" #include "DocAccessible-inl.h" +#include "DocAccessibleChild.h" #include "TextLeafAccessible.h" #include "TextUpdater.h" +#include "mozilla/dom/ContentChild.h" #include "mozilla/dom/Element.h" #include "mozilla/Telemetry.h" @@ -217,8 +219,19 @@ NotificationController::WillRefresh(mozilla::TimeStamp aTime) if (ownerContent) { Accessible* outerDocAcc = mDocument->GetAccessible(ownerContent); if (outerDocAcc && outerDocAcc->AppendChild(childDoc)) { - if (mDocument->AppendChildDocument(childDoc)) + if (mDocument->AppendChildDocument(childDoc)) { + if (IPCAccessibilityActive()) { + DocAccessibleChild* ipcDoc = new DocAccessibleChild(childDoc); + childDoc->SetIPCDoc(ipcDoc); + auto contentChild = dom::ContentChild::GetSingleton(); + DocAccessibleChild* parentIPCDoc = mDocument->IPCDoc(); + uint64_t id = reinterpret_cast(outerDocAcc->UniqueID()); + contentChild->SendPDocAccessibleConstructor(ipcDoc, parentIPCDoc, + id); + } + continue; + } outerDocAcc->RemoveChild(childDoc); } diff --git a/accessible/base/Platform.h b/accessible/base/Platform.h index fa56963fdbed..41954be440d8 100644 --- a/accessible/base/Platform.h +++ b/accessible/base/Platform.h @@ -7,6 +7,8 @@ namespace mozilla { namespace a11y { +class ProxyAccessible; + enum EPlatformDisabledState { ePlatformIsForceEnabled = -1, ePlatformIsEnabled = 0, @@ -47,6 +49,17 @@ void PlatformInit(); */ void PlatformShutdown(); +/** + * called when a new ProxyAccessible is created, so the platform may setup a + * wrapper for it, or take other action. + */ +void ProxyCreated(ProxyAccessible*); + +/** + * Called just before a ProxyAccessible is destroyed so its wrapper can be + * disposed of and other action taken. + */ +void ProxyDestroyed(ProxyAccessible*); } // namespace a11y } // namespace mozilla diff --git a/accessible/base/Role.h b/accessible/base/Role.h index 2cd378d64482..0d5746f3dc13 100644 --- a/accessible/base/Role.h +++ b/accessible/base/Role.h @@ -783,7 +783,9 @@ enum Role { /** * Represent a keyboard or keypad key (ARIA role "key"). */ - KEY = 129 + KEY = 129, + + LAST_ROLE = KEY }; } // namespace role diff --git a/accessible/base/moz.build b/accessible/base/moz.build index e31cf3e5c4c3..0dad363cc445 100644 --- a/accessible/base/moz.build +++ b/accessible/base/moz.build @@ -60,6 +60,7 @@ if CONFIG['A11Y_LOG']: LOCAL_INCLUDES += [ '/accessible/generic', '/accessible/html', + '/accessible/ipc', '/accessible/xpcom', '/accessible/xul', '/dom/xbl', @@ -93,3 +94,5 @@ FINAL_LIBRARY = 'xul' if CONFIG['MOZ_ENABLE_GTK']: CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS'] + +include('/ipc/chromium/chromium-config.mozbuild') diff --git a/accessible/base/nsAccessibilityService.h b/accessible/base/nsAccessibilityService.h index d466e6f457b0..fc616748d50c 100644 --- a/accessible/base/nsAccessibilityService.h +++ b/accessible/base/nsAccessibilityService.h @@ -238,6 +238,19 @@ GetAccService() return nsAccessibilityService::gAccessibilityService; } +/** + * Return true if we're in a content process and not B2G. + */ +inline bool +IPCAccessibilityActive() +{ +#ifdef MOZ_B2G + return false; +#else + return XRE_GetProcessType() != GeckoProcessType_Default; +#endif +} + /** * Map nsIAccessibleEvents constants to strings. Used by * nsIAccessibleRetrieval::getStringEventType() method. diff --git a/accessible/generic/DocAccessible.cpp b/accessible/generic/DocAccessible.cpp index bd63bf17ec95..c950f45b76f5 100644 --- a/accessible/generic/DocAccessible.cpp +++ b/accessible/generic/DocAccessible.cpp @@ -6,6 +6,7 @@ #include "Accessible-inl.h" #include "AccIterator.h" #include "DocAccessible-inl.h" +#include "DocAccessibleChild.h" #include "HTMLImageMapAccessible.h" #include "nsAccCache.h" #include "nsAccessiblePivot.h" @@ -83,7 +84,7 @@ DocAccessible:: mScrollPositionChangedTicks(0), mLoadState(eTreeConstructionPending), mDocFlags(0), mLoadEventType(0), mVirtualCursor(nullptr), - mPresShell(aPresShell) + mPresShell(aPresShell), mIPCDoc(nullptr) { mGenericTypes |= eDocument; mStateFlags |= eNotNodeMapEntry; @@ -473,6 +474,12 @@ DocAccessible::Shutdown() mChildDocuments.Clear(); + // XXX thinking about ordering? + if (IPCAccessibilityActive()) { + DocAccessibleChild::Send__delete__(mIPCDoc); + MOZ_ASSERT(!mIPCDoc); + } + if (mVirtualCursor) { mVirtualCursor->RemoveObserver(this); mVirtualCursor = nullptr; @@ -1446,6 +1453,13 @@ DocAccessible::DoInitialUpdate() nsRefPtr reorderEvent = new AccReorderEvent(Parent()); ParentDocument()->FireDelayedEvent(reorderEvent); } + + uint32_t childCount = ChildCount(); + for (uint32_t i = 0; i < childCount; i++) { + Accessible* child = GetChildAt(i); + nsRefPtr event = new AccShowEvent(child, child->GetContent()); + FireDelayedEvent(event); + } } void diff --git a/accessible/generic/DocAccessible.h b/accessible/generic/DocAccessible.h index 4d7858c1cfa0..42b655e69a55 100644 --- a/accessible/generic/DocAccessible.h +++ b/accessible/generic/DocAccessible.h @@ -33,6 +33,7 @@ namespace a11y { class DocManager; class NotificationController; +class DocAccessibleChild; class RelatedAccIterator; template class TNotification; @@ -519,6 +520,20 @@ protected: */ bool IsLoadEventTarget() const; + /** + * If this document is in a content process return the object responsible for + * communicating with the main process for it. + */ + DocAccessibleChild* IPCDoc() const { return mIPCDoc; } + + /* + * Set the object responsible for communicating with the main process on + * behalf of this document. + */ + void SetIPCDoc(DocAccessibleChild* aIPCDoc) { mIPCDoc = aIPCDoc; } + + friend class DocAccessibleChild; + /** * Used to fire scrolling end event after page scroll. * @@ -642,6 +657,9 @@ protected: private: nsIPresShell* mPresShell; + + // Exclusively owned by IPDL so don't manually delete it! + DocAccessibleChild* mIPCDoc; }; inline DocAccessible* diff --git a/accessible/generic/moz.build b/accessible/generic/moz.build index 236715bda01d..3b393c650fb6 100644 --- a/accessible/generic/moz.build +++ b/accessible/generic/moz.build @@ -28,6 +28,7 @@ UNIFIED_SOURCES += [ LOCAL_INCLUDES += [ '/accessible/base', '/accessible/html', + '/accessible/ipc', '/accessible/xpcom', '/accessible/xul', '/content/base/src', @@ -54,3 +55,5 @@ else: ] FINAL_LIBRARY = 'xul' + +include('/ipc/chromium/chromium-config.mozbuild') diff --git a/accessible/ipc/DocAccessibleChild.cpp b/accessible/ipc/DocAccessibleChild.cpp new file mode 100644 index 000000000000..4b0bf33604b0 --- /dev/null +++ b/accessible/ipc/DocAccessibleChild.cpp @@ -0,0 +1,54 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "DocAccessibleChild.h" + +#include "Accessible-inl.h" + +namespace mozilla { +namespace a11y { + +void +SerializeTree(Accessible* aRoot, nsTArray& aTree) +{ + uint64_t id = reinterpret_cast(aRoot->UniqueID()); + uint32_t role = aRoot->Role(); + uint32_t childCount = aRoot->ChildCount(); + + nsString name; + aRoot->Name(name); + aTree.AppendElement(AccessibleData(id, role, childCount, name)); + for (uint32_t i = 0; i < childCount; i++) + SerializeTree(aRoot->GetChildAt(i), aTree); +} + +void +DocAccessibleChild::ShowEvent(AccShowEvent* aShowEvent) +{ + Accessible* parent = aShowEvent->Parent(); + uint64_t parentID = parent->IsDoc() ? 0 : reinterpret_cast(parent->UniqueID()); + uint32_t idxInParent = aShowEvent->GetAccessible()->IndexInParent(); + nsTArray shownTree; + ShowEventData data(parentID, idxInParent, shownTree); + SerializeTree(aShowEvent->GetAccessible(), data.NewTree()); + SendShowEvent(data); +} + +bool +DocAccessibleChild::RecvState(const uint64_t& aID, uint64_t* aState) +{ + Accessible* acc = mDoc->GetAccessibleByUniqueID((void*)aID); + if (!acc) { + *aState = states::DEFUNCT; + return true; + } + + *aState = acc->State(); + + return true; +} +} +} diff --git a/accessible/ipc/DocAccessibleChild.h b/accessible/ipc/DocAccessibleChild.h new file mode 100644 index 000000000000..9baa1e08f9e7 --- /dev/null +++ b/accessible/ipc/DocAccessibleChild.h @@ -0,0 +1,48 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_a11y_DocAccessibleChild_h +#define mozilla_a11y_DocAccessibleChild_h + +#include "mozilla/a11y/DocAccessible.h" +#include "mozilla/a11y/PDocAccessibleChild.h" +#include "nsISupportsImpl.h" + +namespace mozilla { +namespace a11y { +class AccShowEvent; + + /* + * These objects handle content side communication for an accessible document, + * and their lifetime is the same as the document they represent. + */ +class DocAccessibleChild : public PDocAccessibleChild +{ +public: + DocAccessibleChild(DocAccessible* aDoc) : + mDoc(aDoc) + { MOZ_COUNT_CTOR(DocAccessibleChild); } + ~DocAccessibleChild() + { + mDoc->SetIPCDoc(nullptr); + MOZ_COUNT_DTOR(DocAccessibleChild); + } + + void ShowEvent(AccShowEvent* aShowEvent); + + /* + * Return the state for the accessible with given ID. + */ + virtual bool RecvState(const uint64_t& aID, uint64_t* aState) MOZ_OVERRIDE; + +private: + DocAccessible* mDoc; +}; + +} +} + +#endif diff --git a/accessible/ipc/DocAccessibleParent.cpp b/accessible/ipc/DocAccessibleParent.cpp new file mode 100644 index 000000000000..3bbec6e54117 --- /dev/null +++ b/accessible/ipc/DocAccessibleParent.cpp @@ -0,0 +1,114 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "DocAccessibleParent.h" +#include "nsAutoPtr.h" +#include "mozilla/a11y/Platform.h" + +namespace mozilla { +namespace a11y { + +bool +DocAccessibleParent::RecvShowEvent(const ShowEventData& aData) +{ + if (aData.NewTree().IsEmpty()) { + NS_ERROR("no children being added"); + return false; + } + + ProxyAccessible* parent = nullptr; + if (aData.ID()) { + ProxyEntry* e = mAccessibles.GetEntry(aData.ID()); + if (e) + parent = e->mProxy; + } else { + parent = this; + } + + // XXX This should really never happen, but sometimes we fail to fire the + // required show events. + if (!parent) { + NS_ERROR("adding child to unknown accessible"); + return false; + } + + uint32_t newChildIdx = aData.Idx(); + if (newChildIdx > parent->ChildrenCount()) { + NS_ERROR("invalid index to add child at"); + return false; + } + + uint32_t consumed = AddSubtree(parent, aData.NewTree(), 0, newChildIdx); + MOZ_ASSERT(consumed == aData.NewTree().Length()); + for (uint32_t i = 0; i < consumed; i++) { + uint64_t id = aData.NewTree()[i].ID(); + MOZ_ASSERT(mAccessibles.GetEntry(id)); + } + + return consumed; +} + +uint32_t +DocAccessibleParent::AddSubtree(ProxyAccessible* aParent, + const nsTArray& aNewTree, + uint32_t aIdx, uint32_t aIdxInParent) +{ + if (aNewTree.Length() <= aIdx) { + NS_ERROR("bad index in serialized tree!"); + return 0; + } + + const AccessibleData& newChild = aNewTree[aIdx]; + if (newChild.Role() > roles::LAST_ROLE) { + NS_ERROR("invalid role"); + return 0; + } + + auto role = static_cast(newChild.Role()); + ProxyAccessible* newProxy = + new ProxyAccessible(newChild.ID(), aParent, this, role, newChild.Name()); + aParent->AddChildAt(aIdxInParent, newProxy); + mAccessibles.PutEntry(newChild.ID())->mProxy = newProxy; + ProxyCreated(newProxy); + + uint32_t accessibles = 1; + uint32_t kids = newChild.ChildrenCount(); + for (uint32_t i = 0; i < kids; i++) { + uint32_t consumed = AddSubtree(newProxy, aNewTree, aIdx + accessibles, i); + if (!consumed) + return 0; + + accessibles += consumed; + } + + MOZ_ASSERT(newProxy->ChildrenCount() == kids); + + return accessibles; +} + +bool +DocAccessibleParent::RecvHideEvent(const uint64_t& aRootID) +{ + ProxyEntry* rootEntry = mAccessibles.GetEntry(aRootID); + if (!rootEntry) { + NS_ERROR("invalid root being removed!"); + return true; + } + + ProxyAccessible* root = rootEntry->mProxy; + if (!root) { + NS_ERROR("invalid root being removed!"); + return true; + } + + ProxyAccessible* parent = root->Parent(); + parent->RemoveChild(root); + root->Shutdown(); + + return true; +} +} +} diff --git a/accessible/ipc/DocAccessibleParent.h b/accessible/ipc/DocAccessibleParent.h new file mode 100644 index 000000000000..b92a5e0ac127 --- /dev/null +++ b/accessible/ipc/DocAccessibleParent.h @@ -0,0 +1,141 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_a11y_DocAccessibleParent_h +#define mozilla_a11y_DocAccessibleParent_h + +#include "nsAccessibilityService.h" +#include "ProxyAccessible.h" +#include "mozilla/a11y/PDocAccessibleParent.h" +#include "nsClassHashtable.h" +#include "nsHashKeys.h" +#include "nsISupportsImpl.h" + +namespace mozilla { +namespace a11y { + +/* + * These objects live in the main process and comunicate with and represent + * an accessible document in a content process. + */ +class DocAccessibleParent : public ProxyAccessible, + public PDocAccessibleParent +{ +public: + DocAccessibleParent() : + mParentDoc(nullptr) + { MOZ_COUNT_CTOR_INHERITED(DocAccessibleParent, ProxyAccessible); } + ~DocAccessibleParent() + { + MOZ_COUNT_DTOR_INHERITED(DocAccessibleParent, ProxyAccessible); + MOZ_ASSERT(mChildDocs.Length() == 0); + MOZ_ASSERT(!mParentDoc); + } + + /* + * Called when a message from a document in a child process notifies the main + * process it is firing an event. + */ + virtual bool RecvEvent(const uint32_t& aType) MOZ_OVERRIDE + { + return true; + } + + virtual bool RecvShowEvent(const ShowEventData& aData) MOZ_OVERRIDE; + virtual bool RecvHideEvent(const uint64_t& aRootID) MOZ_OVERRIDE; + + virtual void ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE + { + MOZ_ASSERT(mChildDocs.IsEmpty(), + "why wheren't the child docs destroyed already?"); + mParentDoc ? mParentDoc->RemoveChildDoc(this) + : GetAccService()->RemoteDocShutdown(this); + } + + /* + * Return the main processes representation of the parent document (if any) + * of the document this object represents. + */ + DocAccessibleParent* Parent() const { return mParentDoc; } + + /* + * Called when a document in a content process notifies the main process of a + * new child document. + */ + bool AddChildDoc(DocAccessibleParent* aChildDoc, uint64_t aParentID) + { + ProxyAccessible* outerDoc = mAccessibles.GetEntry(aParentID)->mProxy; + if (!outerDoc) + return false; + + aChildDoc->mParent = outerDoc; + outerDoc->SetChildDoc(aChildDoc); + mChildDocs.AppendElement(aChildDoc); + aChildDoc->mParentDoc = this; + return true; + } + + /* + * Called when the document in the content process this object represents + * notifies the main process a child document has been removed. + */ + void RemoveChildDoc(DocAccessibleParent* aChildDoc) + { + aChildDoc->mParent->SetChildDoc(nullptr); + mChildDocs.RemoveElement(aChildDoc); + aChildDoc->mParentDoc = nullptr; + MOZ_ASSERT(aChildDoc->mChildDocs.Length() == 0); + } + + void RemoveAccessible(ProxyAccessible* aAccessible) + { + MOZ_ASSERT(mAccessibles.GetEntry(aAccessible->ID())); + mAccessibles.RemoveEntry(aAccessible->ID()); + } + +private: + + class ProxyEntry : public PLDHashEntryHdr + { + public: + ProxyEntry(const void*) : mProxy(nullptr) {} + ProxyEntry(ProxyEntry&& aOther) : + mProxy(aOther.mProxy) { aOther.mProxy = nullptr; } + ~ProxyEntry() { delete mProxy; } + + typedef uint64_t KeyType; + typedef const void* KeyTypePointer; + + bool KeyEquals(const void* aKey) const + { return mProxy->ID() == (uint64_t)aKey; } + + static const void* KeyToPointer(uint64_t aKey) { return (void*)aKey; } + + static PLDHashNumber HashKey(const void* aKey) { return (uint64_t)aKey; } + + enum { ALLOW_MEMMOVE = true }; + + ProxyAccessible* mProxy; + }; + + uint32_t AddSubtree(ProxyAccessible* aParent, + const nsTArray& aNewTree, uint32_t aIdx, + uint32_t aIdxInParent); + + nsTArray mChildDocs; + DocAccessibleParent* mParentDoc; + + /* + * Conceptually this is a map from IDs to proxies, but we store the ID in the + * proxy object so we can't use a real map. + */ + nsTHashtable mAccessibles; +}; + +} +} + +#endif diff --git a/accessible/ipc/PDocAccessible.ipdl b/accessible/ipc/PDocAccessible.ipdl new file mode 100644 index 000000000000..2c272e1374bd --- /dev/null +++ b/accessible/ipc/PDocAccessible.ipdl @@ -0,0 +1,47 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +include protocol PContent; + +namespace mozilla { +namespace a11y { + +struct AccessibleData +{ + uint64_t ID; + uint32_t Role; + uint32_t ChildrenCount; + nsString Name; +}; + +struct ShowEventData +{ + uint64_t ID; + uint32_t Idx; + AccessibleData[] NewTree; +}; + +prio(normal upto high) sync protocol PDocAccessible +{ + manager PContent; + +parent: + __delete__(); + + /* + * Notify the parent process the document in the child process is firing an + * event. + */ + Event(uint32_t type); + ShowEvent(ShowEventData data); + HideEvent(uint64_t aRootID); + +child: + prio(high) sync State(uint64_t aID) returns(uint64_t states); +}; + +} +} diff --git a/accessible/ipc/ProxyAccessible.cpp b/accessible/ipc/ProxyAccessible.cpp new file mode 100644 index 000000000000..62558305df85 --- /dev/null +++ b/accessible/ipc/ProxyAccessible.cpp @@ -0,0 +1,51 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ProxyAccessible.h" +#include "DocAccessibleParent.h" +#include "mozilla/unused.h" +#include "mozilla/a11y/Platform.h" + +namespace mozilla { +namespace a11y { + +void +ProxyAccessible::Shutdown() +{ + MOZ_ASSERT(!mOuterDoc); + + uint32_t childCount = mChildren.Length(); + for (uint32_t idx = 0; idx < childCount; idx++) + mChildren[idx]->Shutdown(); + + mChildren.Clear(); + ProxyDestroyed(this); + mDoc->RemoveAccessible(this); +} + +void +ProxyAccessible::SetChildDoc(DocAccessibleParent* aParent) +{ + if (aParent) { + MOZ_ASSERT(mChildren.IsEmpty()); + mChildren.AppendElement(aParent); + mOuterDoc = true; + } else { + MOZ_ASSERT(mChildren.Length() == 1); + mChildren.Clear(); + mOuterDoc = false; + } +} + +uint64_t +ProxyAccessible::State() const +{ + uint64_t state = 0; + unused << mDoc->SendState(mID, &state); + return state; +} +} +} diff --git a/accessible/ipc/ProxyAccessible.h b/accessible/ipc/ProxyAccessible.h new file mode 100644 index 000000000000..373d21c0db5b --- /dev/null +++ b/accessible/ipc/ProxyAccessible.h @@ -0,0 +1,94 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_a11y_ProxyAccessible_h +#define mozilla_a11y_ProxyAccessible_h + +#include "mozilla/a11y/Role.h" +#include "nsString.h" +#include "nsTArray.h" + +namespace mozilla { +namespace a11y { + +class DocAccessibleParent; + +class ProxyAccessible +{ +public: + + ProxyAccessible(uint64_t aID, ProxyAccessible* aParent, + DocAccessibleParent* aDoc, role aRole, + const nsString& aName) : + mParent(aParent), mDoc(aDoc), mID(aID), mRole(aRole), mOuterDoc(false), mName(aName) + { + MOZ_COUNT_CTOR(ProxyAccessible); + } + ~ProxyAccessible() { MOZ_COUNT_DTOR(ProxyAccessible); } + + void AddChildAt(uint32_t aIdx, ProxyAccessible* aChild) + { mChildren.InsertElementAt(aIdx, aChild); } + + uint32_t ChildrenCount() const { return mChildren.Length(); } + + void Shutdown(); + + void SetChildDoc(DocAccessibleParent*); + + /** + * Remove The given child. + */ + void RemoveChild(ProxyAccessible* aChild) + { mChildren.RemoveElement(aChild); } + + /** + * Return the proxy for the parent of the wrapped accessible. + */ + ProxyAccessible* Parent() const { return mParent; } + + /** + * Get the role of the accessible we're proxying. + */ + role Role() const { return mRole; } + + /* + * Return the states for the proxied accessible. + */ + uint64_t State() const; + + /** + * Allow the platform to store a pointers worth of data on us. + */ + uintptr_t GetWrapper() const { return mWrapper; } + void SetWrapper(uintptr_t aWrapper) { mWrapper = aWrapper; } + + /* + * Return the ID of the accessible being proxied. + */ + uint64_t ID() const { return mID; } + +protected: + ProxyAccessible() : + mParent(nullptr), mDoc(nullptr), mWrapper(0), mID(0) + { MOZ_COUNT_CTOR(ProxyAccessible); } + +protected: + ProxyAccessible* mParent; + +private: + nsTArray mChildren; + DocAccessibleParent* mDoc; + uintptr_t mWrapper; + uint64_t mID; + role mRole : 31; + bool mOuterDoc : 1; + nsString mName; +}; + +} +} + +#endif diff --git a/accessible/ipc/moz.build b/accessible/ipc/moz.build new file mode 100644 index 000000000000..340e79bf2984 --- /dev/null +++ b/accessible/ipc/moz.build @@ -0,0 +1,31 @@ +# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +IPDL_SOURCES += ['PDocAccessible.ipdl'] + +# with --disable-accessibility we need to compile PDocAccessible.ipdl, but not +# the C++. +if CONFIG['ACCESSIBILITY']: + EXPORTS.mozilla.a11y += [ + 'DocAccessibleChild.h', + 'DocAccessibleParent.h', + 'ProxyAccessible.h' + ] + + SOURCES += [ + 'DocAccessibleChild.cpp', + 'DocAccessibleParent.cpp', + 'ProxyAccessible.cpp' + ] + + LOCAL_INCLUDES += [ + '../base', + '../generic', + ] + + FINAL_LIBRARY = 'xul' + +include('/ipc/chromium/chromium-config.mozbuild') diff --git a/accessible/mac/Platform.mm b/accessible/mac/Platform.mm index 98bbac02a9b9..4d82f2c5e836 100644 --- a/accessible/mac/Platform.mm +++ b/accessible/mac/Platform.mm @@ -33,6 +33,15 @@ PlatformShutdown() { } +void +ProxyCreated(ProxyAccessible*) +{ +} + +void +ProxyDestroyed(ProxyAccessible*) +{ +} } } diff --git a/accessible/moz.build b/accessible/moz.build index 44f345780018..993d2202937f 100644 --- a/accessible/moz.build +++ b/accessible/moz.build @@ -15,7 +15,7 @@ elif toolkit == 'cocoa': else: DIRS += ['other'] -DIRS += ['base', 'generic', 'html', 'interfaces', 'jsat', 'xpcom'] +DIRS += ['base', 'generic', 'html', 'interfaces', 'ipc', 'jsat', 'xpcom'] if CONFIG['MOZ_XUL']: DIRS += ['xul'] diff --git a/accessible/other/Platform.cpp b/accessible/other/Platform.cpp index 87c9fc6071f8..768f687c738c 100644 --- a/accessible/other/Platform.cpp +++ b/accessible/other/Platform.cpp @@ -18,3 +18,13 @@ void a11y::PlatformShutdown() { } + +void +a11y::ProxyCreated(ProxyAccessible*) +{ +} + +void +a11y::ProxyDestroyed(ProxyAccessible*) +{ +} diff --git a/accessible/windows/msaa/Platform.cpp b/accessible/windows/msaa/Platform.cpp index 263eb1a4d130..0ed1f5ffc073 100644 --- a/accessible/windows/msaa/Platform.cpp +++ b/accessible/windows/msaa/Platform.cpp @@ -34,3 +34,12 @@ a11y::PlatformShutdown() nsWinUtils::ShutdownWindowEmulation(); } +void +a11y::ProxyCreated(ProxyAccessible*) +{ +} + +void +a11y::ProxyDestroyed(ProxyAccessible*) +{ +} diff --git a/b2g/app/b2g.js b/b2g/app/b2g.js index cd00eea1580d..09c7467ec12c 100644 --- a/b2g/app/b2g.js +++ b/b2g/app/b2g.js @@ -851,7 +851,6 @@ pref("memory.system_memory_reporter", true); // Don't dump memory reports on OOM, by default. pref("memory.dump_reports_on_oom", false); -pref("layout.imagevisibility.enabled", true); pref("layout.imagevisibility.numscrollportwidths", 1); pref("layout.imagevisibility.numscrollportheights", 1); diff --git a/b2g/config/dolphin/sources.xml b/b2g/config/dolphin/sources.xml index 9bb3020d6482..2faa707cb104 100644 --- a/b2g/config/dolphin/sources.xml +++ b/b2g/config/dolphin/sources.xml @@ -15,15 +15,15 @@ - + - + - + diff --git a/b2g/config/emulator-ics/sources.xml b/b2g/config/emulator-ics/sources.xml index 9dff27e984ad..6574e54bdb9d 100644 --- a/b2g/config/emulator-ics/sources.xml +++ b/b2g/config/emulator-ics/sources.xml @@ -15,17 +15,17 @@ - + - - + + - + diff --git a/b2g/config/emulator-jb/sources.xml b/b2g/config/emulator-jb/sources.xml index 7cdf0d20b877..252063b5ae92 100644 --- a/b2g/config/emulator-jb/sources.xml +++ b/b2g/config/emulator-jb/sources.xml @@ -17,10 +17,10 @@ - - + + - + diff --git a/b2g/config/emulator-kk/sources.xml b/b2g/config/emulator-kk/sources.xml index 941e0f673e9d..a6f2f10ee85d 100644 --- a/b2g/config/emulator-kk/sources.xml +++ b/b2g/config/emulator-kk/sources.xml @@ -15,15 +15,15 @@ - + - + - + diff --git a/b2g/config/emulator/sources.xml b/b2g/config/emulator/sources.xml index 9dff27e984ad..6574e54bdb9d 100644 --- a/b2g/config/emulator/sources.xml +++ b/b2g/config/emulator/sources.xml @@ -15,17 +15,17 @@ - + - - + + - + diff --git a/b2g/config/flame-kk/sources.xml b/b2g/config/flame-kk/sources.xml index 95ed1ece48b9..4984b7661ca2 100644 --- a/b2g/config/flame-kk/sources.xml +++ b/b2g/config/flame-kk/sources.xml @@ -15,15 +15,15 @@ - + - + - + diff --git a/b2g/config/flame/sources.xml b/b2g/config/flame/sources.xml index dea7db0fc6f1..7825548a5bfc 100644 --- a/b2g/config/flame/sources.xml +++ b/b2g/config/flame/sources.xml @@ -17,10 +17,10 @@ - - + + - + diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json index 801169fce7d6..5d174a4da46e 100644 --- a/b2g/config/gaia.json +++ b/b2g/config/gaia.json @@ -4,6 +4,6 @@ "remote": "", "branch": "" }, - "revision": "2bf3274b9e149c6a0ffc13be4c7d3a2f7236e311", + "revision": "d3b2bb39fd5873ff8d537a3267d21d20491c3d1d", "repo_path": "/integration/gaia-central" } diff --git a/b2g/config/hamachi/sources.xml b/b2g/config/hamachi/sources.xml index db3878b05253..2f5df7275b98 100644 --- a/b2g/config/hamachi/sources.xml +++ b/b2g/config/hamachi/sources.xml @@ -13,16 +13,16 @@ - + - - + + - + diff --git a/b2g/config/helix/sources.xml b/b2g/config/helix/sources.xml index b87474e57f7c..10b485e46b20 100644 --- a/b2g/config/helix/sources.xml +++ b/b2g/config/helix/sources.xml @@ -11,12 +11,12 @@ - + - - + + diff --git a/b2g/config/nexus-4/sources.xml b/b2g/config/nexus-4/sources.xml index adb7ef6886b5..73fe2aac70ac 100644 --- a/b2g/config/nexus-4/sources.xml +++ b/b2g/config/nexus-4/sources.xml @@ -17,10 +17,10 @@ - - + + - + diff --git a/b2g/config/wasabi/sources.xml b/b2g/config/wasabi/sources.xml index fc8e21913870..4148a0138eaa 100644 --- a/b2g/config/wasabi/sources.xml +++ b/b2g/config/wasabi/sources.xml @@ -13,16 +13,16 @@ - + - - + + - + diff --git a/browser/base/content/content.js b/browser/base/content/content.js index 350210cec317..de0a89dba55a 100644 --- a/browser/base/content/content.js +++ b/browser/base/content/content.js @@ -86,7 +86,7 @@ addEventListener("blur", function(event) { }); if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) { - addEventListener("contextmenu", function (event) { + let handleContentContextMenu = function (event) { let defaultPrevented = event.defaultPrevented; if (!Services.prefs.getBoolPref("dom.event.contextmenu.enabled")) { let plugin = null; @@ -112,7 +112,12 @@ if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) { sendSyncMessage("contextmenu", { editFlags, spellInfo }, { event }); } - }, false); + } + + Cc["@mozilla.org/eventlistenerservice;1"] + .getService(Ci.nsIEventListenerService) + .addSystemEventListener(global, "contextmenu", handleContentContextMenu, true); + } else { addEventListener("mozUITour", function(event) { if (!Services.prefs.getBoolPref("browser.uitour.enabled")) diff --git a/browser/installer/Makefile.in b/browser/installer/Makefile.in index ffb5c647689f..38e95dfbdf85 100644 --- a/browser/installer/Makefile.in +++ b/browser/installer/Makefile.in @@ -120,6 +120,9 @@ BINPATH = bin endif DEFINES += -DBINPATH=$(BINPATH) +AB = $(firstword $(subst -, ,$(AB_CD))) +DEFINES += -DAB=$(AB) + DEFINES += -DMOZ_ICU_VERSION=$(MOZ_ICU_VERSION) ifdef MOZ_NATIVE_ICU DEFINES += -DMOZ_NATIVE_ICU diff --git a/browser/installer/package-manifest.in b/browser/installer/package-manifest.in index 03290e42a8bf..13351a828032 100644 --- a/browser/installer/package-manifest.in +++ b/browser/installer/package-manifest.in @@ -25,7 +25,7 @@ @APPNAME@/Contents/PkgInfo @APPNAME@/Contents/Resources/firefox.icns @APPNAME@/Contents/Resources/document.icns -@APPNAME@/Contents/Resources/en.lproj/* +@APPNAME@/Contents/Resources/@AB@.lproj/* #endif [@AB_CD@] diff --git a/browser/metro/profile/metro.js b/browser/metro/profile/metro.js index 836b6496ecef..97e7938c2243 100644 --- a/browser/metro/profile/metro.js +++ b/browser/metro/profile/metro.js @@ -671,7 +671,6 @@ pref("full-screen-api.ignore-widgets", true); // image visibility prefs. // image visibility tries to only keep images near the viewport decoded instead // of keeping all images decoded. -pref("layout.imagevisibility.enabled", true); pref("layout.imagevisibility.numscrollportwidths", 1); pref("layout.imagevisibility.numscrollportheights", 1); diff --git a/content/base/public/nsContentPolicyUtils.h b/content/base/public/nsContentPolicyUtils.h index 448d49b8d7db..84624da5f656 100644 --- a/content/base/public/nsContentPolicyUtils.h +++ b/content/base/public/nsContentPolicyUtils.h @@ -111,6 +111,7 @@ NS_CP_ContentTypeName(uint32_t contentType) CASE_RETURN( TYPE_CSP_REPORT ); CASE_RETURN( TYPE_XSLT ); CASE_RETURN( TYPE_BEACON ); + CASE_RETURN( TYPE_FETCH ); default: return ""; } diff --git a/content/base/public/nsIContentPolicy.idl b/content/base/public/nsIContentPolicy.idl index f57e8e8fa03e..df3fed555332 100644 --- a/content/base/public/nsIContentPolicy.idl +++ b/content/base/public/nsIContentPolicy.idl @@ -24,7 +24,7 @@ typedef unsigned long nsContentPolicyType; * by launching a dialog to prompt the user for something). */ -[scriptable,uuid(b6a71698-c117-441d-86b9-480cf06e3952)] +[scriptable,uuid(8afe3e5c-f916-48fd-8075-9579d3502e1d)] interface nsIContentPolicy : nsISupports { /** @@ -150,6 +150,12 @@ interface nsIContentPolicy : nsISupports */ const nsContentPolicyType TYPE_BEACON = 19; + /** + * Indicates a load initiated by the fetch() function from the Fetch + * specification. + */ + const nsContentPolicyType TYPE_FETCH = 20; + /* When adding new content types, please update nsContentBlocker, * NS_CP_ContentTypeName, nsCSPContext, all nsIContentPolicy * implementations, and other things that are not listed here that are diff --git a/content/base/src/nsCSPUtils.cpp b/content/base/src/nsCSPUtils.cpp index b381cefc7dc5..bc0cf3d0e9d7 100644 --- a/content/base/src/nsCSPUtils.cpp +++ b/content/base/src/nsCSPUtils.cpp @@ -709,6 +709,7 @@ CSP_ContentTypeToDirective(nsContentPolicyType aType) case nsIContentPolicy::TYPE_WEBSOCKET: case nsIContentPolicy::TYPE_XMLHTTPREQUEST: case nsIContentPolicy::TYPE_BEACON: + case nsIContentPolicy::TYPE_FETCH: return CSP_CONNECT_SRC; case nsIContentPolicy::TYPE_OBJECT: diff --git a/content/base/src/nsDOMFileReader.cpp b/content/base/src/nsDOMFileReader.cpp index b23c2b469354..fbeac9e9885e 100644 --- a/content/base/src/nsDOMFileReader.cpp +++ b/content/base/src/nsDOMFileReader.cpp @@ -117,7 +117,7 @@ nsDOMFileReader::Constructor(const GlobalObject& aGlobal, ErrorResult& aRv) nsCOMPtr owner = do_QueryInterface(aGlobal.GetAsSupports()); if (!owner) { - NS_WARNING("Unexpected nsIJSNativeInitializer owner"); + NS_WARNING("Unexpected owner"); aRv.Throw(NS_ERROR_FAILURE); return nullptr; } diff --git a/content/base/src/nsDOMFileReader.h b/content/base/src/nsDOMFileReader.h index 33af0c8caf58..71b2e6e18070 100644 --- a/content/base/src/nsDOMFileReader.h +++ b/content/base/src/nsDOMFileReader.h @@ -14,7 +14,6 @@ #include "nsIInterfaceRequestor.h" #include "nsJSUtils.h" #include "nsTArray.h" -#include "nsIJSNativeInitializer.h" #include "prtime.h" #include "nsITimer.h" #include "nsIAsyncInputStream.h" diff --git a/content/base/src/nsDataDocumentContentPolicy.cpp b/content/base/src/nsDataDocumentContentPolicy.cpp index 06b84ca5f6c8..6695006a8d38 100644 --- a/content/base/src/nsDataDocumentContentPolicy.cpp +++ b/content/base/src/nsDataDocumentContentPolicy.cpp @@ -120,7 +120,8 @@ nsDataDocumentContentPolicy::ShouldLoad(uint32_t aContentType, aContentType == nsIContentPolicy::TYPE_DOCUMENT || aContentType == nsIContentPolicy::TYPE_SUBDOCUMENT || aContentType == nsIContentPolicy::TYPE_SCRIPT || - aContentType == nsIContentPolicy::TYPE_XSLT) { + aContentType == nsIContentPolicy::TYPE_XSLT || + aContentType == nsIContentPolicy::TYPE_FETCH) { *aDecision = nsIContentPolicy::REJECT_TYPE; } diff --git a/content/base/src/nsMixedContentBlocker.cpp b/content/base/src/nsMixedContentBlocker.cpp index 0c9f37ed4bb6..0ddbb8aa6dac 100644 --- a/content/base/src/nsMixedContentBlocker.cpp +++ b/content/base/src/nsMixedContentBlocker.cpp @@ -383,6 +383,7 @@ nsMixedContentBlocker::ShouldLoad(uint32_t aContentType, // purposes and to avoid the assertion and warning for the default case. case TYPE_CSP_REPORT: case TYPE_DTD: + case TYPE_FETCH: case TYPE_FONT: case TYPE_OBJECT: case TYPE_SCRIPT: diff --git a/content/html/content/src/HTMLOptionElement.h b/content/html/content/src/HTMLOptionElement.h index bfba20439ac9..74dbe1368bcb 100644 --- a/content/html/content/src/HTMLOptionElement.h +++ b/content/html/content/src/HTMLOptionElement.h @@ -10,7 +10,6 @@ #include "mozilla/Attributes.h" #include "nsGenericHTMLElement.h" #include "nsIDOMHTMLOptionElement.h" -#include "nsIJSNativeInitializer.h" #include "mozilla/dom/HTMLFormElement.h" namespace mozilla { diff --git a/content/media/gmp/GMPParent.cpp b/content/media/gmp/GMPParent.cpp index 277fa7b5db0f..7a25c5bb8233 100644 --- a/content/media/gmp/GMPParent.cpp +++ b/content/media/gmp/GMPParent.cpp @@ -174,6 +174,49 @@ GMPParent::LoadProcess() return NS_OK; } +void +AbortWaitingForGMPAsyncShutdown(nsITimer* aTimer, void* aClosure) +{ + NS_WARNING("Timed out waiting for GMP async shutdown!"); + GMPParent* parent = reinterpret_cast(aClosure); + nsRefPtr service = + GeckoMediaPluginService::GetGeckoMediaPluginService(); + if (service) { + service->AsyncShutdownComplete(parent); + } +} + +nsresult +GMPParent::EnsureAsyncShutdownTimeoutSet() +{ + if (mAsyncShutdownTimeout) { + return NS_OK; + } + + nsresult rv; + mAsyncShutdownTimeout = do_CreateInstance(NS_TIMER_CONTRACTID, &rv); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // Set timer to abort waiting for plugin to shutdown if it takes + // too long. + rv = mAsyncShutdownTimeout->SetTarget(mGMPThread); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + int32_t timeout = GMP_DEFAULT_ASYNC_SHUTDONW_TIMEOUT; + nsRefPtr service = + GeckoMediaPluginService::GetGeckoMediaPluginService(); + if (service) { + timeout = service->AsyncShutdownTimeoutMs(); + } + return mAsyncShutdownTimeout->InitWithFuncCallback( + &AbortWaitingForGMPAsyncShutdown, this, timeout, + nsITimer::TYPE_ONE_SHOT); +} + void GMPParent::CloseIfUnused() { @@ -199,7 +242,8 @@ GMPParent::CloseIfUnused() LOGD(("%s::%s: %p sending async shutdown notification", __CLASS__, __FUNCTION__, this)); mAsyncShutdownInProgress = true; - if (!SendBeginAsyncShutdown()) { + if (!SendBeginAsyncShutdown() || + NS_FAILED(EnsureAsyncShutdownTimeoutSet())) { AbortAsyncShutdown(); } } @@ -219,6 +263,11 @@ GMPParent::AbortAsyncShutdown() MOZ_ASSERT(GMPThread() == NS_GetCurrentThread()); LOGD(("%s::%s: %p", __CLASS__, __FUNCTION__, this)); + if (mAsyncShutdownTimeout) { + mAsyncShutdownTimeout->Cancel(); + mAsyncShutdownTimeout = nullptr; + } + if (!mAsyncShutdownRequired || !mAsyncShutdownInProgress) { return; } @@ -289,6 +338,8 @@ GMPParent::Shutdown() LOGD(("%s::%s: %p", __CLASS__, __FUNCTION__, this)); MOZ_ASSERT(GMPThread() == NS_GetCurrentThread()); + MOZ_ASSERT(!mAsyncShutdownTimeout, "Should have canceled shutdown timeout"); + if (mAbnormalShutdownInProgress) { return; } diff --git a/content/media/gmp/GMPParent.h b/content/media/gmp/GMPParent.h index cb3c7cca738a..346f026fc1e6 100644 --- a/content/media/gmp/GMPParent.h +++ b/content/media/gmp/GMPParent.h @@ -170,6 +170,8 @@ private: virtual bool RecvAsyncShutdownComplete() MOZ_OVERRIDE; virtual bool RecvAsyncShutdownRequired() MOZ_OVERRIDE; + nsresult EnsureAsyncShutdownTimeoutSet(); + GMPState mState; nsCOMPtr mDirectory; // plugin directory on disk nsString mName; // base name of plugin on disk, UTF-16 because used for paths @@ -188,6 +190,7 @@ private: nsTArray> mTimers; nsTArray> mStorage; nsCOMPtr mGMPThread; + nsCOMPtr mAsyncShutdownTimeout; // GMP Thread only. // NodeId the plugin is assigned to, or empty if the the plugin is not // assigned to a NodeId. nsAutoCString mNodeId; diff --git a/content/media/gmp/GMPService.cpp b/content/media/gmp/GMPService.cpp index d73f9f4395e7..4172fc5042c2 100644 --- a/content/media/gmp/GMPService.cpp +++ b/content/media/gmp/GMPService.cpp @@ -136,8 +136,8 @@ GeckoMediaPluginService::GetGeckoMediaPluginService() NS_IMPL_ISUPPORTS(GeckoMediaPluginService, mozIGeckoMediaPluginService, nsIObserver) -#define GMP_DEFAULT_ASYNC_SHUTDONW_TIMEOUT 3000 static int32_t sMaxAsyncShutdownWaitMs = 0; +static bool sHaveSetTimeoutPrefCache = false; GeckoMediaPluginService::GeckoMediaPluginService() : mMutex("GeckoMediaPluginService::mMutex") @@ -147,9 +147,8 @@ GeckoMediaPluginService::GeckoMediaPluginService() , mWaitingForPluginsAsyncShutdown(false) { MOZ_ASSERT(NS_IsMainThread()); - static bool setTimeoutPrefCache = false; - if (!setTimeoutPrefCache) { - setTimeoutPrefCache = true; + if (!sHaveSetTimeoutPrefCache) { + sHaveSetTimeoutPrefCache = true; Preferences::AddIntVarCache(&sMaxAsyncShutdownWaitMs, "media.gmp.async-shutdown-timeout", GMP_DEFAULT_ASYNC_SHUTDONW_TIMEOUT); @@ -162,6 +161,13 @@ GeckoMediaPluginService::~GeckoMediaPluginService() MOZ_ASSERT(mAsyncShutdownPlugins.IsEmpty()); } +int32_t +GeckoMediaPluginService::AsyncShutdownTimeoutMs() +{ + MOZ_ASSERT(sHaveSetTimeoutPrefCache); + return sMaxAsyncShutdownWaitMs; +} + nsresult GeckoMediaPluginService::Init() { @@ -205,16 +211,6 @@ GeckoMediaPluginService::Init() return GetThread(getter_AddRefs(thread)); } -void -AbortWaitingForGMPAsyncShutdown(nsITimer* aTimer, void* aClosure) -{ - NS_WARNING("Timed out waiting for GMP async shutdown!"); - nsRefPtr service = sSingletonService.get(); - if (service) { - service->AbortAsyncShutdown(); - } -} - NS_IMETHODIMP GeckoMediaPluginService::Observe(nsISupports* aSubject, const char* aTopic, @@ -533,9 +529,11 @@ GeckoMediaPluginService::AsyncShutdownComplete(GMPParent* aParent) mAsyncShutdownPlugins.RemoveElement(aParent); if (mAsyncShutdownPlugins.IsEmpty() && mShuttingDownOnGMPThread) { - // The main thread is waiting for async shutdown of plugins, + // The main thread may be waiting for async shutdown of plugins, // which has completed. Break the main thread out of its waiting loop. - AbortAsyncShutdown(); + nsRefPtr task(NS_NewRunnableMethod( + this, &GeckoMediaPluginService::SetAsyncShutdownComplete)); + NS_DispatchToMainThread(task); } } @@ -546,47 +544,6 @@ GeckoMediaPluginService::SetAsyncShutdownComplete() mWaitingForPluginsAsyncShutdown = false; } -void -GeckoMediaPluginService::AbortAsyncShutdown() -{ - MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread); - for (size_t i = 0; i < mAsyncShutdownPlugins.Length(); i++) { - mAsyncShutdownPlugins[i]->AbortAsyncShutdown(); - } - mAsyncShutdownPlugins.Clear(); - if (mAsyncShutdownTimeout) { - mAsyncShutdownTimeout->Cancel(); - mAsyncShutdownTimeout = nullptr; - } - nsRefPtr task(NS_NewRunnableMethod( - this, &GeckoMediaPluginService::SetAsyncShutdownComplete)); - NS_DispatchToMainThread(task); -} - -nsresult -GeckoMediaPluginService::SetAsyncShutdownTimeout() -{ - MOZ_ASSERT(!mAsyncShutdownTimeout); - - nsresult rv; - mAsyncShutdownTimeout = do_CreateInstance(NS_TIMER_CONTRACTID, &rv); - if (NS_FAILED(rv)) { - NS_WARNING("Failed to create timer for async GMP shutdown"); - return NS_OK; - } - - // Set timer to abort waiting for plugins to shutdown if they take - // too long. - rv = mAsyncShutdownTimeout->SetTarget(mGMPThread); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - return mAsyncShutdownTimeout->InitWithFuncCallback( - &AbortWaitingForGMPAsyncShutdown, nullptr, sMaxAsyncShutdownWaitMs, - nsITimer::TYPE_ONE_SHOT); -} - void GeckoMediaPluginService::UnloadPlugins() { @@ -607,16 +564,7 @@ GeckoMediaPluginService::UnloadPlugins() mPlugins.Clear(); } - if (!mAsyncShutdownPlugins.IsEmpty()) { - // We have plugins that require async shutdown. Set a timer to abort - // waiting if they take too long to shutdown. - if (NS_FAILED(SetAsyncShutdownTimeout())) { - mAsyncShutdownPlugins.Clear(); - } - } - if (mAsyncShutdownPlugins.IsEmpty()) { - mAsyncShutdownPlugins.Clear(); nsRefPtr task(NS_NewRunnableMethod( this, &GeckoMediaPluginService::SetAsyncShutdownComplete)); NS_DispatchToMainThread(task); diff --git a/content/media/gmp/GMPService.h b/content/media/gmp/GMPService.h index 6666aca0d8aa..fb0a98a0dc46 100644 --- a/content/media/gmp/GMPService.h +++ b/content/media/gmp/GMPService.h @@ -28,6 +28,8 @@ namespace gmp { class GMPParent; +#define GMP_DEFAULT_ASYNC_SHUTDONW_TIMEOUT 3000 + class GeckoMediaPluginService MOZ_FINAL : public mozIGeckoMediaPluginService , public nsIObserver { @@ -45,6 +47,8 @@ public: void AsyncShutdownComplete(GMPParent* aParent); void AbortAsyncShutdown(); + int32_t AsyncShutdownTimeoutMs(); + private: ~GeckoMediaPluginService(); @@ -122,7 +126,6 @@ private: MainThreadOnly mWaitingForPluginsAsyncShutdown; nsTArray> mAsyncShutdownPlugins; // GMP Thread only. - nsCOMPtr mAsyncShutdownTimeout; // GMP Thread only. #ifndef MOZ_WIDGET_GONK nsCOMPtr mStorageBaseDir; diff --git a/dom/base/moz.build b/dom/base/moz.build index 1b6e1b3e1e73..d8f05969df65 100644 --- a/dom/base/moz.build +++ b/dom/base/moz.build @@ -29,7 +29,6 @@ EXPORTS += [ 'nsIDOMClassInfo.h', 'nsIDOMScriptObjectFactory.h', 'nsIGlobalObject.h', - 'nsIJSNativeInitializer.h', 'nsIScriptContext.h', 'nsIScriptGlobalObject.h', 'nsIScriptNameSpaceManager.h', diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp index d0498443d204..8123001796f0 100644 --- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -50,7 +50,6 @@ // Window scriptable helper includes #include "nsScriptNameSpaceManager.h" -#include "nsIJSNativeInitializer.h" // DOM base includes #include "nsIDOMWindow.h" @@ -1397,9 +1396,8 @@ BaseStubConstructor(nsIWeakReference* aWeakOwner, return rv; } - nsCOMPtr initializer(do_QueryInterface(native)); nsCOMPtr constructor(do_QueryInterface(native)); - if (initializer || constructor) { + if (constructor) { // Initialize object using the current inner window, but only if // the caller can access it. nsCOMPtr owner = do_QueryReferent(aWeakOwner); @@ -1412,54 +1410,47 @@ BaseStubConstructor(nsIWeakReference* aWeakOwner, return NS_ERROR_DOM_SECURITY_ERR; } - if (initializer) { - rv = initializer->Initialize(currentInner, cx, obj, args); - if (NS_FAILED(rv)) { - return rv; - } - } else { - nsCOMPtr wrappedJS = do_QueryInterface(native); + nsCOMPtr wrappedJS = do_QueryInterface(native); - JS::Rooted thisObject(cx, wrappedJS->GetJSObject()); - if (!thisObject) { - return NS_ERROR_UNEXPECTED; + JS::Rooted thisObject(cx, wrappedJS->GetJSObject()); + if (!thisObject) { + return NS_ERROR_UNEXPECTED; + } + + JSAutoCompartment ac(cx, thisObject); + + JS::Rooted funval(cx); + if (!JS_GetProperty(cx, thisObject, "constructor", &funval) || + !funval.isObject()) { + return NS_ERROR_UNEXPECTED; + } + + // Check if the object is even callable. + NS_ENSURE_STATE(JS::IsCallable(&funval.toObject())); + { + // wrap parameters in the target compartment + // we also pass in the calling window as the first argument + unsigned argc = args.length() + 1; + JS::AutoValueVector argv(cx); + if (!argv.resize(argc)) { + return NS_ERROR_OUT_OF_MEMORY; } - JSAutoCompartment ac(cx, thisObject); + nsCOMPtr currentWin(do_GetInterface(currentInner)); + rv = WrapNative(cx, currentWin, &NS_GET_IID(nsIDOMWindow), + true, argv[0]); - JS::Rooted funval(cx); - if (!JS_GetProperty(cx, thisObject, "constructor", &funval) || - !funval.isObject()) { - return NS_ERROR_UNEXPECTED; - } - - // Check if the object is even callable. - NS_ENSURE_STATE(JS::IsCallable(&funval.toObject())); - { - // wrap parameters in the target compartment - // we also pass in the calling window as the first argument - unsigned argc = args.length() + 1; - JS::AutoValueVector argv(cx); - if (!argv.resize(argc)) { - return NS_ERROR_OUT_OF_MEMORY; - } - - nsCOMPtr currentWin(do_GetInterface(currentInner)); - rv = WrapNative(cx, currentWin, &NS_GET_IID(nsIDOMWindow), - true, argv[0]); - - for (size_t i = 1; i < argc; ++i) { - argv[i].set(args[i - 1]); - if (!JS_WrapValue(cx, argv[i])) - return NS_ERROR_FAILURE; - } - - JS::Rooted frval(cx); - bool ret = JS_CallFunctionValue(cx, thisObject, funval, argv, &frval); - - if (!ret) { + for (size_t i = 1; i < argc; ++i) { + argv[i].set(args[i - 1]); + if (!JS_WrapValue(cx, argv[i])) return NS_ERROR_FAILURE; - } + } + + JS::Rooted frval(cx); + bool ret = JS_CallFunctionValue(cx, thisObject, funval, argv, &frval); + + if (!ret) { + return NS_ERROR_FAILURE; } } } diff --git a/dom/base/nsIJSNativeInitializer.h b/dom/base/nsIJSNativeInitializer.h deleted file mode 100644 index 4ecdb6cbf775..000000000000 --- a/dom/base/nsIJSNativeInitializer.h +++ /dev/null @@ -1,38 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef nsIJSNativeInitializer_h__ -#define nsIJSNativeInitializer_h__ - -#include "nsISupports.h" - -#define NS_IJSNATIVEINITIALIZER_IID \ -{ 0xdb48eee5, 0x89a4, 0x4f18, \ - { 0x86, 0xd0, 0x4c, 0x4e, 0x9d, 0x4b, 0xf8, 0x7e } } - - -/** - * A JavaScript specific interface used to initialize new - * native objects, created as a result of calling a - * JavaScript constructor. The arguments are passed in - * their raw form as JS::Value's. - */ - -class nsIJSNativeInitializer : public nsISupports { -public: - NS_DECLARE_STATIC_IID_ACCESSOR(NS_IJSNATIVEINITIALIZER_IID) - - /** - * Initialize a newly created native instance using the owner of the - * constructor and the parameters passed into the JavaScript constructor. - */ - NS_IMETHOD Initialize(nsISupports* aOwner, JSContext *cx, JSObject *obj, - const JS::CallArgs& args) = 0; -}; - -NS_DEFINE_STATIC_IID_ACCESSOR(nsIJSNativeInitializer, - NS_IJSNATIVEINITIALIZER_IID) - -#endif // nsIJSNativeInitializer_h__ diff --git a/dom/fetch/InternalRequest.cpp b/dom/fetch/InternalRequest.cpp index 077f7f8e23b1..46603f6d346f 100644 --- a/dom/fetch/InternalRequest.cpp +++ b/dom/fetch/InternalRequest.cpp @@ -45,10 +45,9 @@ InternalRequest::GetRequestConstructorCopy(nsIGlobalObject* aGlobal, ErrorResult copy->mOrigin = NS_ConvertUTF16toUTF8(location.mOrigin); } + copy->mContext = nsIContentPolicy::TYPE_FETCH; copy->mMode = mMode; copy->mCredentialsMode = mCredentialsMode; - // FIXME(nsm): Add ContentType fetch to nsIContentPolicy and friends. - // Then set copy's mContext to that. return copy.forget(); } diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp index bfd13920ad45..d5132dcb5aec 100644 --- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -19,6 +19,9 @@ #include "TabChild.h" #include "mozilla/Attributes.h" +#ifdef ACCESSIBILITY +#include "mozilla/a11y/DocAccessibleChild.h" +#endif #include "mozilla/Preferences.h" #include "mozilla/dom/ContentBridgeChild.h" #include "mozilla/dom/ContentBridgeParent.h" @@ -731,6 +734,22 @@ ContentChild::InitXPCOM() InitOnContentProcessCreated(); } +a11y::PDocAccessibleChild* +ContentChild::AllocPDocAccessibleChild(PDocAccessibleChild*, const uint64_t&) +{ + MOZ_ASSERT(false, "should never call this!"); + return nullptr; +} + +bool +ContentChild::DeallocPDocAccessibleChild(a11y::PDocAccessibleChild* aChild) +{ +#ifdef ACCESSIBILITY + delete static_cast(aChild); +#endif + return true; +} + PMemoryReportRequestChild* ContentChild::AllocPMemoryReportRequestChild(const uint32_t& aGeneration, const bool &aAnonymize, diff --git a/dom/ipc/ContentChild.h b/dom/ipc/ContentChild.h index dcf3dc638fed..a8bdf7a1303b 100644 --- a/dom/ipc/ContentChild.h +++ b/dom/ipc/ContentChild.h @@ -382,6 +382,8 @@ public: const uint64_t& aID, const bool& aIsForApp, const bool& aIsForBrowser) MOZ_OVERRIDE; + virtual PDocAccessibleChild* AllocPDocAccessibleChild(PDocAccessibleChild*, const uint64_t&) MOZ_OVERRIDE; + virtual bool DeallocPDocAccessibleChild(PDocAccessibleChild*) MOZ_OVERRIDE; void GetAvailableDictionaries(InfallibleTArray& aDictionaries); diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index d9bdcf1713c3..4f87332b34bf 100755 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -30,6 +30,10 @@ #include "CrashReporterParent.h" #include "IHistory.h" #include "mozIApplication.h" +#ifdef ACCESSIBILITY +#include "mozilla/a11y/DocAccessibleParent.h" +#include "nsAccessibilityService.h" +#endif #include "mozilla/ClearOnShutdown.h" #include "mozilla/dom/DataStoreService.h" #include "mozilla/dom/DOMStorageIPC.h" @@ -2706,6 +2710,42 @@ ContentParent::Observe(nsISupports* aSubject, return NS_OK; } + a11y::PDocAccessibleParent* +ContentParent::AllocPDocAccessibleParent(PDocAccessibleParent* aParent, const uint64_t&) +{ +#ifdef ACCESSIBILITY + return new a11y::DocAccessibleParent(); +#else + return nullptr; +#endif +} + +bool +ContentParent::DeallocPDocAccessibleParent(PDocAccessibleParent* aParent) +{ +#ifdef ACCESSIBILITY + delete static_cast(aParent); +#endif + return true; +} + +bool +ContentParent::RecvPDocAccessibleConstructor(PDocAccessibleParent* aDoc, PDocAccessibleParent* aParentDoc, const uint64_t& aParentID) +{ +#ifdef ACCESSIBILITY + auto doc = static_cast(aDoc); + if (aParentDoc) { + MOZ_ASSERT(aParentID); + auto parentDoc = static_cast(aParentDoc); + return parentDoc->AddChildDoc(doc, aParentID); + } else { + MOZ_ASSERT(!aParentID); + GetAccService()->RemoteDocAdded(doc); + } +#endif + return true; +} + PCompositorParent* ContentParent::AllocPCompositorParent(mozilla::ipc::Transport* aTransport, base::ProcessId aOtherProcess) diff --git a/dom/ipc/ContentParent.h b/dom/ipc/ContentParent.h index c38705e98149..b6bfefdc2de5 100644 --- a/dom/ipc/ContentParent.h +++ b/dom/ipc/ContentParent.h @@ -679,6 +679,11 @@ private: int32_t* aSliceRefCnt, bool* aResult) MOZ_OVERRIDE; + virtual PDocAccessibleParent* AllocPDocAccessibleParent(PDocAccessibleParent*, const uint64_t&) MOZ_OVERRIDE; + virtual bool DeallocPDocAccessibleParent(PDocAccessibleParent*) MOZ_OVERRIDE; + virtual bool RecvPDocAccessibleConstructor(PDocAccessibleParent* aDoc, + PDocAccessibleParent* aParentDoc, const uint64_t& aParentID) MOZ_OVERRIDE; + // If you add strong pointers to cycle collected objects here, be sure to // release these objects in ShutDownProcess. See the comment there for more // details. diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl index c8d3defa0767..cfb59d0a134b 100644 --- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -14,6 +14,7 @@ include protocol PCompositor; include protocol PContentBridge; include protocol PCycleCollectWithLogs; include protocol PCrashReporter; +include protocol PDocAccessible; include protocol PExternalHelperApp; include protocol PDeviceStorageRequest; include protocol PFileDescriptorSet; @@ -333,6 +334,7 @@ prio(normal upto high) intr protocol PContent manages PCellBroadcast; manages PCrashReporter; manages PCycleCollectWithLogs; + manages PDocAccessible; manages PDeviceStorageRequest; manages PFileSystemRequest; manages PExternalHelperApp; @@ -491,6 +493,14 @@ child: OnAppThemeChanged(); parent: + /** + * Tell the parent process a new accessible document has been created. + * aParentDoc is the accessible document it was created in if any, and + * aParentAcc is the id of the accessible in that document the new document + * is a child of. + */ + PDocAccessible(nullable PDocAccessible aParentDoc, uint64_t aParentAcc); + /** * Tell the content process some attributes of itself. This is * among the first information queried by content processes after diff --git a/dom/plugins/ipc/PluginHangUIParent.cpp b/dom/plugins/ipc/PluginHangUIParent.cpp index d41e5f48afb9..5e8e63d00b46 100644 --- a/dom/plugins/ipc/PluginHangUIParent.cpp +++ b/dom/plugins/ipc/PluginHangUIParent.cpp @@ -356,6 +356,7 @@ PluginHangUIParent::RecvUserResponse(const unsigned int& aResponse) mModule->TerminateChildProcess(mMainThreadMessageLoop); responseCode = 1; } else if(aResponse & HANGUI_USER_RESPONSE_CONTINUE) { + mModule->OnHangUIContinue(); // User clicked Continue responseCode = 2; } else { diff --git a/dom/plugins/ipc/PluginModuleParent.cpp b/dom/plugins/ipc/PluginModuleParent.cpp index 96329772addf..dd1b203afe07 100755 --- a/dom/plugins/ipc/PluginModuleParent.cpp +++ b/dom/plugins/ipc/PluginModuleParent.cpp @@ -133,6 +133,7 @@ PluginModuleParent::PluginModuleParent(const char* aFilePath) , mNPNIface(nullptr) , mPlugin(nullptr) , mTaskFactory(MOZ_THIS_IN_INITIALIZER_LIST()) + , mHangAnnotationFlags(0) #ifdef XP_WIN , mPluginCpuUsageOnHang() , mHangUIParent(nullptr) @@ -160,6 +161,8 @@ PluginModuleParent::PluginModuleParent(const char* aFilePath) #ifdef MOZ_ENABLE_PROFILER_SPS InitPluginProfiling(); #endif + + mozilla::HangMonitor::RegisterAnnotator(*this); } PluginModuleParent::~PluginModuleParent() @@ -203,6 +206,8 @@ PluginModuleParent::~PluginModuleParent() mHangUIParent = nullptr; } #endif + + mozilla::HangMonitor::UnregisterAnnotator(*this); } #ifdef MOZ_CRASHREPORTER @@ -224,20 +229,8 @@ PluginModuleParent::WriteExtraDataForMinidump(AnnotationTable& notes) filePos++; notes.Put(NS_LITERAL_CSTRING("PluginFilename"), CS(pluginFile.substr(filePos).c_str())); - nsCString pluginName; - nsCString pluginVersion; - - nsRefPtr ph = nsPluginHost::GetInst(); - if (ph) { - nsPluginTag* tag = ph->TagForPlugin(mPlugin); - if (tag) { - pluginName = tag->mName; - pluginVersion = tag->mVersion; - } - } - - notes.Put(NS_LITERAL_CSTRING("PluginName"), pluginName); - notes.Put(NS_LITERAL_CSTRING("PluginVersion"), pluginVersion); + notes.Put(NS_LITERAL_CSTRING("PluginName"), mPluginName); + notes.Put(NS_LITERAL_CSTRING("PluginVersion"), mPluginVersion); CrashReporterParent* crashReporter = CrashReporter(); if (crashReporter) { @@ -389,13 +382,52 @@ GetProcessCpuUsage(const InfallibleTArray& processHandles, } // anonymous namespace +#endif // #ifdef XP_WIN + +void +PluginModuleParent::EnteredCxxStack() +{ + mHangAnnotationFlags |= kInPluginCall; +} + void PluginModuleParent::ExitedCxxStack() { + mHangAnnotationFlags = 0; +#ifdef XP_WIN FinishHangUI(); +#endif } -#endif // #ifdef XP_WIN +/** + * This function is always called by the HangMonitor thread. + */ +void +PluginModuleParent::AnnotateHang(mozilla::HangMonitor::HangAnnotations& aAnnotations) +{ + uint32_t flags = mHangAnnotationFlags; + if (flags) { + /* We don't actually annotate anything specifically for kInPluginCall; + we use it to determine whether to annotate other things. It will + be pretty obvious from the ChromeHang stack that we're in a plugin + call when the hang occurred. */ + if (flags & kHangUIShown) { + aAnnotations.AddAnnotation(NS_LITERAL_STRING("HangUIShown"), + true); + } + if (flags & kHangUIContinued) { + aAnnotations.AddAnnotation(NS_LITERAL_STRING("HangUIContinued"), + true); + } + if (flags & kHangUIDontShow) { + aAnnotations.AddAnnotation(NS_LITERAL_STRING("HangUIDontShow"), + true); + } + aAnnotations.AddAnnotation(NS_LITERAL_STRING("pluginName"), mPluginName); + aAnnotations.AddAnnotation(NS_LITERAL_STRING("pluginVersion"), + mPluginVersion); + } +} #ifdef MOZ_CRASHREPORTER_INJECTOR static bool @@ -531,6 +563,23 @@ PluginModuleParent::TerminateChildProcess(MessageLoop* aMsgLoop) NS_WARNING("failed to kill subprocess!"); } +bool +PluginModuleParent::GetPluginDetails(nsACString& aPluginName, + nsACString& aPluginVersion) +{ + nsRefPtr host = nsPluginHost::GetInst(); + if (!host) { + return false; + } + nsPluginTag* pluginTag = host->TagForPlugin(mPlugin); + if (!pluginTag) { + return false; + } + aPluginName = pluginTag->mName; + aPluginVersion = pluginTag->mVersion; + return true; +} + #ifdef XP_WIN void PluginModuleParent::EvaluateHangUIState(const bool aReset) @@ -566,21 +615,6 @@ PluginModuleParent::EvaluateHangUIState(const bool aReset) SetChildTimeout(autoStopSecs); } -bool -PluginModuleParent::GetPluginName(nsAString& aPluginName) -{ - nsRefPtr host = nsPluginHost::GetInst(); - if (!host) { - return false; - } - nsPluginTag* pluginTag = host->TagForPlugin(mPlugin); - if (!pluginTag) { - return false; - } - CopyUTF8toUTF16(pluginTag->mName, aPluginName); - return true; -} - bool PluginModuleParent::LaunchHangUI() { @@ -593,7 +627,12 @@ PluginModuleParent::LaunchHangUI() return false; } if (mHangUIParent->DontShowAgain()) { - return !mHangUIParent->WasLastHangStopped(); + mHangAnnotationFlags |= kHangUIDontShow; + bool wasLastHangStopped = mHangUIParent->WasLastHangStopped(); + if (!wasLastHangStopped) { + mHangAnnotationFlags |= kHangUIContinued; + } + return !wasLastHangStopped; } delete mHangUIParent; mHangUIParent = nullptr; @@ -601,12 +640,9 @@ PluginModuleParent::LaunchHangUI() mHangUIParent = new PluginHangUIParent(this, Preferences::GetInt(kHangUITimeoutPref, 0), Preferences::GetInt(kChildTimeoutPref, 0)); - nsAutoString pluginName; - if (!GetPluginName(pluginName)) { - return false; - } - bool retval = mHangUIParent->Init(pluginName); + bool retval = mHangUIParent->Init(NS_ConvertUTF8toUTF16(mPluginName)); if (retval) { + mHangAnnotationFlags |= kHangUIShown; /* Once the UI is shown we switch the timeout over to use kChildTimeoutPref, allowing us to terminate a hung plugin after kChildTimeoutPref seconds if the user doesn't respond to @@ -636,6 +672,12 @@ PluginModuleParent::FinishHangUI() } } } + +void +PluginModuleParent::OnHangUIContinue() +{ + mHangAnnotationFlags |= kHangUIContinued; +} #endif // XP_WIN #ifdef MOZ_CRASHREPORTER @@ -1263,6 +1305,10 @@ PluginModuleParent::NPP_New(NPMIMEType pluginType, NPP instance, return NS_ERROR_FAILURE; } + if (mPluginName.IsEmpty()) { + GetPluginDetails(mPluginName, mPluginVersion); + } + // create the instance on the other side InfallibleTArray names; InfallibleTArray values; diff --git a/dom/plugins/ipc/PluginModuleParent.h b/dom/plugins/ipc/PluginModuleParent.h index 91c0dcc1bf22..47f152af38bf 100644 --- a/dom/plugins/ipc/PluginModuleParent.h +++ b/dom/plugins/ipc/PluginModuleParent.h @@ -9,6 +9,7 @@ #include "base/process.h" #include "mozilla/FileUtils.h" +#include "mozilla/HangMonitor.h" #include "mozilla/PluginLibrary.h" #include "mozilla/plugins/ScopedMethodFactory.h" #include "mozilla/plugins/PluginProcessParent.h" @@ -59,6 +60,7 @@ class PluginModuleParent #ifdef MOZ_CRASHREPORTER_INJECTOR , public CrashReporter::InjectorCrashCallback #endif + , public mozilla::HangMonitor::Annotator { private: typedef mozilla::PluginLibrary PluginLibrary; @@ -112,9 +114,22 @@ public: void TerminateChildProcess(MessageLoop* aMsgLoop); -#ifdef XP_WIN - void + virtual void + EnteredCxxStack() MOZ_OVERRIDE; + + virtual void ExitedCxxStack() MOZ_OVERRIDE; + + virtual void + AnnotateHang(mozilla::HangMonitor::HangAnnotations& aAnnotations) MOZ_OVERRIDE; + +#ifdef XP_WIN + /** + * Called by Plugin Hang UI to notify that the user has clicked continue. + * Used for chrome hang annotations. + */ + void + OnHangUIContinue(); #endif // XP_WIN protected: @@ -287,6 +302,16 @@ private: nsString mBrowserDumpID; nsString mHangID; nsRefPtr mProfilerObserver; + enum HangAnnotationFlags + { + kInPluginCall = (1u << 0), + kHangUIShown = (1u << 1), + kHangUIContinued = (1u << 2), + kHangUIDontShow = (1u << 3) + }; + Atomic mHangAnnotationFlags; + nsCString mPluginName; + nsCString mPluginVersion; #ifdef XP_WIN InfallibleTArray mPluginCpuUsageOnHang; PluginHangUIParent *mHangUIParent; @@ -307,9 +332,6 @@ private: void EvaluateHangUIState(const bool aReset); - bool - GetPluginName(nsAString& aPluginName); - /** * Launches the Plugin Hang UI. * @@ -327,6 +349,9 @@ private: FinishHangUI(); #endif + bool + GetPluginDetails(nsACString& aPluginName, nsACString& aPluginVersion); + #ifdef MOZ_X11 // Dup of plugin's X socket, used to scope its resources to this // object instead of the plugin process's lifetime diff --git a/dom/settings/SettingsRequestManager.jsm b/dom/settings/SettingsRequestManager.jsm index 575af149ef7c..83373041c64d 100644 --- a/dom/settings/SettingsRequestManager.jsm +++ b/dom/settings/SettingsRequestManager.jsm @@ -10,7 +10,7 @@ const Cc = Components.classes; const Ci = Components.interfaces; const Cu = Components.utils; -this.EXPORTED_SYMBOLS = ["SettingsRequestManager"]; +this.EXPORTED_SYMBOLS = []; Cu.import("resource://gre/modules/SettingsDB.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); diff --git a/js/src/builtin/SIMD.cpp b/js/src/builtin/SIMD.cpp index 1f00a993af55..af9a4b8ff485 100644 --- a/js/src/builtin/SIMD.cpp +++ b/js/src/builtin/SIMD.cpp @@ -159,7 +159,7 @@ static bool SignMask(JSContext *cx, unsigned argc, Value *vp) #define SIGN_MASK(type) \ static bool type##SignMask(JSContext *cx, unsigned argc, Value *vp) { \ - return SignMask(cx, argc, vp); \ + return SignMask(cx, argc, vp); \ } SIGN_MASK(Float32x4); SIGN_MASK(Int32x4); @@ -785,6 +785,25 @@ Int32x4BinaryScalar(JSContext *cx, unsigned argc, Value *vp) return StoreResult(cx, args, result); } +template class Op> +static bool +CompareFunc(JSContext *cx, unsigned argc, Value *vp) +{ + typedef typename In::Elem InElem; + + CallArgs args = CallArgsFromVp(argc, vp); + if (args.length() != 2 || !IsVectorObject(args[0]) || !IsVectorObject(args[1])) + return ErrorBadArgs(cx); + + int32_t result[Int32x4::lanes]; + InElem *left = TypedObjectMemory(args[0]); + InElem *right = TypedObjectMemory(args[1]); + for (unsigned i = 0; i < Int32x4::lanes; i++) + result[i] = Op::apply(left[i], right[i]); + + return StoreResult(cx, args, result); +} + template static bool FuncConvert(JSContext *cx, unsigned argc, Value *vp) diff --git a/js/src/builtin/SIMD.h b/js/src/builtin/SIMD.h index 93d7670189c0..3c4a8fc3af73 100644 --- a/js/src/builtin/SIMD.h +++ b/js/src/builtin/SIMD.h @@ -36,15 +36,15 @@ V(add, (BinaryFunc), 2, 0) \ V(and, (CoercedBinaryFunc), 2, 0) \ V(div, (BinaryFunc), 2, 0) \ - V(equal, (BinaryFunc), 2, 0) \ - V(greaterThan, (BinaryFunc), 2, 0) \ - V(greaterThanOrEqual, (BinaryFunc), 2, 0) \ - V(lessThan, (BinaryFunc), 2, 0) \ - V(lessThanOrEqual, (BinaryFunc), 2, 0) \ + V(equal, (CompareFunc), 2, 0) \ + V(greaterThan, (CompareFunc), 2, 0) \ + V(greaterThanOrEqual, (CompareFunc), 2, 0) \ + V(lessThan, (CompareFunc), 2, 0) \ + V(lessThanOrEqual, (CompareFunc), 2, 0) \ V(max, (BinaryFunc), 2, 0) \ V(min, (BinaryFunc), 2, 0) \ V(mul, (BinaryFunc), 2, 0) \ - V(notEqual, (BinaryFunc), 2, 0) \ + V(notEqual, (CompareFunc), 2, 0) \ V(or, (CoercedBinaryFunc), 2, 0) \ V(scale, (FuncWith), 2, 0) \ V(sub, (BinaryFunc), 2, 0) \ @@ -82,9 +82,9 @@ #define INT32X4_BINARY_FUNCTION_LIST(V) \ V(add, (BinaryFunc), 2, 0) \ V(and, (BinaryFunc), 2, 0) \ - V(equal, (BinaryFunc), 2, 0) \ - V(greaterThan, (BinaryFunc), 2, 0) \ - V(lessThan, (BinaryFunc), 2, 0) \ + V(equal, (CompareFunc), 2, 0) \ + V(greaterThan, (CompareFunc), 2, 0) \ + V(lessThan, (CompareFunc), 2, 0) \ V(mul, (BinaryFunc), 2, 0) \ V(or, (BinaryFunc), 2, 0) \ V(sub, (BinaryFunc), 2, 0) \ diff --git a/js/xpconnect/src/XPCShellImpl.cpp b/js/xpconnect/src/XPCShellImpl.cpp index 2affbf704d45..91dd4243442f 100644 --- a/js/xpconnect/src/XPCShellImpl.cpp +++ b/js/xpconnect/src/XPCShellImpl.cpp @@ -12,7 +12,6 @@ #include "nsServiceManagerUtils.h" #include "nsComponentManagerUtils.h" #include "nsIXPConnect.h" -#include "nsIJSNativeInitializer.h" #include "nsIServiceManager.h" #include "nsIFile.h" #include "nsString.h" diff --git a/mobile/android/app/mobile.js b/mobile/android/app/mobile.js index a19d218b7607..ed10692690bb 100644 --- a/mobile/android/app/mobile.js +++ b/mobile/android/app/mobile.js @@ -724,7 +724,6 @@ pref("app.orientation.default", ""); // back to the system. pref("memory.free_dirty_pages", true); -pref("layout.imagevisibility.enabled", true); pref("layout.imagevisibility.numscrollportwidths", 1); pref("layout.imagevisibility.numscrollportheights", 1); diff --git a/testing/mozbase/mozdevice/setup.py b/testing/mozbase/mozdevice/setup.py index e8d2e5bcab5e..c31fa69534f6 100644 --- a/testing/mozbase/mozdevice/setup.py +++ b/testing/mozbase/mozdevice/setup.py @@ -5,7 +5,7 @@ from setuptools import setup PACKAGE_NAME = 'mozdevice' -PACKAGE_VERSION = '0.42' +PACKAGE_VERSION = '0.43' deps = ['mozfile >= 1.0', 'mozlog >= 2.1', diff --git a/toolkit/components/telemetry/Telemetry.cpp b/toolkit/components/telemetry/Telemetry.cpp index 1cd5158037fc..c4a2f96e648b 100644 --- a/toolkit/components/telemetry/Telemetry.cpp +++ b/toolkit/components/telemetry/Telemetry.cpp @@ -63,6 +63,7 @@ #include "mozilla/PoisonIOInterposer.h" #include "mozilla/StartupTimeline.h" #if defined(MOZ_ENABLE_PROFILER_SPS) +#include "mozilla/HangMonitor.h" #include "shared-libraries.h" #endif @@ -72,6 +73,7 @@ namespace { using namespace base; using namespace mozilla; +using namespace mozilla::HangMonitor; template class AutoHashtable : public nsTHashtable @@ -208,14 +210,38 @@ CombinedStacks::SizeOfExcludingThis() const { class HangReports { public: - size_t SizeOfExcludingThis() const; + /** + * This struct encapsulates information for an individual ChromeHang annotation. + * mHangIndex is the index of the corresponding ChromeHang. + */ + struct AnnotationInfo { + AnnotationInfo(uint32_t aHangIndex, + HangAnnotations* aAnnotations) + : mHangIndex(aHangIndex) + , mAnnotations(aAnnotations) + {} + AnnotationInfo(const AnnotationInfo& aOther) + : mHangIndex(aOther.mHangIndex) + , mAnnotations(aOther.mAnnotations) + {} + ~AnnotationInfo() {} + uint32_t mHangIndex; + mutable nsAutoPtr mAnnotations; + }; + size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; void AddHang(const Telemetry::ProcessedStack& aStack, uint32_t aDuration, - int32_t aSystemUptime, int32_t aFirefoxUptime); + int32_t aSystemUptime, int32_t aFirefoxUptime, + HangAnnotations* aAnnotations); uint32_t GetDuration(unsigned aIndex) const; int32_t GetSystemUptime(unsigned aIndex) const; int32_t GetFirefoxUptime(unsigned aIndex) const; + const std::vector& GetAnnotationInfo() const; const CombinedStacks& GetStacks() const; private: + /** + * This struct encapsulates the data for an individual ChromeHang, excluding + * annotations. + */ struct HangInfo { // Hang duration (in seconds) uint32_t mDuration; @@ -225,6 +251,7 @@ private: int32_t mFirefoxUptime; }; std::vector mHangInfo; + std::vector mAnnotationInfo; CombinedStacks mStacks; }; @@ -232,19 +259,30 @@ void HangReports::AddHang(const Telemetry::ProcessedStack& aStack, uint32_t aDuration, int32_t aSystemUptime, - int32_t aFirefoxUptime) { + int32_t aFirefoxUptime, + HangAnnotations* aAnnotations) { HangInfo info = { aDuration, aSystemUptime, aFirefoxUptime }; mHangInfo.push_back(info); + if (aAnnotations) { + AnnotationInfo ainfo(static_cast(mHangInfo.size() - 1), + aAnnotations); + mAnnotationInfo.push_back(ainfo); + } mStacks.AddStack(aStack); } size_t -HangReports::SizeOfExcludingThis() const { +HangReports::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const { size_t n = 0; n += mStacks.SizeOfExcludingThis(); // This is a crude approximation. See comment on // CombinedStacks::SizeOfExcludingThis. n += mHangInfo.capacity() * sizeof(HangInfo); + n += mAnnotationInfo.capacity() * sizeof(AnnotationInfo); + for (std::vector::const_iterator i = mAnnotationInfo.begin(), + e = mAnnotationInfo.end(); i != e; ++i) { + n += i->mAnnotations->SizeOfIncludingThis(aMallocSizeOf); + } return n; } @@ -268,6 +306,11 @@ HangReports::GetFirefoxUptime(unsigned aIndex) const { return mHangInfo[aIndex].mFirefoxUptime; } +const std::vector& +HangReports::GetAnnotationInfo() const { + return mAnnotationInfo; +} + /** * IOInterposeObserver recording statistics of main-thread I/O during execution, * aimed at consumption by TelemetryImpl @@ -575,7 +618,8 @@ public: static void RecordChromeHang(uint32_t aDuration, Telemetry::ProcessedStack &aStack, int32_t aSystemUptime, - int32_t aFirefoxUptime); + int32_t aFirefoxUptime, + HangAnnotations* aAnnotations); #endif static void RecordThreadHangStats(Telemetry::ThreadHangStats& aStats); static nsresult GetHistogramEnumId(const char *name, Telemetry::ID *id); @@ -1798,7 +1842,9 @@ TelemetryImpl::GetChromeHangs(JSContext *cx, JS::MutableHandle ret) JS::Rooted durationArray(cx, JS_NewArrayObject(cx, 0)); JS::Rooted systemUptimeArray(cx, JS_NewArrayObject(cx, 0)); JS::Rooted firefoxUptimeArray(cx, JS_NewArrayObject(cx, 0)); - if (!durationArray || !systemUptimeArray || !firefoxUptimeArray) { + JS::Rooted annotationsArray(cx, JS_NewArrayObject(cx, 0)); + if (!durationArray || !systemUptimeArray || !firefoxUptimeArray || + !annotationsArray) { return NS_ERROR_FAILURE; } @@ -1820,6 +1866,13 @@ TelemetryImpl::GetChromeHangs(JSContext *cx, JS::MutableHandle ret) return NS_ERROR_FAILURE; } + ok = JS_DefineProperty(cx, fullReportObj, "annotations", annotationsArray, + JSPROP_ENUMERATE); + if (!ok) { + return NS_ERROR_FAILURE; + } + + const size_t length = stacks.GetStackCount(); for (size_t i = 0; i < length; ++i) { if (!JS_SetElement(cx, durationArray, i, mHangReports.GetDuration(i))) { @@ -1831,6 +1884,49 @@ TelemetryImpl::GetChromeHangs(JSContext *cx, JS::MutableHandle ret) if (!JS_SetElement(cx, firefoxUptimeArray, i, mHangReports.GetFirefoxUptime(i))) { return NS_ERROR_FAILURE; } + const std::vector& annotationInfo = + mHangReports.GetAnnotationInfo(); + uint32_t annotationsArrayIndex = 0; + for (std::vector::const_iterator + ai = annotationInfo.begin(), e = annotationInfo.end(); ai != e; + ++ai, ++annotationsArrayIndex) { + JS::Rooted keyValueArray(cx, JS_NewArrayObject(cx, 0)); + if (!keyValueArray) { + return NS_ERROR_FAILURE; + } + JS::RootedValue indexValue(cx); + indexValue.setNumber(ai->mHangIndex); + if (!JS_SetElement(cx, keyValueArray, 0, indexValue)) { + return NS_ERROR_FAILURE; + } + JS::Rooted jsAnnotation(cx, JS_NewObject(cx, nullptr, + JS::NullPtr(), + JS::NullPtr())); + if (!jsAnnotation) { + return NS_ERROR_FAILURE; + } + nsAutoPtr annotationsEnum; + if (!ai->mAnnotations->GetEnumerator(annotationsEnum.StartAssignment())) { + return NS_ERROR_FAILURE; + } + nsAutoString key; + nsAutoString value; + while (annotationsEnum->Next(key, value)) { + JS::RootedValue jsValue(cx); + jsValue.setString(JS_NewUCStringCopyN(cx, value.get(), value.Length())); + if (!JS_DefineUCProperty(cx, jsAnnotation, key.get(), key.Length(), + jsValue, JSPROP_ENUMERATE)) { + return NS_ERROR_FAILURE; + } + } + if (!JS_SetElement(cx, keyValueArray, 1, jsAnnotation)) { + return NS_ERROR_FAILURE; + } + if (!JS_SetElement(cx, annotationsArray, annotationsArrayIndex, + keyValueArray)) { + return NS_ERROR_FAILURE; + } + } } return NS_OK; @@ -2543,7 +2639,8 @@ void TelemetryImpl::RecordChromeHang(uint32_t aDuration, Telemetry::ProcessedStack &aStack, int32_t aSystemUptime, - int32_t aFirefoxUptime) + int32_t aFirefoxUptime, + HangAnnotations* aAnnotations) { if (!sTelemetry || !sTelemetry->mCanRecord) return; @@ -2551,7 +2648,8 @@ TelemetryImpl::RecordChromeHang(uint32_t aDuration, MutexAutoLock hangReportMutex(sTelemetry->mHangReportsMutex); sTelemetry->mHangReports.AddHang(aStack, aDuration, - aSystemUptime, aFirefoxUptime); + aSystemUptime, aFirefoxUptime, + aAnnotations); } #endif @@ -2641,7 +2739,7 @@ TelemetryImpl::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) n += mTrackedDBs.SizeOfExcludingThis(aMallocSizeOf); { // Scope for mHangReportsMutex lock MutexAutoLock lock(mHangReportsMutex); - n += mHangReports.SizeOfExcludingThis(); + n += mHangReports.SizeOfExcludingThis(aMallocSizeOf); } { // Scope for mThreadHangStatsMutex lock MutexAutoLock lock(mThreadHangStatsMutex); @@ -2797,10 +2895,11 @@ void Init() void RecordChromeHang(uint32_t duration, ProcessedStack &aStack, int32_t aSystemUptime, - int32_t aFirefoxUptime) + int32_t aFirefoxUptime, + HangAnnotations* aAnnotations) { TelemetryImpl::RecordChromeHang(duration, aStack, - aSystemUptime, aFirefoxUptime); + aSystemUptime, aFirefoxUptime, aAnnotations); } #endif diff --git a/toolkit/components/telemetry/Telemetry.h b/toolkit/components/telemetry/Telemetry.h index d35d8c29d666..dc16a952d6bf 100644 --- a/toolkit/components/telemetry/Telemetry.h +++ b/toolkit/components/telemetry/Telemetry.h @@ -17,6 +17,9 @@ namespace base { } namespace mozilla { +namespace HangMonitor { + class HangAnnotations; +} namespace Telemetry { #include "TelemetryHistogramEnums.h" @@ -195,12 +198,14 @@ class ProcessedStack; * @param aStack - Array of PCs from the hung call stack * @param aSystemUptime - System uptime at the time of the hang, in minutes * @param aFirefoxUptime - Firefox uptime at the time of the hang, in minutes + * @param aAnnotations - Any annotations to be added to the report */ #if defined(MOZ_ENABLE_PROFILER_SPS) void RecordChromeHang(uint32_t aDuration, ProcessedStack &aStack, int32_t aSystemUptime, - int32_t aFirefoxUptime); + int32_t aFirefoxUptime, + mozilla::HangMonitor::HangAnnotations* aAnnotations = nullptr); #endif class ThreadHangStats; diff --git a/toolkit/crashreporter/LoadLibraryRemote.cpp b/toolkit/crashreporter/LoadLibraryRemote.cpp index 02212529be42..f22a6220faef 100644 --- a/toolkit/crashreporter/LoadLibraryRemote.cpp +++ b/toolkit/crashreporter/LoadLibraryRemote.cpp @@ -95,6 +95,35 @@ CopySections(const unsigned char *data, PIMAGE_NT_HEADERS old_headers, PMEMORYMO } } +static bool +CopyRegion(HANDLE hRemoteProcess, void* remoteAddress, void* localAddress, DWORD size, DWORD protect) +{ + if (size > 0) { + // Copy the data from local->remote and set the memory protection + if (!VirtualAllocEx(hRemoteProcess, remoteAddress, size, MEM_COMMIT, PAGE_READWRITE)) + return false; + + if (!WriteProcessMemory(hRemoteProcess, + remoteAddress, + localAddress, + size, + nullptr)) { +#ifdef DEBUG_OUTPUT + OutputLastError("Error writing remote memory.\n"); +#endif + return false; + } + + DWORD oldProtect; + if (VirtualProtectEx(hRemoteProcess, remoteAddress, size, protect, &oldProtect) == 0) { +#ifdef DEBUG_OUTPUT + OutputLastError("Error protecting memory page"); +#endif + return false; + } + } + return true; +} // Protection flags for memory pages (Executable, Readable, Writeable) static int ProtectionFlags[2][2][2] = { { @@ -117,11 +146,20 @@ FinalizeSections(PMEMORYMODULE module, HANDLE hRemoteProcess) #endif int i; + int numSections = module->headers->FileHeader.NumberOfSections; + + if (numSections < 1) + return false; + PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(module->headers); - + + // Copy any data before the first section (i.e. the image header) + if (!CopyRegion(hRemoteProcess, module->remoteCodeBase, module->localCodeBase, section->VirtualAddress, PAGE_READONLY)) + return false; + // loop through all sections and change access flags - for (i=0; iheaders->FileHeader.NumberOfSections; i++, section++) { - DWORD protect, oldProtect, size; + for (i=0; iCharacteristics & IMAGE_SCN_MEM_EXECUTE) != 0; int readable = (section->Characteristics & IMAGE_SCN_MEM_READ) != 0; int writeable = (section->Characteristics & IMAGE_SCN_MEM_WRITE) != 0; @@ -132,39 +170,17 @@ FinalizeSections(PMEMORYMODULE module, HANDLE hRemoteProcess) protect |= PAGE_NOCACHE; } + void* remoteAddress = module->remoteCodeBase + section->VirtualAddress; + void* localAddress = module->localCodeBase + section->VirtualAddress; + // determine size of region size = section->Misc.VirtualSize; - if (size > 0) { - void* remoteAddress = module->remoteCodeBase + section->VirtualAddress; - void* localAddress = module->localCodeBase + section->VirtualAddress; - #ifdef DEBUG_OUTPUT fprintf(stderr, "Copying section %s to %p, size %x, executable %i readable %i writeable %i\n", section->Name, remoteAddress, size, executable, readable, writeable); #endif - - // Copy the data from local->remote and set the memory protection - if (!VirtualAllocEx(hRemoteProcess, remoteAddress, size, MEM_COMMIT, PAGE_READWRITE)) - return false; - - if (!WriteProcessMemory(hRemoteProcess, - remoteAddress, - localAddress, - size, - nullptr)) { -#ifdef DEBUG_OUTPUT - OutputLastError("Error writing remote memory.\n"); -#endif - return false; - } - - if (VirtualProtectEx(hRemoteProcess, remoteAddress, size, protect, &oldProtect) == 0) { -#ifdef DEBUG_OUTPUT - OutputLastError("Error protecting memory page"); -#endif - return false; - } - } + if (!CopyRegion(hRemoteProcess, remoteAddress, localAddress, size, protect)) + return false; } return true; } diff --git a/toolkit/devtools/touch-events.js b/toolkit/devtools/touch-events.js index ab8f85bcbf4b..84e493020331 100644 --- a/toolkit/devtools/touch-events.js +++ b/toolkit/devtools/touch-events.js @@ -37,7 +37,7 @@ function TouchEventHandler (window) { let TouchEventHandler = { enabled: false, - events: ['mousedown', 'mousemove', 'mouseup'], + events: ['mousedown', 'mousemove', 'mouseup', 'touchstart', 'touchend'], start: function teh_start() { if (this.enabled) return false; @@ -61,18 +61,43 @@ function TouchEventHandler (window) { }).bind(this)); }, handleEvent: function teh_handleEvent(evt) { - // Ignore all but real mouse event coming from physical mouse - // (especially ignore mouse event being dispatched from a touch event) - if (evt.button || evt.mozInputSource != Ci.nsIDOMMouseEvent.MOZ_SOURCE_MOUSE || evt.isSynthesized) { - return; - } - // The gaia system window use an hybrid system even on the device which is // a mix of mouse/touch events. So let's not cancel *all* mouse events // if it is the current target. let content = this.getContent(evt.target); let isSystemWindow = content.location.toString().indexOf("system.gaiamobile.org") != -1; + // App touchstart & touchend should also be dispatched on the system app + // to match on-device behavior. + if (evt.type.startsWith('touch') && !isSystemWindow) { + let sysFrame = content.realFrameElement; + let sysDocument = sysFrame.ownerDocument; + let sysWindow = sysDocument.defaultView; + + let touchEvent = sysDocument.createEvent('touchevent'); + let touch = evt.touches[0] || evt.changedTouches[0]; + let point = sysDocument.createTouch(sysWindow, sysFrame, 0, + touch.pageX, touch.pageY, + touch.screenX, touch.screenY, + touch.clientX, touch.clientY, + 1, 1, 0, 0); + + let touches = sysDocument.createTouchList(point); + let targetTouches = touches; + let changedTouches = touches; + touchEvent.initTouchEvent(evt.type, true, true, sysWindow, 0, + false, false, false, false, + touches, targetTouches, changedTouches); + sysFrame.dispatchEvent(touchEvent); + return; + } + + // Ignore all but real mouse event coming from physical mouse + // (especially ignore mouse event being dispatched from a touch event) + if (evt.button || evt.mozInputSource != Ci.nsIDOMMouseEvent.MOZ_SOURCE_MOUSE || evt.isSynthesized) { + return; + } + let eventTarget = this.target; let type = ''; switch (evt.type) { diff --git a/toolkit/toolkit.mozbuild b/toolkit/toolkit.mozbuild index 79a078b42da7..2dcea0d5122c 100644 --- a/toolkit/toolkit.mozbuild +++ b/toolkit/toolkit.mozbuild @@ -118,6 +118,8 @@ if CONFIG['MOZ_UNIVERSALCHARDET']: if CONFIG['ACCESSIBILITY']: DIRS += ['/accessible'] +else: + DIRS += ['/accessible/ipc'] # toolkit diff --git a/widget/windows/nsWindow.cpp b/widget/windows/nsWindow.cpp index 2a03e0812bca..6745e6897be5 100644 --- a/widget/windows/nsWindow.cpp +++ b/widget/windows/nsWindow.cpp @@ -3132,20 +3132,12 @@ NS_METHOD nsWindow::EnableDragDrop(bool aEnable) NS_METHOD nsWindow::CaptureMouse(bool aCapture) { - TRACKMOUSEEVENT mTrack; - mTrack.cbSize = sizeof(TRACKMOUSEEVENT); - mTrack.dwFlags = TME_LEAVE; - mTrack.dwHoverTime = 0; if (aCapture) { - mTrack.hwndTrack = mWnd; ::SetCapture(mWnd); } else { - mTrack.hwndTrack = nullptr; ::ReleaseCapture(); } sIsInMouseCapture = aCapture; - // Requests WM_MOUSELEAVE events for this window. - TrackMouseEvent(&mTrack); return NS_OK; } @@ -4865,6 +4857,15 @@ nsWindow::ProcessMessage(UINT msg, WPARAM& wParam, LPARAM& lParam, case WM_MOUSEMOVE: { + if (!mMousePresent) { + TRACKMOUSEEVENT tme; + tme.cbSize = sizeof(TRACKMOUSEEVENT); + tme.dwFlags = TME_LEAVE; + tme.hwndTrack = mWnd; + // Request WM_MOUSELEAVE events for this window. + TrackMouseEvent(&tme); + } + mMousePresent = true; // Suppress dispatch of pending events @@ -4932,6 +4933,12 @@ nsWindow::ProcessMessage(UINT msg, WPARAM& wParam, LPARAM& lParam, } break; + case WM_NCMOUSELEAVE: + // If upon mouse leave event, only WM_NCMOUSELEAVE message is sent, sending WM_MOUSELEAVE message + // makes the event being properly handled. + SendMessage(mWnd, WM_MOUSELEAVE, 0, 0); + break; + case WM_CONTEXTMENU: { // if the context menu is brought up from the keyboard, |lParam| diff --git a/xpcom/threads/HangMonitor.cpp b/xpcom/threads/HangMonitor.cpp index e308b1d34e35..cc5303695f9d 100644 --- a/xpcom/threads/HangMonitor.cpp +++ b/xpcom/threads/HangMonitor.cpp @@ -5,15 +5,21 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "mozilla/HangMonitor.h" + +#include + #include "mozilla/BackgroundHangMonitor.h" #include "mozilla/Monitor.h" #include "mozilla/Preferences.h" #include "mozilla/Telemetry.h" #include "mozilla/ProcessedStack.h" #include "mozilla/Atomics.h" -#include "nsXULAppAPI.h" -#include "nsThreadUtils.h" +#include "mozilla/StaticPtr.h" +#include "nsAutoPtr.h" +#include "nsReadableUtils.h" #include "nsStackWalk.h" +#include "nsThreadUtils.h" +#include "nsXULAppAPI.h" #ifdef MOZ_CRASHREPORTER #include "nsExceptionHandler.h" @@ -68,6 +74,9 @@ static const int32_t DEFAULT_CHROME_HANG_INTERVAL = 5; // Maximum number of PCs to gather from the stack static const int32_t MAX_CALL_STACK_PCS = 400; + +// Chrome hang annotators +static StaticAutoPtr> gAnnotators; #endif // PrefChangedFunc @@ -113,6 +122,162 @@ Crash() } #ifdef REPORT_CHROME_HANGS +class ChromeHangAnnotations : public HangAnnotations +{ +public: + ChromeHangAnnotations(); + ~ChromeHangAnnotations(); + + void AddAnnotation(const nsAString& aName, const int32_t aData) MOZ_OVERRIDE; + void AddAnnotation(const nsAString& aName, const double aData) MOZ_OVERRIDE; + void AddAnnotation(const nsAString& aName, const nsAString& aData) MOZ_OVERRIDE; + void AddAnnotation(const nsAString& aName, const nsACString& aData) MOZ_OVERRIDE; + void AddAnnotation(const nsAString& aName, const bool aData) MOZ_OVERRIDE; + + size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE; + bool IsEmpty() const MOZ_OVERRIDE; + bool GetEnumerator(Enumerator** aOutEnum) MOZ_OVERRIDE; + + typedef std::pair AnnotationType; + typedef std::vector VectorType; + typedef VectorType::const_iterator IteratorType; + +private: + VectorType mAnnotations; +}; + +ChromeHangAnnotations::ChromeHangAnnotations() +{ + MOZ_COUNT_CTOR(ChromeHangAnnotations); +} + +ChromeHangAnnotations::~ChromeHangAnnotations() +{ + MOZ_COUNT_DTOR(ChromeHangAnnotations); +} + +void +ChromeHangAnnotations::AddAnnotation(const nsAString& aName, const int32_t aData) +{ + nsString dataString; + dataString.AppendInt(aData); + AnnotationType annotation = std::make_pair(nsString(aName), dataString); + mAnnotations.push_back(annotation); +} + +void +ChromeHangAnnotations::AddAnnotation(const nsAString& aName, const double aData) +{ + nsString dataString; + dataString.AppendFloat(aData); + AnnotationType annotation = std::make_pair(nsString(aName), dataString); + mAnnotations.push_back(annotation); +} + +void +ChromeHangAnnotations::AddAnnotation(const nsAString& aName, const nsAString& aData) +{ + AnnotationType annotation = std::make_pair(nsString(aName), nsString(aData)); + mAnnotations.push_back(annotation); +} + +void +ChromeHangAnnotations::AddAnnotation(const nsAString& aName, const nsACString& aData) +{ + nsString dataString; + AppendUTF8toUTF16(aData, dataString); + AnnotationType annotation = std::make_pair(nsString(aName), dataString); + mAnnotations.push_back(annotation); +} + +void +ChromeHangAnnotations::AddAnnotation(const nsAString& aName, const bool aData) +{ + nsString dataString; + dataString += aData ? NS_LITERAL_STRING("true") : NS_LITERAL_STRING("false"); + AnnotationType annotation = std::make_pair(nsString(aName), dataString); + mAnnotations.push_back(annotation); +} + +/** + * This class itself does not use synchronization but it (and its parent object) + * should be protected by mutual exclusion in some way. In Telemetry the chrome + * hang data is protected via TelemetryImpl::mHangReportsMutex. + */ +class ChromeHangAnnotationEnumerator : public HangAnnotations::Enumerator +{ +public: + ChromeHangAnnotationEnumerator(const ChromeHangAnnotations::VectorType& aAnnotations); + ~ChromeHangAnnotationEnumerator(); + + virtual bool Next(nsAString& aOutName, nsAString& aOutValue); + +private: + ChromeHangAnnotations::IteratorType mIterator; + ChromeHangAnnotations::IteratorType mEnd; +}; + +ChromeHangAnnotationEnumerator::ChromeHangAnnotationEnumerator( + const ChromeHangAnnotations::VectorType& aAnnotations) + : mIterator(aAnnotations.begin()) + , mEnd(aAnnotations.end()) +{ + MOZ_COUNT_CTOR(ChromeHangAnnotationEnumerator); +} + +ChromeHangAnnotationEnumerator::~ChromeHangAnnotationEnumerator() +{ + MOZ_COUNT_DTOR(ChromeHangAnnotationEnumerator); +} + +bool +ChromeHangAnnotationEnumerator::Next(nsAString& aOutName, nsAString& aOutValue) +{ + aOutName.Truncate(); + aOutValue.Truncate(); + if (mIterator == mEnd) { + return false; + } + aOutName = mIterator->first; + aOutValue = mIterator->second; + ++mIterator; + return true; +} + +bool +ChromeHangAnnotations::IsEmpty() const +{ + return mAnnotations.empty(); +} + +size_t +ChromeHangAnnotations::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const +{ + size_t result = sizeof(mAnnotations) + + mAnnotations.capacity() * sizeof(AnnotationType); + for (IteratorType i = mAnnotations.begin(), e = mAnnotations.end(); i != e; + ++i) { + result += i->first.SizeOfExcludingThisIfUnshared(aMallocSizeOf); + result += i->second.SizeOfExcludingThisIfUnshared(aMallocSizeOf); + } + + return result; +} + +bool +ChromeHangAnnotations::GetEnumerator(HangAnnotations::Enumerator** aOutEnum) +{ + if (!aOutEnum) { + return false; + } + *aOutEnum = nullptr; + if (mAnnotations.empty()) { + return false; + } + *aOutEnum = new ChromeHangAnnotationEnumerator(mAnnotations); + return true; +} + static void ChromeStackWalker(uint32_t aFrameNumber, void* aPC, void* aSP, void* aClosure) { @@ -163,6 +328,22 @@ GetChromeHangReport(Telemetry::ProcessedStack& aStack, aFirefoxUptime = -1; } } + +static void +ChromeHangAnnotatorCallout(ChromeHangAnnotations& aAnnotations) +{ + gMonitor->AssertCurrentThreadOwns(); + MOZ_ASSERT(gAnnotators); + if (!gAnnotators) { + return; + } + for (std::set::iterator i = gAnnotators->begin(), + e = gAnnotators->end(); + i != e; ++i) { + (*i)->AnnotateHang(aAnnotations); + } +} + #endif void @@ -182,6 +363,7 @@ ThreadMain(void*) Telemetry::ProcessedStack stack; int32_t systemUptime = -1; int32_t firefoxUptime = -1; + nsAutoPtr annotations = new ChromeHangAnnotations(); #endif while (true) { @@ -209,6 +391,7 @@ ThreadMain(void*) // the minimum hang duration has been reached (not when the hang ends) if (waitCount == 2) { GetChromeHangReport(stack, systemUptime, firefoxUptime); + ChromeHangAnnotatorCallout(*annotations); } #else // This is the crash-on-hang feature. @@ -226,9 +409,11 @@ ThreadMain(void*) #ifdef REPORT_CHROME_HANGS if (waitCount >= 2) { uint32_t hangDuration = PR_IntervalToSeconds(now - lastTimestamp); - Telemetry::RecordChromeHang(hangDuration, stack, - systemUptime, firefoxUptime); + Telemetry::RecordChromeHang(hangDuration, stack, systemUptime, + firefoxUptime, annotations->IsEmpty() ? + nullptr : annotations.forget()); stack.Clear(); + annotations = new ChromeHangAnnotations(); } #endif lastTimestamp = timestamp; @@ -268,6 +453,7 @@ Startup() if (!winMainThreadHandle) { return; } + gAnnotators = new std::set(); #endif // Don't actually start measuring hangs until we hit the main event loop. @@ -306,6 +492,11 @@ Shutdown() delete gMonitor; gMonitor = nullptr; + +#ifdef REPORT_CHROME_HANGS + // gAnnotators is a StaticAutoPtr, so we just need to null it out. + gAnnotators = nullptr; +#endif } static bool @@ -394,5 +585,25 @@ Suspend() } } +void +RegisterAnnotator(Annotator& aAnnotator) +{ +#ifdef REPORT_CHROME_HANGS + MonitorAutoLock lock(*gMonitor); + MOZ_ASSERT(gAnnotators); + gAnnotators->insert(&aAnnotator); +#endif +} + +void +UnregisterAnnotator(Annotator& aAnnotator) +{ +#ifdef REPORT_CHROME_HANGS + MonitorAutoLock lock(*gMonitor); + MOZ_ASSERT(gAnnotators); + gAnnotators->erase(&aAnnotator); +#endif +} + } // namespace HangMonitor } // namespace mozilla diff --git a/xpcom/threads/HangMonitor.h b/xpcom/threads/HangMonitor.h index fd0e6ff8390c..b8b2291e1d78 100644 --- a/xpcom/threads/HangMonitor.h +++ b/xpcom/threads/HangMonitor.h @@ -7,6 +7,9 @@ #ifndef mozilla_HangMonitor_h #define mozilla_HangMonitor_h +#include "mozilla/MemoryReporting.h" +#include "nsString.h" + namespace mozilla { namespace HangMonitor { @@ -38,6 +41,57 @@ void Startup(); */ void Shutdown(); +/** + * This class declares an abstraction for a data type that encapsulates all + * of the annotations being reported by a registered hang Annotator. + */ +class HangAnnotations +{ +public: + virtual ~HangAnnotations() {} + + virtual void AddAnnotation(const nsAString& aName, const int32_t aData) = 0; + virtual void AddAnnotation(const nsAString& aName, const double aData) = 0; + virtual void AddAnnotation(const nsAString& aName, const nsAString& aData) = 0; + virtual void AddAnnotation(const nsAString& aName, const nsACString& aData) = 0; + virtual void AddAnnotation(const nsAString& aName, const bool aData) = 0; + + class Enumerator + { + public: + virtual ~Enumerator() {} + virtual bool Next(nsAString& aOutName, nsAString& aOutValue) = 0; + }; + + virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const = 0; + virtual bool IsEmpty() const = 0; + virtual bool GetEnumerator(Enumerator **aOutEnum) = 0; +}; + +class Annotator +{ +public: + /** + * NB: This function is always called by the HangMonitor thread. + * Plan accordingly. + */ + virtual void AnnotateHang(HangAnnotations& aAnnotations) = 0; +}; + +/** + * Registers an Annotator to be called when a hang is detected. + * @param aAnnotator Reference to an object that implements the + * HangMonitor::Annotator interface. + */ +void RegisterAnnotator(Annotator& aAnnotator); + +/** + * Registers an Annotator that was previously registered via RegisterAnnotator. + * @param aAnnotator Reference to an object that implements the + * HangMonitor::Annotator interface. + */ +void UnregisterAnnotator(Annotator& aAnnotator); + /** * Notify the hang monitor of activity which will reset its internal timer. *