mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge f-t to m-c, a=merge
This commit is contained in:
commit
7c18683a54
@ -717,6 +717,10 @@ Toolbox.prototype = {
|
||||
this._buildPickerButton();
|
||||
}
|
||||
|
||||
// Set the visibility of the built in buttons before adding more buttons
|
||||
// so they are shown before calling into the GCLI actor.
|
||||
this.setToolboxButtonsVisibility();
|
||||
|
||||
const options = {
|
||||
environment: CommandUtils.createEnvironment(this, '_target')
|
||||
};
|
||||
|
293
browser/devtools/performance/modules/actors.js
Normal file
293
browser/devtools/performance/modules/actors.js
Normal file
@ -0,0 +1,293 @@
|
||||
/* 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/. */
|
||||
"use strict";
|
||||
|
||||
const { Task } = require("resource://gre/modules/Task.jsm");
|
||||
const { Promise } = require("resource://gre/modules/Promise.jsm");
|
||||
const {
|
||||
actorCompatibilityBridge, getProfiler,
|
||||
MockMemoryFront, MockTimelineFront,
|
||||
memoryActorSupported, timelineActorSupported
|
||||
} = require("devtools/performance/compatibility");
|
||||
|
||||
loader.lazyRequireGetter(this, "EventEmitter",
|
||||
"devtools/toolkit/event-emitter");
|
||||
loader.lazyRequireGetter(this, "RecordingUtils",
|
||||
"devtools/performance/recording-utils", true);
|
||||
loader.lazyRequireGetter(this, "TimelineFront",
|
||||
"devtools/server/actors/timeline", true);
|
||||
loader.lazyRequireGetter(this, "MemoryFront",
|
||||
"devtools/server/actors/memory", true);
|
||||
loader.lazyRequireGetter(this, "timers",
|
||||
"resource://gre/modules/Timer.jsm");
|
||||
|
||||
// how often do we pull allocation sites from the memory actor
|
||||
const ALLOCATION_SITE_POLL_TIMER = 200; // ms
|
||||
|
||||
const MEMORY_ACTOR_METHODS = [
|
||||
"destroy", "attach", "detach", "getState", "getAllocationsSettings",
|
||||
"getAllocations", "startRecordingAllocations", "stopRecordingAllocations"
|
||||
];
|
||||
|
||||
const TIMELINE_ACTOR_METHODS = [
|
||||
"start", "stop",
|
||||
];
|
||||
|
||||
const PROFILER_ACTOR_METHODS = [
|
||||
"isActive", "startProfiler", "getStartOptions", "stopProfiler",
|
||||
"registerEventNotifications", "unregisterEventNotifications"
|
||||
];
|
||||
|
||||
/**
|
||||
* Constructor for a facade around an underlying ProfilerFront.
|
||||
*/
|
||||
function ProfilerFrontFacade (target) {
|
||||
this._target = target;
|
||||
this._onProfilerEvent = this._onProfilerEvent.bind(this);
|
||||
EventEmitter.decorate(this);
|
||||
}
|
||||
|
||||
ProfilerFrontFacade.prototype = {
|
||||
EVENTS: ["console-api-profiler", "profiler-stopped"],
|
||||
|
||||
// Connects to the targets underlying real ProfilerFront.
|
||||
connect: Task.async(function*() {
|
||||
let target = this._target;
|
||||
this._actor = yield getProfiler(target);
|
||||
|
||||
// Fetch and store information about the SPS profiler and
|
||||
// server profiler.
|
||||
this.traits = {};
|
||||
this.traits.filterable = target.getTrait("profilerDataFilterable");
|
||||
|
||||
// Directly register to event notifications when connected
|
||||
// to hook into `console.profile|profileEnd` calls.
|
||||
yield this.registerEventNotifications({ events: this.EVENTS });
|
||||
// TODO bug 1159389, listen directly to actor if supporting new front
|
||||
target.client.addListener("eventNotification", this._onProfilerEvent);
|
||||
}),
|
||||
|
||||
/**
|
||||
* Unregisters events for the underlying profiler actor.
|
||||
*/
|
||||
destroy: Task.async(function *() {
|
||||
yield this.unregisterEventNotifications({ events: this.EVENTS });
|
||||
// TODO bug 1159389, listen directly to actor if supporting new front
|
||||
this._target.client.removeListener("eventNotification", this._onProfilerEvent);
|
||||
}),
|
||||
|
||||
/**
|
||||
* Starts the profiler actor, if necessary.
|
||||
*/
|
||||
start: Task.async(function *(options={}) {
|
||||
// Start the profiler only if it wasn't already active. The built-in
|
||||
// nsIPerformance module will be kept recording, because it's the same instance
|
||||
// for all targets and interacts with the whole platform, so we don't want
|
||||
// to affect other clients by stopping (or restarting) it.
|
||||
let profilerStatus = yield this.isActive();
|
||||
if (profilerStatus.isActive) {
|
||||
this.emit("profiler-already-active");
|
||||
return profilerStatus.currentTime;
|
||||
}
|
||||
|
||||
// Translate options from the recording model into profiler-specific
|
||||
// options for the nsIProfiler
|
||||
let profilerOptions = {
|
||||
entries: options.bufferSize,
|
||||
interval: options.sampleFrequency ? (1000 / (options.sampleFrequency * 1000)) : void 0
|
||||
};
|
||||
|
||||
yield this.startProfiler(profilerOptions);
|
||||
|
||||
this.emit("profiler-activated");
|
||||
return 0;
|
||||
}),
|
||||
|
||||
/**
|
||||
* Returns profile data from now since `startTime`.
|
||||
*/
|
||||
getProfile: Task.async(function *(options) {
|
||||
let profilerData = yield (actorCompatibilityBridge("getProfile").call(this, options));
|
||||
// If the backend does not support filtering by start and endtime on platform (< Fx40),
|
||||
// do it on the client (much slower).
|
||||
if (!this.traits.filterable) {
|
||||
RecordingUtils.filterSamples(profilerData.profile, options.startTime || 0);
|
||||
}
|
||||
|
||||
return profilerData;
|
||||
}),
|
||||
|
||||
/**
|
||||
* Invoked whenever a registered event was emitted by the profiler actor.
|
||||
*
|
||||
* @param object response
|
||||
* The data received from the backend.
|
||||
*/
|
||||
_onProfilerEvent: function (_, { topic, subject, details }) {
|
||||
if (topic === "console-api-profiler") {
|
||||
if (subject.action === "profile") {
|
||||
this.emit("console-profile-start", details);
|
||||
} else if (subject.action === "profileEnd") {
|
||||
this.emit("console-profile-end", details);
|
||||
}
|
||||
} else if (topic === "profiler-stopped") {
|
||||
this.emit("profiler-stopped");
|
||||
}
|
||||
},
|
||||
|
||||
toString: () => "[object ProfilerFrontFacade]"
|
||||
};
|
||||
|
||||
// Bind all the methods that directly proxy to the actor
|
||||
PROFILER_ACTOR_METHODS.forEach(method => ProfilerFrontFacade.prototype[method] = actorCompatibilityBridge(method));
|
||||
exports.ProfilerFront = ProfilerFrontFacade;
|
||||
|
||||
/**
|
||||
* Constructor for a facade around an underlying TimelineFront.
|
||||
*/
|
||||
function TimelineFrontFacade (target) {
|
||||
this._target = target;
|
||||
EventEmitter.decorate(this);
|
||||
}
|
||||
|
||||
TimelineFrontFacade.prototype = {
|
||||
EVENTS: ["markers", "frames", "memory", "ticks"],
|
||||
|
||||
connect: Task.async(function*() {
|
||||
let supported = yield timelineActorSupported(this._target);
|
||||
this._actor = supported ?
|
||||
new TimelineFront(this._target.client, this._target.form) :
|
||||
new MockTimelineFront();
|
||||
|
||||
this.IS_MOCK = !supported;
|
||||
|
||||
// Binds underlying actor events and consolidates them to a `timeline-data`
|
||||
// exposed event.
|
||||
this.EVENTS.forEach(type => {
|
||||
let handler = this[`_on${type}`] = this._onTimelineData.bind(this, type);
|
||||
this._actor.on(type, handler);
|
||||
});
|
||||
}),
|
||||
|
||||
/**
|
||||
* Override actor's destroy, so we can unregister listeners before
|
||||
* destroying the underlying actor.
|
||||
*/
|
||||
destroy: Task.async(function *() {
|
||||
this.EVENTS.forEach(type => this._actor.off(type, this[`_on${type}`]));
|
||||
yield this._actor.destroy();
|
||||
}),
|
||||
|
||||
/**
|
||||
* An aggregate of all events (markers, frames, memory, ticks) and exposes
|
||||
* to PerformanceActorsConnection as a single event.
|
||||
*/
|
||||
_onTimelineData: function (type, ...data) {
|
||||
this.emit("timeline-data", type, ...data);
|
||||
},
|
||||
|
||||
toString: () => "[object TimelineFrontFacade]"
|
||||
};
|
||||
|
||||
// Bind all the methods that directly proxy to the actor
|
||||
TIMELINE_ACTOR_METHODS.forEach(method => TimelineFrontFacade.prototype[method] = actorCompatibilityBridge(method));
|
||||
exports.TimelineFront = TimelineFrontFacade;
|
||||
|
||||
/**
|
||||
* Constructor for a facade around an underlying ProfilerFront.
|
||||
*/
|
||||
function MemoryFrontFacade (target) {
|
||||
this._target = target;
|
||||
this._pullAllocationSites = this._pullAllocationSites.bind(this);
|
||||
EventEmitter.decorate(this);
|
||||
}
|
||||
|
||||
MemoryFrontFacade.prototype = {
|
||||
connect: Task.async(function*() {
|
||||
let supported = yield memoryActorSupported(this._target);
|
||||
this._actor = supported ?
|
||||
new MemoryFront(this._target.client, this._target.form) :
|
||||
new MockMemoryFront();
|
||||
|
||||
this.IS_MOCK = !supported;
|
||||
}),
|
||||
|
||||
/**
|
||||
* Starts polling for allocation information.
|
||||
*/
|
||||
start: Task.async(function *(options) {
|
||||
if (!options.withAllocations) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
yield this.attach();
|
||||
|
||||
let startTime = yield this.startRecordingAllocations({
|
||||
probability: options.allocationsSampleProbability,
|
||||
maxLogLength: options.allocationsMaxLogLength
|
||||
});
|
||||
|
||||
yield this._pullAllocationSites();
|
||||
|
||||
return startTime;
|
||||
}),
|
||||
|
||||
/**
|
||||
* Stops polling for allocation information.
|
||||
*/
|
||||
stop: Task.async(function *(options) {
|
||||
if (!options.withAllocations) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Since `_pullAllocationSites` is usually running inside a timeout, and
|
||||
// it's performing asynchronous requests to the server, a recording may
|
||||
// be stopped before that method finishes executing. Therefore, we need to
|
||||
// wait for the last request to `getAllocations` to finish before actually
|
||||
// stopping recording allocations.
|
||||
yield this._lastPullAllocationSitesFinished;
|
||||
timers.clearTimeout(this._sitesPullTimeout);
|
||||
|
||||
let endTime = yield this.stopRecordingAllocations();
|
||||
yield this.detach();
|
||||
|
||||
return endTime;
|
||||
}),
|
||||
|
||||
/**
|
||||
* At regular intervals, pull allocations from the memory actor, and
|
||||
* forward them on this Front facade as "timeline-data" events. This
|
||||
* gives the illusion that the MemoryActor supports an EventEmitter-style
|
||||
* event stream.
|
||||
*/
|
||||
_pullAllocationSites: Task.async(function *() {
|
||||
let { promise, resolve } = Promise.defer();
|
||||
this._lastPullAllocationSitesFinished = promise;
|
||||
|
||||
if ((yield this.getState()) !== "attached") {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
let memoryData = yield this.getAllocations();
|
||||
// Match the signature of the TimelineFront events, with "timeline-data"
|
||||
// being the event name, and the second argument describing the type.
|
||||
this.emit("timeline-data", "allocations", {
|
||||
sites: memoryData.allocations,
|
||||
timestamps: memoryData.allocationsTimestamps,
|
||||
frames: memoryData.frames,
|
||||
counts: memoryData.counts
|
||||
});
|
||||
|
||||
this._sitesPullTimeout = timers.setTimeout(this._pullAllocationSites, ALLOCATION_SITE_POLL_TIMER);
|
||||
|
||||
resolve();
|
||||
}),
|
||||
|
||||
toString: () => "[object MemoryFrontFacade]"
|
||||
};
|
||||
|
||||
// Bind all the methods that directly proxy to the actor
|
||||
MEMORY_ACTOR_METHODS.forEach(method => MemoryFrontFacade.prototype[method] = actorCompatibilityBridge(method));
|
||||
exports.MemoryFront = MemoryFrontFacade;
|
@ -4,72 +4,9 @@
|
||||
"use strict";
|
||||
|
||||
const { Task } = require("resource://gre/modules/Task.jsm");
|
||||
loader.lazyRequireGetter(this, "promise");
|
||||
const { Promise } = require("resource://gre/modules/Promise.jsm");
|
||||
loader.lazyRequireGetter(this, "EventEmitter",
|
||||
"devtools/toolkit/event-emitter");
|
||||
loader.lazyRequireGetter(this, "RecordingUtils",
|
||||
"devtools/performance/recording-utils", true);
|
||||
|
||||
const REQUIRED_MEMORY_ACTOR_METHODS = [
|
||||
"attach", "detach", "startRecordingAllocations", "stopRecordingAllocations", "getAllocations"
|
||||
];
|
||||
|
||||
/**
|
||||
* Constructor for a facade around an underlying ProfilerFront.
|
||||
*/
|
||||
function ProfilerFront (target) {
|
||||
this._target = target;
|
||||
}
|
||||
|
||||
ProfilerFront.prototype = {
|
||||
// Connects to the targets underlying real ProfilerFront.
|
||||
connect: Task.async(function*() {
|
||||
let target = this._target;
|
||||
// Chrome and content process targets already have obtained a reference
|
||||
// to the profiler tab actor. Use it immediately.
|
||||
if (target.form && target.form.profilerActor) {
|
||||
this._profiler = target.form.profilerActor;
|
||||
}
|
||||
// Check if we already have a grip to the `listTabs` response object
|
||||
// and, if we do, use it to get to the profiler actor.
|
||||
else if (target.root && target.root.profilerActor) {
|
||||
this._profiler = target.root.profilerActor;
|
||||
}
|
||||
// Otherwise, call `listTabs`.
|
||||
else {
|
||||
this._profiler = (yield listTabs(target.client)).profilerActor;
|
||||
}
|
||||
|
||||
// Fetch and store information about the SPS profiler and
|
||||
// server profiler.
|
||||
this.traits = {};
|
||||
this.traits.filterable = target.getTrait("profilerDataFilterable");
|
||||
}),
|
||||
|
||||
/**
|
||||
* Makes a request to the underlying real profiler actor. Handles
|
||||
* backwards compatibility differences based off of the features
|
||||
* and traits of the actor.
|
||||
*/
|
||||
_request: function (method, ...args) {
|
||||
let deferred = promise.defer();
|
||||
let data = args[0] || {};
|
||||
data.to = this._profiler;
|
||||
data.type = method;
|
||||
this._target.client.request(data, res => {
|
||||
// If the backend does not support filtering by start and endtime on platform (< Fx40),
|
||||
// do it on the client (much slower).
|
||||
if (method === "getProfile" && !this.traits.filterable) {
|
||||
RecordingUtils.filterSamples(res.profile, data.startTime || 0);
|
||||
}
|
||||
|
||||
deferred.resolve(res);
|
||||
});
|
||||
return deferred.promise;
|
||||
}
|
||||
};
|
||||
|
||||
exports.ProfilerFront = ProfilerFront;
|
||||
|
||||
/**
|
||||
* A dummy front decorated with the provided methods.
|
||||
@ -87,7 +24,8 @@ function MockFront (blueprint) {
|
||||
|
||||
function MockMemoryFront () {
|
||||
MockFront.call(this, [
|
||||
["initialize"],
|
||||
["start", 0], // for facade
|
||||
["stop", 0], // for facade
|
||||
["destroy"],
|
||||
["attach"],
|
||||
["detach"],
|
||||
@ -101,7 +39,6 @@ exports.MockMemoryFront = MockMemoryFront;
|
||||
|
||||
function MockTimelineFront () {
|
||||
MockFront.call(this, [
|
||||
["initialize"],
|
||||
["destroy"],
|
||||
["start", 0],
|
||||
["stop", 0],
|
||||
@ -169,12 +106,66 @@ function timelineActorSupported(target) {
|
||||
exports.timelineActorSupported = Task.async(timelineActorSupported);
|
||||
|
||||
/**
|
||||
* Returns a promise resolved with a listing of all the tabs in the
|
||||
* provided thread client.
|
||||
* Returns a promise resolving to the location of the profiler actor
|
||||
* within this context.
|
||||
*
|
||||
* @param {TabTarget} target
|
||||
* @return {Promise<ProfilerActor>}
|
||||
*/
|
||||
function listTabs(client) {
|
||||
let deferred = promise.defer();
|
||||
client.listTabs(deferred.resolve);
|
||||
return deferred.promise;
|
||||
function getProfiler (target) {
|
||||
let { promise, resolve } = Promise.defer();
|
||||
// Chrome and content process targets already have obtained a reference
|
||||
// to the profiler tab actor. Use it immediately.
|
||||
if (target.form && target.form.profilerActor) {
|
||||
resolve(target.form.profilerActor);
|
||||
}
|
||||
// Check if we already have a grip to the `listTabs` response object
|
||||
// and, if we do, use it to get to the profiler actor.
|
||||
else if (target.root && target.root.profilerActor) {
|
||||
resolve(target.root.profilerActor);
|
||||
}
|
||||
// Otherwise, call `listTabs`.
|
||||
else {
|
||||
target.client.listTabs(({ profilerActor }) => resolve(profilerActor));
|
||||
}
|
||||
return promise;
|
||||
}
|
||||
exports.getProfiler = Task.async(getProfiler);
|
||||
|
||||
/**
|
||||
* Makes a request to an actor that does not have the modern `Front`
|
||||
* interface.
|
||||
*/
|
||||
function legacyRequest (target, actor, method, args) {
|
||||
let { promise, resolve } = Promise.defer();
|
||||
let data = args[0] || {};
|
||||
data.to = actor;
|
||||
data.type = method;
|
||||
target.client.request(data, resolve);
|
||||
return promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a function to be used as a method on an "Actor" in ./actors.
|
||||
* Calls the underlying actor's method, supporting the modern `Front`
|
||||
* interface if possible, otherwise, falling back to using
|
||||
* `legacyRequest`.
|
||||
*/
|
||||
function actorCompatibilityBridge (method) {
|
||||
return function () {
|
||||
// Check to see if this is a modern ActorFront, which has its
|
||||
// own `request` method. Also, check if its a mock actor, as it mimicks
|
||||
// the ActorFront interface.
|
||||
// The profiler actor does not currently support the modern `Front`
|
||||
// interface, so we have to manually push packets to it.
|
||||
// TODO bug 1159389, fix up profiler actor to not need this, however
|
||||
// we will need it for backwards compat
|
||||
if (this.IS_MOCK || this._actor.request) {
|
||||
return this._actor[method].apply(this._actor, arguments);
|
||||
}
|
||||
else {
|
||||
return legacyRequest(this._target, this._actor, method, arguments);
|
||||
}
|
||||
};
|
||||
}
|
||||
exports.actorCompatibilityBridge = actorCompatibilityBridge;
|
||||
|
@ -9,24 +9,15 @@ const { extend } = require("sdk/util/object");
|
||||
const { RecordingModel } = require("devtools/performance/recording-model");
|
||||
|
||||
loader.lazyRequireGetter(this, "Services");
|
||||
loader.lazyRequireGetter(this, "promise");
|
||||
loader.lazyRequireGetter(this, "EventEmitter",
|
||||
"devtools/toolkit/event-emitter");
|
||||
loader.lazyRequireGetter(this, "TimelineFront",
|
||||
"devtools/server/actors/timeline", true);
|
||||
loader.lazyRequireGetter(this, "MemoryFront",
|
||||
"devtools/server/actors/memory", true);
|
||||
loader.lazyRequireGetter(this, "DevToolsUtils",
|
||||
"devtools/toolkit/DevToolsUtils");
|
||||
loader.lazyRequireGetter(this, "compatibility",
|
||||
"devtools/performance/compatibility");
|
||||
loader.lazyRequireGetter(this, "actors",
|
||||
"devtools/performance/actors");
|
||||
|
||||
loader.lazyImporter(this, "gDevTools",
|
||||
"resource:///modules/devtools/gDevTools.jsm");
|
||||
loader.lazyImporter(this, "setTimeout",
|
||||
"resource://gre/modules/Timer.jsm");
|
||||
loader.lazyImporter(this, "clearTimeout",
|
||||
"resource://gre/modules/Timer.jsm");
|
||||
loader.lazyImporter(this, "Promise",
|
||||
"resource://gre/modules/Promise.jsm");
|
||||
|
||||
@ -41,9 +32,6 @@ const CONNECTION_PIPE_EVENTS = [
|
||||
"recording-started", "recording-stopped"
|
||||
];
|
||||
|
||||
// Events to listen to from the profiler actor
|
||||
const PROFILER_EVENTS = ["console-api-profiler", "profiler-stopped"];
|
||||
|
||||
/**
|
||||
* A cache of all PerformanceActorsConnection instances.
|
||||
* The keys are Target objects.
|
||||
@ -84,17 +72,15 @@ function PerformanceActorsConnection(target) {
|
||||
|
||||
this._target = target;
|
||||
this._client = this._target.client;
|
||||
this._request = this._request.bind(this);
|
||||
this._pendingConsoleRecordings = [];
|
||||
this._sitesPullTimeout = 0;
|
||||
this._recordings = [];
|
||||
|
||||
this._onTimelineMarkers = this._onTimelineMarkers.bind(this);
|
||||
this._onTimelineFrames = this._onTimelineFrames.bind(this);
|
||||
this._onTimelineMemory = this._onTimelineMemory.bind(this);
|
||||
this._onTimelineTicks = this._onTimelineTicks.bind(this);
|
||||
this._onProfilerEvent = this._onProfilerEvent.bind(this);
|
||||
this._pullAllocationSites = this._pullAllocationSites.bind(this);
|
||||
this._pipeToConnection = this._pipeToConnection.bind(this);
|
||||
this._onTimelineData = this._onTimelineData.bind(this);
|
||||
this._onConsoleProfileStart = this._onConsoleProfileStart.bind(this);
|
||||
this._onConsoleProfileEnd = this._onConsoleProfileEnd.bind(this);
|
||||
this._onProfilerUnexpectedlyStopped = this._onProfilerUnexpectedlyStopped.bind(this);
|
||||
|
||||
Services.obs.notifyObservers(null, "performance-actors-connection-created", null);
|
||||
}
|
||||
@ -128,10 +114,7 @@ PerformanceActorsConnection.prototype = {
|
||||
// Only initialize the timeline and memory fronts if the respective actors
|
||||
// are available. Older Gecko versions don't have existing implementations,
|
||||
// in which case all the methods we need can be easily mocked.
|
||||
yield this._connectProfilerActor();
|
||||
yield this._connectTimelineActor();
|
||||
yield this._connectMemoryActor();
|
||||
|
||||
yield this._connectActors();
|
||||
yield this._registerListeners();
|
||||
|
||||
this._connected = true;
|
||||
@ -159,132 +142,64 @@ PerformanceActorsConnection.prototype = {
|
||||
}),
|
||||
|
||||
/**
|
||||
* Initializes a connection to the profiler actor. Uses a facade around the ProfilerFront
|
||||
* for similarity to the other actors in the shared connection.
|
||||
* Initializes fronts and connects to the underlying actors using the facades
|
||||
* found in ./actors.js.
|
||||
*/
|
||||
_connectProfilerActor: Task.async(function*() {
|
||||
this._profiler = new compatibility.ProfilerFront(this._target);
|
||||
yield this._profiler.connect();
|
||||
}),
|
||||
_connectActors: Task.async(function*() {
|
||||
this._profiler = new actors.ProfilerFront(this._target);
|
||||
this._memory = new actors.MemoryFront(this._target);
|
||||
this._timeline = new actors.TimelineFront(this._target);
|
||||
|
||||
/**
|
||||
* Initializes a connection to a timeline actor.
|
||||
*/
|
||||
_connectTimelineActor: function() {
|
||||
let supported = yield compatibility.timelineActorSupported(this._target);
|
||||
if (supported) {
|
||||
this._timeline = new TimelineFront(this._target.client, this._target.form);
|
||||
} else {
|
||||
this._timeline = new compatibility.MockTimelineFront();
|
||||
}
|
||||
this._timelineSupported = supported;
|
||||
},
|
||||
yield Promise.all([
|
||||
this._profiler.connect(),
|
||||
this._memory.connect(),
|
||||
this._timeline.connect()
|
||||
]);
|
||||
|
||||
/**
|
||||
* Initializes a connection to a memory actor.
|
||||
*/
|
||||
_connectMemoryActor: Task.async(function* () {
|
||||
let supported = yield compatibility.memoryActorSupported(this._target);
|
||||
if (supported) {
|
||||
this._memory = new MemoryFront(this._target.client, this._target.form);
|
||||
} else {
|
||||
this._memory = new compatibility.MockMemoryFront();
|
||||
}
|
||||
this._memorySupported = supported;
|
||||
// Expose server support status of underlying actors
|
||||
// after connecting.
|
||||
this._memorySupported = !this._memory.IS_MOCK;
|
||||
this._timelineSupported = !this._timeline.IS_MOCK;
|
||||
}),
|
||||
|
||||
/**
|
||||
* Registers listeners on events from the underlying
|
||||
* actors, so the connection can handle them.
|
||||
*/
|
||||
_registerListeners: Task.async(function*() {
|
||||
// Pipe events from TimelineActor to the PerformanceFront
|
||||
this._timeline.on("markers", this._onTimelineMarkers);
|
||||
this._timeline.on("frames", this._onTimelineFrames);
|
||||
this._timeline.on("memory", this._onTimelineMemory);
|
||||
this._timeline.on("ticks", this._onTimelineTicks);
|
||||
|
||||
// Register events on the profiler actor to hook into `console.profile*` calls.
|
||||
yield this._request("profiler", "registerEventNotifications", { events: PROFILER_EVENTS });
|
||||
this._client.addListener("eventNotification", this._onProfilerEvent);
|
||||
}),
|
||||
_registerListeners: function () {
|
||||
this._timeline.on("timeline-data", this._onTimelineData);
|
||||
this._memory.on("timeline-data", this._onTimelineData);
|
||||
this._profiler.on("console-profile-start", this._onConsoleProfileStart);
|
||||
this._profiler.on("console-profile-end", this._onConsoleProfileEnd);
|
||||
this._profiler.on("profiler-stopped", this._onProfilerUnexpectedlyStopped);
|
||||
this._profiler.on("profiler-already-active", this._pipeToConnection);
|
||||
this._profiler.on("profiler-activated", this._pipeToConnection);
|
||||
},
|
||||
|
||||
/**
|
||||
* Unregisters listeners on events on the underlying actors.
|
||||
*/
|
||||
_unregisterListeners: Task.async(function*() {
|
||||
this._timeline.off("markers", this._onTimelineMarkers);
|
||||
this._timeline.off("frames", this._onTimelineFrames);
|
||||
this._timeline.off("memory", this._onTimelineMemory);
|
||||
this._timeline.off("ticks", this._onTimelineTicks);
|
||||
|
||||
yield this._request("profiler", "unregisterEventNotifications", { events: PROFILER_EVENTS });
|
||||
this._client.removeListener("eventNotification", this._onProfilerEvent);
|
||||
}),
|
||||
_unregisterListeners: function () {
|
||||
this._timeline.off("timeline-data", this._onTimelineData);
|
||||
this._memory.off("timeline-data", this._onTimelineData);
|
||||
this._profiler.off("console-profile-start", this._onConsoleProfileStart);
|
||||
this._profiler.off("console-profile-end", this._onConsoleProfileEnd);
|
||||
this._profiler.off("profiler-stopped", this._onProfilerUnexpectedlyStopped);
|
||||
this._profiler.off("profiler-already-active", this._pipeToConnection);
|
||||
this._profiler.off("profiler-activated", this._pipeToConnection);
|
||||
},
|
||||
|
||||
/**
|
||||
* Closes the connections to non-profiler actors.
|
||||
*/
|
||||
_disconnectActors: Task.async(function* () {
|
||||
yield this._timeline.destroy();
|
||||
yield this._memory.destroy();
|
||||
yield Promise.all([
|
||||
this._profiler.destroy(),
|
||||
this._timeline.destroy(),
|
||||
this._memory.destroy()
|
||||
]);
|
||||
}),
|
||||
|
||||
/**
|
||||
* Sends the request over the remote debugging protocol to the
|
||||
* specified actor.
|
||||
*
|
||||
* @param string actor
|
||||
* Currently supported: "profiler", "timeline", "memory".
|
||||
* @param string method
|
||||
* Method to call on the backend.
|
||||
* @param any args [optional]
|
||||
* Additional data or arguments to send with the request.
|
||||
* @return object
|
||||
* A promise resolved with the response once the request finishes.
|
||||
*/
|
||||
_request: function(actor, method, ...args) {
|
||||
// Handle requests to the profiler actor.
|
||||
if (actor == "profiler") {
|
||||
return this._profiler._request(method, ...args);
|
||||
}
|
||||
|
||||
// Handle requests to the timeline actor.
|
||||
if (actor == "timeline") {
|
||||
return this._timeline[method].apply(this._timeline, args);
|
||||
}
|
||||
|
||||
// Handle requests to the memory actor.
|
||||
if (actor == "memory") {
|
||||
return this._memory[method].apply(this._memory, args);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Invoked whenever a registered event was emitted by the profiler actor.
|
||||
*
|
||||
* @param object response
|
||||
* The data received from the backend.
|
||||
*/
|
||||
_onProfilerEvent: function (_, { topic, subject, details }) {
|
||||
if (topic === "console-api-profiler") {
|
||||
if (subject.action === "profile") {
|
||||
this._onConsoleProfileStart(details);
|
||||
} else if (subject.action === "profileEnd") {
|
||||
this._onConsoleProfileEnd(details);
|
||||
}
|
||||
} else if (topic === "profiler-stopped") {
|
||||
this._onProfilerUnexpectedlyStopped();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* TODO handle bug 1144438
|
||||
*/
|
||||
_onProfilerUnexpectedlyStopped: function () {
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Invoked whenever `console.profile` is called.
|
||||
*
|
||||
@ -294,7 +209,7 @@ PerformanceActorsConnection.prototype = {
|
||||
* The time (in milliseconds) when the call was made, relative to when
|
||||
* the nsIProfiler module was started.
|
||||
*/
|
||||
_onConsoleProfileStart: Task.async(function *({ profileLabel, currentTime: startTime }) {
|
||||
_onConsoleProfileStart: Task.async(function *(_, { profileLabel, currentTime: startTime }) {
|
||||
let recordings = this._recordings;
|
||||
|
||||
// Abort if a profile with this label already exists.
|
||||
@ -325,7 +240,7 @@ PerformanceActorsConnection.prototype = {
|
||||
* The time (in milliseconds) when the call was made, relative to when
|
||||
* the nsIProfiler module was started.
|
||||
*/
|
||||
_onConsoleProfileEnd: Task.async(function *(data) {
|
||||
_onConsoleProfileEnd: Task.async(function *(_, data) {
|
||||
// If no data, abort; can occur if profiler isn't running and we get a surprise
|
||||
// call to console.profileEnd()
|
||||
if (!data) {
|
||||
@ -361,14 +276,12 @@ PerformanceActorsConnection.prototype = {
|
||||
this.emit("console-profile-end", model);
|
||||
}),
|
||||
|
||||
/**
|
||||
* Handlers for TimelineActor events. All pipe to `_onTimelineData`
|
||||
* with the appropriate event name.
|
||||
*/
|
||||
_onTimelineMarkers: function (markers) { this._onTimelineData("markers", markers); },
|
||||
_onTimelineFrames: function (delta, frames) { this._onTimelineData("frames", delta, frames); },
|
||||
_onTimelineMemory: function (delta, measurement) { this._onTimelineData("memory", delta, measurement); },
|
||||
_onTimelineTicks: function (delta, timestamps) { this._onTimelineData("ticks", delta, timestamps); },
|
||||
/**
|
||||
* TODO handle bug 1144438
|
||||
*/
|
||||
_onProfilerUnexpectedlyStopped: function () {
|
||||
Cu.reportError("Profiler unexpectedly stopped.", arguments);
|
||||
},
|
||||
|
||||
/**
|
||||
* Called whenever there is timeline data of any of the following types:
|
||||
@ -380,8 +293,7 @@ PerformanceActorsConnection.prototype = {
|
||||
*
|
||||
* Populate our internal store of recordings for all currently recording sessions.
|
||||
*/
|
||||
|
||||
_onTimelineData: function (...data) {
|
||||
_onTimelineData: function (_, ...data) {
|
||||
this._recordings.forEach(e => e.addTimelineData.apply(e, data));
|
||||
this.emit("timeline-data", ...data);
|
||||
},
|
||||
@ -399,15 +311,13 @@ PerformanceActorsConnection.prototype = {
|
||||
let model = new RecordingModel(options);
|
||||
// All actors are started asynchronously over the remote debugging protocol.
|
||||
// Get the corresponding start times from each one of them.
|
||||
let profilerStartTime = yield this._startProfiler(options);
|
||||
let timelineStartTime = yield this._startTimeline(options);
|
||||
let memoryStartTime = yield this._startMemory(options);
|
||||
// The timeline and memory actors are target-dependent, so start those as well,
|
||||
// even though these are mocked in older Geckos (FF < 35)
|
||||
let profilerStartTime = yield this._profiler.start(options);
|
||||
let timelineStartTime = yield this._timeline.start(options);
|
||||
let memoryStartTime = yield this._memory.start(options);
|
||||
|
||||
let data = {
|
||||
profilerStartTime,
|
||||
timelineStartTime,
|
||||
memoryStartTime
|
||||
};
|
||||
let data = { profilerStartTime, timelineStartTime, memoryStartTime };
|
||||
|
||||
// Signify to the model that the recording has started,
|
||||
// populate with data and store the recording model here.
|
||||
@ -445,7 +355,7 @@ PerformanceActorsConnection.prototype = {
|
||||
|
||||
let config = model.getConfiguration();
|
||||
let startTime = model.getProfilerStartTime();
|
||||
let profilerData = yield this._request("profiler", "getProfile", { startTime });
|
||||
let profilerData = yield this._profiler.getProfile({ startTime });
|
||||
let memoryEndTime = Date.now();
|
||||
let timelineEndTime = Date.now();
|
||||
|
||||
@ -454,8 +364,8 @@ PerformanceActorsConnection.prototype = {
|
||||
// juse use Date.now() for the memory and timeline end times, as those
|
||||
// are only used in tests.
|
||||
if (!this.isRecording()) {
|
||||
memoryEndTime = yield this._stopMemory(config);
|
||||
timelineEndTime = yield this._stopTimeline(config);
|
||||
memoryEndTime = yield this._memory.stop(config);
|
||||
timelineEndTime = yield this._timeline.stop(config);
|
||||
}
|
||||
|
||||
// Set the results on the RecordingModel itself.
|
||||
@ -484,127 +394,12 @@ PerformanceActorsConnection.prototype = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Starts the profiler actor, if necessary.
|
||||
* An event from an underlying actor that we just want
|
||||
* to pipe to the connection itself.
|
||||
*/
|
||||
_startProfiler: Task.async(function *(options={}) {
|
||||
// Start the profiler only if it wasn't already active. The built-in
|
||||
// nsIPerformance module will be kept recording, because it's the same instance
|
||||
// for all targets and interacts with the whole platform, so we don't want
|
||||
// to affect other clients by stopping (or restarting) it.
|
||||
let profilerStatus = yield this._request("profiler", "isActive");
|
||||
if (profilerStatus.isActive) {
|
||||
this.emit("profiler-already-active");
|
||||
return profilerStatus.currentTime;
|
||||
}
|
||||
|
||||
// Translate options from the recording model into profiler-specific
|
||||
// options for the nsIProfiler
|
||||
let profilerOptions = {
|
||||
entries: options.bufferSize,
|
||||
interval: options.sampleFrequency ? (1000 / (options.sampleFrequency * 1000)) : void 0
|
||||
};
|
||||
|
||||
yield this._request("profiler", "startProfiler", profilerOptions);
|
||||
|
||||
this.emit("profiler-activated");
|
||||
return 0;
|
||||
}),
|
||||
|
||||
/**
|
||||
* Starts the timeline actor.
|
||||
*/
|
||||
_startTimeline: Task.async(function *(options) {
|
||||
// The timeline actor is target-dependent, so just make sure it's recording.
|
||||
// It won't, however, be available in older Geckos (FF < 35).
|
||||
return (yield this._request("timeline", "start", options));
|
||||
}),
|
||||
|
||||
/**
|
||||
* Stops the timeline actor.
|
||||
*/
|
||||
_stopTimeline: Task.async(function *(options) {
|
||||
return (yield this._request("timeline", "stop"));
|
||||
}),
|
||||
|
||||
/**
|
||||
* Starts polling for allocations from the memory actor, if necessary.
|
||||
*/
|
||||
_startMemory: Task.async(function *(options) {
|
||||
if (!options.withAllocations) {
|
||||
return 0;
|
||||
}
|
||||
let memoryStartTime = yield this._startRecordingAllocations(options);
|
||||
yield this._pullAllocationSites();
|
||||
return memoryStartTime;
|
||||
}),
|
||||
|
||||
/**
|
||||
* Stops polling for allocations from the memory actor, if necessary.
|
||||
*/
|
||||
_stopMemory: Task.async(function *(options) {
|
||||
if (!options.withAllocations) {
|
||||
return 0;
|
||||
}
|
||||
// Since `_pullAllocationSites` is usually running inside a timeout, and
|
||||
// it's performing asynchronous requests to the server, a recording may
|
||||
// be stopped before that method finishes executing. Therefore, we need to
|
||||
// wait for the last request to `getAllocations` to finish before actually
|
||||
// stopping recording allocations.
|
||||
yield this._lastPullAllocationSitesFinished;
|
||||
clearTimeout(this._sitesPullTimeout);
|
||||
|
||||
return yield this._stopRecordingAllocations();
|
||||
}),
|
||||
|
||||
/**
|
||||
* Starts recording allocations in the memory actor.
|
||||
*/
|
||||
_startRecordingAllocations: Task.async(function*(options) {
|
||||
yield this._request("memory", "attach");
|
||||
let memoryStartTime = yield this._request("memory", "startRecordingAllocations", {
|
||||
probability: options.allocationsSampleProbability,
|
||||
maxLogLength: options.allocationsMaxLogLength
|
||||
});
|
||||
return memoryStartTime;
|
||||
}),
|
||||
|
||||
/**
|
||||
* Stops recording allocations in the memory actor.
|
||||
*/
|
||||
_stopRecordingAllocations: Task.async(function*() {
|
||||
let memoryEndTime = yield this._request("memory", "stopRecordingAllocations");
|
||||
yield this._request("memory", "detach");
|
||||
return memoryEndTime;
|
||||
}),
|
||||
|
||||
/**
|
||||
* At regular intervals, pull allocations from the memory actor, and forward
|
||||
* them to consumers.
|
||||
*/
|
||||
_pullAllocationSites: Task.async(function *() {
|
||||
let deferred = promise.defer();
|
||||
this._lastPullAllocationSitesFinished = deferred.promise;
|
||||
|
||||
let isDetached = (yield this._request("memory", "getState")) !== "attached";
|
||||
if (isDetached) {
|
||||
deferred.resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
let memoryData = yield this._request("memory", "getAllocations");
|
||||
|
||||
this._onTimelineData("allocations", {
|
||||
sites: memoryData.allocations,
|
||||
timestamps: memoryData.allocationsTimestamps,
|
||||
frames: memoryData.frames,
|
||||
counts: memoryData.counts
|
||||
});
|
||||
|
||||
let delay = DEFAULT_ALLOCATION_SITES_PULL_TIMEOUT;
|
||||
this._sitesPullTimeout = setTimeout(this._pullAllocationSites, delay);
|
||||
|
||||
deferred.resolve();
|
||||
}),
|
||||
_pipeToConnection: function (eventName, ...args) {
|
||||
this.emit(eventName, ...args);
|
||||
},
|
||||
|
||||
toString: () => "[object PerformanceActorsConnection]"
|
||||
};
|
||||
@ -620,7 +415,6 @@ function PerformanceFront(connection) {
|
||||
EventEmitter.decorate(this);
|
||||
|
||||
this._connection = connection;
|
||||
this._request = connection._request;
|
||||
|
||||
// Set when mocks are being used
|
||||
this._memorySupported = connection._memorySupported;
|
||||
@ -680,6 +474,17 @@ PerformanceFront.prototype = {
|
||||
*/
|
||||
isRecording: function () {
|
||||
return this._connection.isRecording();
|
||||
},
|
||||
|
||||
/**
|
||||
* Interacts with the connection's actors. Should only be used in tests.
|
||||
*/
|
||||
_request: function (actorName, method, ...args) {
|
||||
if (!gDevTools.testing) {
|
||||
throw new Error("PerformanceFront._request may only be used in tests.");
|
||||
}
|
||||
let actor = this._connection[`_${actorName}`];
|
||||
return actor[method].apply(actor, args);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -4,6 +4,7 @@
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
EXTRA_JS_MODULES.devtools.performance += [
|
||||
'modules/actors.js',
|
||||
'modules/compatibility.js',
|
||||
'modules/front.js',
|
||||
'modules/graphs.js',
|
||||
|
@ -18,7 +18,7 @@ function spawnTest () {
|
||||
|
||||
ok(sharedConnection,
|
||||
"A shared profiler connection for the current toolbox was retrieved.");
|
||||
is(sharedConnection._request, panel.panelWin.gFront._request,
|
||||
is(panel.panelWin.gFront._connection, sharedConnection,
|
||||
"The same shared profiler connection is used by the panel's front.");
|
||||
|
||||
yield sharedConnection.open();
|
||||
|
@ -124,7 +124,9 @@ endef
|
||||
$(foreach subtier,$(filter-out compile,$(TIERS)),$(eval $(call CREATE_SUBTIER_TRAVERSAL_RULE,$(subtier))))
|
||||
|
||||
ifndef TOPLEVEL_BUILD
|
||||
ifdef COMPILE_ENVIRONMENT
|
||||
libs:: target host
|
||||
endif # COMPILE_ENVIRONMENT
|
||||
endif
|
||||
|
||||
endif # ifeq ($(NO_RECURSE_MAKELEVEL),$(MAKELEVEL))
|
||||
|
29
configure.in
29
configure.in
@ -367,6 +367,15 @@ dnl ========================================================
|
||||
dnl AR_FLAGS set here so HOST_AR_FLAGS can be set correctly (see bug 538269)
|
||||
AR_FLAGS='crs $@'
|
||||
|
||||
if test -z "$COMPILE_ENVIRONMENT"; then
|
||||
if test "$target" != "$host"; then
|
||||
# Assert that we're cross compiling, but don't require a compile toolchain (as
|
||||
# MOZ_CROSS_COMPILER does below).
|
||||
CROSS_COMPILE=1
|
||||
AC_DEFINE(CROSS_COMPILE)
|
||||
fi
|
||||
fi # !COMPILE_ENVIRONMENT
|
||||
|
||||
if test "$COMPILE_ENVIRONMENT"; then
|
||||
|
||||
if test "$target" != "$host"; then
|
||||
@ -1965,7 +1974,7 @@ case "$target" in
|
||||
TARGET_COMPILER_ABI="ibmc"
|
||||
CC_VERSION=`lslpp -Lcq vac.C 2>/dev/null | awk -F: '{ print $3 }'`
|
||||
CXX_VERSION=`lslpp -Lcq vacpp.cmp.core 2>/dev/null | awk -F: '{ print $3 }'`
|
||||
fi
|
||||
fi # COMPILE_ENVIRONMENT
|
||||
fi
|
||||
case "${target_os}" in
|
||||
aix4.1*)
|
||||
@ -1974,7 +1983,7 @@ case "$target" in
|
||||
esac
|
||||
if test "$COMPILE_ENVIRONMENT"; then
|
||||
MOZ_CHECK_HEADERS(sys/inttypes.h)
|
||||
fi
|
||||
fi # COMPILE_ENVIRONMENT
|
||||
AC_DEFINE(NSCAP_DISABLE_DEBUG_PTR_TYPES)
|
||||
;;
|
||||
|
||||
@ -2101,7 +2110,9 @@ ia64*-hpux*)
|
||||
MOZ_SYNTH_PICO=1
|
||||
else
|
||||
_PLATFORM_DEFAULT_TOOLKIT=cairo-android
|
||||
MOZ_LINKER=1
|
||||
if test "$COMPILE_ENVIRONMENT"; then
|
||||
MOZ_LINKER=1
|
||||
fi
|
||||
fi
|
||||
TARGET_NSPR_MDCPUCFG='\"md/_linux.cfg\"'
|
||||
|
||||
@ -2587,7 +2598,7 @@ if test -z "$COMPILE_ENVIRONMENT"; then
|
||||
SKIP_LIBRARY_CHECKS=1
|
||||
else
|
||||
MOZ_COMPILER_OPTS
|
||||
fi
|
||||
fi # COMPILE_ENVIRONMENT
|
||||
|
||||
if test -z "$SKIP_COMPILER_CHECKS"; then
|
||||
dnl Checks for typedefs, structures, and compiler characteristics.
|
||||
@ -3919,7 +3930,9 @@ case "${target}" in
|
||||
NSS_DISABLE_DBM=1
|
||||
MOZ_THEME_FASTSTRIPE=1
|
||||
MOZ_TREE_FREETYPE=1
|
||||
MOZ_MEMORY=1
|
||||
if test "$COMPILE_ENVIRONMENT"; then
|
||||
MOZ_MEMORY=1
|
||||
fi
|
||||
MOZ_RAW=1
|
||||
;;
|
||||
|
||||
@ -5451,7 +5464,7 @@ if test -n "$MOZ_VPX" -a -z "$MOZ_NATIVE_LIBVPX"; then
|
||||
VPX_ASFLAGS="-f win32 -rnasm -pnasm -DPIC"
|
||||
VPX_X86_ASM=1
|
||||
dnl The encoder needs obj_int_extract to get asm offsets.
|
||||
fi
|
||||
fi # COMPILE_ENVIRONMENT and others
|
||||
;;
|
||||
*:arm*)
|
||||
if test -n "$GNU_AS" ; then
|
||||
@ -5481,7 +5494,7 @@ if test -n "$MOZ_VPX" -a -z "$MOZ_NATIVE_LIBVPX"; then
|
||||
|
||||
if test -n "$COMPILE_ENVIRONMENT" -a -n "$VPX_X86_ASM" -a -z "$VPX_AS"; then
|
||||
AC_MSG_ERROR([yasm is a required build tool for this architecture when webm is enabled. You may either install yasm or --disable-webm (which disables the WebM video format). See https://developer.mozilla.org/en/YASM for more details.])
|
||||
fi
|
||||
fi # COMPILE_ENVIRONMENT and others
|
||||
|
||||
if test -z "$GNU_CC" -a -z "$INTEL_CC" -a -z "$CLANG_CC" ; then
|
||||
dnl We prefer to get asm offsets using inline assembler, which the above
|
||||
@ -7339,7 +7352,7 @@ if test -n "$COMPILE_ENVIRONMENT" -a -n "$USE_ELF_HACK"; then
|
||||
USE_ELF_HACK=
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi # COMPILE_ENVIRONMENT and others.
|
||||
|
||||
dnl ========================================================
|
||||
dnl = libstdc++ compatibility hacks
|
||||
|
@ -1,19 +0,0 @@
|
||||
Adjust SDK integration
|
||||
======================
|
||||
|
||||
The *Adjust install tracking SDK* is a pure-Java library that is conditionally
|
||||
compiled into Fennec. It's not trivial to integrate such conditional feature
|
||||
libraries into Fennec without pre-processing. To minimize such pre-processing,
|
||||
we define a trivial ``AdjustHelperInterface`` and define two implementations:
|
||||
the real ``AdjustHelper``, which requires the Adjust SDK, and a no-op
|
||||
``StubAdjustHelper``, which has no additional requirements. We use the existing
|
||||
pre-processed ``AppConstants.java.in`` to switch, at build-time, between the two
|
||||
implementations.
|
||||
|
||||
An alternative approach would be to build three jars -- one interface jar and
|
||||
two implementation jars -- and include one of the implementation jars at
|
||||
build-time. The implementation jars could either define a common symbol, or the
|
||||
appropriate symbol could be determined at build-time. That's a rather heavier
|
||||
approach than the one chosen. If the helper class were to grow to multiple
|
||||
classes, with a non-trivial exposed API, this approach could be better. It
|
||||
would also be easier to integrate into the parallel Gradle build system.
|
@ -30,7 +30,13 @@ public class ReferrerReceiver extends BroadcastReceiver {
|
||||
public static final String ACTION_REFERRER_RECEIVED = "org.mozilla.fennec.REFERRER_RECEIVED";
|
||||
|
||||
/**
|
||||
* If the install intent has this source, we'll track the campaign ID.
|
||||
* If the install intent has this source, it is a Mozilla specific or over
|
||||
* the air distribution referral. We'll track the campaign ID using
|
||||
* Mozilla's metrics systems.
|
||||
*
|
||||
* If the install intent has a source different than this one, it is a
|
||||
* referral from an advertising network. We may track these campaigns using
|
||||
* third-party tracking and metrics systems.
|
||||
*/
|
||||
private static final String MOZILLA_UTM_SOURCE = "mozilla";
|
||||
|
||||
|
161
mobile/android/base/docs/adjust.rst
Normal file
161
mobile/android/base/docs/adjust.rst
Normal file
@ -0,0 +1,161 @@
|
||||
.. -*- Mode: rst; fill-column: 100; -*-
|
||||
|
||||
======================================
|
||||
Install tracking with the Adjust SDK
|
||||
======================================
|
||||
|
||||
Fennec (Firefox for Android) tracks certain types of installs using a third party install tracking
|
||||
framework called Adjust. The intention is to determine the origin of Fennec installs by answering
|
||||
the question, "Did this user on this device install Fennec in response to a specific advertising
|
||||
campaign performed by Mozilla?"
|
||||
|
||||
Mozilla is using a third party framework in order to answer this question for the Firefox for
|
||||
Android 38.0.5 release. We hope to remove the framework from Fennec in the future.
|
||||
|
||||
The framework consists of a software development kit (SDK) built into Fennec and a
|
||||
data-collecting Internet service backend run by the German company `adjust GmbH`_. The Adjust SDK
|
||||
is open source and MIT licensed: see the `github repository`_. Fennec ships a copy of the SDK
|
||||
(currently not modified from upstream) in ``mobile/android/thirdparty/com/adjust/sdk``. The SDK is
|
||||
documented at https://docs.adjust.com.
|
||||
|
||||
Data collection
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
When is data collected and sent to the Adjust backend?
|
||||
======================================================
|
||||
|
||||
Data is never collected (or sent to the Adjust backend) unless
|
||||
|
||||
* the Fennec binary is an official Mozilla binary [#official]_; and
|
||||
* the release channel is Release or Beta [#channel]_.
|
||||
|
||||
If both of the above conditions are true, then data is collected and sent to the Adjust backend in
|
||||
the following two circumstances: first, when
|
||||
|
||||
* Fennec is started on the device [#started]_.
|
||||
|
||||
Second, when
|
||||
|
||||
* the Fennec binary was installed from the Google Play Store; and
|
||||
* the Google Play Store sends the installed Fennec binary an `INSTALL_REFERRER Intent`_, and the
|
||||
received Intent includes Google Play Store campaign tracking information. This happens when thea
|
||||
Google Play Store install is in response to a campaign-specific Google Play Store link. For
|
||||
details, see the developer documentation at
|
||||
https://developers.google.com/analytics/devguides/collection/android/v4/campaigns.
|
||||
|
||||
In these two limited circumstances, data is collected and sent to the Adjust backend.
|
||||
|
||||
Where does data sent to the Adjust backend go?
|
||||
==============================================
|
||||
|
||||
The Adjust SDK is hard-coded to send data to the endpoint https://app.adjust.com. The endpoint is
|
||||
defined by ``com.adjust.sdk.Constants.BASE_URL`` at
|
||||
https://hg.mozilla.org/mozilla-central/file/f76f02793f7a/mobile/android/thirdparty/com/adjust/sdk/Constants.java#l27.
|
||||
|
||||
The Adjust backend then sends a limited subset of the collected data -- limited but sufficient to
|
||||
uniquely identify the submitting device -- to a set of advertising network providers that Mozilla
|
||||
elects to share the collected data with. Those advertising networks then confirm or deny that the
|
||||
identifying information corresponds to a specific advertising campaign performed by Mozilla.
|
||||
|
||||
What data is collected and sent to the Adjust backend?
|
||||
======================================================
|
||||
|
||||
The Adjust SDK collects and sends two messages to the Adjust backend. The messages have the
|
||||
following parameters::
|
||||
|
||||
V/Adjust ( 6508): Parameters:
|
||||
V/Adjust ( 6508): screen_format normal
|
||||
V/Adjust ( 6508): device_manufacturer samsung
|
||||
V/Adjust ( 6508): session_count 1
|
||||
V/Adjust ( 6508): device_type phone
|
||||
V/Adjust ( 6508): screen_size normal
|
||||
V/Adjust ( 6508): package_name org.mozilla.firefox
|
||||
V/Adjust ( 6508): app_version 39.0a1
|
||||
V/Adjust ( 6508): android_uuid <guid>
|
||||
V/Adjust ( 6508): display_width 720
|
||||
V/Adjust ( 6508): country GB
|
||||
V/Adjust ( 6508): os_version 18
|
||||
V/Adjust ( 6508): needs_attribution_data 0
|
||||
V/Adjust ( 6508): environment sandbox
|
||||
V/Adjust ( 6508): device_name Galaxy Nexus
|
||||
V/Adjust ( 6508): os_name android
|
||||
V/Adjust ( 6508): tracking_enabled 1
|
||||
V/Adjust ( 6508): created_at 2015-03-24T17:53:38.452Z-0400
|
||||
V/Adjust ( 6508): app_token <private>
|
||||
V/Adjust ( 6508): screen_density high
|
||||
V/Adjust ( 6508): language en
|
||||
V/Adjust ( 6508): display_height 1184
|
||||
V/Adjust ( 6508): gps_adid <guid>
|
||||
|
||||
V/Adjust ( 6508): Parameters:
|
||||
V/Adjust ( 6508): needs_attribution_data 0
|
||||
V/Adjust ( 6508): app_token <private>
|
||||
V/Adjust ( 6508): environment production
|
||||
V/Adjust ( 6508): android_uuid <guid>
|
||||
V/Adjust ( 6508): tracking_enabled 1
|
||||
V/Adjust ( 6508): gps_adid <guid>
|
||||
|
||||
The available parameters (including ones not exposed to Mozilla) are documented at
|
||||
https://partners.adjust.com/placeholders/.
|
||||
|
||||
Notes on what data is collected
|
||||
-------------------------------
|
||||
|
||||
The *android_uuid* uniquely identifies the device.
|
||||
|
||||
The *gps_adid* is a Google Advertising ID. It is capable of uniquely identifying a device to any
|
||||
advertiser, across all applications. If a Google Advertising ID is not available, Adjust may fall
|
||||
back to an Android ID, or, as a last resort, the device's WiFi MAC address.
|
||||
|
||||
The *tracking_enabled* flag is only used to allow or disallow contextual advertising to be sent to a
|
||||
user. It can be, and is, ignored for general install tracking of the type Mozilla is using the
|
||||
Adjust SDK for. (This flag might be used by consumers using the Adjust SDK to provide in-App
|
||||
advertising.)
|
||||
|
||||
It is not clear how much entropy their is in the set of per-device parameters that do not
|
||||
*explicitly* uniquely identify the device. That is, it is not known if the device parameters are
|
||||
likely to uniquely fingerprint the device, in the way that user agent capabilities are likely to
|
||||
uniquely fingerprint the user.
|
||||
|
||||
Technical notes
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Build flags controlling the Adjust SDK integration
|
||||
==================================================
|
||||
|
||||
The Adjust SDK feature is controlled by the build flag ``MOZ_INSTALL_TRACKING``. No trace of the
|
||||
Adjust SDK should be present in Fennec if this is not defined.
|
||||
|
||||
Access to the Adjust backend is controlled by a private App-specific token. Fennec's token is
|
||||
managed by Release Engineering and should not be exposed if at all possible; for example, it should
|
||||
*not* leak to build logs. The value of the token is read from the file specified using the
|
||||
``configure`` flag ``--with-adjust-sdk-keyfile=KEYFILE`` and stored in the build variable
|
||||
``MOZ_INSTALL_TRACKING_ADJUST_SDK_APP_TOKEN``. Nota bene: if ``MOZ_INSTALL_TRACKING`` is defined
|
||||
but the App-specific token is not specified, Fennec will submit data to a special Adjust sandbox.
|
||||
This makes it possible to test the Adjust flow without submitting false data to the install tracking
|
||||
backend.
|
||||
|
||||
Technical notes on the Adjust SDK integration
|
||||
=============================================
|
||||
|
||||
The *Adjust install tracking SDK* is a pure-Java library that is conditionally compiled into Fennec.
|
||||
It's not trivial to integrate such conditional feature libraries into Fennec without pre-processing.
|
||||
To minimize such pre-processing, we define a trivial ``AdjustHelperInterface`` and define two
|
||||
implementations: the real ``AdjustHelper``, which requires the Adjust SDK, and a no-op
|
||||
``StubAdjustHelper``, which has no additional requirements. We use the existing pre-processed
|
||||
``AppConstants.java.in`` to switch, at build-time, between the two implementations.
|
||||
|
||||
Notes and links
|
||||
===============
|
||||
|
||||
.. _adjust GmbH: http://www.adjust.com
|
||||
.. _github repository: https://github.com/adjust/android_sdk
|
||||
.. [#official] Data is not sent for builds not produced by Mozilla: this would include
|
||||
redistributors such as the Palemoon project.
|
||||
.. [#channel] Data is not sent for Aurora, Nightly, or custom builds.
|
||||
.. [#started] *Started* means more than just when the user taps the Fennec icon or otherwise causes
|
||||
the Fennec user interface to appear directly. It includes, for example, when a Fennec service
|
||||
(like the Update Service, or Background Sync), starts and Fennec was not previously running on the
|
||||
device. See http://developer.android.com/reference/android/app/Application.html#onCreate%28%29
|
||||
for details.
|
||||
.. _INSTALL_REFERRER Intent: https://developer.android.com/reference/com/google/android/gms/tagmanager/InstallReferrerReceiver.html
|
@ -7,4 +7,5 @@ Firefox for Android
|
||||
|
||||
localeswitching
|
||||
uitelemetry
|
||||
adjust
|
||||
gradle
|
||||
|
@ -760,6 +760,10 @@ for var in ('ANDROID_PACKAGE_NAME', 'ANDROID_CPU_ARCH',
|
||||
# Mangle our package name to avoid Bug 750548.
|
||||
DEFINES['MANGLED_ANDROID_PACKAGE_NAME'] = CONFIG['ANDROID_PACKAGE_NAME'].replace('fennec', 'f3nn3c')
|
||||
DEFINES['MOZ_APP_ABI'] = CONFIG['TARGET_XPCOM_ABI']
|
||||
if not CONFIG['COMPILE_ENVIRONMENT']:
|
||||
# These should really come from the included binaries, but that's not easy.
|
||||
DEFINES['MOZ_APP_ABI'] = 'arm-eabi-gcc3' # Observe quote differences here ...
|
||||
DEFINES['TARGET_XPCOM_ABI'] = '"arm-eabi-gcc3"' # ... and here.
|
||||
|
||||
if '-march=armv7' in CONFIG['OS_CFLAGS']:
|
||||
DEFINES['MOZ_MIN_CPU_VERSION'] = 7
|
||||
|
@ -6,7 +6,7 @@
|
||||
<resources>
|
||||
|
||||
<dimen name="arrow_popup_container_width">400dp</dimen>
|
||||
<dimen name="doorhanger_offsetY">124dp</dimen>
|
||||
<dimen name="doorhanger_offsetY">100dp</dimen>
|
||||
|
||||
<dimen name="browser_toolbar_height">56dp</dimen>
|
||||
<dimen name="browser_toolbar_height_flipper">60dp</dimen>
|
||||
|
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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/. -->
|
||||
|
||||
<resources>
|
||||
<dimen name="doorhanger_offsetY">124dp</dimen>
|
||||
</resources>
|
@ -46,8 +46,11 @@ MOZ_APP_ID={aa3c5121-dab2-40e2-81ca-7ea25febc110}
|
||||
|
||||
MOZ_APP_STATIC_INI=1
|
||||
|
||||
# Enable on-demand decompression
|
||||
# Enable on-demand decompression. This requires a host compile toolchain to
|
||||
# build szip to use during packaging.
|
||||
if test "$COMPILE_ENVIRONMENT"; then
|
||||
MOZ_ENABLE_SZIP=1
|
||||
fi
|
||||
|
||||
# Enable navigator.mozPay
|
||||
MOZ_PAY=1
|
||||
|
@ -1122,9 +1122,9 @@ class AndroidCommands(MachCommandBase):
|
||||
'--robocop-ini=' +
|
||||
os.path.join(
|
||||
self.topobjdir,
|
||||
'build',
|
||||
'mobile',
|
||||
'robocop',
|
||||
'_tests',
|
||||
'testing',
|
||||
'mochitest',
|
||||
'robocop.ini'),
|
||||
'--log-mach=-',
|
||||
]
|
||||
|
@ -66,7 +66,7 @@ RUN_MOCHITEST_ROBOCOP = \
|
||||
$(PYTHON) _tests/testing/mochitest/runtestsremote.py \
|
||||
--robocop-apk=$(DEPTH)/build/mobile/robocop/robocop-debug.apk \
|
||||
--robocop-ids=$(DEPTH)/mobile/android/base/fennec_ids.txt \
|
||||
--robocop-ini=$(DEPTH)/build/mobile/robocop/robocop.ini \
|
||||
--robocop-ini=_tests/testing/mochitest/robocop.ini \
|
||||
--console-level=INFO --log-tbpl=./$@.log $(DM_FLAGS) --dm_trans=$(DM_TRANS) \
|
||||
--app=$(TEST_PACKAGE_NAME) --deviceIP=${TEST_DEVICE} --xre-path=${MOZ_HOST_BIN} \
|
||||
$(SYMBOLS_PATH) $(TEST_PATH_ARG) $(EXTRA_TEST_ARGS)
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -3,6 +3,7 @@
|
||||
; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
[DEFAULT]
|
||||
tags = appupdate
|
||||
skip-if = (buildapp == 'b2g' || buildapp == 'mulet')
|
||||
support-files =
|
||||
utils.js
|
||||
|
@ -3,6 +3,7 @@
|
||||
; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
[DEFAULT]
|
||||
tags = appupdate
|
||||
head = head_update.js
|
||||
tail =
|
||||
|
||||
|
@ -7,6 +7,7 @@
|
||||
; from running the tests in the moz.build file.
|
||||
|
||||
[DEFAULT]
|
||||
tags = appupdate
|
||||
head = head_update.js
|
||||
tail =
|
||||
|
||||
|
@ -5,6 +5,7 @@
|
||||
; Tests that require the updater binary and the maintenance service.
|
||||
|
||||
[DEFAULT]
|
||||
tags = appupdate
|
||||
head = head_update.js
|
||||
tail =
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user