merge mozilla-inbound to mozilla-central a=merge

This commit is contained in:
Carsten "Tomcat" Book 2016-09-20 12:01:29 +02:00
Родитель 65ef85e47c 2f7e87d4f5
Коммит 8910ca900f
42 изменённых файлов: 1154 добавлений и 417 удалений

Просмотреть файл

@ -82,6 +82,29 @@ DocAccessibleChildBase::SerializeTree(Accessible* aRoot,
}
}
#if defined(XP_WIN)
/* static */ void
DocAccessibleChildBase::SetMsaaIds(Accessible* aRoot,
uint32_t& aMsaaIdIndex,
const nsTArray<MsaaMapping>& aNewMsaaIds)
{
const MsaaMapping& mapping = aNewMsaaIds[aMsaaIdIndex];
#if defined(DEBUG)
uint64_t id = reinterpret_cast<uint64_t>(aRoot->UniqueID());
MOZ_ASSERT(mapping.ID() == id);
#endif // defined(DEBUG)
static_cast<AccessibleWrap*>(aRoot)->SetID(mapping.MsaaID());
++aMsaaIdIndex;
if (aRoot->IsOuterDoc()) {
// This needs to match the tree traversal in SerializeTree
return;
}
for (uint32_t i = 0, n = aRoot->ChildCount(); i < n; ++i) {
SetMsaaIds(aRoot->GetChildAt(i), aMsaaIdIndex, aNewMsaaIds);
}
}
#endif // defined(XP_WIN)
void
DocAccessibleChildBase::ShowEvent(AccShowEvent* aShowEvent)
{
@ -91,7 +114,22 @@ DocAccessibleChildBase::ShowEvent(AccShowEvent* aShowEvent)
nsTArray<AccessibleData> shownTree;
ShowEventData data(parentID, idxInParent, shownTree);
SerializeTree(aShowEvent->GetAccessible(), data.NewTree());
#if defined(XP_WIN)
nsTArray<MsaaMapping> newMsaaIds;
SendShowEventInfo(data, &newMsaaIds);
// newMsaaIds could be empty if something went wrong in SendShowEvent()
if (!newMsaaIds.IsEmpty()) {
uint32_t index = 0;
SetMsaaIds(aShowEvent->GetAccessible(), index, newMsaaIds);
}
// NB: On Windows, SendShowEvent attaches the subtree and generates new IDs,
// but does *NOT* fire the native event. We need to do that after
// we've called SetMsaaIds.
SendEvent(reinterpret_cast<uint64_t>(aShowEvent->GetAccessible()->UniqueID()),
nsIAccessibleEvent::EVENT_SHOW);
#else
SendShowEvent(data, aShowEvent->IsFromUserInput());
#endif // defined(XP_WIN)
}
} // namespace a11y

Просмотреть файл

@ -60,6 +60,10 @@ public:
protected:
static uint32_t InterfacesFor(Accessible* aAcc);
static void SerializeTree(Accessible* aRoot, nsTArray<AccessibleData>& aTree);
#if defined(XP_WIN)
static void SetMsaaIds(Accessible* aRoot, uint32_t& aMsaaIdIndex,
const nsTArray<MsaaMapping>& aNewMsaaIds);
#endif
DocAccessible* mDoc;
};

Просмотреть файл

@ -16,8 +16,13 @@ namespace mozilla {
namespace a11y {
bool
#if defined(XP_WIN)
DocAccessibleParent::RecvShowEventInfo(const ShowEventData& aData,
nsTArray<MsaaMapping>* aNewMsaaIds)
#else
DocAccessibleParent::RecvShowEvent(const ShowEventData& aData,
const bool& aFromUser)
#endif // defined(XP_WIN)
{
if (mShutdown)
return true;
@ -44,7 +49,13 @@ DocAccessibleParent::RecvShowEvent(const ShowEventData& aData,
return true;
}
#if defined(XP_WIN)
aNewMsaaIds->SetCapacity(aData.NewTree().Length());
uint32_t consumed = AddSubtree(parent, aData.NewTree(), 0, newChildIdx,
aNewMsaaIds);
#else
uint32_t consumed = AddSubtree(parent, aData.NewTree(), 0, newChildIdx);
#endif
MOZ_ASSERT(consumed == aData.NewTree().Length());
// XXX This shouldn't happen, but if we failed to add children then the below
@ -62,6 +73,9 @@ DocAccessibleParent::RecvShowEvent(const ShowEventData& aData,
MOZ_DIAGNOSTIC_ASSERT(CheckDocTree());
// NB: On Windows we dispatch the native event via a subsequent call to
// RecvEvent().
#if !defined(XP_WIN)
ProxyAccessible* target = parent->ChildAt(newChildIdx);
ProxyShowHideEvent(target, parent, true, aFromUser);
@ -76,6 +90,7 @@ DocAccessibleParent::RecvShowEvent(const ShowEventData& aData,
RefPtr<xpcAccEvent> event = new xpcAccEvent(type, xpcAcc, doc, node,
aFromUser);
nsCoreUtils::DispatchAccEvent(Move(event));
#endif
return true;
}
@ -83,7 +98,11 @@ DocAccessibleParent::RecvShowEvent(const ShowEventData& aData,
uint32_t
DocAccessibleParent::AddSubtree(ProxyAccessible* aParent,
const nsTArray<a11y::AccessibleData>& aNewTree,
uint32_t aIdx, uint32_t aIdxInParent)
uint32_t aIdx, uint32_t aIdxInParent
#if defined(XP_WIN)
, nsTArray<MsaaMapping>* aNewMsaaIds
#endif
)
{
if (aNewTree.Length() <= aIdx) {
NS_ERROR("bad index in serialized tree!");
@ -124,10 +143,22 @@ DocAccessibleParent::AddSubtree(ProxyAccessible* aParent,
mAccessibles.PutEntry(newChild.ID())->mProxy = newProxy;
ProxyCreated(newProxy, newChild.Interfaces());
#if defined(XP_WIN)
Accessible* idForAcc = WrapperFor(newProxy);
MOZ_ASSERT(idForAcc);
uint32_t newMsaaId = AccessibleWrap::GetChildIDFor(idForAcc);
MOZ_ASSERT(newMsaaId);
aNewMsaaIds->AppendElement(MsaaMapping(newChild.ID(), newMsaaId));
#endif // defined(XP_WIN)
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);
uint32_t consumed = AddSubtree(newProxy, aNewTree, aIdx + accessibles, i
#if defined(XP_WIN)
, aNewMsaaIds
#endif
);
if (!consumed)
return 0;
@ -476,7 +507,8 @@ DocAccessibleParent::GetXPCAccessible(ProxyAccessible* aProxy)
*/
bool
DocAccessibleParent::RecvCOMProxy(const IAccessibleHolder& aCOMProxy,
IAccessibleHolder* aParentCOMProxy)
IAccessibleHolder* aParentCOMProxy,
uint32_t* aMsaaID)
{
RefPtr<IAccessible> ptr(aCOMProxy.Get());
SetCOMInterface(ptr);
@ -488,6 +520,8 @@ DocAccessibleParent::RecvCOMProxy(const IAccessibleHolder& aCOMProxy,
}
aParentCOMProxy->Set(IAccessibleHolder::COMPtrType(rawNative));
Accessible* wrapper = WrapperFor(this);
*aMsaaID = AccessibleWrap::GetChildIDFor(wrapper);
return true;
}
#endif // defined(XP_WIN)

Просмотреть файл

@ -50,8 +50,13 @@ public:
virtual bool RecvEvent(const uint64_t& aID, const uint32_t& aType)
override;
#if defined(XP_WIN)
virtual bool RecvShowEventInfo(const ShowEventData& aData,
nsTArray<MsaaMapping>* aNewMsaaIds) override;
#else
virtual bool RecvShowEvent(const ShowEventData& aData, const bool& aFromUser)
override;
#endif // defined(XP_WIN)
virtual bool RecvHideEvent(const uint64_t& aRootID, const bool& aFromUser)
override;
virtual bool RecvStateChangeEvent(const uint64_t& aID,
@ -144,7 +149,8 @@ public:
#if defined(XP_WIN)
virtual bool RecvCOMProxy(const IAccessibleHolder& aCOMProxy,
IAccessibleHolder* aParentCOMProxy) override;
IAccessibleHolder* aParentCOMProxy,
uint32_t* aMsaaID) override;
#endif
private:
@ -174,7 +180,11 @@ private:
uint32_t AddSubtree(ProxyAccessible* aParent,
const nsTArray<AccessibleData>& aNewTree, uint32_t aIdx,
uint32_t aIdxInParent);
uint32_t aIdxInParent
#if defined(XP_WIN)
, nsTArray<MsaaMapping>* aNewMsaaIds
#endif // defined(XP_WIN)
);
MOZ_MUST_USE bool CheckDocTree() const;
xpcAccessibleGeneric* GetXPCAccessible(ProxyAccessible* aProxy);

Просмотреть файл

@ -34,8 +34,10 @@ void
DocAccessibleChild::SendCOMProxy(const IAccessibleHolder& aProxy)
{
IAccessibleHolder parentProxy;
PDocAccessibleChild::SendCOMProxy(aProxy, &parentProxy);
uint32_t msaaID = AccessibleWrap::kNoID;
PDocAccessibleChild::SendCOMProxy(aProxy, &parentProxy, &msaaID);
mParentProxy.reset(parentProxy.Release());
mDoc->SetID(msaaID);
}
} // namespace a11y

Просмотреть файл

@ -27,6 +27,12 @@ struct ShowEventData
AccessibleData[] NewTree;
};
struct MsaaMapping
{
uint64_t ID;
uint32_t MsaaID;
};
struct Attribute
{
nsCString Name;
@ -45,7 +51,7 @@ parent:
* event.
*/
async Event(uint64_t aID, uint32_t type);
async ShowEvent(ShowEventData data, bool aFromuser);
sync ShowEventInfo(ShowEventData data) returns (MsaaMapping[] aNewMsaaIds);
async HideEvent(uint64_t aRootID, bool aFromUser);
async StateChangeEvent(uint64_t aID, uint64_t aState, bool aEnabled);
async CaretMoveEvent(uint64_t aID, int32_t aOffset);
@ -63,7 +69,7 @@ parent:
// For now we'll add the command to send the proxy here. This might move to
// PDocAccessible constructor in PBrowser.
sync COMProxy(IAccessibleHolder aDocCOMProxy)
returns(IAccessibleHolder aParentCOMProxy);
returns(IAccessibleHolder aParentCOMProxy, uint32_t aMsaaID);
child:
async __delete__();

Просмотреть файл

@ -9,6 +9,8 @@
#include "Compatibility.h"
#include "DocAccessible-inl.h"
#include "mozilla/dom/TabChild.h"
#include "mozilla/a11y/DocAccessibleChild.h"
#include "mozilla/a11y/DocAccessibleParent.h"
#include "EnumVariant.h"
#include "nsAccUtils.h"
@ -69,16 +71,14 @@ static const int32_t kIEnumVariantDisconnected = -1;
////////////////////////////////////////////////////////////////////////////////
AccessibleWrap::AccessibleWrap(nsIContent* aContent, DocAccessible* aDoc) :
Accessible(aContent, aDoc)
#ifdef _WIN64
, mID(kNoID)
#endif
{
}
AccessibleWrap::~AccessibleWrap()
{
#ifdef _WIN64
if (mID != kNoID)
if (mID != kNoID && XRE_IsParentProcess())
sIDGen.ReleaseID(mID);
#endif
}
@ -90,7 +90,6 @@ NS_IMPL_ISUPPORTS_INHERITED0(AccessibleWrap, Accessible)
void
AccessibleWrap::Shutdown()
{
#ifdef _WIN64
if (mID != kNoID) {
auto doc = static_cast<DocAccessibleWrap*>(mDoc);
MOZ_ASSERT(doc);
@ -98,7 +97,6 @@ AccessibleWrap::Shutdown()
doc->RemoveID(mID);
}
}
#endif
Accessible::Shutdown();
}
@ -1147,9 +1145,22 @@ AccessibleWrap::GetNativeInterface(void** aOutAccessible)
NS_ADDREF_THIS();
}
void
AccessibleWrap::SetID(uint32_t aID)
{
MOZ_ASSERT(XRE_IsContentProcess());
mID = aID;
DocAccessibleWrap* doc = static_cast<DocAccessibleWrap*>(Document());
DebugOnly<AccessibleWrap*> checkAcc = nullptr;
MOZ_ASSERT(!(checkAcc = doc->GetAccessibleByID(aID)) ||
checkAcc->GetExistingID() == aID);
doc->AddID(aID, this);
}
void
AccessibleWrap::FireWinEvent(Accessible* aTarget, uint32_t aEventType)
{
MOZ_ASSERT(XRE_IsParentProcess());
static_assert(sizeof(gWinEventMap)/sizeof(gWinEventMap[0]) == nsIAccessibleEvent::EVENT_LAST_ENTRY,
"MSAA event map skewed");
@ -1246,6 +1257,13 @@ AccessibleWrap::GetChildIDFor(Accessible* aAccessible)
return 0;
}
// Content should use mID which has been generated by the chrome process.
if (XRE_IsContentProcess() && !aAccessible->IsApplication()) {
const uint32_t id = static_cast<AccessibleWrap*>(aAccessible)->mID;
MOZ_ASSERT(id != kNoID);
return id;
}
#ifdef _WIN64
if (!aAccessible->Document() && !aAccessible->IsProxy())
return 0;
@ -1286,6 +1304,22 @@ AccessibleWrap::GetHWNDFor(Accessible* aAccessible)
return nullptr;
}
if (XRE_IsContentProcess()) {
DocAccessible* doc = aAccessible->Document();
if (!doc) {
return nullptr;
}
DocAccessibleChild* ipcDoc = doc->IPCDoc();
if (!ipcDoc) {
return nullptr;
}
auto tab = static_cast<dom::TabChild*>(ipcDoc->Manager());
MOZ_ASSERT(tab);
return reinterpret_cast<HWND>(tab->GetNativeWindowHandle());
}
// Accessibles in child processes are said to have the HWND of the window
// their tab is within. Popups are always in the parent process, and so
// never proxied, which means this is basically correct.
@ -1345,7 +1379,6 @@ AccessibleWrap::NativeAccessible(Accessible* aAccessible)
return static_cast<IDispatch*>(msaaAccessible);
}
#ifdef _WIN64
static Accessible*
GetAccessibleInSubtree(DocAccessible* aDoc, uint32_t aID)
{
@ -1362,7 +1395,6 @@ GetAccessibleInSubtree(DocAccessible* aDoc, uint32_t aID)
return nullptr;
}
#endif
static AccessibleWrap*
GetProxiedAccessibleInSubtree(const DocAccessibleParent* aDoc, uint32_t aID)
@ -1426,7 +1458,9 @@ AccessibleWrap::GetXPAccessibleFor(const VARIANT& aVarChild)
#ifdef _WIN64
GetAccessibleInSubtree(document, static_cast<uint32_t>(aVarChild.lVal));
#else
document->GetAccessibleByUniqueIDInSubtree(uniqueID);
XRE_IsContentProcess() ?
GetAccessibleInSubtree(document, static_cast<uint32_t>(aVarChild.lVal)) :
document->GetAccessibleByUniqueIDInSubtree(uniqueID);
#endif
// If it is a document then just return an accessible.

