Merge f-t to m-c, a=merge
authorPhil Ringnalda <philringnalda@gmail.com>
Sat, 02 May 2015 10:13:08 -0700
changeset 242137 dc5f85980a828aaa32b599b15b93ab8d1287ff5b
parent 242123 0f5eacc986e89338f40fc5478698850d756b496e (current diff)
parent 242136 a2bcc5840bd0d7cab902989bbc2ceb54d7d7be93 (diff)
child 242138 b8af7eb3f5131d98e63a23792ca9c2a89b0a7ade
child 242180 87a8c00836a09f68d44bd4938576ae2292be84a6
child 242195 acf6ede9d6781db9cc0e4c5dc509baed0f9863d1
push id59313
push userphilringnalda@gmail.com
push dateSat, 02 May 2015 17:16:09 +0000
treeherdermozilla-inbound@dc5f85980a82 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone40.0a1
first release with
nightly linux32
dc5f85980a82 / 40.0a1 / 20150503030209 / files
nightly linux64
dc5f85980a82 / 40.0a1 / 20150503030209 / files
nightly mac
dc5f85980a82 / 40.0a1 / 20150503030209 / files
nightly win32
dc5f85980a82 / 40.0a1 / 20150503030209 / files
nightly win64
dc5f85980a82 / 40.0a1 / 20150503030209 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge f-t to m-c, a=merge
mobile/android/base/adjust/adjust.rst
--- a/browser/devtools/framework/toolbox.js
+++ b/browser/devtools/framework/toolbox.js
@@ -712,16 +712,20 @@ Toolbox.prototype = {
   /**
    * Add buttons to the UI as specified in the devtools.toolbox.toolbarSpec pref
    */
   _buildButtons: function() {
     if (!this.target.isAddon) {
       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')
     };
     return CommandUtils.createRequisition(this.target, options).then(requisition => {
       this._requisition = requisition;
 
       const spec = CommandUtils.getCommandbarSpec("devtools.toolbox.toolbarSpec");
       return CommandUtils.createButtons(spec, this.target, this.doc,
new file mode 100644
--- /dev/null
+++ b/browser/devtools/performance/modules/actors.js
@@ -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;
--- a/browser/devtools/performance/modules/compatibility.js
+++ b/browser/devtools/performance/modules/compatibility.js
@@ -1,80 +1,17 @@
 /* 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");
-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.
  *
  * @param array blueprint
  *        A list of [funcName, retVal] describing the class.
  */
 function MockFront (blueprint) {
@@ -82,31 +19,31 @@ function MockFront (blueprint) {
 
   for (let [funcName, retVal] of blueprint) {
     this[funcName] = (x => typeof x === "function" ? x() : x).bind(this, retVal);
   }
 }
 
 function MockMemoryFront () {
   MockFront.call(this, [
-    ["initialize"],
+    ["start", 0], // for facade
+    ["stop", 0], // for facade
     ["destroy"],
     ["attach"],
     ["detach"],
     ["getState", "detached"],
     ["startRecordingAllocations", 0],
     ["stopRecordingAllocations", 0],
     ["getAllocations", createMockAllocations],
   ]);
 }
 exports.MockMemoryFront = MockMemoryFront;
 
 function MockTimelineFront () {
   MockFront.call(this, [
-    ["initialize"],
     ["destroy"],
     ["start", 0],
     ["stop", 0],
   ]);
 }
 exports.MockTimelineFront = MockTimelineFront;
 
 /**
@@ -164,17 +101,71 @@ function timelineActorSupported(target) 
     return false;
   }
 
   return target.hasActor("timeline");
 }
 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;
--- a/browser/devtools/performance/modules/front.js
+++ b/browser/devtools/performance/modules/front.js
@@ -4,51 +4,39 @@
 "use strict";
 
 const { Cc, Ci, Cu, Cr } = require("chrome");
 const { Task } = require("resource://gre/modules/Task.jsm");
 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");
 
 
 // How often do we pull allocation sites from the memory actor.
 const DEFAULT_ALLOCATION_SITES_PULL_TIMEOUT = 200; // ms
 
 // Events to pipe from PerformanceActorsConnection to the PerformanceFront
 const CONNECTION_PIPE_EVENTS = [
   "console-profile-start", "console-profile-ending", "console-profile-end",
   "timeline-data", "profiler-already-active", "profiler-activated",
   "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.
  */
 let SharedPerformanceActors = new WeakMap();
 
 /**
  * Instantiates a shared PerformanceActorsConnection for the specified target.
@@ -79,27 +67,25 @@ SharedPerformanceActors.forTarget = func
  * @param Target target
  *        The target owning this connection.
  */
 function PerformanceActorsConnection(target) {
   EventEmitter.decorate(this);
 
   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);
 }
 
 PerformanceActorsConnection.prototype = {
 
   // Properties set based off of server actor support
   _memorySupported: true,
@@ -123,20 +109,17 @@ PerformanceActorsConnection.prototype = 
 
     // Local debugging needs to make the target remote.
     yield this._target.makeRemote();
 
     // Sets `this._profiler`, `this._timeline` and `this._memory`.
     // 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;
 
     this._connecting.resolve();
     Services.obs.notifyObservers(null, "performance-actors-connection-opened", null);
   }),
 
@@ -154,152 +137,84 @@ PerformanceActorsConnection.prototype = 
     yield this._disconnectActors();
 
     this._memory = this._timeline = this._profiler = this._target = this._client = null;
     this._connected = false;
     this._connecting = null;
   }),
 
   /**
-   * Initializes a connection to the profiler actor. Uses a facade around the ProfilerFront
-   * for similarity to the other actors in the shared connection.
-   */
-  _connectProfilerActor: Task.async(function*() {
-    this._profiler = new compatibility.ProfilerFront(this._target);
-    yield this._profiler.connect();
-  }),
-
-  /**
-   * Initializes a connection to a timeline actor.
+   * Initializes fronts and connects to the underlying actors using the facades
+   * found in ./actors.js.
    */
-  _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;
-  },
+  _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 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;
+    yield Promise.all([
+      this._profiler.connect(),
+      this._memory.connect(),
+      this._timeline.connect()
+    ]);
+
+    // 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.
    *
    * @param string profileLabel
    *        The provided string argument if available; undefined otherwise.
    * @param number currentTime
    *        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.
     if (recordings.find(e => e.getLabel() === profileLabel)) {
       return;
     }
 
     // Ensure the performance front is set up and ready.
@@ -320,17 +235,17 @@ PerformanceActorsConnection.prototype = 
    * Invoked whenever `console.profileEnd` is called.
    *
    * @param string profileLabel
    *        The provided string argument if available; undefined otherwise.
    * @param number currentTime
    *        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) {
       return;
     }
     let { profileLabel, currentTime: endTime } = data;
 
     let pending = this._recordings.filter(r => r.isConsole() && r.isRecording());
