diff --git a/mobile/android/base/BrowserApp.java b/mobile/android/base/BrowserApp.java index 40408a2829d..bf7fdbb8442 100644 --- a/mobile/android/base/BrowserApp.java +++ b/mobile/android/base/BrowserApp.java @@ -429,7 +429,7 @@ abstract public class BrowserApp extends GeckoApp @Override public void onCreate(Bundle savedInstanceState) { - mAboutHomeStartupTimer = new Telemetry.Timer("FENNEC_STARTUP_TIME_ABOUTHOME"); + mAboutHomeStartupTimer = new Telemetry.UptimeTimer("FENNEC_STARTUP_TIME_ABOUTHOME"); final Intent intent = getIntent(); diff --git a/mobile/android/base/GeckoApp.java b/mobile/android/base/GeckoApp.java index 83709caa1ef..ff5167d57b5 100644 --- a/mobile/android/base/GeckoApp.java +++ b/mobile/android/base/GeckoApp.java @@ -1153,8 +1153,8 @@ public abstract class GeckoApp } // The clock starts...now. Better hurry! - mJavaUiStartupTimer = new Telemetry.Timer("FENNEC_STARTUP_TIME_JAVAUI"); - mGeckoReadyStartupTimer = new Telemetry.Timer("FENNEC_STARTUP_TIME_GECKOREADY"); + mJavaUiStartupTimer = new Telemetry.UptimeTimer("FENNEC_STARTUP_TIME_JAVAUI"); + mGeckoReadyStartupTimer = new Telemetry.UptimeTimer("FENNEC_STARTUP_TIME_GECKOREADY"); Intent intent = getIntent(); String args = intent.getStringExtra("args"); diff --git a/mobile/android/base/GeckoEvent.java b/mobile/android/base/GeckoEvent.java index 0a6352f18f2..fa12547bba2 100644 --- a/mobile/android/base/GeckoEvent.java +++ b/mobile/android/base/GeckoEvent.java @@ -43,7 +43,7 @@ public class GeckoEvent { private static final String LOGTAG = "GeckoEvent"; // Make sure to keep these values in sync with the enum in - // AndroidGeckoEvent in widget/android/AndroidJavaWrapper.h + // AndroidGeckoEvent in widget/android/AndroidJavaWrappers.h @JNITarget private enum NativeGeckoEvent { NATIVE_POKE(0), @@ -76,13 +76,16 @@ public class GeckoEvent { TELEMETRY_HISTOGRAM_ADD(37), PREFERENCES_OBSERVE(39), PREFERENCES_GET(40), - PREFERENCES_REMOVE_OBSERVERS(41); + PREFERENCES_REMOVE_OBSERVERS(41), + TELEMETRY_UI_SESSION_START(42), + TELEMETRY_UI_SESSION_STOP(43), + TELEMETRY_UI_EVENT(44); public final int value; private NativeGeckoEvent(int value) { this.value = value; - } + } } /** @@ -747,6 +750,30 @@ public class GeckoEvent { return event; } + public static GeckoEvent createTelemetryUISessionStartEvent(String session, long timestamp) { + GeckoEvent event = new GeckoEvent(NativeGeckoEvent.TELEMETRY_UI_SESSION_START); + event.mCharacters = session; + event.mTime = timestamp; + return event; + } + + public static GeckoEvent createTelemetryUISessionStopEvent(String session, String reason, long timestamp) { + GeckoEvent event = new GeckoEvent(NativeGeckoEvent.TELEMETRY_UI_SESSION_STOP); + event.mCharacters = session; + event.mCharactersExtra = reason; + event.mTime = timestamp; + return event; + } + + public static GeckoEvent createTelemetryUIEvent(String action, String method, long timestamp, String extras) { + GeckoEvent event = new GeckoEvent(NativeGeckoEvent.TELEMETRY_UI_EVENT); + event.mData = action; + event.mCharacters = method; + event.mCharactersExtra = extras; + event.mTime = timestamp; + return event; + } + public void setAckNeeded(boolean ackNeeded) { mAckNeeded = ackNeeded; } diff --git a/mobile/android/base/Telemetry.java b/mobile/android/base/Telemetry.java index 284fa2e4f5d..bd5cf7354f6 100644 --- a/mobile/android/base/Telemetry.java +++ b/mobile/android/base/Telemetry.java @@ -5,31 +5,52 @@ package org.mozilla.gecko; +import org.mozilla.gecko.mozglue.RobocopTarget; + import android.os.SystemClock; import android.util.Log; +/** + * All telemetry times are relative to one of two clocks: + * + * * Real time since the device was booted, including deep sleep. Use this + * as a substitute for wall clock. + * * Uptime since the device was booted, excluding deep sleep. Use this to + * avoid timing a user activity when their phone is in their pocket! + * + * The majority of methods in this class are defined in terms of real time. + */ +@RobocopTarget public class Telemetry { private static final String LOGTAG = "Telemetry"; + public static long uptime() { + return SystemClock.uptimeMillis(); + } + + public static long realtime() { + return SystemClock.elapsedRealtime(); + } + // Define new histograms in: // toolkit/components/telemetry/Histograms.json - public static void HistogramAdd(String name, - int value) { - GeckoEvent event = - GeckoEvent.createTelemetryHistogramAddEvent(name, value); + public static void HistogramAdd(String name, int value) { + GeckoEvent event = GeckoEvent.createTelemetryHistogramAddEvent(name, value); GeckoAppShell.sendEventToGecko(event); } - public static class Timer { - private long mStartTime; - private String mName; - private boolean mHasFinished; + public abstract static class Timer { + private final long mStartTime; + private final String mName; + + private volatile boolean mHasFinished = false; private volatile long mElapsed = -1; + protected abstract long now(); + public Timer(String name) { mName = name; - mStartTime = SystemClock.uptimeMillis(); - mHasFinished = false; + mStartTime = now(); } public void cancel() { @@ -44,17 +65,76 @@ public class Telemetry { // Only the first stop counts. if (mHasFinished) { return; - } else { - mHasFinished = true; } - final long elapsed = SystemClock.uptimeMillis() - mStartTime; - mElapsed = elapsed; - if (elapsed < Integer.MAX_VALUE) { - HistogramAdd(mName, (int)(elapsed)); - } else { - Log.e(LOGTAG, "Duration of " + elapsed + " ms is too long to add to histogram."); + mHasFinished = true; + + final long elapsed = now() - mStartTime; + if (elapsed < 0) { + Log.e(LOGTAG, "Current time less than start time -- clock shenanigans?"); + return; } + + mElapsed = elapsed; + if (elapsed > Integer.MAX_VALUE) { + Log.e(LOGTAG, "Duration of " + elapsed + "ms is too great to add to histogram."); + return; + } + + HistogramAdd(mName, (int)(elapsed)); } } + + public static class RealtimeTimer extends Timer { + public RealtimeTimer(String name) { + super(name); + } + + @Override + protected long now() { + return Telemetry.realtime(); + } + } + + public static class UptimeTimer extends Timer { + public UptimeTimer(String name) { + super(name); + } + + @Override + protected long now() { + return Telemetry.uptime(); + } + } + + public static void startUISession(String sessionName) { + GeckoEvent event = GeckoEvent.createTelemetryUISessionStartEvent(sessionName, realtime()); + GeckoAppShell.sendEventToGecko(event); + } + + public static void stopUISession(String sessionName, String reason) { + GeckoEvent event = GeckoEvent.createTelemetryUISessionStopEvent(sessionName, reason, realtime()); + GeckoAppShell.sendEventToGecko(event); + } + + public static void sendUIEvent(String action, String method, long timestamp, String extras) { + GeckoEvent event = GeckoEvent.createTelemetryUIEvent(action, method, timestamp, extras); + GeckoAppShell.sendEventToGecko(event); + } + + public static void sendUIEvent(String action, String method, long timestamp) { + sendUIEvent(action, method, timestamp, null); + } + + public static void sendUIEvent(String action, String method, String extras) { + sendUIEvent(action, method, realtime(), extras); + } + + public static void sendUIEvent(String action, String method) { + sendUIEvent(action, method, realtime(), null); + } + + public static void sendUIEvent(String action) { + sendUIEvent(action, null, realtime(), null); + } } diff --git a/mobile/android/base/tests/robocop.ini b/mobile/android/base/tests/robocop.ini index 58112b02e5d..6169d5433ea 100644 --- a/mobile/android/base/tests/robocop.ini +++ b/mobile/android/base/tests/robocop.ini @@ -16,7 +16,6 @@ skip-if = processor == "x86" [testBrowserProvider] [testBrowserSearchVisibility] [testClearPrivateData] -[testDeviceSearchEngine] [testDistribution] [testDoorHanger] [testFindInPage] @@ -32,14 +31,11 @@ skip-if = processor == "x86" skip-if = processor == "x86" [testInputUrlBar] [testJarReader] -[testJNI] [testLinkContextMenu] [testLoad] [testMailToContextMenu] [testMasterPassword] -# [testMozPay] # see bug 945675 [testNewTab] -[testOrderedBroadcast] [testOverscroll] [testPanCorrectness] # disabled on x86 only; bug 927476 @@ -58,7 +54,6 @@ skip-if = processor == "x86" [testSessionOOMSave] [testSessionOOMRestore] [testSettingsMenuItems] -[testSharedPreferences] # [testShareLink] # see bug 915897 [testSystemPages] # disabled on x86 only; bug 907383 @@ -66,6 +61,13 @@ skip-if = processor == "x86" # [testThumbnails] # see bug 813107 # [testVkbOverlap] # see bug 907274 +# Using JavascriptTest +[testDeviceSearchEngine] +[testJNI] +# [testMozPay] # see bug 945675 +[testOrderedBroadcast] +[testSharedPreferences] +[testUITelemetry] # Used for Talos, please don't use in mochitest #[testPan] diff --git a/mobile/android/base/tests/testUITelemetry.java b/mobile/android/base/tests/testUITelemetry.java new file mode 100644 index 00000000000..a6f180df6ef --- /dev/null +++ b/mobile/android/base/tests/testUITelemetry.java @@ -0,0 +1,38 @@ +package org.mozilla.gecko.tests; + +import org.mozilla.gecko.Telemetry; + +import android.util.Log; + +public class testUITelemetry extends JavascriptTest { + public testUITelemetry() { + super("testUITelemetry.js"); + } + + @Override + public void testJavascript() throws Exception { + blockForGeckoReady(); + Log.i("GeckoTest", "Adding telemetry events."); + + try { + Telemetry.sendUIEvent("enone", "method0"); + Telemetry.startUISession("foo"); + Telemetry.sendUIEvent("efoo", "method1"); + Telemetry.startUISession("foo"); + Telemetry.sendUIEvent("efoo", "method2"); + Telemetry.startUISession("bar"); + Telemetry.sendUIEvent("efoobar", "method3", "foobarextras"); + Telemetry.stopUISession("foo", "reasonfoo"); + Telemetry.sendUIEvent("ebar", "method4", "barextras"); + Telemetry.stopUISession("bar", "reasonbar"); + Telemetry.stopUISession("bar", "reasonbar2"); + Telemetry.sendUIEvent("enone", "method5"); + } catch (Exception e) { + Log.e("GeckoTest", "Oops.", e); + } + + Log.i("GeckoTest", "Running remaining JS test code."); + super.testJavascript(); + } +} + diff --git a/mobile/android/base/tests/testUITelemetry.js b/mobile/android/base/tests/testUITelemetry.js new file mode 100644 index 00000000000..0407314bb2d --- /dev/null +++ b/mobile/android/base/tests/testUITelemetry.js @@ -0,0 +1,63 @@ +// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*- +/* 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/. */ + +Components.utils.import("resource://gre/modules/Services.jsm"); + +function do_check_array_eq(a1, a2) { + do_check_eq(a1.length, a2.length); + for (let i = 0; i < a1.length; ++i) { + do_check_eq(a1[i], a2[i]); + } +} + +add_test(function test_telemetry_events() { + let bridge = Components.classes["@mozilla.org/android/bridge;1"] + .getService(Components.interfaces.nsIAndroidBridge); + let obsXPCOM = bridge.browserApp.getUITelemetryObserver(); + do_check_true(!!obsXPCOM); + + let obs = obsXPCOM.wrappedJSObject; + do_check_true(!!obs); + + let measurements = obs.getUIMeasurements(); + + let expected = [ + ["event", "enone", "method0", [], null], + ["event", "efoo", "method1", ["foo"], null], + ["event", "efoo", "method2", ["foo"], null], + ["event", "efoobar", "method3", ["foo", "bar"], "foobarextras"], + ["session", "foo", "reasonfoo"], + ["event", "ebar", "method4", ["bar"], "barextras"], + ["session", "bar", "reasonbar"], + ["event", "enone", "method5", [], null], + ]; + + do_check_eq(expected.length, measurements.length); + + for (let i = 0; i < measurements.length; ++i) { + let m = measurements[i]; + + let type = m[0]; + if (type == "event") { + let [type, action, method, sessions, extras] = expected[i]; + do_check_eq(m.action, action); + do_check_eq(m.method, method); + do_check_array_eq(m.sessions, sessions); + do_check_eq(m.extras, extras); + continue; + } + + if (type == "session") { + let [type, name, reason] = expected[i]; + do_check_eq(m.name, name); + do_check_eq(m.reason, method); + continue; + } + } + + run_next_test(); +}); + +run_next_test(); diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index f4436f45907..5c3e7e66a36 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -338,7 +338,6 @@ var BrowserApp = { DesktopUserAgent.init(); Distribution.init(); Tabs.init(); - UITelemetry.init(); #ifdef ACCESSIBILITY AccessFu.attach(window); #endif @@ -1530,6 +1529,10 @@ var BrowserApp = { return this.getTabForId(tabId); }, + getUITelemetryObserver: function() { + return UITelemetry; + }, + getPreferences: function getPreferences(requestId, prefNames, count) { this.handlePreferencesRequest(requestId, prefNames, false); }, diff --git a/toolkit/components/telemetry/UITelemetry.jsm b/toolkit/components/telemetry/UITelemetry.jsm index c8720f0b2d6..e7aa8838740 100644 --- a/toolkit/components/telemetry/UITelemetry.jsm +++ b/toolkit/components/telemetry/UITelemetry.jsm @@ -7,80 +7,78 @@ const Cu = Components.utils; this.EXPORTED_SYMBOLS = [ - "UITelemetry" + "UITelemetry", ]; Cu.import("resource://gre/modules/Services.jsm"); /** * UITelemetry is a helper JSM used to record UI specific telemetry events. + * + * It implements nsIUITelemetryObserver, defined in nsIAndroidBridge.idl. */ -this.UITelemetry = { +this.UITelemetry = Object.freeze({ + _activeSessions: {}, + _measurements: [], - measurements: [], - - init: function init() { - Services.obs.addObserver(this, "UITelemetry:Event", false); - Services.obs.addObserver(this, "UITelemetry:Session", false); - }, - - observe: function observe(aMessage, aTopic, aData) { - switch(aTopic) { - case "UITelemetry:Event": - let args = JSON.parse(aData); - this.addEvent(args.action, args.method, args.extras, args.timestamp); - break; - case "UITelemetry:Session": - args = JSON.parse(aData); - let sessionName = args.name; - let timestamp = args.timestamp; - if (args.state == "start") { - this.startSession(sessionName, timestamp); - } else if (args.state == "stop") { - this.stopSession(sessionName, timestamp); - } - break; - } + /** + * This exists exclusively for testing -- our events are not intended to + * be retrieved via an XPCOM interface. + */ + get wrappedJSObject() { + return this; }, /** - * Adds a single event described by an action, and the calling method. Optional - * paramaters are extras and timestamp. The timestamp will be set here if it is - * not passed in by the caller. + * Holds the functions that provide UITelemetry's simple + * measurements. Those functions are mapped to unique names, + * and should be registered with addSimpleMeasureFunction. */ - addEvent: function addEvent(aAction, aMethod, aExtras, aTimestamp) { - let timestamp = aTimestamp || Date.now(); + _simpleMeasureFunctions: {}, + + /** + * Adds a single event described by a timestamp, an action, and the calling + * method. + * + * Optionally provide a string 'extras', which will be recorded as part of + * the event. + * + * All extant sessions will be recorded by name for each event. + */ + addEvent: function(aAction, aMethod, aTimestamp, aExtras) { + let sessions = Object.keys(this._activeSessions); let aEvent = { type: "event", action: aAction, method: aMethod, - timestamp: timestamp + sessions: sessions, + timestamp: aTimestamp, }; - if (aExtras) aEvent.extras = aExtras; - this._logEvent(aEvent); - }, + if (aExtras) { + aEvent.extras = aExtras; + } - activeSessions: {}, + this._recordEvent(aEvent); + }, /** * Begins tracking a session by storing a timestamp for session start. */ - startSession: function startSession(aName, aTimestamp) { - let timestamp = aTimestamp || Date.now(); - if (this.activeSessions[aName]) { - // Do not overwrite a previous event start if it already exsts. - return; - } - this.activeSessions[aName] = timestamp; + startSession: function(aName, aTimestamp) { + if (this._activeSessions[aName]) { + // Do not overwrite a previous event start if it already exists. + return; + } + this._activeSessions[aName] = aTimestamp; }, /** * Tracks the end of a session with a timestamp. */ - stopSession: function stopSession(aName, aTimestamp) { - let timestamp = aTimestamp || Date.now(); - let sessionStart = this.activeSessions[aName]; + stopSession: function(aName, aReason, aTimestamp) { + let sessionStart = this._activeSessions[aName]; + delete this._activeSessions[aName]; if (!sessionStart) { Services.console.logStringMessage("UITelemetry error: no session [" + aName + "] to stop!"); @@ -90,24 +88,18 @@ this.UITelemetry = { let aEvent = { type: "session", name: aName, + reason: aReason, start: sessionStart, - end: timestamp + end: aTimestamp, }; - this._logEvent(aEvent); + this._recordEvent(aEvent); }, - _logEvent: function sendEvent(aEvent) { - this.measurements.push(aEvent); + _recordEvent: function(aEvent) { + this._measurements.push(aEvent); }, - /** - * Holds the functions that provide UITelemety's simple - * measurements. Those functions are mapped to unique names, - * and should be registered with addSimpleMeasureFunction. - */ - _simpleMeasureFuncs: {}, - /** * Called by TelemetryPing to populate the simple measurement * blob. This function will iterate over all functions added @@ -116,8 +108,8 @@ this.UITelemetry = { */ getSimpleMeasures: function() { let result = {}; - for (let name in this._simpleMeasureFuncs) { - result[name] = this._simpleMeasureFuncs[name](); + for (let name in this._simpleMeasureFunctions) { + result[name] = this._simpleMeasureFunctions[name](); } return result; }, @@ -132,22 +124,22 @@ this.UITelemetry = { * registered for it. */ addSimpleMeasureFunction: function(aName, aFunction) { - if (aName in this._simpleMeasureFuncs) { - throw new Error("A simple measurement function is already registered for " - + aName); - } - if (!aFunction || typeof aFunction !== 'function') { - throw new Error("A function must be passed as the second argument."); + if (aName in this._simpleMeasureFunctions) { + throw new Error("A simple measurement function is already registered for " + aName); } - this._simpleMeasureFuncs[aName] = aFunction; + if (!aFunction || typeof aFunction !== 'function') { + throw new Error("addSimpleMeasureFunction called with non-function argument."); + } + + this._simpleMeasureFunctions[aName] = aFunction; }, removeSimpleMeasureFunction: function(aName) { - delete this._simpleMeasureFuncs[aName]; + delete this._simpleMeasureFunctions[aName]; }, getUIMeasurements: function getUIMeasurements() { - return this.measurements.slice(); + return this._measurements.slice(); } -}; +}); diff --git a/widget/android/AndroidJavaWrappers.cpp b/widget/android/AndroidJavaWrappers.cpp index e984ea2be79..21ccb490de9 100644 --- a/widget/android/AndroidJavaWrappers.cpp +++ b/widget/android/AndroidJavaWrappers.cpp @@ -564,6 +564,27 @@ AndroidGeckoEvent::Init(JNIEnv *jenv, jobject jobj) break; } + case TELEMETRY_UI_SESSION_START: { + ReadCharactersField(jenv); + mTime = jenv->GetLongField(jobj, jTimeField); + break; + } + + case TELEMETRY_UI_SESSION_STOP: { + ReadCharactersField(jenv); + ReadCharactersExtraField(jenv); + mTime = jenv->GetLongField(jobj, jTimeField); + break; + } + + case TELEMETRY_UI_EVENT: { + ReadCharactersField(jenv); + ReadCharactersExtraField(jenv); + ReadDataField(jenv); + mTime = jenv->GetLongField(jobj, jTimeField); + break; + } + case PREFERENCES_OBSERVE: case PREFERENCES_GET: { ReadStringArray(mPrefNames, jenv, jPrefNamesField); diff --git a/widget/android/AndroidJavaWrappers.h b/widget/android/AndroidJavaWrappers.h index f0f26c6030f..19977cc7667 100644 --- a/widget/android/AndroidJavaWrappers.h +++ b/widget/android/AndroidJavaWrappers.h @@ -686,11 +686,14 @@ public: PREFERENCES_OBSERVE = 39, PREFERENCES_GET = 40, PREFERENCES_REMOVE_OBSERVERS = 41, + TELEMETRY_UI_SESSION_START = 42, + TELEMETRY_UI_SESSION_STOP = 43, + TELEMETRY_UI_EVENT = 44, dummy_java_enum_list_end }; enum { - // Memory pressue levels, keep in sync with those in MemoryMonitor.java + // Memory pressure levels. Keep these in sync with those in MemoryMonitor.java. MEMORY_PRESSURE_NONE = 0, MEMORY_PRESSURE_CLEANUP = 1, MEMORY_PRESSURE_LOW = 2, diff --git a/widget/android/nsAppShell.cpp b/widget/android/nsAppShell.cpp index 98b77f712ef..f6a9e13b972 100644 --- a/widget/android/nsAppShell.cpp +++ b/widget/android/nsAppShell.cpp @@ -259,8 +259,7 @@ nsAppShell::ProcessNextNativeEvent(bool mayWait) NativeEventCallback(); break; - case AndroidGeckoEvent::SENSOR_EVENT: - { + case AndroidGeckoEvent::SENSOR_EVENT: { InfallibleTArray values; mozilla::hal::SensorType type = (mozilla::hal::SensorType) curEvent->Flags(); @@ -371,7 +370,6 @@ nsAppShell::ProcessNextNativeEvent(bool mayWait) case AndroidGeckoEvent::VIEWPORT: case AndroidGeckoEvent::BROADCAST: { - if (curEvent->Characters().Length() == 0) break; @@ -385,6 +383,57 @@ nsAppShell::ProcessNextNativeEvent(bool mayWait) break; } + case AndroidGeckoEvent::TELEMETRY_UI_SESSION_STOP: { + if (curEvent->Characters().Length() == 0) + break; + + nsCOMPtr obs; + mBrowserApp->GetUITelemetryObserver(getter_AddRefs(obs)); + if (!obs) + break; + + obs->StopSession( + nsString(curEvent->Characters()).get(), + nsString(curEvent->CharactersExtra()).get(), + curEvent->Time() + ); + break; + } + + case AndroidGeckoEvent::TELEMETRY_UI_SESSION_START: { + if (curEvent->Characters().Length() == 0) + break; + + nsCOMPtr obs; + mBrowserApp->GetUITelemetryObserver(getter_AddRefs(obs)); + if (!obs) + break; + + obs->StartSession( + nsString(curEvent->Characters()).get(), + curEvent->Time() + ); + break; + } + + case AndroidGeckoEvent::TELEMETRY_UI_EVENT: { + if (curEvent->Characters().Length() == 0) + break; + + nsCOMPtr obs; + mBrowserApp->GetUITelemetryObserver(getter_AddRefs(obs)); + if (!obs) + break; + + obs->AddEvent( + nsString(curEvent->Data()).get(), + nsString(curEvent->Characters()).get(), + curEvent->Time(), + nsString(curEvent->CharactersExtra()).get() + ); + break; + } + case AndroidGeckoEvent::LOAD_URI: { nsCOMPtr cmdline (do_CreateInstance("@mozilla.org/toolkit/command-line;1")); diff --git a/widget/android/nsIAndroidBridge.idl b/widget/android/nsIAndroidBridge.idl index e214391373c..5f04aff0116 100644 --- a/widget/android/nsIAndroidBridge.idl +++ b/widget/android/nsIAndroidBridge.idl @@ -11,7 +11,20 @@ interface nsIBrowserTab : nsISupports { readonly attribute float scale; }; -[scriptable, uuid(7508b826-4129-40a0-91da-2a6bba33681f)] +[scriptable, uuid(08426a73-e70b-4680-9282-630932e2b2bb)] +interface nsIUITelemetryObserver : nsISupports { + void startSession(in wstring name, + in unsigned long timestamp); + void stopSession(in wstring name, + in wstring reason, + in unsigned long timestamp); + void addEvent(in wstring action, + in wstring method, + in unsigned long timestamp, + in wstring extras); +}; + +[scriptable, uuid(c31331d2-afad-460f-9c66-728b8c838cec)] interface nsIAndroidBrowserApp : nsISupports { nsIBrowserTab getBrowserTab(in int32_t tabId); void getPreferences(in int32_t requestId, @@ -21,7 +34,9 @@ interface nsIAndroidBrowserApp : nsISupports { [array, size_is(count)] in wstring prefNames, in unsigned long count); void removePreferenceObservers(in int32_t requestId); + nsIUITelemetryObserver getUITelemetryObserver(); }; + [scriptable, uuid(59cfcb35-69b7-47b2-8155-32b193272666)] interface nsIAndroidViewport : nsISupports { readonly attribute float x;