Просмотреть файл

@ -179,17 +179,15 @@ public: // construction, destruction
static IDispatch* NativeAccessible(Accessible* aAccessible);
#ifdef _WIN64
uint32_t GetExistingID() const { return mID; }
static const uint32_t kNoID = 0;
#endif
// This is only valid to call in content
void SetID(uint32_t aID);
protected:
virtual ~AccessibleWrap();
#ifdef _WIN64
uint32_t mID;
#endif
/**
* Return the wrapper for the document's proxy.

Просмотреть файл

@ -48,15 +48,10 @@ STDMETHODIMP
DocAccessibleWrap::get_accParent(
/* [retval][out] */ IDispatch __RPC_FAR *__RPC_FAR *ppdispParent)
{
HRESULT hr = DocAccessible::get_accParent(ppdispParent);
if (*ppdispParent) {
return hr;
}
// We might be a top-level document in a content process.
DocAccessibleChild* ipcDoc = IPCDoc();
if (!ipcDoc) {
return S_FALSE;
return DocAccessible::get_accParent(ppdispParent);
}
IAccessible* dispParent = ipcDoc->GetParentIAccessible();
if (!dispParent) {

Просмотреть файл

@ -40,13 +40,11 @@ public:
/**
* Manage the mapping from id to Accessible.
*/
#ifdef _WIN64
void AddID(uint32_t aID, AccessibleWrap* aAcc)
{ mIDToAccessibleMap.Put(aID, aAcc); }
void RemoveID(uint32_t aID) { mIDToAccessibleMap.Remove(aID); }
AccessibleWrap* GetAccessibleByID(uint32_t aID) const
{ return mIDToAccessibleMap.Get(aID); }
#endif
protected:
// DocAccessible
@ -58,9 +56,7 @@ protected:
/*
* This provides a mapping from 32 bit id to accessible objects.
*/
#ifdef _WIN64
nsDataHashtable<nsUint32HashKey, AccessibleWrap*> mIDToAccessibleMap;
#endif
};
} // namespace a11y

Просмотреть файл