@@ -356,37 +271,34 @@ PerformanceActorsConnection.prototype = 
       return;
     }
 
     this.emit("console-profile-ending", model);
     yield this.stopRecording(model);
     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:
    * - markers
    * - frames
    * - memory
    * - ticks
    * - allocations
    *
    * 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);
   },
 
   /**
    * Begins a recording session
    *
    * @param object options
@@ -394,25 +306,23 @@ PerformanceActorsConnection.prototype = 
    *        `withTicks`, `withMemory` and `withAllocations`, `probability`, and `maxLogLength`.
    * @return object
    *         A promise that is resolved once recording has started.
    */
   startRecording: Task.async(function*(options = {}) {
     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.
     model.populate(data);
     this._recordings.push(model);
 
     this.emit("recording-started", model);
     return model;
@@ -440,27 +350,27 @@ PerformanceActorsConnection.prototype = 
     // remove it from the internal store.
     //
     // In the case where a console.profile is generated via the console (so the tools are
     // open), we initialize the Performance tool so it can listen to those events.
     this._recordings.splice(this._recordings.indexOf(model), 1);
 
     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();
 
     // Only if there are no more sessions recording do we stop
     // the underlying memory and timeline actors. If we're still recording,
     // 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.
     model._onStopRecording({
       // Data available only at the end of a recording.
       profile: profilerData.profile,
 
       // End times for all the actors.
@@ -479,153 +389,37 @@ PerformanceActorsConnection.prototype = 
    *
    * @return Boolean
    */
   isRecording: function () {
     return this._recordings.some(recording => recording.isRecording());
   },
 
   /**
-   * Starts the profiler actor, if necessary.
-   */
-  _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.
+   * An event from an underlying actor that we just want
+   * to pipe to the connection itself.
    */
-  _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]"
 };
 
 /**
  * A thin wrapper around a shared PerformanceActorsConnection for the parent target.
  * Handles manually starting and stopping a recording.
  *
  * @param PerformanceActorsConnection connection
  *        The shared instance for the parent target.
  */
 function PerformanceFront(connection) {
   EventEmitter.decorate(this);
 
   this._connection = connection;
-  this._request = connection._request;
 
   // Set when mocks are being used
   this._memorySupported = connection._memorySupported;
   this._timelineSupported = connection._timelineSupported;
 
   // Pipe the console profile events from the connection
   // to the front so that the UI can listen.
   CONNECTION_PIPE_EVENTS.forEach(eventName => this._connection.on(eventName, () => this.emit.apply(this, arguments)));
@@ -675,16 +469,27 @@ PerformanceFront.prototype = {
 
   /**
    * Returns a boolean indicating whether or not the current performance connection is recording.
    *
    * @return Boolean
    */
   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);
   }
 };
 
 /**
  * Creates an object of configurations based off of preferences for a RecordingModel.
  */
 function getRecordingModelPrefs () {
   return {
--- a/browser/devtools/performance/moz.build
+++ b/browser/devtools/performance/moz.build
@@ -1,14 +1,15 @@
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 EXTRA_JS_MODULES.devtools.performance += [
+    'modules/actors.js',
     'modules/compatibility.js',
     'modules/front.js',
     'modules/graphs.js',
     'modules/io.js',
     'modules/recording-model.js',
     'modules/recording-utils.js',
     'panel.js'
 ]
--- a/browser/devtools/performance/test/browser_perf-shared-connection-02.js
+++ b/browser/devtools/performance/test/browser_perf-shared-connection-02.js
@@ -13,17 +13,17 @@ function spawnTest () {
 
   is(gProfilerConnectionsOpened, 1,
     "Only one profiler connection was opened.");
 
   let sharedConnection = getPerformanceActorsConnection(target);
 
   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();
 
   is(gProfilerConnectionsOpened, 1,
     "No additional profiler connections were opened.");
 
   yield teardown(panel);
--- a/config/recurse.mk
+++ b/config/recurse.mk
@@ -119,17 +119,19 @@ define CREATE_SUBTIER_TRAVERSAL_RULE
 $(1):: $$(SUBMAKEFILES)
 	$$(LOOP_OVER_DIRS)
 
 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))
 
 endif # ifeq (.,$(DEPTH))
 
 recurse:
 	@$(RECURSED_COMMAND)
--- a/configure.in
+++ b/configure.in
@@ -362,16 +362,25 @@ AC_SUBST(OBJCOPY)
 
 dnl ========================================================
 dnl Checks for compilers.
 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
     MOZ_CROSS_COMPILER
 else
     AC_PROG_CC
     case "$target" in
     *-mingw*)
@@ -1960,26 +1969,26 @@ case "$target" in
                 AC_MSG_ERROR([IBM XLC/C++ 9.0.0.7 or higher is required to build.])
             else
                 AC_MSG_RESULT([yes])
             fi
             AC_LANG_RESTORE
             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*)
         DLL_SUFFIX='_shr.a'
         ;;
     esac
     if test "$COMPILE_ENVIRONMENT"; then
         MOZ_CHECK_HEADERS(sys/inttypes.h)