@ -3,6 +3,7 @@
requestLongerTimeout(2);
var {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
Cu.import("resource://gre/modules/Services.jsm");
const gHttpTestRoot = "http://example.com/browser/dom/base/test/";
@ -10,7 +11,12 @@ const gHttpTestRoot = "http://example.com/browser/dom/base/test/";
* Enable local telemetry recording for the duration of the tests.
*/
var gOldContentCanRecord = false;
var gOldParentCanRecord = false;
add_task(function* test_initialize() {
let Telemetry = Cc["@mozilla.org/base/telemetry;1"].getService(Ci.nsITelemetry);
gOldParentCanRecord = Telemetry.canRecordExtended
Telemetry.canRecordExtended = true;
// Because canRecordExtended is a per-process variable, we need to make sure
// that all of the pages load in the same content process. Limit the number
// of content processes to at most 1 (or 0 if e10s is off entirely).
@ -74,6 +80,9 @@ add_task(function* () {
});
add_task(function* () {
let Telemetry = Cc["@mozilla.org/base/telemetry;1"].getService(Ci.nsITelemetry);
Telemetry.canRecordExtended = gOldParentCanRecord;
yield ContentTask.spawn(gBrowser.selectedBrowser, { oldCanRecord: gOldContentCanRecord }, function (arg) {
Cu.import("resource://gre/modules/PromiseUtils.jsm");
yield new Promise(resolve => {
@ -104,22 +113,18 @@ function waitForPageLoad(browser) {
});
}
function grabHistogramsFromContent(browser, use_counter_middlefix) {
return ContentTask.spawn(browser, { middlefix: use_counter_middlefix }, function* (arg) {
let telemetry = Cc["@mozilla.org/base/telemetry;1"].getService(Ci.nsITelemetry);
function snapshot_histogram(name) {
return telemetry.getHistogramById(name).snapshot();
}
let histogram_page_name = "USE_COUNTER2_" + arg.middlefix + "_PAGE";
let histogram_document_name = "USE_COUNTER2_" + arg.middlefix + "_DOCUMENT";
let histogram_page = snapshot_histogram(histogram_page_name);
let histogram_document = snapshot_histogram(histogram_document_name);
let histogram_docs = snapshot_histogram("CONTENT_DOCUMENTS_DESTROYED");
let histogram_toplevel_docs = snapshot_histogram("TOP_LEVEL_CONTENT_DOCUMENTS_DESTROYED");
return [histogram_page.sum, histogram_document.sum,
histogram_docs.sum, histogram_toplevel_docs.sum];
});
function grabHistogramsFromContent(use_counter_middlefix, page_before = null) {
let telemetry = Cc["@mozilla.org/base/telemetry;1"].getService(Ci.nsITelemetry);
let suffix = Services.appinfo.browserTabsRemoteAutostart ? "#content" : "";
let gather = () => [
telemetry.getHistogramById("USE_COUNTER2_" + use_counter_middlefix + "_PAGE" + suffix).snapshot().sum,
telemetry.getHistogramById("USE_COUNTER2_" + use_counter_middlefix + "_DOCUMENT" + suffix).snapshot().sum,
telemetry.getHistogramById("CONTENT_DOCUMENTS_DESTROYED" + suffix).snapshot().sum,
telemetry.getHistogramById("TOP_LEVEL_CONTENT_DOCUMENTS_DESTROYED" + suffix).snapshot().sum,
];
return BrowserTestUtils.waitForCondition(() => {
return page_before != telemetry.getHistogramById("USE_COUNTER2_" + use_counter_middlefix + "_PAGE" + suffix).snapshot().sum;
}).then(gather, gather);
}
var check_use_counter_iframe = Task.async(function* (file, use_counter_middlefix, check_documents=true) {
@ -133,7 +138,7 @@ var check_use_counter_iframe = Task.async(function* (file, use_counter_middlefix
// interested in.
let [histogram_page_before, histogram_document_before,
histogram_docs_before, histogram_toplevel_docs_before] =
yield grabHistogramsFromContent(gBrowser.selectedBrowser, use_counter_middlefix);
yield grabHistogramsFromContent(use_counter_middlefix);
gBrowser.selectedBrowser.loadURI(gHttpTestRoot + "file_use_counter_outer.html");
yield waitForPageLoad(gBrowser.selectedBrowser);
@ -151,7 +156,7 @@ var check_use_counter_iframe = Task.async(function* (file, use_counter_middlefix
event.target.removeEventListener("load", listener, true);
// We flush the main document first, then the iframe's document to
// ensure any propagation that might happen from child->parent should
// ensure any propagation that might happen from content->parent should
// have already happened when counters are reported to telemetry.
wu.forceUseCounterFlush(content.document);
wu.forceUseCounterFlush(iframe.contentDocument);
@ -174,7 +179,7 @@ var check_use_counter_iframe = Task.async(function* (file, use_counter_middlefix
// Grab histograms again and compare.
let [histogram_page_after, histogram_document_after,
histogram_docs_after, histogram_toplevel_docs_after] =
yield grabHistogramsFromContent(gBrowser.selectedBrowser, use_counter_middlefix);
yield grabHistogramsFromContent(use_counter_middlefix, histogram_page_before);
is(histogram_page_after, histogram_page_before + 1,
"page counts for " + use_counter_middlefix + " after are correct");
@ -197,7 +202,7 @@ var check_use_counter_img = Task.async(function* (file, use_counter_middlefix) {
// interested in.
let [histogram_page_before, histogram_document_before,
histogram_docs_before, histogram_toplevel_docs_before] =
yield grabHistogramsFromContent(gBrowser.selectedBrowser, use_counter_middlefix);
yield grabHistogramsFromContent(use_counter_middlefix);
gBrowser.selectedBrowser.loadURI(gHttpTestRoot + "file_use_counter_outer.html");
yield waitForPageLoad(gBrowser.selectedBrowser);
@ -239,7 +244,7 @@ var check_use_counter_img = Task.async(function* (file, use_counter_middlefix) {
// Grab histograms again and compare.
let [histogram_page_after, histogram_document_after,
histogram_docs_after, histogram_toplevel_docs_after] =
yield grabHistogramsFromContent(gBrowser.selectedBrowser, use_counter_middlefix);
yield grabHistogramsFromContent(use_counter_middlefix, histogram_page_before);
is(histogram_page_after, histogram_page_before + 1,
"page counts for " + use_counter_middlefix + " after are correct");
is(histogram_document_after, histogram_document_before + 1,
@ -263,7 +268,7 @@ var check_use_counter_direct = Task.async(function* (file, use_counter_middlefix
// interested in.
let [histogram_page_before, histogram_document_before,
histogram_docs_before, histogram_toplevel_docs_before] =
yield grabHistogramsFromContent(gBrowser.selectedBrowser, use_counter_middlefix);
yield grabHistogramsFromContent(use_counter_middlefix);
gBrowser.selectedBrowser.loadURI(gHttpTestRoot + file);
yield ContentTask.spawn(gBrowser.selectedBrowser, null, function*() {
@ -292,7 +297,7 @@ var check_use_counter_direct = Task.async(function* (file, use_counter_middlefix
// Grab histograms again and compare.
let [histogram_page_after, histogram_document_after,
histogram_docs_after, histogram_toplevel_docs_after] =
yield grabHistogramsFromContent(gBrowser.selectedBrowser, use_counter_middlefix);
yield grabHistogramsFromContent(use_counter_middlefix, histogram_page_before);
(xfail ? todo_is : is)(histogram_page_after, histogram_page_before + 1,
"page counts for " + use_counter_middlefix + " after are correct");
(xfail ? todo_is : is)(histogram_document_after, histogram_document_before + 1,

Просмотреть файл

@ -5413,3 +5413,19 @@ ContentParent::ForceTabPaint(TabParent* aTabParent, uint64_t aLayerObserverEpoch
}
ProcessHangMonitor::ForcePaint(mHangMonitorActor, aTabParent, aLayerObserverEpoch);
}
bool
ContentParent::RecvAccumulateChildHistogram(
InfallibleTArray<Accumulation>&& aAccumulations)
{
Telemetry::AccumulateChild(aAccumulations);
return true;
}
bool
ContentParent::RecvAccumulateChildKeyedHistogram(
InfallibleTArray<KeyedAccumulation>&& aAccumulations)
{
Telemetry::AccumulateChildKeyed(aAccumulations);
return true;
}

Просмотреть файл

@ -1137,6 +1137,10 @@ private:
virtual bool RecvDeleteGetFilesRequest(const nsID& aID) override;
virtual bool RecvAccumulateChildHistogram(
InfallibleTArray<Accumulation>&& aAccumulations) override;
virtual bool RecvAccumulateChildKeyedHistogram(
InfallibleTArray<KeyedAccumulation>&& aAccumulations) override;
public:
void SendGetFilesResponseAndForget(const nsID& aID,
const GetFilesResponseResult& aResult);

Просмотреть файл

@ -837,6 +837,14 @@ child:
*/
async Print(uint64_t aOuterWindowID, PrintData aPrintData);
/**
* Update the child with the tab's current top-level native window handle.
* This is used by a11y objects who must expose their native window.
*
* @param aNewHandle The native window handle of the tab's top-level window.
*/
async UpdateNativeWindowHandle(uintptr_t aNewHandle);
/*
* FIXME: write protocol!

Просмотреть файл

@ -100,6 +100,8 @@ using mozilla::DataStorageType from "ipc/DataStorageIPCUtils.h";
using mozilla::DocShellOriginAttributes from "mozilla/ipc/BackgroundUtils.h";
using struct mozilla::layers::TextureFactoryIdentifier from "mozilla/layers/CompositorTypes.h";
using struct mozilla::dom::FlyWebPublishOptions from "mozilla/dom/FlyWebPublishOptionsIPCSerializer.h";
using mozilla::Telemetry::Accumulation from "mozilla/TelemetryComms.h";
using mozilla::Telemetry::KeyedAccumulation from "mozilla/TelemetryComms.h";
union ChromeRegistryItem
{
@ -1216,6 +1218,12 @@ parent:
async UnstoreAndBroadcastBlobURLUnregistration(nsCString url);
/**
* Messages for communicating child Telemetry to the parent process
*/
async AccumulateChildHistogram(Accumulation[] accumulations);
async AccumulateChildKeyedHistogram(KeyedAccumulation[] accumulations);
both:
async AsyncMessage(nsString aMessage, CpowEntry[] aCpows,
Principal aPrincipal, ClonedMessageData aData);

Просмотреть файл

@ -549,6 +549,9 @@ TabChild::TabChild(nsIContentChild* aManager,
, mDidLoadURLInit(false)
, mAPZChild(nullptr)
, mLayerObserverEpoch(0)
#if defined(XP_WIN) && defined(ACCESSIBILITY)
, mNativeWindowHandle(0)
#endif
{
// In the general case having the TabParent tell us if APZ is enabled or not
// doesn't really work because the TabParent itself may not have a reference
@ -2534,6 +2537,17 @@ TabChild::RecvPrint(const uint64_t& aOuterWindowID, const PrintData& aPrintData)
return true;
}
bool
TabChild::RecvUpdateNativeWindowHandle(const uintptr_t& aNewHandle)
{
#if defined(XP_WIN) && defined(ACCESSIBILITY)
mNativeWindowHandle = aNewHandle;
return true;
#else
return false;
#endif
}
bool
TabChild::RecvDestroy()
{

Просмотреть файл

@ -590,6 +590,8 @@ public:
virtual bool RecvPrint(const uint64_t& aOuterWindowID,
const PrintData& aPrintData) override;
virtual bool RecvUpdateNativeWindowHandle(const uintptr_t& aNewHandle) override;
/**
* Native widget remoting protocol for use with windowed plugins with e10s.
*/
@ -651,6 +653,10 @@ public:
// Request that the docshell be marked as active.
void ForcePaint(uint64_t aLayerObserverEpoch);
#if defined(XP_WIN) && defined(ACCESSIBILITY)
uintptr_t GetNativeWindowHandle() const { return mNativeWindowHandle; }
#endif
protected:
virtual ~TabChild();
@ -789,6 +795,11 @@ private:
// The most recently seen layer observer epoch in RecvSetDocShellIsActive.
uint64_t mLayerObserverEpoch;
#if defined(XP_WIN) && defined(ACCESSIBILITY)
// The handle associated with the native window that contains this tab
uintptr_t mNativeWindowHandle;
#endif // defined(XP_WIN)
DISALLOW_EVIL_CONSTRUCTORS(TabChild);
};

Просмотреть файл

@ -400,6 +400,17 @@ TabParent::SetOwnerElement(Element* aElement)
Unused << SendSetUseGlobalHistory(useGlobalHistory);
}
#if defined(XP_WIN) && defined(ACCESSIBILITY)
if (!mIsDestroyed) {
uintptr_t newWindowHandle = 0;
if (nsCOMPtr<nsIWidget> widget = GetWidget()) {
newWindowHandle =
reinterpret_cast<uintptr_t>(widget->GetNativeData(NS_NATIVE_WINDOW));
}
Unused << SendUpdateNativeWindowHandle(newWindowHandle);
}
#endif
AddWindowListeners();
TryCacheDPIAndScale();
}

Просмотреть файл

@ -1,6 +1,6 @@
# 468496-1 will also detect bugs in video drivers.
== 468496-1.html 468496-1-ref.html
fuzzy-if(winWidget,175,443) == 611498-1.html 611498-ref.html
fuzzy(175,443) == 611498-1.html 611498-ref.html
skip-if(B2G) fuzzy-if(Android,8,1000) == 709477-1.html 709477-1-ref.html # bug 773482
skip-if(!asyncPan) == 1086723.html 1086723-ref.html
== 853889-1.html 853889-1-ref.html

Просмотреть файл

@ -538,6 +538,10 @@ private:
// This affects whether events will be routed through APZ or not.
DECL_GFX_PREF(Live, "mousewheel.system_scroll_override_on_root_content.enabled",
MouseWheelHasRootScrollDeltaOverride, bool, false);
DECL_GFX_PREF(Live, "mousewheel.system_scroll_override_on_root_content.horizontal.factor",
MouseWheelRootScrollHorizontalFactor, int32_t, 0);
DECL_GFX_PREF(Live, "mousewheel.system_scroll_override_on_root_content.vertical.factor",
MouseWheelRootScrollVerticalFactor, int32_t, 0);
DECL_GFX_PREF(Live, "mousewheel.transaction.ignoremovedelay",MouseWheelIgnoreMoveDelayMs, int32_t, (int32_t)100);
DECL_GFX_PREF(Live, "mousewheel.transaction.timeout", MouseWheelTransactionTimeoutMs, int32_t, (int32_t)1500);

Просмотреть файл

@ -488,6 +488,7 @@ MessageChannel::MessageChannel(MessageListener *aListener)
mSawInterruptOutMsg(false),
mIsWaitingForIncoming(false),
mAbortOnError(false),
mNotifiedChannelDone(false),
mFlags(REQUIRE_DEFAULT),
mPeerPidSet(false),
mPeerPid(-1)
@ -2081,6 +2082,13 @@ MessageChannel::NotifyMaybeChannelError()
// Oops, error! Let the listener know about it.
mChannelState = ChannelError;
// IPDL assumes these notifications do not fire twice, so we do not let
// that happen.
if (mNotifiedChannelDone) {
return;
}
mNotifiedChannelDone = true;
// After this, the channel may be deleted. Based on the premise that
// mListener owns this channel, any calls back to this class that may
// work with mListener should still work on living objects.
@ -2238,6 +2246,13 @@ MessageChannel::NotifyChannelClosed()
Clear();
// IPDL assumes these notifications do not fire twice, so we do not let
// that happen.
if (mNotifiedChannelDone) {
return;
}
mNotifiedChannelDone = true;
// OK, the IO thread just closed the channel normally. Let the
// listener know about it. After this point the channel may be
// deleted.

Просмотреть файл

@ -783,6 +783,10 @@ class MessageChannel : HasResultCodes
// a channel error occurs?
bool mAbortOnError;
// True if the listener has already been notified of a channel close or
// error.
bool mNotifiedChannelDone;
// See SetChannelFlags
ChannelFlags mFlags;

Просмотреть файл

@ -509,6 +509,9 @@ public:
return true;
}
bool IsValid() const {
return mValid;
}
private:
friend struct IPC::ParamTraits<Endpoint<PFooSide>>;
@ -649,7 +652,10 @@ struct ParamTraits<mozilla::ipc::Endpoint<PFooSide>>
static void Write(Message* aMsg, const paramType& aParam)
{
MOZ_RELEASE_ASSERT(aParam.mValid);
IPC::WriteParam(aMsg, aParam.mValid);
if (!aParam.mValid) {
return;
}
IPC::WriteParam(aMsg, static_cast<uint32_t>(aParam.mMode));
@ -668,7 +674,15 @@ struct ParamTraits<mozilla::ipc::Endpoint<PFooSide>>
static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
{
MOZ_RELEASE_ASSERT(!aResult->mValid);
aResult->mValid = true;
if (!IPC::ReadParam(aMsg, aIter, &aResult->mValid)) {
return false;
}
if (!aResult->mValid) {
// Object is empty, but read succeeded.
return true;
}
uint32_t mode, protocolId;
if (!IPC::ReadParam(aMsg, aIter, &mode) ||
!IPC::ReadParam(aMsg, aIter, &aResult->mTransport) ||

Просмотреть файл

@ -2384,7 +2384,10 @@ def _generateCxxUnion(ud):
# ~Union()
dtor = DestructorDefn(DestructorDecl(ud.name))
dtor.addstmt(StmtExpr(callMaybeDestroy(tnonevar)))
# The void cast prevents Coverity from complaining about missing return
# value checks.
dtor.addstmt(StmtExpr(ExprCast(callMaybeDestroy(tnonevar), Type.VOID,
static=1)))
cls.addstmts([ dtor, Whitespace.NL ])
# type()
@ -2425,9 +2428,14 @@ def _generateCxxUnion(ud):
StmtBreak()
])
opeqswitch.addcase(CaseLabel(c.enum()), case)
opeqswitch.addcase(CaseLabel(tnonevar.name),
StmtBlock([ StmtExpr(callMaybeDestroy(rhstypevar)),
StmtBreak() ]))
opeqswitch.addcase(
CaseLabel(tnonevar.name),
# The void cast prevents Coverity from complaining about missing return
# value checks.
StmtBlock([ StmtExpr(ExprCast(callMaybeDestroy(rhstypevar), Type.VOID,
static=1)),
StmtBreak() ])
)
opeqswitch.addcase(
DefaultLabel(),
StmtBlock([ _logicError('unreached'), StmtBreak() ]))

Просмотреть файл

@ -6074,6 +6074,8 @@ CodeGenerator::visitCreateArgumentsObject(LCreateArgumentsObject* lir)
Register objTemp = ToRegister(lir->temp1());
Register cxTemp = ToRegister(lir->temp2());
masm.Push(callObj);
// Try to allocate an arguments object. This will leave the reserved
// slots uninitialized, so it's important we don't GC until we
// initialize these slots in ArgumentsObject::finishForIon.
@ -6082,7 +6084,7 @@ CodeGenerator::visitCreateArgumentsObject(LCreateArgumentsObject* lir)
/* initContents = */ false);
masm.moveStackPtrTo(temp);
masm.addPtr(Imm32(frameSize()), temp);
masm.addPtr(Imm32(masm.framePushed()), temp);
masm.setupUnalignedABICall(cxTemp);
masm.loadJSContext(cxTemp);
@ -6092,10 +6094,14 @@ CodeGenerator::visitCreateArgumentsObject(LCreateArgumentsObject* lir)
masm.passABIArg(objTemp);
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, ArgumentsObject::finishForIon));
masm.branchTestPtr(Assembler::Zero, ReturnReg, ReturnReg, masm.exceptionLabel());
masm.branchTestPtr(Assembler::Zero, ReturnReg, ReturnReg, &failure);
// Discard saved callObj on the stack.
masm.addToStackPtr(Imm32(sizeof(uintptr_t)));
masm.jump(&done);
masm.bind(&failure);
masm.Pop(callObj);
}
masm.moveStackPtrTo(temp);

Просмотреть файл

@ -383,7 +383,9 @@ ArgumentsObject::finishForIon(JSContext* cx, jit::JitFrameLayout* frame,
ArgumentsData* data =
reinterpret_cast<ArgumentsData*>(AllocateObjectBuffer<uint8_t>(cx, obj, numBytes));
if (!data) {
// Make the object safe for GC.
// Make the object safe for GC. Don't report OOM, the slow path will
// retry the allocation.
cx->recoverFromOutOfMemory();
obj->initFixedSlot(DATA_SLOT, PrivateValue(nullptr));
return nullptr;
}

Просмотреть файл

@ -49,17 +49,20 @@ jobs:
when:
files-changed:
- build/**
- config/**
- configure.py
- dom/bindings/**
- intl/icu/**
- js/moz.configure
- layout/tools/reftest/reftest/**
- Makefile.in
- media/webrtc/trunk/tools/gyp/**
- memory/**
- mfbt/**
- modules/fdlibm/**
- modules/zlib/src/**
- mozglue/**
- moz.build
- moz.configure
- nsprpub/**
- python/**

Просмотреть файл

@ -1,3 +1,4 @@
[iframe-allow-fullscreen.html.ini]
type: testharness
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1282024
prefs: [full-screen-api.unprefix.enabled:true]

Просмотреть файл

@ -14,6 +14,7 @@ import sys
import tempfile
import unittest
from buildconfig import substs
from StringIO import StringIO
from mozlog import structured
from mozbuild.base import MozbuildObject
@ -27,7 +28,6 @@ mozinfo.find_and_update_from_json()
objdir = build_obj.topobjdir.encode("utf-8")
if mozinfo.isMac:
from buildconfig import substs
xpcshellBin = os.path.join(objdir, "dist", substs['MOZ_MACBUNDLE_NAME'], "Contents", "MacOS", "xpcshell")
else:
xpcshellBin = os.path.join(objdir, "dist", "bin", "xpcshell")
@ -858,7 +858,9 @@ add_test({
self.assertTestResult(False)
self.assertInLog(TEST_FAIL_STRING)
self.assertInLog("test_simple_uncaught_rejection.js:3:3")
if not substs.get('RELEASE_BUILD'):
# async stacks are currently not enabled in release builds.
self.assertInLog("test_simple_uncaught_rejection.js:3:3")
self.assertInLog("Test rejection.")
self.assertEquals(1, self.x.testCount)
self.assertEquals(0, self.x.passCount)

Просмотреть файл

@ -2348,6 +2348,13 @@ TelemetryImpl::ClearScalars()
return NS_OK;
}
NS_IMETHODIMP
TelemetryImpl::FlushBatchedChildTelemetry()
{
TelemetryHistogram::IPCTimerFired(nullptr, nullptr);
return NS_OK;
}
size_t
TelemetryImpl::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
{
@ -2793,6 +2800,18 @@ AccumulateTimeDelta(ID aHistogram, TimeStamp start, TimeStamp end)
static_cast<uint32_t>((end - start).ToMilliseconds()));
}
void
AccumulateChild(const nsTArray<Accumulation>& aAccumulations)
{
TelemetryHistogram::AccumulateChild(aAccumulations);
}
void
AccumulateChildKeyed(const nsTArray<KeyedAccumulation>& aAccumulations)
{
TelemetryHistogram::AccumulateChildKeyed(aAccumulations);
}
void
ClearHistogram(ID aId)
{

Просмотреть файл

@ -33,6 +33,9 @@ namespace HangMonitor {
} // namespace HangMonitor
namespace Telemetry {
struct Accumulation;
struct KeyedAccumulation;
enum TimerResolution {
Millisecond,
Microsecond
@ -125,6 +128,20 @@ void AccumulateCategorical(ID id, const nsCString& label);
*/
void AccumulateTimeDelta(ID id, TimeStamp start, TimeStamp end = TimeStamp::Now());
/**
* Accumulate child data into child histograms
*
* @param aAccumulations - accumulation actions to perform
*/
void AccumulateChild(const nsTArray<Accumulation>& aAccumulations);
/**
* Accumulate child data into child keyed histograms
*
* @param aAccumulations - accumulation actions to perform
*/
void AccumulateChildKeyed(const nsTArray<KeyedAccumulation>& aAccumulations);
/**
* This clears the data for a histogram in TelemetryHistogramEnums.h.
*

Просмотреть файл

@ -0,0 +1,84 @@
/* -*- 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
*/
#ifndef Telemetry_Comms_h__
#define Telemetry_Comms_h__
#include "ipc/IPCMessageUtils.h"
namespace mozilla {
namespace Telemetry {
enum ID : uint32_t;
struct Accumulation
{
mozilla::Telemetry::ID mId;
uint32_t mSample;
};
struct KeyedAccumulation
{
mozilla::Telemetry::ID mId;
uint32_t mSample;
nsCString mKey;
};
} // namespace Telemetry
} // namespace mozilla
namespace IPC {
template<>
struct
ParamTraits<mozilla::Telemetry::Accumulation>
{
typedef mozilla::Telemetry::Accumulation paramType;
static void Write(Message* aMsg, const paramType& aParam)
{
aMsg->WriteUInt32(aParam.mId);
WriteParam(aMsg, aParam.mSample);
}
static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
{
if (!aMsg->ReadUInt32(aIter, reinterpret_cast<uint32_t*>(&(aResult->mId))) ||
!ReadParam(aMsg, aIter, &(aResult->mSample))) {
return false;
}
return true;
}
};
template<>
struct
ParamTraits<mozilla::Telemetry::KeyedAccumulation>
{
typedef mozilla::Telemetry::KeyedAccumulation paramType;
static void Write(Message* aMsg, const paramType& aParam)
{
aMsg->WriteUInt32(aParam.mId);
WriteParam(aMsg, aParam.mSample);
WriteParam(aMsg, aParam.mKey);
}
static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
{
if (!aMsg->ReadUInt32(aIter, reinterpret_cast<uint32_t*>(&(aResult->mId))) ||
!ReadParam(aMsg, aIter, &(aResult->mSample)) ||
!ReadParam(aMsg, aIter, &(aResult->mKey))) {
return false;
}
return true;
}
};
} // namespace IPC
#endif // Telemetry_Comms_h__

Просмотреть файл

@ -14,9 +14,13 @@
#include "nsClassHashtable.h"
#include "nsITelemetry.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/ToJSValue.h"
#include "mozilla/Atomics.h"
#include "mozilla/StartupTimeline.h"
#include "mozilla/StaticMutex.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/Unused.h"
#include "TelemetryCommon.h"
#include "TelemetryHistogram.h"
@ -31,6 +35,9 @@ using base::FlagHistogram;
using base::LinearHistogram;
using mozilla::StaticMutex;
using mozilla::StaticMutexAutoLock;
using mozilla::StaticAutoPtr;
using mozilla::Telemetry::Accumulation;
using mozilla::Telemetry::KeyedAccumulation;
////////////////////////////////////////////////////////////////////////
@ -92,6 +99,7 @@ using mozilla::StaticMutexAutoLock;
#define EXPIRED_ID "__expired__"
#define SUBSESSION_HISTOGRAM_PREFIX "sub#"
#define KEYED_HISTOGRAM_NAME_SEPARATOR "#"
#define CHILD_HISTOGRAM_SUFFIX "#content"
namespace {
@ -185,6 +193,13 @@ AddonMapType gAddonMap;
// The singleton StatisticsRecorder object for this process.
base::StatisticsRecorder* gStatisticsRecorder = nullptr;
// For batching and sending child process accumulations to the parent
nsITimer* gIPCTimer = nullptr;
mozilla::Atomic<bool, mozilla::Relaxed> gIPCTimerArmed(false);
mozilla::Atomic<bool, mozilla::Relaxed> gIPCTimerArming(false);
StaticAutoPtr<nsTArray<Accumulation>> gAccumulations;
StaticAutoPtr<nsTArray<KeyedAccumulation>> gKeyedAccumulations;
} // namespace
@ -204,6 +219,17 @@ const mozilla::Telemetry::ID kRecordingInitiallyDisabledIDs[] = {
mozilla::Telemetry::TELEMETRY_TEST_KEYED_COUNT_INIT_NO_RECORD
};
// Sending each remote accumulation immediately places undue strain on the
// IPC subsystem. Batch the remote accumulations for a period of time before
// sending them all at once. This value was chosen as a balance between data
// timeliness and performance (see bug 1218576)
const uint32_t kBatchTimeoutMs = 2000;
// To stop growing unbounded in memory while waiting for kBatchTimeoutMs to
// drain the g*Accumulations arrays, request an immediate flush if the arrays
// manage to reach this high water mark of elements.
const size_t kAccumulationsArrayHighWaterMark = 5 * 1024;
} // namespace
@ -311,6 +337,16 @@ HistogramInfo::label_id(const char* label, uint32_t* labelId) const
return NS_ERROR_FAILURE;
}
bool
StringEndsWith(const std::string& name, const std::string& suffix)
{
if (name.size() < suffix.size()) {
return false;
}
return name.compare(name.size() - suffix.size(), suffix.size(), suffix) == 0;
}
} // namespace
@ -395,6 +431,18 @@ internal_HistogramGet(const char *name, const char *expiration,
return NS_OK;
}
CharPtrEntryType*
internal_GetHistogramMapEntry(const char* name)
{
nsDependentCString histogramName(name);
NS_NAMED_LITERAL_CSTRING(suffix, CHILD_HISTOGRAM_SUFFIX);
if (!StringEndsWith(histogramName, suffix)) {
return gHistogramMap.GetEntry(name);
}
auto root = Substring(histogramName, 0, histogramName.Length() - suffix.Length());
return gHistogramMap.GetEntry(PromiseFlatCString(root).get());
}
nsresult
internal_GetHistogramEnumId(const char *name, mozilla::Telemetry::ID *id)
{
@ -402,7 +450,7 @@ internal_GetHistogramEnumId(const char *name, mozilla::Telemetry::ID *id)
return NS_ERROR_FAILURE;
}
CharPtrEntryType *entry = gHistogramMap.GetEntry(name);
CharPtrEntryType *entry = internal_GetHistogramMapEntry(name);
if (!entry) {
return NS_ERROR_INVALID_ARG;
}
@ -412,10 +460,12 @@ internal_GetHistogramEnumId(const char *name, mozilla::Telemetry::ID *id)
// O(1) histogram lookup by numeric id
nsresult
internal_GetHistogramByEnumId(mozilla::Telemetry::ID id, Histogram **ret)
internal_GetHistogramByEnumId(mozilla::Telemetry::ID id, Histogram **ret,
bool child = false)
{
static Histogram* knownHistograms[mozilla::Telemetry::HistogramCount] = {0};
Histogram *h = knownHistograms[id];
static Histogram* knownChildHistograms[mozilla::Telemetry::HistogramCount] = {0};
Histogram *h = child ? knownChildHistograms[id] : knownHistograms[id];
if (h) {
*ret = h;
return NS_OK;
@ -426,8 +476,15 @@ internal_GetHistogramByEnumId(mozilla::Telemetry::ID id, Histogram **ret)
return NS_ERROR_FAILURE;
}
nsresult rv = internal_HistogramGet(p.id(), p.expiration(), p.histogramType,
p.min, p.max, p.bucketCount, true, &h);
nsCString histogramName;
histogramName.Append(p.id());
if (child) {
histogramName.AppendLiteral(CHILD_HISTOGRAM_SUFFIX);
}
nsresult rv = internal_HistogramGet(histogramName.get(), p.expiration(),
p.histogramType, p.min, p.max,
p.bucketCount, true, &h);
if (NS_FAILED(rv))
return rv;
@ -447,7 +504,11 @@ internal_GetHistogramByEnumId(mozilla::Telemetry::ID id, Histogram **ret)
}
#endif
*ret = knownHistograms[id] = h;
if (child) {
*ret = knownChildHistograms[id] = h;
} else {
*ret = knownHistograms[id] = h;
}
return NS_OK;
}
@ -461,7 +522,9 @@ internal_GetHistogramByName(const nsACString &name, Histogram **ret)
return rv;
}
rv = internal_GetHistogramByEnumId(id, ret);
bool isChild = StringEndsWith(name,
NS_LITERAL_CSTRING(CHILD_HISTOGRAM_SUFFIX));
rv = internal_GetHistogramByEnumId(id, ret, isChild);
if (NS_FAILED(rv))
return rv;
@ -517,6 +580,7 @@ internal_CloneHistogram(const nsACString& newName,
}
#if !defined(MOZ_WIDGET_GONK) && !defined(MOZ_WIDGET_ANDROID)
Histogram*
internal_GetSubsessionHistogram(Histogram& existing)
{
@ -527,9 +591,14 @@ internal_GetSubsessionHistogram(Histogram& existing)
return nullptr;
}
bool isChild = StringEndsWith(existing.histogram_name(),
CHILD_HISTOGRAM_SUFFIX);
static Histogram* subsession[mozilla::Telemetry::HistogramCount] = {};
if (subsession[id]) {
return subsession[id];
static Histogram* subsessionChild[mozilla::Telemetry::HistogramCount] = {};
Histogram* cached = isChild ? subsessionChild[id] : subsession[id];
if (cached) {
return cached;
}
NS_NAMED_LITERAL_CSTRING(prefix, SUBSESSION_HISTOGRAM_PREFIX);
@ -539,10 +608,15 @@ internal_GetSubsessionHistogram(Histogram& existing)
}
nsCString subsessionName(prefix);
subsessionName.Append(existingName);
subsessionName.Append(existing.histogram_name().c_str());
subsession[id] = internal_CloneHistogram(subsessionName, id, existing);
return subsession[id];
Histogram* clone = internal_CloneHistogram(subsessionName, id, existing);
if (isChild) {
subsessionChild[id] = clone;
} else {
subsession[id] = clone;
}
return clone;
}
#endif
@ -591,26 +665,13 @@ internal_HistogramAdd(Histogram& histogram, int32_t value)
return internal_HistogramAdd(histogram, value, dataset);
}
nsresult
internal_HistogramAddCategorical(mozilla::Telemetry::ID id, const nsCString& label)
{
uint32_t labelId = 0;
if (NS_FAILED(gHistograms[id].label_id(label.get(), &labelId))) {
return NS_ERROR_ILLEGAL_VALUE;
}
Histogram* h = nullptr;
nsresult rv = internal_GetHistogramByEnumId(id, &h);
if (NS_FAILED(rv)) {
return rv;
}
return internal_HistogramAdd(*h, labelId);
}
void
internal_HistogramClear(Histogram& aHistogram, bool onlySubsession)
{
MOZ_ASSERT(XRE_IsParentProcess());
if (!XRE_IsParentProcess()) {
return;
}
if (!onlySubsession) {
aHistogram.Clear();
}
@ -806,6 +867,8 @@ public:
nsresult Add(const nsCString& key, uint32_t aSample);
void Clear(bool subsession);
nsresult GetEnumId(mozilla::Telemetry::ID& id);
private:
typedef nsBaseHashtableET<nsCStringHashKey, Histogram*> KeyedHistogramEntry;
typedef AutoHashtable<KeyedHistogramEntry> KeyedHistogramMapType;
@ -948,6 +1011,10 @@ KeyedHistogram::Add(const nsCString& key, uint32_t sample)
void
KeyedHistogram::Clear(bool onlySubsession)
{
MOZ_ASSERT(XRE_IsParentProcess());
if (!XRE_IsParentProcess()) {
return;
}
#if !defined(MOZ_WIDGET_GONK) && !defined(MOZ_WIDGET_ANDROID)
for (auto iter = mSubsessionMap.Iter(); !iter.Done(); iter.Next()) {
iter.Get()->mData->Clear();
@ -1035,6 +1102,12 @@ KeyedHistogram::GetJSSnapshot(JSContext* cx, JS::Handle<JSObject*> obj,
return NS_OK;
}
nsresult
KeyedHistogram::GetEnumId(mozilla::Telemetry::ID& id)
{
return internal_GetHistogramEnumId(mName.get(), &id);
}
} // namespace
@ -1060,6 +1133,319 @@ internal_GetKeyedHistogramById(const nsACString &name)
} // namespace
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
//
// PRIVATE: functions related to addon histograms
namespace {
// Compute the name to pass into Histogram for the addon histogram
// 'name' from the addon 'id'. We can't use 'name' directly because it
// might conflict with other histograms in other addons or even with our
// own.
void
internal_AddonHistogramName(const nsACString &id, const nsACString &name,
nsACString &ret)
{
ret.Append(id);
ret.Append(':');
ret.Append(name);
}
bool
internal_CreateHistogramForAddon(const nsACString &name,
AddonHistogramInfo &info)
{
Histogram *h;
nsresult rv = internal_HistogramGet(PromiseFlatCString(name).get(), "never",
info.histogramType, info.min, info.max,
info.bucketCount, true, &h);
if (NS_FAILED(rv)) {
return false;
}
// Don't let this histogram be reported via the normal means
// (e.g. Telemetry.registeredHistograms); we'll make it available in
// other ways.
h->ClearFlags(Histogram::kUmaTargetedHistogramFlag);
info.h = h;
return true;
}
bool
internal_AddonHistogramReflector(AddonHistogramEntryType *entry,
JSContext *cx, JS::Handle<JSObject*> obj)
{
AddonHistogramInfo &info = entry->mData;
// Never even accessed the histogram.
if (!info.h) {
// Have to force creation of HISTOGRAM_FLAG histograms.
if (info.histogramType != nsITelemetry::HISTOGRAM_FLAG)
return true;
if (!internal_CreateHistogramForAddon(entry->GetKey(), info)) {
return false;
}
}
if (internal_IsEmpty(info.h)) {
return true;
}
JS::Rooted<JSObject*> snapshot(cx, JS_NewPlainObject(cx));
if (!snapshot) {
// Just consider this to be skippable.
return true;
}
switch (internal_ReflectHistogramSnapshot(cx, snapshot, info.h)) {
case REFLECT_FAILURE:
case REFLECT_CORRUPT:
return false;
case REFLECT_OK:
const nsACString &histogramName = entry->GetKey();
if (!JS_DefineProperty(cx, obj, PromiseFlatCString(histogramName).get(),
snapshot, JSPROP_ENUMERATE)) {
return false;
}
break;
}
return true;
}
bool
internal_AddonReflector(AddonEntryType *entry, JSContext *cx,
JS::Handle<JSObject*> obj)
{
const nsACString &addonId = entry->GetKey();
JS::Rooted<JSObject*> subobj(cx, JS_NewPlainObject(cx));
if (!subobj) {
return false;
}
AddonHistogramMapType *map = entry->mData;
if (!(map->ReflectIntoJS(internal_AddonHistogramReflector, cx, subobj)
&& JS_DefineProperty(cx, obj, PromiseFlatCString(addonId).get(),
subobj, JSPROP_ENUMERATE))) {
return false;
}
return true;
}
} // namespace
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
//
// PRIVATE: thread-unsafe helpers for the external interface
// This is a StaticMutex rather than a plain Mutex (1) so that
// it gets initialised in a thread-safe manner the first time
// it is used, and (2) because it is never de-initialised, and
// a normal Mutex would show up as a leak in BloatView. StaticMutex
// also has the "OffTheBooks" property, so it won't show as a leak
// in BloatView.
static StaticMutex gTelemetryHistogramMutex;
namespace {
void
internal_SetHistogramRecordingEnabled(mozilla::Telemetry::ID aID, bool aEnabled)
{
if (!internal_IsHistogramEnumId(aID)) {
MOZ_ASSERT(false, "Telemetry::SetHistogramRecordingEnabled(...) must be used with an enum id");
return;
}
if (gHistograms[aID].keyed) {
const nsDependentCString id(gHistograms[aID].id());
KeyedHistogram* keyed = internal_GetKeyedHistogramById(id);
if (keyed) {
keyed->SetRecordingEnabled(aEnabled);
return;
}
} else {
Histogram *h;
nsresult rv = internal_GetHistogramByEnumId(aID, &h);
if (NS_SUCCEEDED(rv)) {
h->SetRecordingEnabled(aEnabled);
return;
}
}
MOZ_ASSERT(false, "Telemetry::SetHistogramRecordingEnabled(...) id not found");
}
void internal_armIPCTimerMainThread()
{
MOZ_ASSERT(NS_IsMainThread());
gIPCTimerArming = false;
if (gIPCTimerArmed) {
return;
}
if (!gIPCTimer) {
CallCreateInstance(NS_TIMER_CONTRACTID, &gIPCTimer);
}
if (gIPCTimer) {
gIPCTimer->InitWithFuncCallback(TelemetryHistogram::IPCTimerFired,
nullptr, kBatchTimeoutMs,
nsITimer::TYPE_ONE_SHOT);
gIPCTimerArmed = true;
}
}
void internal_armIPCTimer()
{
if (gIPCTimerArmed || gIPCTimerArming) {
return;
}
gIPCTimerArming = true;
if (NS_IsMainThread()) {
internal_armIPCTimerMainThread();
} else {
NS_DispatchToMainThread(NS_NewRunnableFunction([]() -> void {
StaticMutexAutoLock locker(gTelemetryHistogramMutex);
internal_armIPCTimerMainThread();
}));
}
}
bool
internal_RemoteAccumulate(mozilla::Telemetry::ID aId, uint32_t aSample)
{
if (XRE_IsParentProcess()) {
return false;
}
if (!gAccumulations) {
gAccumulations = new nsTArray<Accumulation>();
}
if (gAccumulations->Length() == kAccumulationsArrayHighWaterMark) {
NS_DispatchToMainThread(NS_NewRunnableFunction([]() -> void {
TelemetryHistogram::IPCTimerFired(nullptr, nullptr);
}));
}
gAccumulations->AppendElement(Accumulation{aId, aSample});
internal_armIPCTimer();
return true;
}
bool
internal_RemoteAccumulate(mozilla::Telemetry::ID aId,
const nsCString& aKey, uint32_t aSample)
{
if (XRE_IsParentProcess()) {
return false;
}
if (!gKeyedAccumulations) {
gKeyedAccumulations = new nsTArray<KeyedAccumulation>();
}
if (gKeyedAccumulations->Length() == kAccumulationsArrayHighWaterMark) {
NS_DispatchToMainThread(NS_NewRunnableFunction([]() -> void {
TelemetryHistogram::IPCTimerFired(nullptr, nullptr);
}));
}
gKeyedAccumulations->AppendElement(KeyedAccumulation{aId, aSample, aKey});
internal_armIPCTimer();
return true;
}
void internal_Accumulate(mozilla::Telemetry::ID aHistogram, uint32_t aSample)
{
bool isValid = internal_IsHistogramEnumId(aHistogram);
MOZ_ASSERT(isValid, "Accumulation using invalid id");
if (!internal_CanRecordBase() || !isValid ||
internal_RemoteAccumulate(aHistogram, aSample)) {
return;
}
Histogram *h;
nsresult rv = internal_GetHistogramByEnumId(aHistogram, &h);
if (NS_SUCCEEDED(rv)) {
internal_HistogramAdd(*h, aSample, gHistograms[aHistogram].dataset);
}
}
void
internal_Accumulate(mozilla::Telemetry::ID aID,
const nsCString& aKey, uint32_t aSample)
{
bool isValid = internal_IsHistogramEnumId(aID);
MOZ_ASSERT(isValid, "Child keyed telemetry accumulation using invalid id");
if (!gInitDone || !internal_CanRecordBase() || !isValid ||
internal_RemoteAccumulate(aID, aKey, aSample)) {
return;
}
const HistogramInfo& th = gHistograms[aID];
KeyedHistogram* keyed
= internal_GetKeyedHistogramById(nsDependentCString(th.id()));
MOZ_ASSERT(keyed);
keyed->Add(aKey, aSample);
}
void
internal_Accumulate(Histogram& aHistogram, uint32_t aSample)
{
if (XRE_IsParentProcess()) {
internal_HistogramAdd(aHistogram, aSample);
return;
}
mozilla::Telemetry::ID id;
nsresult rv = internal_GetHistogramEnumId(aHistogram.histogram_name().c_str(), &id);
if (NS_SUCCEEDED(rv)) {
internal_RemoteAccumulate(id, aSample);
}
}
void
internal_Accumulate(KeyedHistogram& aKeyed,
const nsCString& aKey, uint32_t aSample)
{
if (XRE_IsParentProcess()) {
aKeyed.Add(aKey, aSample);
return;
}
mozilla::Telemetry::ID id;
if (NS_SUCCEEDED(aKeyed.GetEnumId(id))) {
internal_RemoteAccumulate(id, aKey, aSample);
}
}
void
internal_AccumulateChild(mozilla::Telemetry::ID aId, uint32_t aSample)
{
if (!internal_CanRecordBase()) {
return;
}
Histogram* h;
nsresult rv = internal_GetHistogramByEnumId(aId, &h, true);
if (NS_SUCCEEDED(rv)) {
internal_HistogramAdd(*h, aSample, gHistograms[aId].dataset);
} else {
NS_WARNING("NS_FAILED GetHistogramByEnumId for CHILD");
}
}
void
internal_AccumulateChildKeyed(mozilla::Telemetry::ID aId,
const nsCString& aKey, uint32_t aSample)
{
if (!gInitDone || !internal_CanRecordBase()) {
return;
}
const HistogramInfo& th = gHistograms[aId];
nsCString id;
id.Append(th.id());
id.AppendLiteral(CHILD_HISTOGRAM_SUFFIX);
KeyedHistogram* keyed = internal_GetKeyedHistogramById(id);
MOZ_ASSERT(keyed);
keyed->Add(aKey, aSample);
}
} // namespace
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
//
@ -1102,52 +1488,47 @@ internal_JSHistogram_Add(JSContext *cx, unsigned argc, JS::Value *vp)
return true;
}
// If we don't have an argument for the count histogram, assume an increment of 1.
// Otherwise, make sure to run some sanity checks on the argument.
if ((type == base::CountHistogram::COUNT_HISTOGRAM) && (args.length() == 0)) {
internal_HistogramAdd(*h, 1);
return true;
}
// For categorical histograms we allow passing a string argument that specifies the label.
uint32_t value = 0;
mozilla::Telemetry::ID id;
if (type == base::LinearHistogram::LINEAR_HISTOGRAM &&
if ((type == base::CountHistogram::COUNT_HISTOGRAM) && (args.length() == 0)) {
// If we don't have an argument for the count histogram, assume an increment of 1.
// Otherwise, make sure to run some sanity checks on the argument.
value = 1;
} else if (type == base::LinearHistogram::LINEAR_HISTOGRAM &&
(args.length() > 0) && args[0].isString() &&
NS_SUCCEEDED(internal_GetHistogramEnumId(h->histogram_name().c_str(), &id)) &&
gHistograms[id].histogramType == nsITelemetry::HISTOGRAM_CATEGORICAL) {
// For categorical histograms we allow passing a string argument that specifies the label.
nsAutoJSString label;
if (!label.init(cx, args[0])) {
JS_ReportError(cx, "Invalid string parameter");
return false;
}
nsresult rv = internal_HistogramAddCategorical(id, NS_ConvertUTF16toUTF8(label));
nsresult rv = gHistograms[id].label_id(NS_ConvertUTF16toUTF8(label).get(), &value);
if (NS_FAILED(rv)) {
JS_ReportError(cx, "Unknown label for categorical histogram");
return false;
}
} else {
// All other accumulations expect one numerical argument.
if (!args.length()) {
JS_ReportError(cx, "Expected one argument");
return false;
}
return true;
if (!(args[0].isNumber() || args[0].isBoolean())) {
JS_ReportError(cx, "Not a number");
return false;
}
if (!JS::ToUint32(cx, args[0], &value)) {
JS_ReportError(cx, "Failed to convert argument");
return false;
}
}
// All other accumulations expect one numerical argument.
int32_t value = 0;
if (!args.length()) {
JS_ReportError(cx, "Expected one argument");
return false;
}
if (!(args[0].isNumber() || args[0].isBoolean())) {
JS_ReportError(cx, "Not a number");
return false;
}
if (!JS::ToInt32(cx, args[0], &value)) {
JS_ReportError(cx, "Failed to convert argument");
return false;
}
internal_HistogramAdd(*h, value);
internal_Accumulate(*h, value);
return true;
}
@ -1395,7 +1776,7 @@ internal_JSKeyedHistogram_Add(JSContext *cx, unsigned argc, JS::Value *vp)
}
}
keyed->Add(NS_ConvertUTF16toUTF8(key), value);
internal_Accumulate(*keyed, NS_ConvertUTF16toUTF8(key), value);
return true;
}
@ -1545,184 +1926,11 @@ internal_WrapAndReturnKeyedHistogram(KeyedHistogram *h, JSContext *cx,
} // namespace
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
//
// PRIVATE: functions related to addon histograms
namespace {
// Compute the name to pass into Histogram for the addon histogram
// 'name' from the addon 'id'. We can't use 'name' directly because it
// might conflict with other histograms in other addons or even with our
// own.
void
internal_AddonHistogramName(const nsACString &id, const nsACString &name,
nsACString &ret)
{
ret.Append(id);
ret.Append(':');
ret.Append(name);
}
bool
internal_CreateHistogramForAddon(const nsACString &name,
AddonHistogramInfo &info)
{
Histogram *h;
nsresult rv = internal_HistogramGet(PromiseFlatCString(name).get(), "never",
info.histogramType, info.min, info.max,
info.bucketCount, true, &h);
if (NS_FAILED(rv)) {
return false;
}
// Don't let this histogram be reported via the normal means
// (e.g. Telemetry.registeredHistograms); we'll make it available in
// other ways.
h->ClearFlags(Histogram::kUmaTargetedHistogramFlag);
info.h = h;
return true;
}
bool
internal_AddonHistogramReflector(AddonHistogramEntryType *entry,
JSContext *cx, JS::Handle<JSObject*> obj)
{
AddonHistogramInfo &info = entry->mData;
// Never even accessed the histogram.
if (!info.h) {
// Have to force creation of HISTOGRAM_FLAG histograms.
if (info.histogramType != nsITelemetry::HISTOGRAM_FLAG)
return true;
if (!internal_CreateHistogramForAddon(entry->GetKey(), info)) {
return false;
}
}
if (internal_IsEmpty(info.h)) {
return true;
}
JS::Rooted<JSObject*> snapshot(cx, JS_NewPlainObject(cx));
if (!snapshot) {
// Just consider this to be skippable.
return true;
}
switch (internal_ReflectHistogramSnapshot(cx, snapshot, info.h)) {
case REFLECT_FAILURE:
case REFLECT_CORRUPT:
return false;
case REFLECT_OK:
const nsACString &histogramName = entry->GetKey();
if (!JS_DefineProperty(cx, obj, PromiseFlatCString(histogramName).get(),
snapshot, JSPROP_ENUMERATE)) {
return false;
}
break;
}
return true;
}
bool
internal_AddonReflector(AddonEntryType *entry, JSContext *cx,
JS::Handle<JSObject*> obj)
{
const nsACString &addonId = entry->GetKey();
JS::Rooted<JSObject*> subobj(cx, JS_NewPlainObject(cx));
if (!subobj) {
return false;
}
AddonHistogramMapType *map = entry->mData;
if (!(map->ReflectIntoJS(internal_AddonHistogramReflector, cx, subobj)
&& JS_DefineProperty(cx, obj, PromiseFlatCString(addonId).get(),
subobj, JSPROP_ENUMERATE))) {
return false;
}
return true;
}
} // namespace
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
//
// PRIVATE: thread-unsafe helpers for the external interface
namespace {
void
internal_SetHistogramRecordingEnabled(mozilla::Telemetry::ID aID, bool aEnabled)
{
if (!internal_IsHistogramEnumId(aID)) {
MOZ_ASSERT(false, "Telemetry::SetHistogramRecordingEnabled(...) must be used with an enum id");
return;
}
if (gHistograms[aID].keyed) {
const nsDependentCString id(gHistograms[aID].id());
KeyedHistogram* keyed = internal_GetKeyedHistogramById(id);
if (keyed) {
keyed->SetRecordingEnabled(aEnabled);
return;
}
} else {
Histogram *h;
nsresult rv = internal_GetHistogramByEnumId(aID, &h);
if (NS_SUCCEEDED(rv)) {
h->SetRecordingEnabled(aEnabled);
return;
}
}
MOZ_ASSERT(false, "Telemetry::SetHistogramRecordingEnabled(...) id not found");
}
void internal_Accumulate(mozilla::Telemetry::ID aHistogram, uint32_t aSample)
{
if (!internal_CanRecordBase()) {
return;
}
Histogram *h;
nsresult rv = internal_GetHistogramByEnumId(aHistogram, &h);
if (NS_SUCCEEDED(rv)) {
internal_HistogramAdd(*h, aSample, gHistograms[aHistogram].dataset);
}
}
void
internal_Accumulate(mozilla::Telemetry::ID aID,
const nsCString& aKey, uint32_t aSample)
{
if (!gInitDone || !internal_CanRecordBase()) {
return;
}
const HistogramInfo& th = gHistograms[aID];
KeyedHistogram* keyed
= internal_GetKeyedHistogramById(nsDependentCString(th.id()));
MOZ_ASSERT(keyed);
keyed->Add(aKey, aSample);
}
} // namespace
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
//
// EXTERNALLY VISIBLE FUNCTIONS in namespace TelemetryHistogram::
// This is a StaticMutex rather than a plain Mutex (1) so that
// it gets initialised in a thread-safe manner the first time
// it is used, and (2) because it is never de-initialised, and
// a normal Mutex would show up as a leak in BloatView. StaticMutex
// also has the "OffTheBooks" property, so it won't show as a leak
// in BloatView.
static StaticMutex gTelemetryHistogramMutex;
// All of these functions are actually in namespace TelemetryHistogram::,
// but the ::TelemetryHistogram prefix is given explicitly. This is
// because it is critical to see which calls from these functions are
@ -1786,6 +1994,16 @@ void TelemetryHistogram::InitializeGlobalState(bool canRecordBase,
const nsDependentCString expiration(h.expiration());
gKeyedHistograms.Put(id, new KeyedHistogram(id, expiration, h.histogramType,
h.min, h.max, h.bucketCount, h.dataset));
if (XRE_IsParentProcess()) {
// We must create registered child keyed histograms as well or else the
// same code in TelemetrySession.jsm that fails without parent keyed
// histograms will fail without child keyed histograms.
nsCString childId(id);
childId.AppendLiteral(CHILD_HISTOGRAM_SUFFIX);
gKeyedHistograms.Put(childId,
new KeyedHistogram(id, expiration, h.histogramType,
h.min, h.max, h.bucketCount, h.dataset));
}
}
// Some Telemetry histograms depend on the value of C++ constants and hardcode
@ -1816,6 +2034,11 @@ void TelemetryHistogram::DeInitializeGlobalState()
gHistogramMap.Clear();
gKeyedHistograms.Clear();
gAddonMap.Clear();
gAccumulations = nullptr;
gKeyedAccumulations = nullptr;
if (gIPCTimer) {
NS_RELEASE(gIPCTimer);
}
gInitDone = false;
}
@ -1921,12 +2144,7 @@ TelemetryHistogram::Accumulate(const char* name, uint32_t sample)
if (NS_FAILED(rv)) {
return;
}
Histogram *h;
rv = internal_GetHistogramByEnumId(id, &h);
if (NS_SUCCEEDED(rv)) {
internal_HistogramAdd(*h, sample, gHistograms[id].dataset);
}
internal_Accumulate(id, sample);
}
void
@ -1949,7 +2167,52 @@ TelemetryHistogram::AccumulateCategorical(mozilla::Telemetry::ID aId,
const nsCString& label)
{
StaticMutexAutoLock locker(gTelemetryHistogramMutex);
internal_HistogramAddCategorical(aId, label);
if (!internal_CanRecordBase()) {
return;
}
uint32_t labelId = 0;
if (NS_FAILED(gHistograms[aId].label_id(label.get(), &labelId))) {
return;
}
internal_Accumulate(aId, labelId);
}
void
TelemetryHistogram::AccumulateChild(const nsTArray<Accumulation>& aAccumulations)
{
MOZ_ASSERT(XRE_IsParentProcess());
StaticMutexAutoLock locker(gTelemetryHistogramMutex);
if (!internal_CanRecordBase()) {
return;
}
for (uint32_t i = 0; i < aAccumulations.Length(); ++i) {
bool isValid = internal_IsHistogramEnumId(aAccumulations[i].mId);
MOZ_ASSERT(isValid);
if (!isValid) {
continue;
}
internal_AccumulateChild(aAccumulations[i].mId, aAccumulations[i].mSample);
}
}
void
TelemetryHistogram::AccumulateChildKeyed(const nsTArray<KeyedAccumulation>& aAccumulations)
{
MOZ_ASSERT(XRE_IsParentProcess());
StaticMutexAutoLock locker(gTelemetryHistogramMutex);
if (!internal_CanRecordBase()) {
return;
}
for (uint32_t i = 0; i < aAccumulations.Length(); ++i) {
bool isValid = internal_IsHistogramEnumId(aAccumulations[i].mId);
MOZ_ASSERT(isValid);
if (!isValid) {
continue;
}
internal_AccumulateChildKeyed(aAccumulations[i].mId,
aAccumulations[i].mKey,
aAccumulations[i].mSample);
}
}
void
@ -2058,6 +2321,8 @@ TelemetryHistogram::CreateHistogramSnapshots(JSContext *cx,
mozilla::DebugOnly<nsresult> rv
= internal_GetHistogramByEnumId(mozilla::Telemetry::ID(i), &h);
MOZ_ASSERT(NS_SUCCEEDED(rv));
rv = internal_GetHistogramByEnumId(mozilla::Telemetry::ID(i), &h, true);
MOZ_ASSERT(NS_SUCCEEDED(rv));
}
}
@ -2321,3 +2586,43 @@ TelemetryHistogram::GetHistogramSizesofIncludingThis(mozilla::MallocSizeOf
}
return n;
}
// This method takes the lock only to double-buffer the batched telemetry.
// It releases the lock before calling out to IPC code which can (and does)
// Accumulate (which would deadlock)
//
// To ensure we don't loop IPCTimerFired->AccumulateChild->arm timer, we don't
// unset gIPCTimerArmed until the IPC completes
//
// This function must be called on the main thread, otherwise IPC will fail.
void
TelemetryHistogram::IPCTimerFired(nsITimer* aTimer, void* aClosure)
{
MOZ_ASSERT(NS_IsMainThread());
nsTArray<Accumulation> accumulationsToSend;
nsTArray<KeyedAccumulation> keyedAccumulationsToSend;
{
StaticMutexAutoLock locker(gTelemetryHistogramMutex);
if (gAccumulations) {
accumulationsToSend.SwapElements(*gAccumulations);
}
if (gKeyedAccumulations) {
keyedAccumulationsToSend.SwapElements(*gKeyedAccumulations);
}
}
mozilla::dom::ContentChild* contentChild = mozilla::dom::ContentChild::GetSingleton();
mozilla::Unused << NS_WARN_IF(!contentChild);
if (contentChild) {
if (accumulationsToSend.Length()) {
mozilla::Unused <<
NS_WARN_IF(!contentChild->SendAccumulateChildHistogram(accumulationsToSend));
}
if (keyedAccumulationsToSend.Length()) {
mozilla::Unused <<
NS_WARN_IF(!contentChild->SendAccumulateChildKeyedHistogram(keyedAccumulationsToSend));
}
}
gIPCTimerArmed = false;
}

Просмотреть файл

@ -8,6 +8,8 @@
#include "mozilla/TelemetryHistogramEnums.h"
#include "mozilla/TelemetryComms.h"
// This module is internal to Telemetry. It encapsulates Telemetry's
// histogram accumulation and storage logic. It should only be used by
// Telemetry.cpp. These functions should not be used anywhere else.
@ -42,6 +44,9 @@ void Accumulate(const char* name, const nsCString& key, uint32_t sample);
void AccumulateCategorical(mozilla::Telemetry::ID aId, const nsCString& aLabel);
void AccumulateChild(const nsTArray<mozilla::Telemetry::Accumulation>& aAccumulations);
void AccumulateChildKeyed(const nsTArray<mozilla::Telemetry::KeyedAccumulation>& aAccumulations);
void
ClearHistogram(mozilla::Telemetry::ID aId);
@ -96,6 +101,8 @@ GetMapShallowSizesOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf);
size_t
GetHistogramSizesofIncludingThis(mozilla::MallocSizeOf aMallocSizeOf);
void
IPCTimerFired(nsITimer* aTimer, void* aClosure);
} // namespace TelemetryHistogram
#endif // TelemetryHistogram_h__

Просмотреть файл

@ -41,6 +41,11 @@ const REASON_TEST_PING = "test-ping";
const REASON_ENVIRONMENT_CHANGE = "environment-change";
const REASON_SHUTDOWN = "shutdown";
const HISTOGRAM_SUFFIXES = {
PARENT: "",
CONTENT: "#content",
}
const ENVIRONMENT_CHANGE_LISTENER = "TelemetrySession::onEnvironmentChange";
const MS_IN_ONE_HOUR = 60 * 60 * 1000;
@ -57,7 +62,6 @@ const PREF_UNIFIED = PREF_BRANCH + "unified";
const MESSAGE_TELEMETRY_PAYLOAD = "Telemetry:Payload";
const MESSAGE_TELEMETRY_GET_CHILD_PAYLOAD = "Telemetry:GetChildPayload";
const MESSAGE_TELEMETRY_THREAD_HANGS = "Telemetry:ChildThreadHangs";
const MESSAGE_TELEMETRY_GET_CHILD_THREAD_HANGS = "Telemetry:GetChildThreadHangs";
const MESSAGE_TELEMETRY_USS = "Telemetry:USS";
@ -540,13 +544,6 @@ this.TelemetrySession = Object.freeze({
getPayload: function(reason, clearSubsession = false) {
return Impl.getPayload(reason, clearSubsession);
},
/**
* Asks the content processes to send their payloads.
* @returns Object
*/
requestChildPayloads: function() {
return Impl.requestChildPayloads();
},
/**
* Returns a promise that resolves to an array of thread hang stats from content processes, one entry per process.
* The structure of each entry is identical to that of "threadHangStats" in nsITelemetry.
@ -884,23 +881,28 @@ var Impl = {
},
getHistograms: function getHistograms(subsession, clearSubsession) {
this._log.trace("getHistograms - subsession: " + subsession + ", clearSubsession: " + clearSubsession);
this._log.trace("getHistograms - subsession: " + subsession +
", clearSubsession: " + clearSubsession);
let registered =
Telemetry.registeredHistograms(this.getDatasetType(), []);
if (this._testing == false) {
// Omit telemetry test histograms outside of tests.
registered = registered.filter(n => !n.startsWith("TELEMETRY_TEST_"));
}
registered = registered.concat(registered.map(n => "STARTUP_" + n));
let hls = subsession ? Telemetry.snapshotSubsessionHistograms(clearSubsession)
: Telemetry.histogramSnapshots;
let ret = {};
for (let name of registered) {
for (let n of [name, "STARTUP_" + name]) {
if (n in hls) {
// Omit telemetry test histograms outside of tests.
if (n.startsWith('TELEMETRY_TEST_') && this._testing == false) {
this._log.trace("getHistograms - Skipping test histogram: " + n);
} else {
ret[n] = this.packHistogram(hls[n]);
for (let suffix of Object.values(HISTOGRAM_SUFFIXES)) {
if (name + suffix in hls) {
if (!(suffix in ret)) {
ret[suffix] = {};
}
ret[suffix][name] = this.packHistogram(hls[name + suffix]);
}
}
}
@ -928,36 +930,41 @@ var Impl = {
},
getKeyedHistograms: function(subsession, clearSubsession) {
this._log.trace("getKeyedHistograms - subsession: " + subsession + ", clearSubsession: " + clearSubsession);
this._log.trace("getKeyedHistograms - subsession: " + subsession +
", clearSubsession: " + clearSubsession);
let registered =
Telemetry.registeredKeyedHistograms(this.getDatasetType(), []);
if (this._testing == false) {
// Omit telemetry test histograms outside of tests.
registered = registered.filter(id => !id.startsWith("TELEMETRY_TEST_"));
}
let ret = {};
for (let id of registered) {
// Omit telemetry test histograms outside of tests.
if (id.startsWith('TELEMETRY_TEST_') && this._testing == false) {
this._log.trace("getKeyedHistograms - Skipping test histogram: " + id);
continue;
}
let keyed = Telemetry.getKeyedHistogramById(id);
let snapshot = null;
if (subsession) {
snapshot = clearSubsession ? keyed.snapshotSubsessionAndClear()
: keyed.subsessionSnapshot();
} else {
snapshot = keyed.snapshot();
}
for (let suffix of Object.values(HISTOGRAM_SUFFIXES)) {
let keyed = Telemetry.getKeyedHistogramById(id + suffix);
let snapshot = null;
if (subsession) {
snapshot = clearSubsession ? keyed.snapshotSubsessionAndClear()
: keyed.subsessionSnapshot();
} else {
snapshot = keyed.snapshot();
}
let keys = Object.keys(snapshot);
if (keys.length == 0) {
// Skip empty keyed histogram.
continue;
}
let keys = Object.keys(snapshot);
if (keys.length == 0) {
// Skip empty keyed histogram.
continue;
}
ret[id] = {};
for (let key of keys) {
ret[id][key] = this.packHistogram(snapshot[key]);
if (!(suffix in ret)) {
ret[suffix] = {};
}
ret[suffix][id] = {};
for (let key of keys) {
ret[suffix][id][key] = this.packHistogram(snapshot[key]);
}
}
}
@ -1243,12 +1250,12 @@ var Impl = {
// This allows wrapping data retrieval calls in a try-catch block so that
// failures don't break the rest of the ping assembly.
const protect = (fn) => {
const protect = (fn, defaultReturn = null) => {
try {
return fn();
} catch (ex) {
this._log.error("assemblePayloadWithMeasurements - caught exception", ex);
return null;
return defaultReturn;
}
};
@ -1256,8 +1263,6 @@ var Impl = {
let payloadObj = {
ver: PAYLOAD_VERSION,
simpleMeasurements: simpleMeasurements,
histograms: protect(() => this.getHistograms(isSubsession, clearSubsession)),
keyedHistograms: protect(() => this.getKeyedHistograms(isSubsession, clearSubsession)),
};
// Add extended set measurements common to chrome & content processes
@ -1272,14 +1277,21 @@ var Impl = {
return payloadObj;
}
// Set the scalars for the parent process.
// Additional payload for chrome process.
let histograms = protect(() => this.getHistograms(isSubsession, clearSubsession), {});
let keyedHistograms = protect(() => this.getKeyedHistograms(isSubsession, clearSubsession), {});
payloadObj.histograms = histograms[HISTOGRAM_SUFFIXES.PARENT] || {};
payloadObj.keyedHistograms = keyedHistograms[HISTOGRAM_SUFFIXES.PARENT] || {};
payloadObj.processes = {
parent: {
scalars: protect(() => this.getScalars(isSubsession, clearSubsession)),
}
},
content: {
histograms: histograms[HISTOGRAM_SUFFIXES.CONTENT],
keyedHistograms: keyedHistograms[HISTOGRAM_SUFFIXES.CONTENT],
},
};
// Additional payload for chrome process.
payloadObj.info = info;
// Add extended set measurements for chrome process.
@ -1521,7 +1533,6 @@ var Impl = {
}
Services.obs.addObserver(this, "content-child-shutdown", false);
cpml.addMessageListener(MESSAGE_TELEMETRY_GET_CHILD_PAYLOAD, this);
cpml.addMessageListener(MESSAGE_TELEMETRY_GET_CHILD_THREAD_HANGS, this);
cpml.addMessageListener(MESSAGE_TELEMETRY_GET_CHILD_USS, this);
@ -1559,14 +1570,6 @@ var Impl = {
let source = message.data.childUUID;
delete message.data.childUUID;
for (let child of this._childTelemetry) {
if (child.source === source) {
// Update existing telemetry data.
child.payload = message.data;
return;
}
}
// Did not find existing child in this._childTelemetry.
this._childTelemetry.push({
source: source,
payload: message.data,
@ -1579,12 +1582,6 @@ var Impl = {
break;
}
case MESSAGE_TELEMETRY_GET_CHILD_PAYLOAD:
{
// In child process, send the requested Telemetry payload
this.sendContentProcessPing("saved-session");
break;
}
case MESSAGE_TELEMETRY_THREAD_HANGS:
{
// Accumulate child thread hang stats from this child
@ -1764,11 +1761,6 @@ var Impl = {
return this.getSessionPayload(reason, clearSubsession);
},
requestChildPayloads: function() {
this._log.trace("requestChildPayloads");
ppmm.broadcastAsyncMessage(MESSAGE_TELEMETRY_GET_CHILD_PAYLOAD, {});
},
getChildThreadHangs: function getChildThreadHangs() {
return new Promise((resolve) => {
// Return immediately if there are no child processes to get stats from
@ -1846,7 +1838,7 @@ var Impl = {
// content-child-shutdown is only registered for content processes.
Services.obs.removeObserver(this, "content-child-shutdown");
this.uninstall();
Telemetry.flushBatchedChildTelemetry();
this.sendContentProcessPing(REASON_SAVED_SESSION);
break;
case TOPIC_CYCLE_COLLECTOR_BEGIN:

Просмотреть файл

@ -19,6 +19,7 @@ EXPORTS.mozilla += [
'!TelemetryScalarEnums.h',
'ProcessedStack.h',
'Telemetry.h',
'TelemetryComms.h',
'ThreadHangStats.h',
]

Просмотреть файл

@ -396,4 +396,10 @@ interface nsITelemetry : nsISupports
* Resets all the stored scalars. This is intended to be only used in tests.
*/
void clearScalars();
/**
* Immediately sends any Telemetry batched on this process to the parent
* process. This is intended only to be used on process shutdown.
*/
void flushBatchedChildTelemetry();
};

Просмотреть файл

@ -2,6 +2,7 @@
Cu.import("resource://gre/modules/TelemetryController.jsm", this);
Cu.import("resource://gre/modules/TelemetrySession.jsm", this);
Cu.import("resource://gre/modules/PromiseUtils.jsm", this);
Cu.import("resource://testing-common/ContentTaskUtils.jsm", this);
const MESSAGE_TELEMETRY_PAYLOAD = "Telemetry:Payload";
const MESSAGE_TELEMETRY_GET_CHILD_PAYLOAD = "Telemetry:GetChildPayload";
@ -19,6 +20,9 @@ function run_child_test() {
let countHist = Telemetry.getHistogramById("TELEMETRY_TEST_COUNT");
countHist.add();
countHist.add();
let categHist = Telemetry.getHistogramById("TELEMETRY_TEST_CATEGORICAL");
categHist.add("Label2");
categHist.add("Label3");
let flagKeyed = Telemetry.getKeyedHistogramById("TELEMETRY_TEST_KEYED_FLAG");
flagKeyed.add("a", 1);
@ -27,20 +31,19 @@ function run_child_test() {
countKeyed.add("a");
countKeyed.add("b");
countKeyed.add("b");
// Check payload values.
const payload = TelemetrySession.getPayload("test-ping");
check_histogram_values(payload);
}
function check_histogram_values(payload) {
const hs = payload.histograms;
Assert.ok("TELEMETRY_TEST_COUNT" in hs, "Should have count test histogram.");
Assert.ok("TELEMETRY_TEST_FLAG" in hs, "Should have flag test histogram.");
Assert.ok("TELEMETRY_TEST_CATEGORICAL" in hs, "Should have categorical test histogram.");
Assert.equal(hs["TELEMETRY_TEST_COUNT"].sum, 2,
"Count test histogram should have the right value.");
Assert.equal(hs["TELEMETRY_TEST_FLAG"].sum, 1,
"Flag test histogram should have the right value.");
Assert.equal(hs["TELEMETRY_TEST_CATEGORICAL"].sum, 3,
"Categorical test histogram should have the right sum.");
const kh = payload.keyedHistograms;
Assert.ok("TELEMETRY_TEST_KEYED_COUNT" in kh, "Should have keyed count test histogram.");
@ -61,8 +64,6 @@ add_task(function*() {
run_child_test();
dump("... done with child test\n");
do_send_remote_message(MESSAGE_CHILD_TEST_DONE);
dump("... waiting for child payload collection\n");
yield do_await_remote_message(MESSAGE_TELEMETRY_GET_CHILD_PAYLOAD);
return;
}
@ -80,20 +81,20 @@ add_task(function*() {
let childPromise = run_test_in_child("test_ChildHistograms.js");
yield do_await_remote_message(MESSAGE_CHILD_TEST_DONE);
// Gather payload from child.
dump("... requesting child payloads\n");
let promiseMessage = do_await_remote_message(MESSAGE_TELEMETRY_PAYLOAD);
TelemetrySession.requestChildPayloads();
yield promiseMessage;
dump("... received child payload\n");
// Check child payload.
yield ContentTaskUtils.waitForCondition(() => {
let payload = TelemetrySession.getPayload("test-ping");
return payload &&
"processes" in payload &&
"content" in payload.processes &&
"histograms" in payload.processes.content &&
"TELEMETRY_TEST_COUNT" in payload.processes.content.histograms;
});
const payload = TelemetrySession.getPayload("test-ping");
Assert.ok("childPayloads" in payload, "Should have child payloads.");
Assert.equal(payload.childPayloads.length, 1, "Should have received one child payload so far.");
Assert.ok("histograms" in payload.childPayloads[0], "Child payload should have histograms.");
Assert.ok("keyedHistograms" in payload.childPayloads[0], "Child payload should have keyed histograms.");
check_histogram_values(payload.childPayloads[0]);
Assert.ok("processes" in payload, "Should have processes section");
Assert.ok("content" in payload.processes, "Should have child process section");
Assert.ok("histograms" in payload.processes.content, "Child process section should have histograms.");
Assert.ok("keyedHistograms" in payload.processes.content, "Child process section should have keyed histograms.");
check_histogram_values(payload.processes.content);
do_test_finished();
});

Просмотреть файл

@ -229,6 +229,14 @@ body[dir="rtl"] .copy-node {
display: inline;
}
.processes-ui {
display: none;
}
.has-data.expanded .processes-ui {
display: initial;
}
.filter-blocked {
display: none;
}
@ -257,3 +265,7 @@ body[dir="rtl"] .copy-node {
font-size: larger;
margin: 5px 0;
}
.process-picker {
margin: 0 0.5em;
}

Просмотреть файл

@ -306,6 +306,10 @@ var PingPicker = {
.addEventListener("click", () => this._movePingIndex(1), false);
document.getElementById("choose-payload")
.addEventListener("change", () => displayPingData(gPingData), false);
document.getElementById("histograms-processes")
.addEventListener("change", () => displayPingData(gPingData), false);
document.getElementById("keyed-histograms-processes")
.addEventListener("change", () => displayPingData(gPingData), false);
},
onPingSourceChanged: function() {
@ -1782,6 +1786,33 @@ function sortStartupMilestones(aSimpleMeasurements) {
return result;
}
function renderProcessList(ping, selectEl) {
removeAllChildNodes(selectEl);
let option = document.createElement("option");
option.appendChild(document.createTextNode("parent"));
option.setAttribute("value", "");
option.selected = true;
selectEl.appendChild(option);
if (!("processes" in ping.payload)) {
selectEl.disabled = true;
return;
}
selectEl.disabled = false;
for (let process of Object.keys(ping.payload.processes)) {
// TODO: parent hgrams are on root payload, not in payload.processes.parent
// When/If that gets moved, you'll need to remove this:
if (process === "parent") {
continue;
}
option = document.createElement("option");
option.appendChild(document.createTextNode(process));
option.setAttribute("value", process);
selectEl.appendChild(option);
}
}
function renderPayloadList(ping) {
// Rebuild the payload select with options:
// Parent Payload (selected)
@ -1850,9 +1881,11 @@ function displayPingData(ping, updatePayloadList = false) {
const keysHeader = bundle.GetStringFromName("keysHeader");
const valuesHeader = bundle.GetStringFromName("valuesHeader");
// Update the payload list
// Update the payload list and process lists
if (updatePayloadList) {
renderPayloadList(ping);
renderProcessList(ping, document.getElementById("histograms-processes"));
renderProcessList(ping, document.getElementById("keyed-histograms-processes"));
}
// Show general data.
@ -1929,8 +1962,18 @@ function displayPingData(ping, updatePayloadList = false) {
removeAllChildNodes(hgramDiv);
let histograms = payload.histograms;
let hgramsSelect = document.getElementById("histograms-processes");
let hgramsOption = hgramsSelect.selectedOptions.item(0);
let hgramsProcess = hgramsOption.getAttribute("value");
if (hgramsProcess &&
"processes" in ping.payload &&
hgramsProcess in ping.payload.processes) {
histograms = ping.payload.processes[hgramsProcess].histograms;
}
hasData = Object.keys(histograms).length > 0;
setHasData("histograms-section", hasData);
setHasData("histograms-section", hasData || hgramsSelect.options.length);
if (hasData) {
for (let [name, hgram] of Object.entries(histograms)) {
@ -1950,8 +1993,18 @@ function displayPingData(ping, updatePayloadList = false) {
let keyedDiv = document.getElementById("keyed-histograms");
removeAllChildNodes(keyedDiv);
setHasData("keyed-histograms-section", false);
let keyedHistograms = payload.keyedHistograms;
let keyedHgramsSelect = document.getElementById("keyed-histograms-processes");
let keyedHgramsOption = keyedHgramsSelect.selectedOptions.item(0);
let keyedHgramsProcess = keyedHgramsOption.getAttribute("value");
if (keyedHgramsProcess &&
"processes" in ping.payload &&
keyedHgramsProcess in ping.payload.processes) {
keyedHistograms = ping.payload.processes[keyedHgramsProcess].keyedHistograms;
}
setHasData("keyed-histograms-section", keyedHgramsSelect.options.length);
if (keyedHistograms) {
let hasData = false;
for (let [id, keyed] of Object.entries(keyedHistograms)) {
@ -1960,7 +2013,7 @@ function displayPingData(ping, updatePayloadList = false) {
KeyedHistogram.render(keyedDiv, id, keyed, {unpacked: true});
}
}
setHasData("keyed-histograms-section", hasData);
setHasData("keyed-histograms-section", hasData || keyedHgramsSelect.options.length);
}
// Show addon histogram data

Просмотреть файл

@ -152,6 +152,9 @@
<span class="filter-ui">
&aboutTelemetry.filterText; <input type="text" class="filter" id="histograms-filter" target_id="histograms"/>
</span>
<div class="processes-ui">
<select id="histograms-processes" class="process-picker"></select>
</div>
<div id="histograms" class="data">
</div>
</section>
@ -161,6 +164,9 @@
<h1 class="section-name">&aboutTelemetry.keyedHistogramsSection;</h1>
<span class="toggle-caption">&aboutTelemetry.toggle;</span>
<span class="empty-caption">&aboutTelemetry.emptySection;</span>
<div class="processes-ui">
<select id="keyed-histograms-processes" class="process-picker"></select>
</div>
<div id="keyed-histograms" class="data">
</div>
</section>

Просмотреть файл

@ -3,6 +3,7 @@
* 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 "gfxPrefs.h"
#include "mozilla/BasicEvents.h"
#include "mozilla/ContentEvents.h"
#include "mozilla/InternalMutationEvent.h"
@ -412,35 +413,15 @@ WidgetInputEvent::AccelModifier()
* mozilla::WidgetWheelEvent (MouseEvents.h)
******************************************************************************/
bool WidgetWheelEvent::sInitialized = false;
bool WidgetWheelEvent::sIsSystemScrollSpeedOverrideEnabled = false;
int32_t WidgetWheelEvent::sOverrideFactorX = 0;
int32_t WidgetWheelEvent::sOverrideFactorY = 0;
/* static */ void
WidgetWheelEvent::Initialize()
{
if (sInitialized) {
return;
}
Preferences::AddBoolVarCache(&sIsSystemScrollSpeedOverrideEnabled,
"mousewheel.system_scroll_override_on_root_content.enabled", false);
Preferences::AddIntVarCache(&sOverrideFactorX,
"mousewheel.system_scroll_override_on_root_content.horizontal.factor", 0);
Preferences::AddIntVarCache(&sOverrideFactorY,
"mousewheel.system_scroll_override_on_root_content.vertical.factor", 0);
sInitialized = true;
}
/* static */ double
WidgetWheelEvent::ComputeOverriddenDelta(double aDelta, bool aIsForVertical)
{
Initialize();
if (!sIsSystemScrollSpeedOverrideEnabled) {
if (!gfxPrefs::MouseWheelHasRootScrollDeltaOverride()) {
return aDelta;
}
int32_t intFactor = aIsForVertical ? sOverrideFactorY : sOverrideFactorX;
int32_t intFactor = aIsForVertical
? gfxPrefs::MouseWheelRootScrollVerticalFactor()
: gfxPrefs::MouseWheelRootScrollHorizontalFactor();
// Making the scroll speed slower doesn't make sense. So, ignore odd factor
// which is less than 1.0.
if (intFactor <= 100) {