-    fi
+    fi # COMPILE_ENVIRONMENT
     AC_DEFINE(NSCAP_DISABLE_DEBUG_PTR_TYPES)
     ;;
 
 *-darwin*)
     MKSHLIB='$(CXX) $(CXXFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -o $@'
     MKCSHLIB='$(CC) $(CFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -o $@'
     MOZ_OPTIMIZE_FLAGS="-O3"
     # Statically disable jemalloc on 10.5 and 32-bit 10.6.  See bug 702250.
@@ -2096,17 +2105,19 @@ ia64*-hpux*)
     no_x=yes
     if test -n "$gonkdir"; then
         _PLATFORM_DEFAULT_TOOLKIT=cairo-gonk
         _PLATFORM_HAVE_RIL=1
         MOZ_B2G_FM=1
         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\"'
 
     MOZ_GFX_OPTIMIZE_MOBILE=1
     MOZ_OPTIMIZE_FLAGS="-Os -fno-reorder-functions"
     if test -z "$CLANG_CC"; then
        MOZ_OPTIMIZE_FLAGS="-freorder-blocks $MOZ_OPTIMIZE_FLAGS"
     fi
@@ -2582,17 +2593,17 @@ case "$target" in
         ;;
 esac
 
 if test -z "$COMPILE_ENVIRONMENT"; then
     SKIP_COMPILER_CHECKS=1
     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.
 dnl ========================================================
 AC_HEADER_STDC
 AC_C_CONST
 AC_TYPE_MODE_T
 AC_TYPE_OFF_T
@@ -3914,17 +3925,19 @@ case "${target}" in
     *-android*|*-linuxandroid*)
         if test "$CPU_ARCH" = "arm" ; then
           USE_ARM_KUSER=1
         fi
 
         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
         ;;
 
 esac
 
 MOZ_ARG_WITH_STRING(external-source-dir,
 [  --with-external-source-dir=dir
                           External directory containing additional build files.],
@@ -5446,17 +5459,17 @@ if test -n "$MOZ_VPX" -a -z "$MOZ_NATIVE
       if test -n "$COMPILE_ENVIRONMENT" -a -z "$YASM"; then
         AC_MSG_ERROR([yasm 1.1 or greater is required to build libvpx on Win32, but it appears not to be installed.  Install it (included in MozillaBuild 1.5.1 and newer) or configure with --disable-webm (which disables the WebM video format). See https://developer.mozilla.org/en/YASM for more details.])
       elif test -n "$COMPILE_ENVIRONMENT" -a "$_YASM_MAJOR_VERSION" -lt "1" -o \( "$_YASM_MAJOR_VERSION" -eq "1" -a "$_YASM_MINOR_VERSION" -lt "1" \) ; then
         AC_MSG_ERROR([yasm 1.1 or greater is required to build libvpx on Win32, but you appear to have version $_YASM_MAJOR_VERSION.$_YASM_MINOR_VERSION.  Upgrade to the newest version (included in MozillaBuild 1.5.1 and newer) or configure with --disable-webm (which disables the WebM video format). See https://developer.mozilla.org/en/YASM for more details.])
       else
         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
         VPX_AS=$AS
         dnl These flags are a lie; they're just used to enable the requisite
         dnl opcodes; actual arch detection is done at runtime.
         VPX_ASFLAGS="-march=armv7-a -mfpu=neon"
         VPX_DASH_C_FLAG="-c"
@@ -5476,17 +5489,17 @@ if test -n "$MOZ_VPX" -a -z "$MOZ_NATIVE
         VPX_ASFLAGS="-f elf64 -rnasm -pnasm -DPIC"
         VPX_X86_ASM=1
       fi
     ;;
     esac
 
     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
       dnl compilers can do. When we're not using one of those, we have to fall
       dnl back to obj_int_extract, which reads them from a compiled object
       dnl file. Unfortunately, that only works if we're compiling on a system
       dnl with the header files for the appropriate object file format.
       VPX_NEED_OBJ_INT_EXTRACT=1
@@ -7334,17 +7347,17 @@ if test -n "$COMPILE_ENVIRONMENT" -a -n 
                         [NSPR_LDFLAGS="$NSPR_LDFLAGS -Wl,-z,norelro"],
                         AC_ERROR([--enable-elf-hack is not compatible with a linker creating a PT_GNU_RELRO segment and that doesn't support the "-z norelro" option.]))
             USE_ELF_HACK=1
         else
             AC_MSG_WARN([Disabling elfhack])
             USE_ELF_HACK=
         fi
     fi
-fi
+fi # COMPILE_ENVIRONMENT and others.
 
 dnl ========================================================
 dnl = libstdc++ compatibility hacks
 dnl ========================================================
 
 STDCXX_COMPAT=
 MOZ_ARG_ENABLE_BOOL(stdcxx-compat,
 [  --enable-stdcxx-compat  Enable compatibility with older libstdc++],
deleted file mode 100644
--- a/mobile/android/base/adjust/adjust.rst
+++ /dev/null
@@ -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.
--- a/mobile/android/base/distribution/ReferrerReceiver.java
+++ b/mobile/android/base/distribution/ReferrerReceiver.java
@@ -25,17 +25,23 @@ public class ReferrerReceiver extends Br
 
     private static final String ACTION_INSTALL_REFERRER = "com.android.vending.INSTALL_REFERRER";
 
     // Sent when we're done.
     @RobocopTarget
     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";
 
     /**
      * If the install intent has this campaign, we'll load the specified distribution.
      */
     private static final String DISTRIBUTION_UTM_CAMPAIGN = "distribution";
 
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/docs/adjust.rst
@@ -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
--- a/mobile/android/base/docs/index.rst
+++ b/mobile/android/base/docs/index.rst
@@ -2,9 +2,10 @@
 Firefox for Android
 ===================
 
 .. toctree::
    :maxdepth: 1
 
    localeswitching
    uitelemetry
+   adjust
    gradle
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -755,16 +755,20 @@ for var in ('ANDROID_PACKAGE_NAME', 'AND
             'MOZ_APP_VENDOR', 'MOZ_APP_VERSION', 'MOZ_CHILD_PROCESS_NAME',
             'MOZ_CRASHREPORTER', 'MOZ_UPDATE_CHANNEL', 'OMNIJAR_NAME',
             'OS_TARGET', 'TARGET_XPCOM_ABI'):
     DEFINES[var] = CONFIG[var]
 
 # 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
 else:
     DEFINES['MOZ_MIN_CPU_VERSION'] = 5
 
 if CONFIG['MOZ_ANDROID_SEARCH_ACTIVITY']:
     # The Search Activity is mostly independent of Fennec proper, but
--- a/mobile/android/base/resources/values-large-v11/dimens.xml
+++ b/mobile/android/base/resources/values-large-v11/dimens.xml
@@ -1,17 +1,17 @@
 <?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="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>
     <dimen name="browser_toolbar_button_padding">16dp</dimen>
     <dimen name="browser_toolbar_favicon_size">16dp</dimen>
 
     <dimen name="browser_toolbar_site_security_height">60dp</dimen>
     <dimen name="browser_toolbar_site_security_width">34dp</dimen>
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/resources/values-large-v16/dimens.xml
@@ -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>
--- a/mobile/android/confvars.sh
+++ b/mobile/android/confvars.sh
@@ -41,18 +41,21 @@ MOZ_DISABLE_EXPORT_JS=1
 
 # use custom widget for html:select
 MOZ_USE_NATIVE_POPUP_WINDOWS=1
 
 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
 
 # Enable UI for healthreporter
 MOZ_SERVICES_HEALTHREPORT=1
 
 # Enable runtime locale switching.
--- a/testing/mochitest/mach_commands.py
+++ b/testing/mochitest/mach_commands.py
@@ -1117,19 +1117,19 @@ class AndroidCommands(MachCommandBase):
                 self.topobjdir,
                 'build',
                 'mobile',
                 'robocop',
                 'robocop-debug.apk'),
             '--robocop-ini=' +
             os.path.join(
                 self.topobjdir,
-                'build',
-                'mobile',
-                'robocop',
+                '_tests',
+                'testing',
+                'mochitest',
                 'robocop.ini'),
             '--log-mach=-',
         ]
 
         if test_path:
             args.append('--test-path=%s' % test_path)
 
         mochitest = self._spawn(MochitestRunner)
--- a/testing/testsuite-targets.mk
+++ b/testing/testsuite-targets.mk
@@ -61,17 +61,17 @@ RUN_MOCHITEST_REMOTE = \
     --testing-modules-dir=$(abspath _tests/modules) \
     $(SYMBOLS_PATH) $(TEST_PATH_ARG) $(EXTRA_TEST_ARGS)
 
 RUN_MOCHITEST_ROBOCOP = \
   rm -f ./$@.log && \
   $(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)
 
 ifndef NO_FAIL_ON_TEST_ERRORS
 define check_test_error_internal
   @errors=`grep 'TEST-UNEXPECTED-' $@.log` ;\
   if test "$$errors" ; then \
index a62faf1c52b3b5aadb1383d16b4cb5419eaedd1f..ec38fcc651ee28c7e5115ee3048baf2a16be7d75
GIT binary patch
literal 5161
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
index 5444b951a48a7b9114d69cf44fbe7eb45e7280ab..fc284239972191672fb36cd4e170f2a6bc931610
GIT binary patch
literal 5150
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
index 122c4d56b3d37bb98458ae9055630ca1f9aed670..327c8a1875c025a9cd3d740de6c900b5ea18db7f
GIT binary patch
literal 5149
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
index 150918a8c157f6855b4d9ea6796965153af4fe17..efad21d1b65576b75a0ac0efe44583c15fed97c1
GIT binary patch
literal 5155
zc$|$`1yoe)+8(+?sX=N8=@L-71cruz0cptrhK3nRkS?WL8UzJt0YSP$QaY59?ixh;
z;`!IV&VSE6XZ`ovYp=E6z25zN-+ta$>wO@q7?|V$000-DVvz^_QR2vWNC*Jvk^%s~
z->NI<$nq#_fcYGqU%(w4%z51q=JR^qA`{n?!X=Il(UO<xut8`3`%e?(Q>qHAsLAeA
z59mLb?J>0C33d1~)mL5+$Ae19+n*7ri75T@U|IX%^5??Yjq7d0$jj`@#e);^fwRn$
z%oEWOw7$&3rNQITuu*#l5ZHw`4N3w+*agdi2)Wt_!#OJD+JgA815Q)#v!_PcqV-j>
z1`u-ehX%-+@zYv$oKs)iH@k!qbT!zMFWhwEb>ZVH29<I1KMh!-H<M*j$7K;FA<3N4
z3W}v_$5BKwds%!1*lG5^PCA!+(bc6k_&waEousu8WWBt;y|uo@X}!F=auDX(V+KHF
zqhWft?PxwsI?#MbPG1<(=n}ZJRf0S(6n+>K%LS%@+z85BlUCFmXzI1F2c=eYXkO?1
zZ1C4##!E%JC-sDovJD+L$QbqD+1__v9Sj}afJH{q5Lt9%X-k`Pii6hmVFH%}G%!mB
zUi(O51mc9e+5<t{=CVt<r>MT9=qGU-PC<6x7ljL^CB2Sk5^hZ-X`i2krf%-4UZo}h
z1oVGYxZ|_~L00@Yo%{Zj{M3XN`iDclbeG;^rX^hZ(YNgygo;#;S58b$0n-Xor5DIx
zuVn*eoyYS{-)GvokCaYU#X9cSa^$!^`^g}HrxE4;*hoC|#DRP!pFncu=K*JwvE`Sr
zUP&V;x*NamIG&U$rZznz^Fe}VktH*g7)f~mrBu6B?f1I#wJDcl9jBkKChu9R6rMEB
zraf)0Kh0L(>Wo<1DBI0oNUg~rQ6C#<;1fBMm#=#DO|(n@^C%44g8tE97FG1nx(fXf
zK|9xT#w?*6C0F}cZ9ZM?Jb-4otjYJL5q?vp1k*LK@r6Y82iq&7v{mJsV+EYA<#Tj8
zH5Rs?MuXsI*HqSA_B=AVWuZWVGlJ$R0z5?K^#>S&4@-?l$hqez#Ph2})xIySYD!-^
zB<X4b`h+D?{R4FVfye>JRWc5(b4$XDsQ!7TvMY{PCEV?+mRo(oC23?@y<^x_WJV{~
zEX4uc@bgS)Fh896y?h4825ZI|9=B1+);!NAnUp&A+0iJE{oRe9-%4*v9_B~6)-=_=
zcJhnEVyK=_%LIwa#hGfrOgmPIcqI)U7+mwOY|3xBy@|vfhqz(Ii!&LF*70X4eVEe=
z%ZR8+P>tO0oiebB*^2bS+*px5t<U+TZCB*{ERg8KR@0!TUcZq?pA+cG^TOk(iK$Zc
z`hb_*+Rh@r1;pvQPJ@?ka0t;J1>8JIi)-#-FP@(H_^~7~v{%FiG0Zy2<5C=-v6a8D
zfDj2yM|f5q%Rmg&CKxqWL~36jXqkO1bDf=#Ker)YfAVz5KCi+X^=mZ1HCK)>3o=SY
zDDhyUZQP`zbZAbX6~brlSxQT<@@Xd>HzB*S`zpnT1@)1u5Z#|>wcDOUVsi)jnCc<v
zHcuF;LYjWnJ=w0Q-Ao`{D=kL7LZE@UZ=&DE8y+q9(YtgsSzXwHhreDlPX&5l`P`mp
zlcNTeiAq+Dp^B9&JJhp~LT!|1=7O#5C`Rqd{N=Eyxtqrp4}N^&SuuKZ$sIAHJ8iOG
zWL@_zKU=uS=O!68l1P3l$ij4X^7?St4Bl$NkvsZ%@9Cm|wC-3%=uv-1rBo=>92=m0
z##CqucGfTASNpYfBQ8RN_F>?8N^lDl?@P_bb3C?h8KR{|u}VeiiH1v7wiuSs<ZQK6
z#Q|>Pi~jgRTkWC=WJW_d^WJ9RmpSmx&_YQ#Ijhzyf*L=e&n`PGyuOrx5+Q*_uaURK
zVGz!nXu8rdy~=Il86nHaSt_UDedV$+xFZ9kC$VD++zOsr5qcR<<lVGnNhD5FFUh=y
zlY1dP`zlj{DmrMUzGkapVVg<qE$d#E5K6J=^fKHiUUvP3hoXr6I~-@E7SE)N`=sPa
z5*sk{)rsMYie1a=d+ngQL_e-h0CfvqTpoj;wn#`K+SN3#?C!DWp?`9V0@`v8YG_9e
zMCyr0b4vA~*M#*-qmlDXa6hL$On1#>PIn==xV7^(<McQO*!0c_wi*Am#gjXMh#LJ@
z5Jx0Xb}_>(5?)<aZV<L8*N}7M@r0$m^g{|RNtEOVj%KUlw$U0!GlB7fCWX9k)_dVV
zW#h?#?5mi8uMI@j(NgS|6WZOmpYTq4b;JfZ9H=}=S%mYP<AWnM&Qnx>Flo?woYw@<
z;MKEqDlAi>_dGM`pi7H>xF2#nVrUVVg=bpvcmX3hls!e$AQP#6+GT`eS~}(Y#x1mH
z{a3S=hU_$Ak+@2j{BY(*O~j;<g_*LDg=#=Ce$VLnwM?v`y^l|aH>y6<sner%`S1g&
zLdCE*w_IJ&i>k=LH{|k-Z#o|&{AhqMDi@<=!J=3k(`J7~LOWSIMmt}+^Nl;c$n4yi
z55uXGe`*wBd$s;iiEFga`83>=jIFvbN0jy|NM)VaLc1+t6`1-mE&+>V((ak%OYGZw
zdkXFvkNc6XkW4%cYm$V`cbducIhu0Dh4v08?8Vn#k$FiuWyL0Nel<B~V1^G31EK9C
zvPq?vz{9pTa$bum#QU3A5hdK)fI<2;H!b-KC<|;!Hs1KN15+E$l6WB^yl`8sa2b~)
z6u)xXI}9oFbe-0e_LG=^XiralSPr*BO9QhwuW|jloD)(?MIo9<xA$9LYy=0tx#|@9
zv{Tb@VAWWm-g_*ftE7q$FUEjeKmKTr$EsGf`7zA{gB*xnC-z1Yn-0AX#2ql>sAA4S
zjx8gTOBe8x#C6wCHm}i=5XbAR5=9RNaJ#T#iQr;ehRFCurSpp?QT3}gZMT^mwt&{;
zPGMfINtNkRIkw!S&zsm%L&7%DmkrFnPma8Ij7aj#4m{RPYD{42_Uir?d(zQ)5PlQO
z5lD!R#_3|+YbysqHPGYYCod;AD6zbN=Z@Y=McYacN1q)*j@??Wge@*V-p~^ZVUffP
zn(JW5iIL?P#1u9^mNFBZV|Ai<);<tlgTQOmpm3LF_r8!Mvd9e{kl+?lRk<oKk|0@F
z-kEwOVK`r5T;s2dTjI(@`quQ>dkxX+1~<T@)NsCP+!$4B>50>^WT9ANOq*&mXUi14
z+Xr$3eg!O#&Pz4K-?G-TV~`G6U_a2fcyK+xVi(+0*(XdePI-I1?JU9=B11jyfLMMT
zNb)|?2JtnO{#UGC`Ha-)=WGT0<s%_QEZ;%Sf$$BkrJ=qv(?gQP3i(RfGyZQ;3;xpa
zkq*ZR{EH}+hxQ?dFm}cFr`8nBxk_Tom#jf#CEnZm(RrP2MoNhtY+e^W5%Su|h5SvA
z;0u*q6RAybW^*+GZ04zx^)VxQOLxz#7c=7gZOadrA1E#PE~>A=4Pp?59<tfamCI1p
zMPY8-VTw$6-A>E1&I=mxSfpJ;dR=xRkLlT#N?0VGtZn%ZnJ%X3I@bwQ^A97e*~v%y
zV`{3t7Mh`BdSwIz`&|1ZZ>`aokipsw8qW=U6UGx}y~5-72RwA<o9~a3uUCH>dEC(3
zKQZ;@BrG^Tc3tK@ahY>VY-sZsiexCo;b?q)jC#wEA$@4J5ZELTT6N!h&S|=KVwQfB
zbqQX-Kyx(Z>>ndSs_y@mh%d;^@-lcQ9U_=EK%A=>OYoS?DgBwgcPGvxtG={(f*y;r
z3ABz6w|oPuBhcOHy%c+#!&^7Z_N&t*j1WI%Ghj^XylB8G9|w8$0G)_&ym;aF*A~Fn
z3x&}rZM8NLo*PGZ*)L|;Q)j5cOMZK0?zdb!B#F*dl;D>hX$Q1FUhS7vPfxcnhPJxD
zPVekZrE!-QhMvY>oGaeb-K&fMCW0m0#beZ}&cBm*j`T7=to7|_%y^o}u98{H7r|x=
zudvI&uqj^?Bk#seB3{K@QA+7em!>7Z>7Y5@)kv3XI67ZS0Ahnu4MdBrdEWIDqc49J
zEt^ykcX?qAJ!P#>zY_31x*UYEt=HPnIF<j5sRIpjRE%Vg5ssG>;Dzbi@6vp7EFg?$
zk?SDV`NaK=Sg+~cv;ACRhR@_{Nyd~04&n@RJ6T>*eoMQ+5S^E8eTL!eI%nnEJj9#t
zDBjPvtXvqLEKvWPgw0UEoE}!(L@S{U8Xi5~t}PiytqKnr`w6~t)<4Rt=RO<FTu#A}
zGOHU`F=&rz(-XX{YpzRsE<pz>ckoVcS$3i{`CL7s{?#SJwz`zA#*grb(^lq?);|0Y
z8R4|QNeL~21%7ztKgIZg1IiPTaxd#3MO+yy_*QH2(B}c`Gm~<*PlCBHZhTXpV>-aw
znzOF7@<NH1`rUcQ?Q;dB`G$p-d4gi`ko{li_Qm(01~9~S1SUzs=ty72OO_@F*C_7h
z9zBuuI`b4UBVW4FNq;Dl>o}PZiP4Q!K@!LCbF9didtS;T_>-!@-XHTvoAL|L0OT0<
zzA@yY#6F2a6B^VdjRyH+LPEcrkSyRqV1ELIJ#I7rpac^DAOrvb7S7H{1k%mih1U+D
ztAho=(0fa#>+VGWK*Rib=k~{J^(j$PwNr+)`{0~hPNOU-+N;LvK1p+t{?=oML^#>(
zZb;TF_Py3dvh-TeTU2b_eD9$1qEF@7M9u0bK?eILOX!YdA3ISWxm;kKK`rr{$2#Ef
z%%MjP+vu}Ru8A>)%Gz-@f!&=0zCTg8o-X-@DeYXv{Xsl(TC;PVoDdAhHj1wa6hVQ3
z62@z2Jc&6dRW&mcn?-6MRzVf|a1~3*Ccy{yhN-$zME=<8tTqn>Ap>69g5E7A&3ulx
z_tXiy$$dQ4%CaIhK~UwO4*|Y9oA}v-4KIjBxkTIxOK@`cGtubJL&^IX8i^yTUjv6R
zU+sy~=|6Nl{Em*zOvP=cc$FW(siK_KYKt+gC`Ko-O5f<btSicMMSSjav3a{iMmyWK
z(2K<-!tcMmMDQouCU<NfU;g4&zoW4d9RMJ>V;k;-K$<%^@VZ%9|3S9)f07-VFyPd|
zPpWWsjxStF7f!CrgrBFBq}wR#c*gq3>`QE4wh+9vCFTSm|L%o3hv#q>wdq5nI_+FO
zYwl?~is5__;|<xky*lw#&i?WaL7C+HV?yc^65zSaKHqxd1QAB%Aa(DqpGIhoAFHbQ
zW#*51lD^PXOh_B!Zqu;1Hi6VU_6;_B7?T{+cnB-n$^tiKrz`R8^T*ng-C%+Efe&@B
z`nW~(ggGn))?maM-ut{hlB~=x-I>B|)|JUG=YC0|;gq!p<>?Z?x6ZCtl8u!#i}C<-
z$?z4+I}>iEpeq1P=h!(7ZuX~SGpf1_&%w_#;~F?QDr2whrQ}(VvfTzoN>uxCE%fyP
zP3J*4P=eg`E;FcZ5Ec1*&+pcvc2FHh-5Ei$A32}F;j7ySCHQh6*D__nUXk?ak^=CI
z$urXXI5r&96AAv#mxj~&I~w?R(EtF#?xOMXQ-!_o-Lvo?dI1oCgdvc;NH63cp~(2p
zQ0Umg5uo2rAZxe-3}k8U1hRmE%pH(0H<%R&=?t=PgPGfd5O5nOxRVXY(iUcEkKo0J
z{F@|UIAhOyc4x(+{Z*1McY<60K7pfk^pLu95I>;jNIWN~F@8AJ@o5KI7m=_#Igq{X
zex6RAjM2N3agyr6U^ZvX<EzBQ2U-a>#)Nlcnl@C94zBsSN><mpD8wzWU)%E1*cG?M
z`q?Xcu)LP5oQe2|-I5{mJBH5IMM(aH`M1fi=d4`d@qGI0rXXe^^)<tV;+cxbDNaW-
zs$Z9t^q0eKUqeO-IC9S`3f*}=n-z@`Uzz)f6H#LJ^TidCQ6p2-nqlAOPg;luA}4I-
zc_ltYvIOS%An6+~#5;taqu#L=c)g)qCTH9lG-7D|$m$kJ7yw)xukMc|!*7;R<eusz
z6VW?q50Bg$-rh3(ccOZe!-DPZEbqIU?C-Q2gf+pi+K4JQ2an1mktz@8@Zd<bHvfd+
z(y{xaN%t>mnmoLtr|N?PY8V2g<i3(bOu><f{h4ymr(-q2^9YPYW5EGO$NC^iTB1kE
z;-Zw7B5NsWHozp{R>p>CXu+?r)q0;4HpR3VH&-+OL=_F49OIvbrT<+4Xn=o^nSLYC
zWBhaD{k?vFToA}a1o*Go@c%{pyXNvIVjMBxziBo91NhJN@h2et-EaSY)$&)${+jrI
oQkF>i_hs-`guiCQp9oF&{x&}#s#w^+9kB1N+PiJ_?*F#_1wgnc=Kufz
index 3c6cd647031e61ea5d9c2f60419956a629185451..d6ddbcec38c8c9093cab75d0fc0eab3d316f42d9
GIT binary patch
literal 4627
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
Q2jXAd`PZ?9bib|t0^2YpUH||9
index cdf4325acde44b9ef829471ea730ba3c3ab90c72..5898d83e499daed0c245040b98c6da3ce3448316
GIT binary patch
literal 4634
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
WZ0g@9BmeC{_N$|Q9h=GU+xjogx=>93
--- a/toolkit/mozapps/update/tests/chrome/chrome.ini
+++ b/toolkit/mozapps/update/tests/chrome/chrome.ini
@@ -1,13 +1,14 @@
 ; 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/.
 
 [DEFAULT]
+tags = appupdate
 skip-if = (buildapp == 'b2g' || buildapp == 'mulet')
 support-files =
   utils.js
   update.sjs
 
 ; mochitest-chrome tests must start with "test_" and are executed in sorted
 ; order and not in the order specified in the manifest.
 [test_0011_check_basic.xul]
--- a/toolkit/mozapps/update/tests/unit_aus_update/xpcshell.ini
+++ b/toolkit/mozapps/update/tests/unit_aus_update/xpcshell.ini
@@ -1,13 +1,14 @@
 ; 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/.
 
 [DEFAULT]
+tags = appupdate
 head = head_update.js
 tail =
 
 [canCheckForAndCanApplyUpdates.js]
 [urlConstruction.js]
 [updateCheckOnLoadOnErrorStatusText.js]
 [updateManagerXML.js]
 [remoteUpdateXML.js]
--- a/toolkit/mozapps/update/tests/unit_base_updater/xpcshell.ini
+++ b/toolkit/mozapps/update/tests/unit_base_updater/xpcshell.ini
@@ -2,16 +2,17 @@
 ; 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/.
 
 ; Tests that require the updater binary. These tests should never run on Android
 ; which doesn't use the updater binary as other applications do and are excluded
 ; from running the tests in the moz.build file.
 
 [DEFAULT]
+tags = appupdate
 head = head_update.js
 tail =
 
 [marSuccessComplete.js]
 [marSuccessPartial.js]
 [marFailurePartial.js]
 [marStageSuccessComplete.js]
 skip-if = toolkit == 'gonk'
--- a/toolkit/mozapps/update/tests/unit_service_updater/xpcshell.ini
+++ b/toolkit/mozapps/update/tests/unit_service_updater/xpcshell.ini
@@ -1,15 +1,16 @@
 ; 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/.
 
 ; Tests that require the updater binary and the maintenance service.
 
 [DEFAULT]
+tags = appupdate
 head = head_update.js
 tail =
 
 [bootstrapSvc.js]
 run-sequentially = Uses the Mozilla Maintenance Service.
 [marSuccessCompleteSvc.js]
 run-sequentially = Uses the Mozilla Maintenance Service.
 [marSuccessPartialSvc.js]