import changes from `dev' branch of rmottola/Arctic-Fox:

- Bug 1198588 - Remove unused MSG-specific code from AudioStream. r=padenot (79bd29d60c)
- Bug 1187195 - Remove all agents with null window. r=baku (c3733647bb)
- Bug 1137151: Marked destructors of ref-counted audio-manager classes as protected, r=dhylands (d0b84530f6)
- Bug 1179181 - Store separate volume setting into setting database. r=baku (a6cb4bab03)
- Bug 1175447 - mono audio support. r=padenot, r=sotaro (ce011f6556)
- Bug 1140763 - Build dom/media/gtest in unified mode; r=cpearce (b3061b094b)
- Bug 1194442 - Code clean up of AudioManager r=alwu (d25f51cda2)
- Bug 1137659 - Re-enable LogShake xpcshell tests on B2G Emulator debug. r=me (f8b037756a)
- Bug 1136777 - Enable LogShake by default and listen for new content events. r=fabrice, r=gerard-majax (1cf207dbe8)
- Bug 1101994 - Add a low-pass filter to LogShake's shake detection and refactor unit tests to be less redundant. r=mhenretty, r=gerard-majax (7f59e328ec)
- Bug 1144499 - Consolidate two long-running tests in test_logshake_gonk into one test of equivalent strength. r=gerard-majax (765d0bc243)
- Bug 1079763 - Compress logs produced by LogShake. r=gerard-majax (26e5ca1dec)
- Bug 1181561 - Expose a Kill Switch enabling/disabling. r=dhylands, sr=sicking (03d2754eea)
- Bug 1174682 - Rename logshake-screenshot to screenshot.png. r=gerard-majax (b6faaa3fc2)
- Bug 1188999 - LogShake does not fetch about:memory reports. r=gerard-majax (03a75e6408)
- let-var (1114e25a65)
- Bug 1188487 - Add API to BrowserElement to mute and set volume. r=fabrice (3d49951639)
- Bug 1195801 - Add GetStructuredData() method to Browser API. r=kanru, r=bholley (b4cc0cba61)
- Bug 1167465 - Exposing Allowed Audio Channels in System App's Window, r=alwu, r=fabrice (e8d59eb0e1)
- fix build (8a58b834fe)
- Bug 1183301 - GetAllowedAudioChannels should not throw an exception if nsIFrameElement is not ready. r=baku (1adbb9b140)
- Bug 1184821 - Use CheckAllPermissions in BrowserElement.webidl r=bz (65a22b5bc1)
- Bug 1206212 - Remove AUDIO_STREAM_FM after KK r=alwu (4d83c541dc)
- Bug 1131927 - [Automounter] Add reporter to get current information. r=dhylands (d5d2e94b8c)
- Bug 1197689 - Avoid unnecessary sync IPC in AudioChannelManager ctor. r=baku (128abefeb6)
- Bug 1208155 - Make hal functions explicit in AudioManager r=alwu (d320b83fa7)
- Bug 1179691 - [Automounter] Fix the problem of get format and share request in the same time. r=dhylands (768ad35244)
- Bug 1137151: Marked destructors of ref-counted auto-mounter classes as protected, r=dhylands (c8cf90bdff)
- Bug 1158047 - [AutoMounter] Resolve the problem of stucking in UMS_CONFIGURING state. r=dhylands (793f023fd2)
- Bug 1012403 - Reenable SettingsService chrome tests, on B2G only; a=TEST-ONLY (aa8c416f75)
- Bug 1196358 - update volume setting to database when the volume changing. r=sotaro. (5227fca7f7)
- let-var (ca6890c85b)
- update (9b9ff0ca42)
- Bug 1178081 - Make ro.product.manufacturer and ro.product.device available through settings. r=fabrice (fe962dfe41)
- Bug 1206741 - [OTA] Make URL pref changes overwrite B2G's setting. r=fabrice f=gerard-majax (df8bf913ae)
- Bug 1196884 - Disable device discovery (again) r=gerard-majax (9f11b3fa9d)
- Bug 1178512 - [Metrics] Make the developer HUD send Advanced Telemetry events. r=janx (ddc3643e4e)
- Bug 1177143 - Throttle HUD memory collection to 2 seconds. r=jryans (94fea375a8)
- Bug 1190622 - [Telemetry] Report USS memory at time of performance marks r=jryans (fd7713fda0)
- missing of Bug 1183101 - Collect Histogram Data from Gecko and send to Telemetry Server. r=janx (176b076539)
- Bug 1176992 - Allow hud.js to support custom metrics with exponential and counter histograms. r=janx (c536b35be1)
- Bug 1198517 - [Metrics] Histogram support for user-timing-based metrics. r=janx (549741a1e4)
- Bug 1180793 - [Metrics] Hud should report specific app for those Apps housed under 'Communications'. r=janx (93407f1873)
- Bug 1189871 - Add event to add/remove permissions for Graphene. r=fabrice (004144b5ba)
- let-var (8c98d39873)
- Bug 1160923 - [B2G] Waiting for explicit mozContentEvent before sending out mozChromeEvents, r=vingtetun, f=ochaumeau (63fc3b5ea8)
- missing bits of 1177143 and cleanup (339e589ba1)
- Bug 1188762 - Use mozSystemWindowChromeEvent to instead of mozChromeEvent to send system-audiochannel-state-changed event. r=alwu (d107f0caab)
- Bug 1192122 - Safe mode startup, Part 1: check the power key state at startup r=mwu (90af9b56c0)
- Bug 1192122 - Safe mode startup, Part 2: shell.js hook r=ferjm (02676818ff)
- Bug 1156715 - Create shell-remote for launching system-remote, r=fabrice (43b18841c9)
- missing of Bug 973933 - New updater-xpcshell binary for updater tests. (e6f8cd734a)
- Bug 1178760: Add ability to read ua-update.json from APK for Fennec. r=jchen (649d110fdf)
- bits of Bug 1076314 - Re-prompt nightly users to enable e10s. (5530f26d51)
- Bug 1167601 - Convert newTab.xul to newTab.xhtml. r=mconley (659c19a586)
- Bug 1040369 - Replace sponsored icon with identifying text [r=adw] (6ce29de713)
- Bug 1192924: Expose the update URL formatting code a new UpdateUtils module. r=rstrong (f2bc4ba6a7)
- some of the newer icons (bf3c415d72)
- various cleanup and missing bits of Backed out changeset 84da123a0af0 (bug 895359) (d7feb9e535)
- bits of Bug 1158853 (02223f32ee)
- Bug 1196437 - Moving a sponsored tile in newtab breaks various things [r=marcosc] (0c93d54931)
- Bug 1203787: When the add-on ID is longer than 64 characters compare the signing certificate's common name to the sha256 hash of the ID. r=dveditz (838d6ce69c)
- bits of Bug 1036284 - Update styling of newtab tiles (886f7d2b71)
- let-var (e0ed7d020c)
- bug 890026 - Add mozcrash.kill_and_get_minidump r=jimm (11fe69e302)
- Bug 1162115 - Bump mozdevice to 0.45, r=wlach (4b5891b54e)
- Bug 1140145 - Update to latest wptrunner, a=testonly (a7860252bd)
- Bug 1146321 - Update to latest wptrunner, a=testonly (c81ea8eddd)
-  Bug 115107 - Update to version of wptrunner that allows prefs to be set, r=Ms2ger, ahal (972f8c55bd)
- Bug 1154691 - Update wptrunner to remove old exception type, a=testonly (f96ce919b1)
- Bug 1150821 - Update to latest wptrunner, a=testonly (bea754f26c)
- Bug 1153521 - Update to latest wptrunner, a=testonly (830e395995)
-  Bug 1155079 -Update to latest wptrunner, a=testonly (27cfd9c8a5)
- Bug 1163709 - Update to latest wptrunner, a=testonly (76672ace86)
- Bug 1160085 - Update to latest wptrunner, a=testonly (168e165a26)
- Bug 1157218 - Update to latest wptrunner, a=testonly (d9eabd0213)
- Bug 1171755 - Update to latest wptrunner, a=testonly (4f5b411410)
-  Bug 1139407 - [mozversion] Remove non-text formatters from command line log options. r=dhunt (32f7596553)
- Bug 1160087 - [moznetwork] Add command line interface. r=wlach (2efccde362)
- Bug 1175101 - [moznetwork] Bump version number to 0.25. r=wlachance (c8a0fa9ff2)
- Bug 1176677 - [moznetwork] ImportError: "cannot import name structured", and release version 0.26. r=davehunt (009449ec79)
- Bug 1160090 - [moznetwork] Add structured logging. r=wlach (05a7bc26bc)
- Bug 1160094 - [moznetwork] Attempt to pick most suitable IP when multiple are associated with the hostname. r=wlach (db464651e1)
- Bug 1163992 - [moznetwork] When multiple IPs are found on Windows pick the first one. r=wlachance (88207df55a)
- Bug 1146292 - [mozlog] Bump version to 2.11. r=jgraham (3f9e252ac4)
- Bug 1171032 - Log raw messages at debug level by default, r=chmanchester (1ac8fa11ff)
-  Bg 1171849 Let consumers override mozlog default formatter options, r=chmanchester (beb37921ca)
- Bug 1066643 - [mozlog] Allow users of mozlog's command line options to exclude inappropriate log types. r=jgraham (8f4758a6c0)
- Bug 1132409 - [mozlog] Create directories for log specified on the command line if not present. r=jgraham (2bda47bd25)
- Bug 1177630 - Add formatter to mozlog for producing a machine readable error summary, r=chmanchester (a5930babb0)
- Bug 1173380 - [mozprofile] cloned profiles are not cleaned (__del__ method is not called); r=ahal (9d7d931dbf)
- Bug 1173682 - [mozbase] tests do not remove created directories; r=ahal (a4b3c112ab)
- Bug 1161198 - Update mozdevice test for getLogcat; r=bc (10be1b1a85)
- Bug 1014760 - Move mozlog.structured to mozlog; Move mozlog to mozlog.unstructured, r=jgraham (8c1eba0f64)
- Bug 1176408 - Bump marionette-transport to 0.5 and marionette-driver to 0.9, r=dburns (fae313803a)
- Bug 1178778 - Bump marionette-driver to 0.10. r=automatedtester DONTBUILD (4196568a38)
- Bug 1183157 - make marionette --version flag also show the transport and driver package versions. r=dburns (e068536c58)
- Bug 1189027: Bump marionette driver to 0.11; r=ato (b1d103c9ef)
- Bug 1188826: Bump marionette client version for release; r=ato (018809aae3)
- Bug 1176882 - Don't pin marionette-transport. r=davehunt (c660f3ab7b)
- Bug 1169381 - Bump dependency for mozinfo in marionette-client to >=0.8. r=jgriffin (a6c0edc9bf)
- Bug 1190817 - marionette install from pypi is broken. r=automatedtester (c43fbcfaee)
- Bug 1163801 - Upgrade marionette-client from optparse to argparse, r=ahal (5843864209)
- Bug 1163801 - Refactor marionette's options mixin system for argparse compatibility, r=AutomatedTester (c8153ebde4)
- Bug 1197835 - Version bump marionette-client == 0.19, marionette-transport == 0.7, marionette-driver == 0.13, r=ato (4d7a79ae9a)
- Bug 1200973 - Remove unneeded app cache code from Marionette; r=jgriffin (0c2792e2a5)
- Bug 1194923 - Call glFlush before glDeleteFramebuffers on Adreno 420 devices. r=snorp (cc1597d3f1)
- Bug 1150668 - Assume EXT_texture_format_BGRA8888 supported on Android emulator; r=jgilbert (d6e1884d28)
- Bug 1140459 - Skip IsRenderbuffer assertions on Android emulator; r=jgilbert (2163e7c66b)
- Bug 683218 - Disable __noSuchMethod__ support. r=jorendorff (b00cc0c8ae)
- Bug 1206168 - Rename JS_DefaultValue to JS::ToPrimitive. r=jandem. (0815b56474)
- pointer style (d5a0b0f1cc)
- Bug 1133191 - Part 0: Add indentation variant to JS::BuildStackString. r=jandem (2383f33b84)
- Bug 1133191 - Part 1: Display JS stack trace on exception in js shell. r=jandem,jorendorff (5000cb3ec8)
- fix patch order (a84ffb1d33)
This commit is contained in:
2022-08-23 09:25:35 +08:00
parent 3497b91b44
commit 09dc34e9fb
414 changed files with 11568 additions and 4270 deletions
+5
View File
@@ -1097,3 +1097,8 @@ pref("dom.bluetooth.app-origin", "app://bluetooth.gaiamobile.org");
// Enable notification of performance timing
pref("dom.performance.enable_notify_performance_timing", true);
// Multi-screen
pref("b2g.multiscreen.chrome_remote_url", "chrome://b2g/content/shell_remote.html");
pref("b2g.multiscreen.system_remote_url", "index_remote.html");
+4 -4
View File
@@ -4,13 +4,13 @@
'use strict';
let Cu = Components.utils;
let Cc = Components.classes;
let Ci = Components.interfaces;
var Cu = Components.utils;
var Cc = Components.classes;
var Ci = Components.interfaces;
dump("############ ErrorPage.js\n");
let ErrorPageHandler = {
var ErrorPageHandler = {
_reload: function() {
docShell.QueryInterface(Ci.nsIWebNavigation).reload(Ci.nsIWebNavigation.LOAD_FLAGS_NONE);
},
+320 -63
View File
@@ -68,11 +68,11 @@ var developerHUD = {
* observed metrics with `target.register(metric)`, and keep them up-to-date
* with `target.update(metric, message)` when necessary.
*/
registerWatcher: function dwp_registerWatcher(watcher) {
registerWatcher(watcher) {
this._watchers.unshift(watcher);
},
init: function dwp_init() {
init() {
if (this._client) {
return;
}
@@ -107,9 +107,13 @@ var developerHUD = {
SettingsListener.observe('hud.logging', this._logging, enabled => {
this._logging = enabled;
});
SettingsListener.observe('debug.performance_data.advanced_telemetry', this._telemetry, enabled => {
this._telemetry = enabled;
});
},
uninit: function dwp_uninit() {
uninit() {
if (!this._client) {
return;
}
@@ -128,7 +132,7 @@ var developerHUD = {
* This method will ask all registered watchers to track and update metrics
* on an app frame.
*/
trackFrame: function dwp_trackFrame(frame) {
trackFrame(frame) {
if (this._targets.has(frame)) {
return;
}
@@ -143,7 +147,7 @@ var developerHUD = {
});
},
untrackFrame: function dwp_untrackFrame(frame) {
untrackFrame(frame) {
let target = this._targets.get(frame);
if (target) {
for (let w of this._watchers) {
@@ -155,7 +159,7 @@ var developerHUD = {
}
},
onFrameCreated: function (frame, isFirstAppFrame) {
onFrameCreated(frame, isFirstAppFrame) {
let mozapp = frame.getAttribute('mozapp');
if (!mozapp) {
return;
@@ -163,7 +167,7 @@ var developerHUD = {
this.trackFrame(frame);
},
onFrameDestroyed: function (frame, isLastAppFrame) {
onFrameDestroyed(frame, isLastAppFrame) {
let mozapp = frame.getAttribute('mozapp');
if (!mozapp) {
return;
@@ -171,7 +175,7 @@ var developerHUD = {
this.untrackFrame(frame);
},
log: function dwp_log(message) {
log(message) {
if (this._logging) {
dump(DEVELOPER_HUD_LOG_PREFIX + ': ' + message + '\n');
}
@@ -189,14 +193,54 @@ function Target(frame, actor) {
this.frame = frame;
this.actor = actor;
this.metrics = new Map();
this._appName = null;
}
Target.prototype = {
get manifest() {
return this.frame.appManifestURL;
},
get appName() {
if (this._appName) {
return this._appName;
}
let manifest = this.manifest;
if (!manifest) {
let msg = DEVELOPER_HUD_LOG_PREFIX + ': Unable to determine app for telemetry metric. src: ' +
this.frame.src;
console.error(msg);
return null;
}
// "communications" apps are a special case
if (manifest.indexOf('communications') === -1) {
let start = manifest.indexOf('/') + 2;
let end = manifest.indexOf('.', start);
this._appName = manifest.substring(start, end).toLowerCase();
} else {
let src = this.frame.src;
if (src) {
// e.g., `app://communications.gaiamobile.org/contacts/index.html`
let parts = src.split('/');
let APP = 3;
let EXPECTED_PARTS_LENGTH = 5;
if (parts.length === EXPECTED_PARTS_LENGTH) {
this._appName = parts[APP];
}
}
}
return this._appName;
},
/**
* Register a metric that can later be updated. Does not update the front-end.
*/
register: function target_register(metric) {
register(metric) {
this.metrics.set(metric, 0);
},
@@ -204,7 +248,7 @@ Target.prototype = {
* Modify one of a target's metrics, and send out an event to notify relevant
* parties (e.g. the developer HUD, automated tests, etc).
*/
update: function target_update(metric, message) {
update(metric, message) {
if (!metric.name) {
throw new Error('Missing metric.name');
}
@@ -220,7 +264,7 @@ Target.prototype = {
let data = {
metrics: [], // FIXME(Bug 982066) Remove this field.
manifest: this.frame.appManifestURL,
manifest: this.manifest,
metric: metric,
message: message
};
@@ -235,6 +279,7 @@ Target.prototype = {
if (message) {
developerHUD.log('[' + data.manifest + '] ' + data.message);
}
this._send(data);
},
@@ -242,7 +287,7 @@ Target.prototype = {
* Nicer way to call update() when the metric value is a number that needs
* to be incremented.
*/
bump: function target_bump(metric, message) {
bump(metric, message) {
metric.value = (this.metrics.get(metric.name) || 0) + 1;
this.update(metric, message);
},
@@ -251,7 +296,7 @@ Target.prototype = {
* Void a metric value and make sure it isn't displayed on the front-end
* anymore.
*/
clear: function target_clear(metric) {
clear(metric) {
metric.value = 0;
this.update(metric);
},
@@ -260,22 +305,120 @@ Target.prototype = {
* Tear everything down, including the front-end by sending a message without
* widgets.
*/
destroy: function target_destroy() {
destroy() {
delete this.metrics;
this._send({});
this._send({metric: {skipTelemetry: true}});
},
_send: function target_send(data) {
_send(data) {
let frame = this.frame;
let systemapp = document.querySelector('#systemapp');
if (this.frame === systemapp) {
frame = getContentWindow();
shell.sendEvent(frame, 'developer-hud-update', Cu.cloneInto(data, frame));
this._logHistogram(data.metric);
},
_getAddonHistogram(item) {
let APPNAME_IDX = 3;
let HISTNAME_IDX = 4;
let array = item.split('_');
let appName = array[APPNAME_IDX].toUpperCase();
let histName = array[HISTNAME_IDX].toUpperCase();
return Services.telemetry.getAddonHistogram(appName,
CUSTOM_HISTOGRAM_PREFIX + histName);
},
_clearTelemetryData() {
developerHUD._histograms.forEach(function(item) {
Services.telemetry.getKeyedHistogramById(item).clear();
});
developerHUD._customHistograms.forEach(item => {
this._getAddonHistogram(item).clear();
});
},
_sendTelemetryData() {
if (!developerHUD._telemetry) {
return;
}
telemetryDebug('calling sendTelemetryData');
let frame = this.frame;
let payload = {
keyedHistograms: {},
addonHistograms: {}
};
// Package the hud histograms.
developerHUD._histograms.forEach(function(item) {
payload.keyedHistograms[item] =
Services.telemetry.getKeyedHistogramById(item).snapshot();
});
// Package the registered hud custom histograms
developerHUD._customHistograms.forEach(item => {
payload.addonHistograms[item] = this._getAddonHistogram(item).snapshot();
});
shell.sendEvent(frame, 'advanced-telemetry-update', Cu.cloneInto(payload, frame));
},
_logHistogram(metric) {
if (!developerHUD._telemetry || metric.skipTelemetry) {
return;
}
shell.sendEvent(frame, 'developer-hud-update', Cu.cloneInto(data, frame));
}
metric.appName = this.appName;
if (!metric.appName) {
return;
}
let metricName = metric.name.toUpperCase();
let metricAppName = metric.appName.toUpperCase();
if (!metric.custom) {
let keyedMetricName = 'DEVTOOLS_HUD_' + metricName;
try {
let keyed = Services.telemetry.getKeyedHistogramById(keyedMetricName);
if (keyed) {
keyed.add(metric.appName, parseInt(metric.value, 10));
developerHUD._histograms.add(keyedMetricName);
telemetryDebug(keyedMetricName, metric.value, metric.appName);
}
} catch(err) {
console.error('Histogram error is metricname added to histograms.json:'
+ keyedMetricName);
}
} else {
let histogramName = CUSTOM_HISTOGRAM_PREFIX + metricAppName + '_'
+ metricName;
// This is a call to add a value to an existing histogram.
if (typeof metric.value !== 'undefined') {
Services.telemetry.getAddonHistogram(metricAppName,
CUSTOM_HISTOGRAM_PREFIX + metricName).add(parseInt(metric.value, 10));
telemetryDebug(histogramName, metric.value);
return;
}
// The histogram already exists and are not adding data to it.
if (developerHUD._customHistograms.has(histogramName)) {
return;
}
// This is a call to create a new histogram.
try {
let metricType = parseInt(metric.type, 10);
if (metricType === Services.telemetry.HISTOGRAM_COUNT) {
Services.telemetry.registerAddonHistogram(metricAppName,
CUSTOM_HISTOGRAM_PREFIX + metricName, metricType);
} else {
Services.telemetry.registerAddonHistogram(metricAppName,
CUSTOM_HISTOGRAM_PREFIX + metricName, metricType, metric.min,
metric.max, metric.buckets);
}
developerHUD._customHistograms.add(histogramName);
} catch (err) {
console.error('Histogram error: ' + err);
}
}
}
};
@@ -283,7 +426,7 @@ Target.prototype = {
* The Console Watcher tracks the following metrics in apps: reflows, warnings,
* and errors, with security errors reported separately.
*/
let consoleWatcher = {
var consoleWatcher = {
_client: null,
_targets: new Map(),
@@ -304,7 +447,7 @@ let consoleWatcher = {
'CORS'
],
init: function cw_init(client) {
init(client) {
this._client = client;
this.consoleListener = this.consoleListener.bind(this);
@@ -331,7 +474,7 @@ let consoleWatcher = {
client.addListener('reflowActivity', this.consoleListener);
},
trackTarget: function cw_trackTarget(target) {
trackTarget(target) {
target.register('reflows');
target.register('warnings');
target.register('errors');
@@ -346,7 +489,7 @@ let consoleWatcher = {
});
},
untrackTarget: function cw_untrackTarget(target) {
untrackTarget(target) {
this._client.request({
to: target.actor.consoleActor,
type: 'stopListeners',
@@ -356,7 +499,7 @@ let consoleWatcher = {
this._targets.delete(target.actor.consoleActor);
},
consoleListener: function cw_consoleListener(type, packet) {
consoleListener(type, packet) {
let target = this._targets.get(packet.from);
let metric = {};
let output = '';
@@ -376,6 +519,18 @@ let consoleWatcher = {
if (this._security.indexOf(pageError.category) > -1) {
metric.name = 'security';
// Telemetry sends the security error category not the
// count of security errors.
target._logHistogram({
name: 'security_category',
value: pageError.category
});
// Indicate that the 'hud' security metric (the count of security
// errors) should not be sent as a telemetry metric since the
// security error category is being sent instead.
metric.skipTelemetry = true;
}
let {errorMessage, sourceName, category, lineNumber, columnNumber} = pageError;
@@ -459,7 +614,7 @@ let consoleWatcher = {
target.bump(metric, output);
},
formatSourceURL: function cw_formatSourceURL(packet) {
formatSourceURL(packet) {
// Abbreviate source URL
let source = WebConsoleUtils.abbreviateSourceURL(packet.sourceURL);
@@ -469,6 +624,65 @@ let consoleWatcher = {
', ' + source + ':' + sourceLine;
return source;
},
handleTelemetryMessage(target, packet) {
if (!developerHUD._telemetry) {
return;
}
// If this is a 'telemetry' log entry, create a telemetry metric from
// the log content.
let separator = '|';
let logContent = packet.message.arguments.toString();
if (logContent.indexOf('telemetry') < 0) {
return;
}
let telemetryData = logContent.split(separator);
// Positions of the components of a telemetry log entry.
let TELEMETRY_IDENTIFIER_IDX = 0;
let NAME_IDX = 1;
let VALUE_IDX = 2;
let TYPE_IDX = 2;
let MIN_IDX = 3;
let MAX_IDX = 4;
let BUCKETS_IDX = 5;
let MAX_CUSTOM_ARGS = 6;
let MIN_CUSTOM_ARGS = 3;
if (telemetryData[TELEMETRY_IDENTIFIER_IDX] != 'telemetry' ||
telemetryData.length < MIN_CUSTOM_ARGS ||
telemetryData.length > MAX_CUSTOM_ARGS) {
return;
}
let metric = {
name: telemetryData[NAME_IDX]
};
if (metric.name === 'MGMT') {
metric.value = telemetryData[VALUE_IDX];
if (metric.value === 'TIMETOSHIP') {
telemetryDebug('Received a Ship event');
target._sendTelemetryData();
} else if (metric.value === 'CLEARMETRICS') {
target._clearTelemetryData();
}
} else {
if (telemetryData.length === MIN_CUSTOM_ARGS) {
metric.value = telemetryData[VALUE_IDX];
} else if (telemetryData.length === MAX_CUSTOM_ARGS) {
metric.type = telemetryData[TYPE_IDX];
metric.min = telemetryData[MIN_IDX];
metric.max = telemetryData[MAX_IDX];
metric.buckets = telemetryData[BUCKETS_IDX];
}
metric.custom = true;
target._logHistogram(metric);
}
}
};
developerHUD.registerWatcher(consoleWatcher);
@@ -479,13 +693,13 @@ var eventLoopLagWatcher = {
_fronts: new Map(),
_active: false,
init: function(client) {
init(client) {
this._client = client;
SettingsListener.observe('hud.jank', false, this.settingsListener.bind(this));
},
settingsListener: function(value) {
settingsListener(value) {
if (this._active == value) {
return;
}
@@ -504,7 +718,7 @@ var eventLoopLagWatcher = {
}
},
trackTarget: function(target) {
trackTarget(target) {
target.register('jank');
let front = new EventLoopLagFront(this._client, target.actor);
@@ -519,7 +733,7 @@ var eventLoopLagWatcher = {
}
},
untrackTarget: function(target) {
untrackTarget(target) {
let fronts = this._fronts;
if (fronts.has(target)) {
fronts.get(target).destroy();
@@ -544,9 +758,24 @@ var performanceEntriesWatcher = {
_fronts: new Map(),
_appLaunchName: null,
_appLaunchStartTime: null,
_supported: [
'contentInteractive',
'navigationInteractive',
'navigationLoaded',
'visuallyLoaded',
'fullyLoaded',
'mediaEnumerated',
'scanEnd'
],
init(client) {
this._client = client;
let setting = 'devtools.telemetry.supported_performance_marks';
let defaultValue = this._supported.join(',');
SettingsListener.observe(setting, defaultValue, supported => {
this._supported = supported.split(',');
});
},
trackTarget(target) {
@@ -562,31 +791,56 @@ var performanceEntriesWatcher = {
front.start();
front.on('entry', detail => {
if (detail.type === 'mark') {
let name = detail.name;
let epoch = detail.epoch;
let CHARS_UNTIL_APP_NAME = 7; // '@app://'
// FIXME There is a potential race condition that can result
// in some performance entries being disregarded. See bug 1189942.
if (name.indexOf('appLaunch') != -1) {
let appStartPos = name.indexOf('@app') + CHARS_UNTIL_APP_NAME;
let length = (name.indexOf('.') - appStartPos);
this._appLaunchName = name.substr(appStartPos, length);
this._appLaunchStartTime = epoch;
} else {
let origin = detail.origin;
origin = origin.substr(0, origin.indexOf('.'));
if (this._appLaunchName === origin) {
let time = epoch - this._appLaunchStartTime;
let eventName = 'app-startup-time-' + name;
// Events based on performance marks are for telemetry only, they are
// not displayed in the HUD front end.
target._sendTelemetryEvent({name: eventName, value: time});
}
}
// Only process performance marks.
if (detail.type !== 'mark') {
return;
}
let name = detail.name;
let epoch = detail.epoch;
// FIXME There is a potential race condition that can result
// in some performance entries being disregarded. See bug 1189942.
//
// If this is an "app launch" mark, record the app that was
// launched and the epoch of when it was launched.
if (name.indexOf('appLaunch') !== -1) {
let CHARS_UNTIL_APP_NAME = 7; // '@app://'
let startPos = name.indexOf('@app') + CHARS_UNTIL_APP_NAME;
let endPos = name.indexOf('.');
this._appLaunchName = name.slice(startPos, endPos);
this._appLaunchStartTime = epoch;
return;
}
// Only process supported performance marks
if (this._supported.indexOf(name) === -1) {
return;
}
let origin = detail.origin;
origin = origin.slice(0, origin.indexOf('.'));
// Continue if the performance mark corresponds to the app
// for which we have recorded app launch information.
if (this._appLaunchName !== origin) {
return;
}
let time = epoch - this._appLaunchStartTime;
let eventName = 'app_startup_time_' + name;
// Events based on performance marks are for telemetry only, they are
// not displayed in the HUD front end.
target._logHistogram({name: eventName, value: time});
memoryWatcher.front(target).residentUnique().then(value => {
eventName = 'app_memory_' + name;
target._logHistogram({name: eventName, value: value});
}, err => {
console.error(err);
});
});
},
@@ -620,7 +874,7 @@ var memoryWatcher = {
},
_active: false,
init: function mw_init(client) {
init(client) {
this._client = client;
let watching = this._watching;
@@ -633,7 +887,7 @@ var memoryWatcher = {
}
},
update: function mw_update() {
update() {
let watching = this._watching;
let active = watching.appmemory || watching.uss;
@@ -651,13 +905,12 @@ var memoryWatcher = {
this._active = active;
},
measure: function mw_measure(target) {
measure(target) {
let watch = this._watching;
let front = this._fronts.get(target);
let format = this.formatMemory;
if (watch.uss) {
front.residentUnique().then(value => {
this.front(target).residentUnique().then(value => {
target.update({name: 'uss', value: value}, 'USS: ' + format(value));
}, err => {
console.error(err);
@@ -694,11 +947,11 @@ var memoryWatcher = {
});
}
let timer = setTimeout(() => this.measure(target), 800);
let timer = setTimeout(() => this.measure(target), 2000);
this._timers.set(target, timer);
},
formatMemory: function mw_formatMemory(bytes) {
formatMemory(bytes) {
var prefix = ['','K','M','G','T','P','E','Z','Y'];
var i = 0;
for (; bytes > 1024 && i < prefix.length; ++i) {
@@ -707,7 +960,7 @@ var memoryWatcher = {
return (Math.round(bytes * 100) / 100) + ' ' + prefix[i] + 'B';
},
trackTarget: function mw_trackTarget(target) {
trackTarget(target) {
target.register('uss');
target.register('memory');
this._fronts.set(target, MemoryFront(this._client, target.actor));
@@ -716,7 +969,7 @@ var memoryWatcher = {
}
},
untrackTarget: function mw_untrackTarget(target) {
untrackTarget(target) {
let front = this._fronts.get(target);
if (front) {
front.destroy();
@@ -724,6 +977,10 @@ var memoryWatcher = {
this._fronts.delete(target);
this._timers.delete(target);
}
},
front(target) {
return this._fronts.get(target);
}
};
developerHUD.registerWatcher(memoryWatcher);
+1 -1
View File
@@ -3,7 +3,7 @@
// runapp.js:
// Provide a --runapp APPNAME command-line option.
let runAppObj;
var runAppObj;
window.addEventListener('load', function() {
// Get the command line arguments that were passed to the b2g client
let args;
+9 -5
View File
@@ -5,12 +5,16 @@
// TODO: support multiple device pixels per CSS pixel
//
let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
let isMulet = "ResponsiveUI" in browserWindow;
var browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
var isMulet = "ResponsiveUI" in browserWindow;
Cu.import("resource://gre/modules/GlobalSimulatorScreen.jsm");
// We do this on ContentStart because querying the displayDPI fails otherwise.
window.addEventListener('ContentStart', function() {
window.addEventListener('ContentStart', onStart);
window.addEventListener('SafeModeStart', onStart);
// We do this on ContentStart and SafeModeStart because querying the
// displayDPI fails otherwise.
function onStart() {
// This is the toplevel <window> element
let shell = document.getElementById('shell');
@@ -255,4 +259,4 @@ window.addEventListener('ContentStart', function() {
// Exit the b2g client
Services.startup.quit(Ci.nsIAppStartup.eAttemptQuit);
}
});
}
+75 -32
View File
@@ -78,6 +78,12 @@ var SettingsListener = {
SettingsListener.init();
// =================== Mono Audio ======================
SettingsListener.observe('accessibility.monoaudio.enable', false, function(value) {
Services.prefs.setBoolPref('accessibility.monoaudio.enable', value);
});
// =================== Console ======================
SettingsListener.observe('debug.console.enabled', true, function(value) {
@@ -149,12 +155,16 @@ Components.utils.import('resource://gre/modules/ctypes.jsm');
// Get the hardware info and firmware revision from device properties.
let hardware_info = null;
let firmware_revision = null;
let product_manufacturer = null;
let product_model = null;
let product_device = null;
let build_number = null;
#ifdef MOZ_WIDGET_GONK
hardware_info = libcutils.property_get('ro.hardware');
firmware_revision = libcutils.property_get('ro.firmware_revision');
product_manufacturer = libcutils.property_get('ro.product.manufacturer');
product_model = libcutils.property_get('ro.product.model');
product_device = libcutils.property_get('ro.product.device');
build_number = libcutils.property_get('ro.build.version.incremental');
#endif
@@ -174,7 +184,9 @@ Components.utils.import('resource://gre/modules/ctypes.jsm');
'deviceinfo.platform_build_id': appInfo.platformBuildID,
'deviceinfo.hardware': hardware_info,
'deviceinfo.firmware_revision': firmware_revision,
'deviceinfo.product_model': product_model
'deviceinfo.product_manufacturer': product_manufacturer,
'deviceinfo.product_model': product_model,
'deviceinfo.product_device': product_device
}
lock.set(setting);
}
@@ -199,19 +211,28 @@ SettingsListener.observe('devtools.overlay', false, (value) => {
});
#ifdef MOZ_WIDGET_GONK
let LogShake;
SettingsListener.observe('devtools.logshake', false, (value) => {
var LogShake;
(function() {
let scope = {};
Cu.import('resource://gre/modules/LogShake.jsm', scope);
LogShake = scope.LogShake;
LogShake.init();
})();
SettingsListener.observe('devtools.logshake.enabled', false, value => {
if (value) {
if (!LogShake) {
let scope = {};
Cu.import('resource://gre/modules/LogShake.jsm', scope);
LogShake = scope.LogShake;
}
LogShake.init();
LogShake.enableDeviceMotionListener();
} else {
if (LogShake) {
LogShake.uninit();
}
LogShake.disableDeviceMotionListener();
}
});
SettingsListener.observe('devtools.logshake.qa_enabled', false, value => {
if (value) {
LogShake.enableQAMode();
} else {
LogShake.disableQAMode();
}
});
#endif
@@ -292,30 +313,48 @@ setUpdateTrackingId();
// modify them, that's where we need to make our changes.
let defaultBranch = Services.prefs.getDefaultBranch(null);
function syncCharPref(prefName) {
SettingsListener.observe(prefName, null, function(value) {
// If set, propagate setting value to pref.
if (value) {
defaultBranch.setCharPref(prefName, value);
function syncPrefDefault(prefName) {
// The pref value at boot-time will serve as default for the setting.
let defaultValue = defaultBranch.getCharPref(prefName);
let defaultSetting = {};
defaultSetting[prefName] = defaultValue;
// We back up that value in order to detect pref changes across reboots.
// Such a change can happen e.g. when the user installs an OTA update that
// changes the update URL format.
let backupName = prefName + '.old';
try {
// Everything relies on the comparison below: When pushing a new Gecko
// that changes app.update.url or app.update.channel, we overwrite any
// existing setting with the new pref value.
let backupValue = Services.prefs.getCharPref(backupName);
if (defaultValue !== backupValue) {
// If the pref has changed since our last backup, overwrite the setting.
navigator.mozSettings.createLock().set(defaultSetting);
}
} catch(e) {
// There was no backup: Overwrite the setting and create a backup below.
navigator.mozSettings.createLock().set(defaultSetting);
}
// Initialize or update the backup value.
Services.prefs.setCharPref(backupName, defaultValue);
// Propagate setting changes to the pref.
SettingsListener.observe(prefName, defaultValue, value => {
if (!value) {
// If the setting value is invalid, reset it to its default.
navigator.mozSettings.createLock().set(defaultSetting);
return;
}
// If unset, initialize setting to pref value.
try {
let value = defaultBranch.getCharPref(prefName);
if (value) {
let setting = {};
setting[prefName] = value;
window.navigator.mozSettings.createLock().set(setting);
}
} catch(e) {
console.log('Unable to read pref ' + prefName + ': ' + e);
}
// Here we will overwrite the pref with the setting value.
defaultBranch.setCharPref(prefName, value);
});
}
syncCharPref(AppConstants.MOZ_B2GDROID ? 'app.update.url.android'
: 'app.update.url');
syncCharPref('app.update.channel');
syncPrefDefault(AppConstants.MOZ_B2GDROID ? 'app.update.url.android'
: 'app.update.url');
syncPrefDefault('app.update.channel');
})();
// ================ Debug ================
@@ -602,9 +641,13 @@ var settingsToObserve = {
'devtools.remote.wifi.visible': {
resetToPref: true
},
'devtools.telemetry.supported_performance_marks': {
resetToPref: true
},
'dom.mozApps.use_reviewer_certs': false,
'dom.mozApps.signed_apps_installable_from': 'https://marketplace.firefox.com',
'dom.presentation.discovery.enabled': true,
'dom.presentation.discovery.enabled': false,
'dom.presentation.discoverable': false,
'dom.serviceWorkers.interception.enabled': true,
'dom.serviceWorkers.testing.enabled': false,
+120 -60
View File
@@ -20,9 +20,11 @@ Cu.import('resource://gre/modules/AlertsHelper.jsm');
Cu.import('resource://gre/modules/RequestSyncService.jsm');
Cu.import('resource://gre/modules/SystemUpdateService.jsm');
#ifdef MOZ_WIDGET_GONK
Cu.import('resource://gre/modules/MultiscreenHandler.jsm');
Cu.import('resource://gre/modules/NetworkStatsService.jsm');
Cu.import('resource://gre/modules/ResourceStatsService.jsm');
#endif
Cu.import('resource://gre/modules/KillSwitchMain.jsm');
// Identity
Cu.import('resource://gre/modules/SignInToWebsite.jsm');
@@ -75,11 +77,10 @@ XPCOMUtils.defineLazyServiceGetter(Services, 'captivePortalDetector',
'@mozilla.org/toolkit/captive-detector;1',
'nsICaptivePortalDetector');
window.performance.measure('gecko-shell-jsm-loaded', 'gecko-shell-loadstart');
XPCOMUtils.defineLazyModuleGetter(this, "SafeMode",
"resource://gre/modules/SafeMode.jsm");
function getContentWindow() {
return shell.contentBrowser.contentWindow;
}
window.performance.measure('gecko-shell-jsm-loaded', 'gecko-shell-loadstart');
function debug(str) {
dump(' -*- Shell.js: ' + str + '\n');
@@ -91,7 +92,13 @@ var shell = {
get CrashSubmit() {
delete this.CrashSubmit;
#ifdef MOZ_CRASHREPORTER
Cu.import("resource://gre/modules/CrashSubmit.jsm", this);
return this.CrashSubmit;
#else
dump('Crash reporter : disabled at build time.');
return this.CrashSubmit = null;
#endif
},
onlineForCrashReport: function shell_onlineForCrashReport() {
@@ -224,17 +231,21 @@ var shell = {
#endif
window.performance.mark('gecko-shell-bootstrap');
let startManifestURL =
Cc['@mozilla.org/commandlinehandler/general-startup;1?type=b2gbootstrap']
.getService(Ci.nsISupports).wrappedJSObject.startManifestURL;
if (startManifestURL) {
Cu.import('resource://gre/modules/Bootstraper.jsm');
Bootstraper.ensureSystemAppInstall(startManifestURL)
.then(this.start.bind(this))
.catch(Bootstraper.bailout);
} else {
this.start();
}
// Before anything, check if we want to start in safe mode.
SafeMode.check(window).then(() => {
let startManifestURL =
Cc['@mozilla.org/commandlinehandler/general-startup;1?type=b2gbootstrap']
.getService(Ci.nsISupports).wrappedJSObject.startManifestURL;
if (startManifestURL) {
Cu.import('resource://gre/modules/Bootstraper.jsm');
Bootstraper.ensureSystemAppInstall(startManifestURL)
.then(this.start.bind(this))
.catch(Bootstraper.bailout);
} else {
this.start();
}
});
},
start: function shell_start() {
@@ -368,7 +379,7 @@ var shell = {
CaptivePortalLoginHelper.init();
this.contentBrowser.src = homeURL;
this.isHomeLoaded = false;
this._isEventListenerReady = false;
window.performance.mark('gecko-shell-system-frame-set');
@@ -463,6 +474,13 @@ var shell = {
this.contentBrowser.setVisible(true);
}
break;
case 'load':
if (content.document.location == 'about:blank') {
return;
}
content.removeEventListener('load', this, true);
this.notifyContentWindowLoaded();
break;
case 'mozbrowserloadstart':
if (content.document.location == 'about:blank') {
this.contentBrowser.addEventListener('mozbrowserlocationchange', this, true);
@@ -569,9 +587,12 @@ var shell = {
break;
case 'MozAfterPaint':
window.removeEventListener('MozAfterPaint', this);
this.sendChromeEvent({
// This event should be sent before System app returns with
// system-message-listener-ready mozContentEvent, because it's on
// the critical launch path of the app.
SystemAppProxy._sendCustomEvent('mozChromeEvent', {
type: 'system-first-paint'
});
}, /* noPending */ true);
break;
case 'unload':
this.stop();
@@ -581,7 +602,7 @@ var shell = {
// TODO: We should get the `isActive` state from evt.isActive.
// Then we don't need to do `channel.isActive()` here.
channel.isActive().onsuccess = function(evt) {
this.sendChromeEvent({
SystemAppProxy._sendCustomEvent('mozSystemWindowChromeEvent', {
type: 'system-audiochannel-state-changed',
name: channel.name,
isActive: evt.target.result
@@ -593,6 +614,14 @@ var shell = {
// Send an event to a specific window, document or element.
sendEvent: function shell_sendEvent(target, type, details) {
if (target === this.contentBrowser) {
// We must ask SystemAppProxy to send the event in this case so
// that event would be dispatched from frame.contentWindow instead of
// on the System app frame.
SystemAppProxy._sendCustomEvent(type, details);
return;
}
let doc = target.document || target.ownerDocument || target;
let event = doc.createEvent('CustomEvent');
event.initCustomEvent(type, true, true, details ? details : {});
@@ -600,21 +629,10 @@ var shell = {
},
sendCustomEvent: function shell_sendCustomEvent(type, details) {
let target = getContentWindow();
let payload = details ? Cu.cloneInto(details, target) : {};
this.sendEvent(target, type, payload);
SystemAppProxy._sendCustomEvent(type, details);
},
sendChromeEvent: function shell_sendChromeEvent(details) {
if (!this.isHomeLoaded) {
if (!('pendingChromeEvents' in this)) {
this.pendingChromeEvents = [];
}
this.pendingChromeEvents.push(details);
return;
}
this.sendCustomEvent("mozChromeEvent", details);
},
@@ -655,6 +673,7 @@ var shell = {
this.contentBrowser.removeEventListener('mozbrowserlocationchange', this, true);
let content = this.contentBrowser.contentWindow;
content.addEventListener('load', this, true);
this.reportCrash(true);
@@ -668,28 +687,7 @@ var shell = {
Cu.import('resource://gre/modules/OperatorApps.jsm');
#endif
content.addEventListener('load', function shell_homeLoaded() {
content.removeEventListener('load', shell_homeLoaded);
shell.isHomeLoaded = true;
if (Services.prefs.getBoolPref('b2g.orientation.animate')) {
Cu.import('resource://gre/modules/OrientationChangeHandler.jsm');
}
#ifdef MOZ_WIDGET_GONK
libcutils.property_set('sys.boot_completed', '1');
#endif
Services.obs.notifyObservers(null, "browser-ui-startup-complete", "");
SystemAppProxy.setIsReady();
if ('pendingChromeEvents' in shell) {
shell.pendingChromeEvents.forEach((shell.sendChromeEvent).bind(shell));
}
delete shell.pendingChromeEvents;
});
shell.handleCmdLine();
this.handleCmdLine();
},
handleCmdLine: function shell_handleCmdLine() {
@@ -710,6 +708,38 @@ var shell = {
}
#endif
},
// This gets called when window.onload fires on the System app content window,
// which means things in <html> are parsed and statically referenced <script>s
// and <script defer>s are loaded and run.
notifyContentWindowLoaded: function shell_notifyContentWindowLoaded() {
#ifdef MOZ_WIDGET_GONK
libcutils.property_set('sys.boot_completed', '1');
#endif
// This will cause Gonk Widget to remove boot animation from the screen
// and reveals the page.
Services.obs.notifyObservers(null, "browser-ui-startup-complete", "");
SystemAppProxy.setIsLoaded();
},
// This gets called when the content sends us system-message-listener-ready
// mozContentEvent, OR when an observer message tell us we should consider
// the content as ready.
notifyEventListenerReady: function shell_notifyEventListenerReady() {
if (this._isEventListenerReady) {
Cu.reportError('shell.js: SystemApp has already been declared as being ready.');
return;
}
this._isEventListenerReady = true;
if (Services.prefs.getBoolPref('b2g.orientation.animate')) {
Cu.import('resource://gre/modules/OrientationChangeHandler.jsm');
}
SystemAppProxy.setIsReady();
}
};
Services.obs.addObserver(function onFullscreenOriginChange(subject, topic, data) {
@@ -717,11 +747,13 @@ Services.obs.addObserver(function onFullscreenOriginChange(subject, topic, data)
fullscreenorigin: data });
}, "fullscreen-origin-change", false);
DOMApplicationRegistry.registryStarted.then(function () {
shell.sendChromeEvent({ type: 'webapps-registry-start' });
});
DOMApplicationRegistry.registryReady.then(function () {
shell.sendChromeEvent({ type: 'webapps-registry-ready' });
// This event should be sent before System app returns with
// system-message-listener-ready mozContentEvent, because it's on
// the critical launch path of the app.
SystemAppProxy._sendCustomEvent('mozChromeEvent', {
type: 'webapps-registry-ready'
}, /* noPending */ true);
});
Services.obs.addObserver(function onBluetoothVolumeChange(subject, topic, data) {
@@ -735,6 +767,18 @@ Services.obs.addObserver(function(subject, topic, data) {
shell.sendCustomEvent('mozmemorypressure');
}, 'memory-pressure', false);
Services.obs.addObserver(function(subject, topic, data) {
shell.notifyEventListenerReady();
}, 'system-message-listener-ready', false);
var permissionMap = new Map([
['unknown', Services.perms.UNKNOWN_ACTION],
['allow', Services.perms.ALLOW_ACTION],
['deny', Services.perms.DENY_ACTION],
['prompt', Services.perms.PROMPT_ACTION],
]);
var permissionMapRev = new Map(Array.from(permissionMap.entries()).reverse());
var CustomEventManager = {
init: function custevt_init() {
window.addEventListener("ContentStart", (function(evt) {
@@ -780,11 +824,27 @@ var CustomEventManager = {
Services.obs.notifyObservers({ wrappedJSObject: shell.contentBrowser },
'ask-children-to-execute-copypaste-command', detail.cmd);
break;
case 'add-permission':
Services.perms.add(Services.io.newURI(detail.uri, null, null),
detail.permissionType, permissionMap.get(detail.permission));
break;
case 'remove-permission':
Services.perms.remove(Services.io.newURI(detail.uri, null, null),
detail.permissionType);
break;
case 'test-permission':
let result = Services.perms.testExactPermission(
Services.io.newURI(detail.uri, null, null), detail.permissionType);
// Not equal check here because we want to prevent default only if it's not set
if (result !== permissionMapRev.get(detail.permission)) {
evt.preventDefault();
}
break;
}
}
}
let DoCommandHelper = {
var DoCommandHelper = {
_event: null,
setEvent: function docommand_setEvent(evt) {
this._event = evt;
@@ -885,7 +945,7 @@ var WebappsHelper = {
}
}
let KeyboardHelper = {
var KeyboardHelper = {
handleEvent: function keyboard_handleEvent(detail) {
switch (detail.type) {
case 'inputmethod-update-layouts':
@@ -901,7 +961,7 @@ let KeyboardHelper = {
}
};
let SystemAppMozBrowserHelper = {
var SystemAppMozBrowserHelper = {
handleEvent: function systemAppMozBrowser_handleEvent(detail) {
let request;
let name;
+19
View File
@@ -0,0 +1,19 @@
<!DOCTYPE html>
<!-- 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/. -->
<html xmlns="http://www.w3.org/1999/xhtml"
id="shellRemote"
windowtype="navigator:remote-browser"
sizemode="fullscreen"
>
<head>
<link rel="stylesheet" href="shell.css" type="text/css">
<script type="application/javascript;version=1.8"
src="chrome://b2g/content/shell_remote.js"> </script>
</head>
<body id="container">
</body>
</html>
+71
View File
@@ -0,0 +1,71 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- /
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
/* 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 {utils: Cu} = Components;
Cu.import("resource://gre/modules/Services.jsm");
function debug(aStr) {
// dump(" -*- ShellRemote.js: " + aStr + "\n");
}
let remoteShell = {
get homeURL() {
let systemAppManifestURL = Services.io.newURI(this.systemAppManifestURL, null, null);
let shellRemoteURL = Services.prefs.getCharPref("b2g.multiscreen.system_remote_url");
shellRemoteURL = Services.io.newURI(shellRemoteURL, null, systemAppManifestURL);
return shellRemoteURL.spec;
},
get systemAppManifestURL() {
return Services.prefs.getCharPref("b2g.system_manifest_url");
},
_started: false,
hasStarted: function () {
return this._started;
},
start: function () {
this._started = true;
let homeURL = this.homeURL;
if (!homeURL) {
debug("ERROR! Remote home URL undefined.");
return;
}
let manifestURL = this.systemAppManifestURL;
// <html:iframe id="remote-systemapp"
// mozbrowser="true" allowfullscreen="true"
// src="blank.html"/>
let systemAppFrame =
document.createElementNS("http://www.w3.org/1999/xhtml", "html:iframe");
systemAppFrame.setAttribute("id", "remote-systemapp");
systemAppFrame.setAttribute("mozbrowser", "true");
systemAppFrame.setAttribute("mozapp", manifestURL);
systemAppFrame.setAttribute("allowfullscreen", "true");
systemAppFrame.setAttribute("src", "blank.html");
let container = document.getElementById("container");
this.contentBrowser = container.appendChild(systemAppFrame);
this.contentBrowser.src = homeURL + window.location.hash;
},
stop: function () {
},
};
window.onload = function() {
if (remoteShell.hasStarted() == false) {
remoteShell.start();
}
};
+2
View File
@@ -13,6 +13,8 @@ chrome.jar:
* content/shell.html (content/shell.html)
* content/shell.js (content/shell.js)
content/shell.css (content/shell.css)
content/shell_remote.html (content/shell_remote.html)
content/shell_remote.js (content/shell_remote.js)
content/blank.html (content/blank.html)
content/blank.css (content/blank.css)
#ifdef MOZ_WIDGET_GONK
+1 -1
View File
@@ -63,7 +63,7 @@ const kMessages = [
kMessageAlertNotificationClose
];
let AlertsHelper = {
var AlertsHelper = {
_listeners: {},
+4
View File
@@ -128,3 +128,7 @@ contract @mozilla.org/presentation-device/prompt;1 {4a300c26-e99b-4018-ab9b-c48c
# PresentationRequestUIGlue.js
component {ccc8a839-0b64-422b-8a60-fb2af0e376d0} PresentationRequestUIGlue.js
contract @mozilla.org/presentation/requestuiglue;1 {ccc8a839-0b64-422b-8a60-fb2af0e376d0}
# KillSwitch.js
component {b6eae5c6-971c-4772-89e5-5df626bf3f09} KillSwitch.js
contract @mozilla.org/moz-kill-switch;1 {b6eae5c6-971c-4772-89e5-5df626bf3f09}
+1 -1
View File
@@ -132,7 +132,7 @@ SSLExceptions.prototype = {
}
};
let ErrorPage = {
var ErrorPage = {
_addCertException: function(aMessage) {
let frameLoaderOwner = aMessage.target.QueryInterface(Ci.nsIFrameLoaderOwner);
let win = frameLoaderOwner.ownerDocument.defaultView;
+1 -1
View File
@@ -117,7 +117,7 @@ const Observer = {
};
let Frames = this.Frames = {
var Frames = this.Frames = {
list: () => SystemAppProxy.getFrames(),
+116
View File
@@ -0,0 +1,116 @@
/* 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 DEBUG = false;
function debug(s) {
dump("-*- KillSwitch.js: " + s + "\n");
}
const {interfaces: Ci, utils: Cu} = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/DOMRequestHelper.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Services",
"resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
"@mozilla.org/childprocessmessagemanager;1",
"nsIMessageSender");
const KILLSWITCH_CID = "{b6eae5c6-971c-4772-89e5-5df626bf3f09}";
const KILLSWITCH_CONTRACTID = "@mozilla.org/moz-kill-switch;1";
const kEnableKillSwitch = "KillSwitch:Enable";
const kEnableKillSwitchOK = "KillSwitch:Enable:OK";
const kEnableKillSwitchKO = "KillSwitch:Enable:KO";
const kDisableKillSwitch = "KillSwitch:Disable";
const kDisableKillSwitchOK = "KillSwitch:Disable:OK";
const kDisableKillSwitchKO = "KillSwitch:Disable:KO";
function KillSwitch() {
this._window = null;
}
KillSwitch.prototype = {
__proto__: DOMRequestIpcHelper.prototype,
init: function(aWindow) {
DEBUG && debug("init");
this._window = aWindow;
this.initDOMRequestHelper(this._window);
},
enable: function() {
DEBUG && debug("KillSwitch: enable");
cpmm.addMessageListener(kEnableKillSwitchOK, this);
cpmm.addMessageListener(kEnableKillSwitchKO, this);
return this.createPromise((aResolve, aReject) => {
cpmm.sendAsyncMessage(kEnableKillSwitch, {
requestID: this.getPromiseResolverId({
resolve: aResolve,
reject: aReject
})
});
});
},
disable: function() {
DEBUG && debug("KillSwitch: disable");
cpmm.addMessageListener(kDisableKillSwitchOK, this);
cpmm.addMessageListener(kDisableKillSwitchKO, this);
return this.createPromise((aResolve, aReject) => {
cpmm.sendAsyncMessage(kDisableKillSwitch, {
requestID: this.getPromiseResolverId({
resolve: aResolve,
reject: aReject
})
});
});
},
receiveMessage: function(message) {
DEBUG && debug("Received: " + message.name);
cpmm.removeMessageListener(kEnableKillSwitchOK, this);
cpmm.removeMessageListener(kEnableKillSwitchKO, this);
cpmm.removeMessageListener(kDisableKillSwitchOK, this);
cpmm.removeMessageListener(kDisableKillSwitchKO, this);
let req = this.takePromiseResolver(message.data.requestID);
switch (message.name) {
case kEnableKillSwitchKO:
case kDisableKillSwitchKO:
req.reject(false);
break;
case kEnableKillSwitchOK:
case kDisableKillSwitchOK:
req.resolve(true);
break;
default:
DEBUG && debug("Unrecognized message: " + message.name);
break;
}
},
classID : Components.ID(KILLSWITCH_CID),
contractID : KILLSWITCH_CONTRACTID,
QueryInterface: XPCOMUtils.generateQI([Ci.nsIKillSwitch,
Ci.nsIDOMGlobalPropertyInitializer,
Ci.nsIObserver,
Ci.nsIMessageListener,
Ci.nsISupportsWeakReference]),
};
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([KillSwitch]);
+505
View File
@@ -0,0 +1,505 @@
/* 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";
this.EXPORTED_SYMBOLS = [ "KillSwitchMain" ];
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Promise", "resource://gre/modules/Promise.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "settings",
"@mozilla.org/settingsService;1",
"nsISettingsService");
XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
"@mozilla.org/parentprocessmessagemanager;1",
"nsIMessageBroadcaster");
XPCOMUtils.defineLazyGetter(this, "permMgr", function() {
return Cc["@mozilla.org/permissionmanager;1"]
.getService(Ci.nsIPermissionManager);
});
#ifdef MOZ_WIDGET_GONK
XPCOMUtils.defineLazyGetter(this, "libcutils", function () {
Cu.import("resource://gre/modules/systemlibs.js");
return libcutils;
});
#else
this.libcutils = null;
#endif
const DEBUG = false;
const kEnableKillSwitch = "KillSwitch:Enable";
const kEnableKillSwitchOK = "KillSwitch:Enable:OK";
const kEnableKillSwitchKO = "KillSwitch:Enable:KO";
const kDisableKillSwitch = "KillSwitch:Disable";
const kDisableKillSwitchOK = "KillSwitch:Disable:OK";
const kDisableKillSwitchKO = "KillSwitch:Disable:KO";
const kMessages = [kEnableKillSwitch, kDisableKillSwitch];
const kXpcomShutdownObserverTopic = "xpcom-shutdown";
const kProperty = "persist.moz.killswitch";
const kUserValues =
OS.Path.join(OS.Constants.Path.profileDir, "killswitch.json");
var inParent = Cc["@mozilla.org/xre/app-info;1"]
.getService(Ci.nsIXULRuntime)
.processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
function debug(aStr) {
dump("--*-- KillSwitchMain: " + aStr + "\n");
}
this.KillSwitchMain = {
_ksState: null,
_libcutils: null,
_enabledValues: {
// List of settings to set to a specific value
settings: {
"debugger.remote-mode": "disabled",
"developer.menu.enabled": false,
"devtools.unrestricted": false,
"lockscreen.enabled": true,
"lockscreen.locked": true,
"lockscreen.lock-immediately": true,
"tethering.usb.enabled": false,
"tethering.wifi.enabled": false,
"ums.enabled": false
},
// List of preferences to set to a specific value
prefs: {
"b2g.killswitch.test": true
},
// List of Android properties to set to a specific value
properties: {
"persist.sys.usb.config": "none" // will change sys.usb.config and sys.usb.state
},
// List of Android services to control
services: {
"adbd": "stop"
}
},
init: function() {
DEBUG && debug("init");
if (libcutils) {
this._libcutils = libcutils;
}
kMessages.forEach(m => {
ppmm.addMessageListener(m, this);
});
Services.obs.addObserver(this, kXpcomShutdownObserverTopic, false);
this.readStateProperty();
},
uninit: function() {
kMessages.forEach(m => {
ppmm.removeMessageListener(m, this);
});
Services.obs.removeObserver(this, kXpcomShutdownObserverTopic);
},
checkLibcUtils: function() {
DEBUG && debug("checkLibcUtils");
if (!this._libcutils) {
debug("No proper libcutils binding, aborting.");
throw Cr.NS_ERROR_NO_INTERFACE;
}
return true;
},
readStateProperty: function() {
DEBUG && debug("readStateProperty");
try {
this.checkLibcUtils();
} catch (ex) {
return;
}
this._ksState =
this._libcutils.property_get(kProperty, "false") === "true";
},
writeStateProperty: function() {
DEBUG && debug("writeStateProperty");
try {
this.checkLibcUtils();
} catch (ex) {
return;
}
this._libcutils.property_set(kProperty, this._ksState.toString());
},
getPref(name, value) {
let rv = undefined;
try {
switch (typeof value) {
case "boolean":
rv = Services.prefs.getBoolPref(name, value);
break;
case "number":
rv = Services.prefs.getIntPref(name, value);
break;
case "string":
rv = Services.prefs.getCharPref(name, value);
break;
default:
debug("Unexpected pref type " + value);
break;
}
} catch (ex) {
}
return rv;
},
setPref(name, value) {
switch (typeof value) {
case "boolean":
Services.prefs.setBoolPref(name, value);
break;
case "number":
Services.prefs.setIntPref(name, value);
break;
case "string":
Services.prefs.setCharPref(name, value);
break;
default:
debug("Unexpected pref type " + value);
break;
}
},
doEnable: function() {
return new Promise((resolve, reject) => {
// Make sure that the API cannot do a new |enable()| call once the
// feature has been enabled, otherwise we will overwrite the user values.
if (this._ksState) {
reject(true);
return;
}
this.saveUserValues().then(() => {
DEBUG && debug("Toggling settings: " +
JSON.stringify(this._enabledValues.settings));
let lock = settings.createLock();
for (let key of Object.keys(this._enabledValues.settings)) {
lock.set(key, this._enabledValues.settings[key], this);
}
DEBUG && debug("Toggling prefs: " +
JSON.stringify(this._enabledValues.prefs));
for (let key of Object.keys(this._enabledValues.prefs)) {
this.setPref(key, this._enabledValues.prefs[key]);
}
DEBUG && debug("Toggling properties: " +
JSON.stringify(this._enabledValues.properties));
for (let key of Object.keys(this._enabledValues.properties)) {
this._libcutils.property_set(key, this._enabledValues.properties[key]);
}
DEBUG && debug("Toggling services: " +
JSON.stringify(this._enabledValues.services));
for (let key of Object.keys(this._enabledValues.services)) {
let value = this._enabledValues.services[key];
if (value !== "start" && value !== "stop") {
debug("Unexpected service " + key + " value:" + value);
}
this._libcutils.property_set("ctl." + value, key);
}
this._ksState = true;
this.writeStateProperty();
resolve(true);
}).catch(err => {
DEBUG && debug("doEnable: " + err);
reject(false);
});
});
},
saveUserValues: function() {
return new Promise((resolve, reject) => {
try {
this.checkLibcUtils();
} catch (ex) {
reject("nolibcutils");
}
let _userValues = {
settings: { },
prefs: { },
properties: { }
};
// Those will be sync calls
for (let key of Object.keys(this._enabledValues.prefs)) {
_userValues.prefs[key] =
this.getPref(key, this._enabledValues.prefs[key]);
}
for (let key of Object.keys(this._enabledValues.properties)) {
_userValues.properties[key] = this._libcutils.property_get(key);
}
let self = this;
let getCallback = {
handleAbort: function(m) {
DEBUG && debug("getCallback: handleAbort: m=" + m);
reject(m);
},
handleError: function(m) {
DEBUG && debug("getCallback: handleError: m=" + m);
reject(m);
},
handle: function(n, v) {
DEBUG && debug("getCallback: handle: n=" + n + " ; v=" + v);
if (self._pendingSettingsGet) {
// We have received a settings callback value for saving user data
let pending = self._pendingSettingsGet.indexOf(n);
if (pending !== -1) {
_userValues.settings[n] = v;
self._pendingSettingsGet.splice(pending, 1);
}
if (self._pendingSettingsGet.length === 0) {
delete self._pendingSettingsGet;
let payload = JSON.stringify(_userValues);
DEBUG && debug("Dumping to " + kUserValues + ": " + payload);
OS.File.writeAtomic(kUserValues, payload).then(
function writeOk() {
resolve(true);
},
function writeNok(err) {
reject("write error");
}
);
}
}
}
};
// For settings we have to wait all the callbacks to come back before
// we can resolve or reject
this._pendingSettingsGet = [];
let lock = settings.createLock();
for (let key of Object.keys(this._enabledValues.settings)) {
this._pendingSettingsGet.push(key);
lock.get(key, getCallback);
}
});
},
doDisable: function() {
return new Promise((resolve, reject) => {
this.restoreUserValues().then(() => {
this._ksState = false;
this.writeStateProperty();
resolve(true);
}).catch(err => {
DEBUG && debug("doDisable: " + err);
reject(false);
});
});
},
restoreUserValues: function() {
return new Promise((resolve, reject) => {
try {
this.checkLibcUtils();
} catch (ex) {
reject("nolibcutils");
}
OS.File.read(kUserValues, { encoding: "utf-8" }).then(content => {
let values = JSON.parse(content);
for (let key of Object.keys(values.prefs)) {
this.setPref(key, values.prefs[key]);
}
for (let key of Object.keys(values.properties)) {
this._libcutils.property_set(key, values.properties[key]);
}
let self = this;
let saveCallback = {
handleAbort: function(m) {
DEBUG && debug("saveCallback: handleAbort: m=" + m);
reject(m);
},
handleError: function(m) {
DEBUG && debug("saveCallback: handleError: m=" + m);
reject(m);
},
handle: function(n, v) {
DEBUG && debug("saveCallback: handle: n=" + n + " ; v=" + v);
if (self._pendingSettingsSet) {
// We have received a settings callback value for setting user data
let pending = self._pendingSettingsSet.indexOf(n);
if (pending !== -1) {
self._pendingSettingsSet.splice(pending, 1);
}
if (self._pendingSettingsSet.length === 0) {
delete self._pendingSettingsSet;
DEBUG && debug("Restored from " + kUserValues + ": " + JSON.stringify(values));
resolve(values);
}
}
}
};
// For settings we have to wait all the callbacks to come back before
// we can resolve or reject
this._pendingSettingsSet = [];
let lock = settings.createLock();
for (let key of Object.keys(values.settings)) {
this._pendingSettingsSet.push(key);
lock.set(key, values.settings[key], saveCallback);
}
}).catch(err => {
reject(err);
});
});
},
// Settings Callbacks
handle: function(aName, aValue) {
DEBUG && debug("handle: aName=" + aName + " ; aValue=" + aValue);
// We don't have to do anything for now.
},
handleAbort: function(aMessage) {
debug("handleAbort: " + JSON.stringify(aMessage));
throw Cr.NS_ERROR_ABORT;
},
handleError: function(aMessage) {
debug("handleError: " + JSON.stringify(aMessage));
throw Cr.NS_ERROR_FAILURE;
},
// addObserver
observe: function(aSubject, aTopic, aData) {
switch (aTopic) {
case kXpcomShutdownObserverTopic:
this.uninit();
break;
default:
DEBUG && debug("Wrong observer topic: " + aTopic);
break;
}
},
// addMessageListener
receiveMessage: function(aMessage) {
let hasPermission = aMessage.target.assertPermission("killswitch");
DEBUG && debug("hasPermission: " + hasPermission);
if (!hasPermission) {
debug("Message " + aMessage.name + " from a process with no killswitch perm.");
aMessage.target.killChild();
throw Cr.NS_ERROR_NOT_AVAILABLE;
return;
}
function returnMessage(name, data) {
if (aMessage.target) {
data.requestID = aMessage.data.requestID;
try {
aMessage.target.sendAsyncMessage(name, data);
} catch (e) {
if (DEBUG) debug("Return message failed, " + name + ": " + e);
}
}
}
switch (aMessage.name) {
case kEnableKillSwitch:
this.doEnable().then(
() => {
returnMessage(kEnableKillSwitchOK, {});
},
err => {
debug("doEnable failed: " + err);
returnMessage(kEnableKillSwitchKO, {});
}
);
break;
case kDisableKillSwitch:
this.doDisable().then(
() => {
returnMessage(kDisableKillSwitchOK, {});
},
err => {
debug("doDisable failed: " + err);
returnMessage(kDisableKillSwitchKO, {});
}
);
break;
default:
debug("Unsupported message: " + aMessage.name);
aMessage.target && aMessage.target.killChild();
throw Cr.NS_ERROR_ILLEGAL_VALUE;
return;
break;
}
}
};
// This code should ALWAYS be living only on the parent side.
if (!inParent) {
debug("KillSwitchMain should only be living on parent side.");
throw Cr.NS_ERROR_ABORT;
} else {
this.KillSwitchMain.init();
}
+17 -8
View File
@@ -24,7 +24,7 @@ function debug(msg) {
dump("LogCapture.jsm: " + msg + "\n");
}
let LogCapture = {
var LogCapture = {
ensureLoaded: function() {
if (!this.ctypes) {
this.load();
@@ -194,17 +194,26 @@ let LogCapture = {
* as an ArrayBuffer.
*/
getScreenshot: function() {
this.ensureLoaded();
let deferred = Promise.defer();
try {
this.ensureLoaded();
let fr = Cc["@mozilla.org/files/filereader;1"]
.createInstance(Ci.nsIDOMFileReader);
let fr = Cc["@mozilla.org/files/filereader;1"]
.createInstance(Ci.nsIDOMFileReader);
fr.onload = function(evt) {
deferred.resolve(new Uint8Array(evt.target.result));
};
fr.onload = function(evt) {
deferred.resolve(new Uint8Array(evt.target.result));
};
fr.readAsArrayBuffer(Screenshot.get());
fr.onerror = function(evt) {
deferred.reject(evt);
};
fr.readAsArrayBuffer(Screenshot.get());
} catch(e) {
// We pass any errors through to the deferred Promise
deferred.reject(e);
}
return deferred.promise;
}
+19 -5
View File
@@ -16,10 +16,14 @@ function parseLogArray(array) {
let data = new DataView(array.buffer);
let byteString = String.fromCharCode.apply(null, array);
// Length of bytes that precede the payload of a log message
// From the 5 Uint32 and 1 Uint8 reads
const HEADER_LENGTH = 21;
let logMessages = [];
let pos = 0;
while (pos < byteString.length) {
while (pos + HEADER_LENGTH < byteString.length) {
// Parse a single log entry
// Track current offset from global position
@@ -203,33 +207,43 @@ function formatLogMessage(logMessage) {
": " + logMessage.message;
}
/**
* Convert a string to a utf-8 Uint8Array
* @param {String} str
* @return {Uint8Array}
*/
function textEncode(str) {
return new TextEncoder("utf-8").encode(str);
}
/**
* Pretty-print an array of bytes read from a log file by parsing then
* threadtime formatting its entries.
* @param array {Uint8Array} Array of a log file's bytes
* @return {String} Pretty-printed log
* @return {Uint8Array} Pretty-printed log
*/
function prettyPrintLogArray(array) {
let logMessages = parseLogArray(array);
return logMessages.map(formatLogMessage).join("");
return textEncode(logMessages.map(formatLogMessage).join(""));
}
/**
* Pretty-print an array read from the list of propreties.
* @param {Object} Object representing the properties
* @return {String} Human-readable string of property name: property value
* @return {Uint8Array} Human-readable string of property name: property value
*/
function prettyPrintPropertiesArray(properties) {
let propertiesString = "";
for(let propName in properties) {
propertiesString += "[" + propName + "]: [" + properties[propName] + "]\n";
}
return propertiesString;
return textEncode(propertiesString);
}
/**
* Pretty-print a normal array. Does nothing.
* @param array {Uint8Array} Input array
* @return {Uint8Array} The same array
*/
function prettyPrintArray(array) {
return array;
+293 -97
View File
@@ -7,15 +7,16 @@
* response to a sufficiently large acceleration (a shake), it will save log
* files to an arbitrary directory which it will then return on a
* 'capture-logs-success' event with detail.logFilenames representing each log
* file's filename in the directory. If an error occurs it will instead produce
* a 'capture-logs-error' event.
* file's name and detail.logPaths representing the patch to each log file or
* the path to the archive.
* If an error occurs it will instead produce a 'capture-logs-error' event.
* We send a capture-logs-start events to notify the system app and the user,
* since dumping can be a bit long sometimes.
*/
/* enable Mozilla javascript extensions and global strictness declaration,
* disable valid this checking */
/* jshint moz: true */
/* jshint moz: true, esnext: true */
/* jshint -W097 */
/* jshint -W040 */
/* global Services, Components, dump, LogCapture, LogParser,
@@ -23,11 +24,17 @@
"use strict";
const Cu = Components.utils;
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
// Constants for creating zip file taken from toolkit/webapps/tests/head.js
const PR_RDWR = 0x04;
const PR_CREATE_FILE = 0x08;
const PR_TRUNCATE = 0x20;
XPCOMUtils.defineLazyModuleGetter(this, "LogCapture", "resource://gre/modules/LogCapture.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "LogParser", "resource://gre/modules/LogParser.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm");
@@ -51,17 +58,30 @@ function debug(msg) {
/**
* An empirically determined amount of acceleration corresponding to a
* shake
* shake.
*/
const EXCITEMENT_THRESHOLD = 500;
/**
* The maximum fraction to update the excitement value per frame. This
* corresponds to requiring shaking for approximately 10 motion events (1.6
* seconds)
*/
const EXCITEMENT_FILTER_ALPHA = 0.2;
const DEVICE_MOTION_EVENT = "devicemotion";
const SCREEN_CHANGE_EVENT = "screenchange";
const CAPTURE_LOGS_CONTENT_EVENT = "requestSystemLogs";
const CAPTURE_LOGS_START_EVENT = "capture-logs-start";
const CAPTURE_LOGS_ERROR_EVENT = "capture-logs-error";
const CAPTURE_LOGS_SUCCESS_EVENT = "capture-logs-success";
let LogShake = {
var LogShake = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
/**
* If LogShake is in QA Mode, which bundles all files into a compressed archive
*/
qaModeEnabled: false,
/**
* If LogShake is listening for device motion events. Required due to lag
* between HAL layer of device motion events and listening for device motion
@@ -69,12 +89,29 @@ let LogShake = {
*/
deviceMotionEnabled: false,
/**
* We only listen to motion events when the screen is enabled, keep track
* of its state.
*/
screenEnabled: true,
/**
* Flag monitoring if the preference to enable shake to capture is
* enabled in gaia.
*/
listenToDeviceMotion: true,
/**
* If a capture has been requested and is waiting for reads/parsing. Used for
* debouncing.
*/
captureRequested: false,
/**
* The current excitement (movement) level
*/
excitement: 0,
/**
* Map of files which have log-type information to their parsers
*/
@@ -109,6 +146,10 @@ let LogShake = {
screenEnabled: true
}});
// Reset excitement to clear residual motion
this.excitement = 0;
SystemAppProxy.addEventListener(CAPTURE_LOGS_CONTENT_EVENT, this, false);
SystemAppProxy.addEventListener(SCREEN_CHANGE_EVENT, this, false);
Services.obs.addObserver(this, "xpcom-shutdown", false);
@@ -129,6 +170,10 @@ let LogShake = {
case SCREEN_CHANGE_EVENT:
this.handleScreenChangeEvent(event);
break;
case CAPTURE_LOGS_CONTENT_EVENT:
this.startCapture();
break;
}
},
@@ -141,8 +186,30 @@ let LogShake = {
}
},
enableQAMode: function() {
debug("Enabling QA Mode");
this.qaModeEnabled = true;
},
disableQAMode: function() {
debug("Disabling QA Mode");
this.qaModeEnabled = false;
},
enableDeviceMotionListener: function() {
this.listenToDeviceMotion = true;
this.startDeviceMotionListener();
},
disableDeviceMotionListener: function() {
this.listenToDeviceMotion = false;
this.stopDeviceMotionListener();
},
startDeviceMotionListener: function() {
if (!this.deviceMotionEnabled) {
if (!this.deviceMotionEnabled &&
this.listenToDeviceMotion &&
this.screenEnabled) {
SystemAppProxy.addEventListener(DEVICE_MOTION_EVENT, this, false);
this.deviceMotionEnabled = true;
}
@@ -164,33 +231,41 @@ let LogShake = {
return;
}
var acc = event.accelerationIncludingGravity;
let acc = event.accelerationIncludingGravity;
var excitement = acc.x * acc.x + acc.y * acc.y + acc.z * acc.z;
// Updates excitement by a factor of at most alpha, ignoring sudden device
// motion. See bug #1101994 for more information.
let newExcitement = acc.x * acc.x + acc.y * acc.y + acc.z * acc.z;
this.excitement += (newExcitement - this.excitement) * EXCITEMENT_FILTER_ALPHA;
if (excitement > EXCITEMENT_THRESHOLD) {
if (!this.captureRequested) {
this.captureRequested = true;
SystemAppProxy._sendCustomEvent(CAPTURE_LOGS_START_EVENT, {});
this.captureLogs().then(logResults => {
// On resolution send the success event to the requester
SystemAppProxy._sendCustomEvent(CAPTURE_LOGS_SUCCESS_EVENT, {
logFilenames: logResults.logFilenames,
logPrefix: logResults.logPrefix
});
this.captureRequested = false;
},
error => {
// On an error send the error event
SystemAppProxy._sendCustomEvent(CAPTURE_LOGS_ERROR_EVENT, {error: error});
this.captureRequested = false;
});
}
if (this.excitement > EXCITEMENT_THRESHOLD) {
this.startCapture();
}
},
startCapture: function() {
if (this.captureRequested) {
return;
}
this.captureRequested = true;
SystemAppProxy._sendCustomEvent(CAPTURE_LOGS_START_EVENT, {});
this.captureLogs().then(logResults => {
// On resolution send the success event to the requester
SystemAppProxy._sendCustomEvent(CAPTURE_LOGS_SUCCESS_EVENT, {
logPaths: logResults.logPaths,
logFilenames: logResults.logFilenames
});
this.captureRequested = false;
}, error => {
// On an error send the error event
SystemAppProxy._sendCustomEvent(CAPTURE_LOGS_ERROR_EVENT, {error: error});
this.captureRequested = false;
});
},
handleScreenChangeEvent: function(event) {
if (event.detail.screenEnabled) {
this.screenEnabled = event.detail.screenEnabled;
if (this.screenEnabled) {
this.startDeviceMotionListener();
} else {
this.stopDeviceMotionListener();
@@ -202,15 +277,18 @@ let LogShake = {
* resolve to an array of log filenames.
*/
captureLogs: function() {
let logArrays = this.readLogs();
return saveLogs(logArrays);
return this.readLogs().then(logArrays => {
return this.saveLogs(logArrays);
});
},
/**
* Read in all log files, returning their formatted contents
* @return {Promise<Array>}
*/
readLogs: function() {
let logArrays = {};
let readPromises = [];
try {
logArrays["properties"] =
@@ -220,14 +298,15 @@ let LogShake = {
}
// Let Gecko perfom the dump to a file, and just collect it
try {
let readAboutMemoryPromise = new Promise(resolve => {
// Wrap the readAboutMemory promise to make it infallible
LogCapture.readAboutMemory().then(aboutMemory => {
let file = OS.Path.basename(aboutMemory);
let logArray;
try {
logArray = LogCapture.readLogFile(aboutMemory);
if (!logArray) {
debug("LogCapture.readLogFile() returned nothing about:memory ");
debug("LogCapture.readLogFile() returned nothing for about:memory");
}
// We need to remove the dumped file, now that we have it in memory
OS.File.remove(aboutMemory);
@@ -235,18 +314,25 @@ let LogShake = {
Cu.reportError("Unable to handle about:memory dump: " + ex);
}
logArrays[file] = LogParser.prettyPrintArray(logArray);
resolve();
}, ex => {
Cu.reportError("Unable to get about:memory dump: " + ex);
resolve();
});
} catch (ex) {
Cu.reportError("Unable to get about:memory dump: " + ex);
}
});
readPromises.push(readAboutMemoryPromise);
try {
// Wrap the promise to make it infallible
let readScreenshotPromise = new Promise(resolve => {
LogCapture.getScreenshot().then(screenshot => {
logArrays["logshake-screenshot.png"] = screenshot;
logArrays["screenshot.png"] = screenshot;
resolve();
}, ex => {
Cu.reportError("Unable to get screenshot dump: " + ex);
resolve();
});
} catch (ex) {
Cu.reportError("Unable to get screenshot dump: " + ex);
}
});
readPromises.push(readScreenshotPromise);
for (let loc in this.LOGS_WITH_PARSERS) {
let logArray;
@@ -268,7 +354,31 @@ let LogShake = {
continue;
}
}
return logArrays;
// Because the promises we depend upon can't fail this means that the
// blocking log reads will always be honored.
return Promise.all(readPromises).then(() => {
return logArrays;
});
},
/**
* Save the formatted arrays of log files to an sdcard if available
*/
saveLogs: function(logArrays) {
if (!logArrays || Object.keys(logArrays).length === 0) {
return Promise.reject("Zero logs saved");
}
if (this.qaModeEnabled) {
return makeBaseLogsDirectory().then(writeLogArchive(logArrays),
rejectFunction("Error making base log directory"));
} else {
return makeBaseLogsDirectory().then(makeLogsDirectory,
rejectFunction("Error making base log directory"))
.then(writeLogFiles(logArrays),
rejectFunction("Error creating log directory"));
}
},
/**
@@ -307,82 +417,168 @@ function getLogDirectoryRoot() {
return "logs";
}
function getLogDirectory() {
function getLogIdentifier() {
let d = new Date();
d = new Date(d.getTime() - d.getTimezoneOffset() * 60000);
let timestamp = d.toISOString().slice(0, -5).replace(/[:T]/g, "-");
return timestamp;
}
/**
* Save the formatted arrays of log files to an sdcard if available
*/
function saveLogs(logArrays) {
if (!logArrays || Object.keys(logArrays).length === 0) {
return Promise.resolve({
logFilenames: [],
logPrefix: ""
});
}
function rejectFunction(message) {
return function(err) {
debug(message + ": " + err);
return Promise.reject(err);
};
}
let sdcardPrefix, dirNameRoot, dirName;
function makeBaseLogsDirectory() {
let sdcardPrefix;
try {
sdcardPrefix = getSdcardPrefix();
dirNameRoot = getLogDirectoryRoot();
dirName = getLogDirectory();
} catch(e) {
// Return promise failed with exception e
// Handles missing sdcard
return Promise.reject(e);
}
debug("making a directory all the way from " + sdcardPrefix + " to " + (sdcardPrefix + "/" + dirNameRoot + "/" + dirName) );
let dirNameRoot = getLogDirectoryRoot();
let logsRoot = OS.Path.join(sdcardPrefix, dirNameRoot);
debug("Creating base log directory at root " + sdcardPrefix);
return OS.File.makeDir(logsRoot, {from: sdcardPrefix}).then(
function() {
debug("First OS.File.makeDir done");
let logsDir = OS.Path.join(logsRoot, dirName);
debug("Creating " + logsDir);
return OS.File.makeDir(logsDir, {ignoreExisting: false}).then(
function() {
debug("Created: " + logsDir);
// Now the directory is guaranteed to exist, save the logs
let logFilenames = [];
let saveRequests = [];
return {
sdcardPrefix: sdcardPrefix,
basePrefix: dirNameRoot
};
}
);
}
debug("Will now traverse logArrays: " + logArrays.length);
function makeLogsDirectory({sdcardPrefix, basePrefix}) {
let dirName = getLogIdentifier();
for (let logLocation in logArrays) {
debug("requesting save of " + logLocation);
let logArray = logArrays[logLocation];
// The filename represents the relative path within the SD card, not the
// absolute path because Gaia will refer to it using the DeviceStorage
// API
let filename = OS.Path.join(dirNameRoot, dirName, getLogFilename(logLocation));
logFilenames.push(filename);
let saveRequest = OS.File.writeAtomic(OS.Path.join(sdcardPrefix, filename), logArray);
saveRequests.push(saveRequest);
}
let logsRoot = OS.Path.join(sdcardPrefix, basePrefix);
let logsDir = OS.Path.join(logsRoot, dirName);
return Promise.all(saveRequests).then(
function() {
debug("returning logfilenames: "+logFilenames.toSource());
return {
logFilenames: logFilenames,
logPrefix: OS.Path.join(dirNameRoot, dirName)
};
}, function(err) {
debug("Error at some save request: " + err);
return Promise.reject(err);
});
}, function(err) {
debug("Error at OS.File.makeDir for " + logsDir + ": " + err);
return Promise.reject(err);
});
}, function(err) {
debug("Error at first OS.File.makeDir: " + err);
return Promise.reject(err);
});
debug("Creating base log directory at root " + sdcardPrefix);
debug("Final created directory will be " + logsDir);
return OS.File.makeDir(logsDir, {ignoreExisting: false}).then(
function() {
debug("Created: " + logsDir);
return {
logPrefix: OS.Path.join(basePrefix, dirName),
sdcardPrefix: sdcardPrefix
};
},
rejectFunction("Error at OS.File.makeDir for " + logsDir)
);
}
function getFile(filename) {
let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
file.initWithPath(filename);
return file;
}
/**
* Make a zip file
* @param {String} absoluteZipFilename - Fully qualified desired location of the zip file
* @param {Map<String, Uint8Array>} logArrays - Map from log location to log data
* @return {Array<String>} Paths of entries in the archive
*/
function makeZipFile(absoluteZipFilename, logArrays) {
let logFilenames = [];
let zipWriter = Cc["@mozilla.org/zipwriter;1"].createInstance(Ci.nsIZipWriter);
let zipFile = getFile(absoluteZipFilename);
zipWriter.open(zipFile, PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE);
for (let logLocation in logArrays) {
let logArray = logArrays[logLocation];
let logFilename = getLogFilename(logLocation);
logFilenames.push(logFilename);
debug("Adding " + logFilename + " to the zip");
let logArrayStream = Cc["@mozilla.org/io/arraybuffer-input-stream;1"]
.createInstance(Ci.nsIArrayBufferInputStream);
// Set data to be copied, default offset to 0 because it is not present on
// ArrayBuffer objects
logArrayStream.setData(logArray.buffer, logArray.byteOffset || 0,
logArray.byteLength);
zipWriter.addEntryStream(logFilename, Date.now(),
Ci.nsIZipWriter.COMPRESSION_DEFAULT,
logArrayStream, false);
}
zipWriter.close();
return logFilenames;
}
function writeLogArchive(logArrays) {
return function({sdcardPrefix, basePrefix}) {
// Now the directory is guaranteed to exist, save the logs into their
// archive file
let zipFilename = getLogIdentifier() + "-logs.zip";
let zipPath = OS.Path.join(basePrefix, zipFilename);
let zipPrefix = OS.Path.dirname(zipPath);
let absoluteZipPath = OS.Path.join(sdcardPrefix, zipPath);
debug("Creating zip file at " + zipPath);
let logFilenames = [];
try {
logFilenames = makeZipFile(absoluteZipPath, logArrays);
} catch(e) {
return Promise.reject(e);
}
debug("Zip file created");
return {
logFilenames: logFilenames,
logPaths: [zipPath],
compressed: true
};
};
}
function writeLogFiles(logArrays) {
return function({sdcardPrefix, logPrefix}) {
// Now the directory is guaranteed to exist, save the logs
let logFilenames = [];
let logPaths = [];
let saveRequests = [];
for (let logLocation in logArrays) {
debug("Requesting save of " + logLocation);
let logArray = logArrays[logLocation];
let logFilename = getLogFilename(logLocation);
// The local pathrepresents the relative path within the SD card, not the
// absolute path because Gaia will refer to it using the DeviceStorage
// API
let localPath = OS.Path.join(logPrefix, logFilename);
logFilenames.push(logFilename);
logPaths.push(localPath);
let absolutePath = OS.Path.join(sdcardPrefix, localPath);
let saveRequest = OS.File.writeAtomic(absolutePath, logArray);
saveRequests.push(saveRequest);
}
return Promise.all(saveRequests).then(
function() {
return {
logFilenames: logFilenames,
logPaths: logPaths,
compressed: false
};
},
rejectFunction("Error at some save request")
);
};
}
LogShake.init();
+92
View File
@@ -0,0 +1,92 @@
/* 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";
this.EXPORTED_SYMBOLS = ["MultiscreenHandler"];
const Ci = Components.interfaces;
const Cu = Components.utils;
Cu.import("resource://gre/modules/Services.jsm");
function debug(aStr) {
// dump("MultiscreenHandler: " + aStr + "\n");
}
let window = Services.wm.getMostRecentWindow("navigator:browser");
// Multi-screen support on b2g. The following implementation will open a new
// top-level window once we receive a display connected event.
let MultiscreenHandler = {
topLevelWindows: new Map(),
init: function init() {
Services.obs.addObserver(this, "display-changed", false);
Services.obs.addObserver(this, "xpcom-shutdown", false);
},
uninit: function uninit() {
Services.obs.removeObserver(this, "display-changed");
Services.obs.removeObserver(this, "xpcom-shutdown");
},
observe: function observe(aSubject, aTopic, aData) {
switch (aTopic) {
case "display-changed":
this.handleDisplayChangeEvent(aSubject);
break
case "xpcom-shutdown":
this.uninit();
break
}
},
openTopLevelWindow: function openTopLevelWindow(aDisplay) {
if (this.topLevelWindows.get(aDisplay.id)) {
debug("Top level window for display id: " + aDisplay.id + " has been opened.");
return;
}
let flags = Services.prefs.getCharPref("toolkit.defaultChromeFeatures") +
",mozDisplayId=" + aDisplay.id;
let remoteShellURL = Services.prefs.getCharPref("b2g.multiscreen.chrome_remote_url") +
"#" + aDisplay.id;
let win = Services.ww.openWindow(null, remoteShellURL, "myTopWindow" + aDisplay.id, flags, null);
this.topLevelWindows.set(aDisplay.id, win);
},
closeTopLevelWindow: function closeTopLevelWindow(aDisplay) {
let win = this.topLevelWindows.get(aDisplay.id);
if (win) {
win.close();
this.topLevelWindows.delete(aDisplay.id);
}
},
handleDisplayChangeEvent: function handleDisplayChangeEvent(aSubject) {
let display = aSubject.QueryInterface(Ci.nsIDisplayInfo);
let name = "multiscreen.enabled";
let req = window.navigator.mozSettings.createLock().get(name);
req.addEventListener("success", () => {
let isMultiscreenEnabled = req.result[name];
if (display.connected) {
if (isMultiscreenEnabled) {
this.openTopLevelWindow(display);
}
} else {
this.closeTopLevelWindow(display);
}
});
},
};
MultiscreenHandler.init();
this.MultiscreenHandler = MultiscreenHandler;
+1 -1
View File
@@ -18,7 +18,7 @@ function log(msg) {
}
#ifdef MOZ_WIDGET_GONK
let librecovery = (function() {
var librecovery = (function() {
let library;
try {
library = ctypes.open("librecovery.so");
+151
View File
@@ -0,0 +1,151 @@
/* 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";
this.EXPORTED_SYMBOLS = ["SafeMode"];
const Cu = Components.utils;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/AppConstants.jsm");
Cu.import("resource://gre/modules/Webapps.jsm");
const kSafeModePref = "b2g.safe_mode";
const kSafeModePage = "safe_mode.html";
function debug(aStr) {
//dump("-*- SafeMode: " + aStr + "\n");
}
// This module is responsible for checking whether we want to start in safe
// mode or not. The flow is as follow:
// - wait for the `b2g.safe_mode` preference to be set to something different
// than `unset` by nsAppShell
// - If it's set to `no`, just start normally.
// - If it's set to `yes`, we load a stripped down system app from safe_mode.html"
// - This page is responsible to dispatch a mozContentEvent to us.
// - If the user choose SafeMode, we disable all add-ons.
// - We go on with startup.
this.SafeMode = {
// Returns a promise that resolves when nsAppShell has set the
// b2g.safe_mode_state_ready preference to `true`.
_waitForPref: function() {
debug("waitForPref");
try {
let currentMode = Services.prefs.getCharPref(kSafeModePref);
debug("current mode: " + currentMode);
if (currentMode !== "unset") {
return Promise.resolve();
}
} catch(e) { debug("No current mode available!"); }
// Wait for the preference to toggle.
return new Promise((aResolve, aReject) => {
let observer = function(aSubject, aTopic, aData) {
if (Services.prefs.getCharPref(kSafeModePref)) {
Services.prefs.removeObserver(kSafeModePref, observer, false);
aResolve();
}
}
Services.prefs.addObserver(kSafeModePref, observer, false);
});
},
// Resolves once the user has decided how to start.
// Note that all the actions happen here, so there is no other action from
// consumers than to go on.
_waitForUser: function() {
debug("waitForUser");
let isSafeMode = Services.prefs.getCharPref(kSafeModePref) === "yes";
if (!isSafeMode) {
return Promise.resolve();
}
debug("Starting in Safe Mode!");
// Load $system_app/safe_mode.html as a full screen iframe, and wait for
// the user to make a choice.
return DOMApplicationRegistry.registryReady.then(() => {
let shell = SafeMode.window.shell;
let document = SafeMode.window.document;
let url = Services.io.newURI(shell.homeURL, null, null)
.resolve(kSafeModePage);
debug("Registry is ready, loading " + url);
let frame = document.createElementNS("http://www.w3.org/1999/xhtml", "html:iframe");
frame.setAttribute("mozbrowser", "true");
frame.setAttribute("mozapp", shell.manifestURL);
frame.setAttribute("id", "systemapp"); // To keep screen.js happy.
let contentBrowser = document.body.appendChild(frame);
return new Promise((aResolve, aReject) => {
let content = contentBrowser.contentWindow;
// Stripped down version of the system app bootstrap.
function handleEvent(e) {
switch(e.type) {
case "mozbrowserloadstart":
if (content.document.location == "about:blank") {
contentBrowser.addEventListener("mozbrowserlocationchange", handleEvent, true);
contentBrowser.removeEventListener("mozbrowserloadstart", handleEvent, true);
return;
}
notifyContentStart();
break;
case "mozbrowserlocationchange":
if (content.document.location == "about:blank") {
return;
}
contentBrowser.removeEventListener("mozbrowserlocationchange", handleEvent, true);
notifyContentStart();
break;
case "mozContentEvent":
content.removeEventListener("mozContentEvent", handleEvent, true);
contentBrowser.parentNode.removeChild(contentBrowser);
if (e.detail == "safemode-yes") {
// Really starting in safe mode, let's disable add-ons first.
DOMApplicationRegistry.disableAllAddons().then(aResolve);
} else {
aResolve();
}
break;
}
}
function notifyContentStart() {
let window = SafeMode.window;
window.shell.sendEvent(window, "SafeModeStart");
contentBrowser.setVisible(true);
// browser-ui-startup-complete is used by the AppShell to stop the
// boot animation and start gecko rendering.
Services.obs.notifyObservers(null, "browser-ui-startup-complete", "");
content.addEventListener("mozContentEvent", handleEvent, true);
}
contentBrowser.addEventListener("mozbrowserloadstart", handleEvent, true);
contentBrowser.src = url;
});
});
},
// Returns a Promise that resolves once we have decided to run in safe mode
// or not. All the safe mode switching actions happen before resolving the
// promise.
check: function(aWindow) {
debug("check");
this.window = aWindow;
if (AppConstants.platform !== "gonk") {
// For now we only have gonk support.
return Promise.resolve();
}
return this._waitForPref().then(this._waitForUser);
}
}
+1 -1
View File
@@ -12,7 +12,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "SystemAppProxy", "resource://gre/module
this.EXPORTED_SYMBOLS = ['Screenshot'];
let Screenshot = {
var Screenshot = {
get: function screenshot_get() {
let systemAppFrame = SystemAppProxy.getFrame();
let window = systemAppFrame.ownerDocument.defaultView;
+1 -1
View File
@@ -94,7 +94,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "SystemAppProxy",
// There's no point in setting up an observer to monitor the pref, as b2g prefs
// can only be overwritten when the profie is recreated. So just get the value
// on start-up.
let kPersonaUri = "https://firefoxos.persona.org";
var kPersonaUri = "https://firefoxos.persona.org";
try {
kPersonaUri = Services.prefs.getCharPref("toolkit.identity.uri");
} catch(noSuchPref) {
+3 -3
View File
@@ -2,8 +2,8 @@
* 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/. */
let Ci = Components.interfaces;
let Cu = Components.utils;
var Ci = Components.interfaces;
var Cu = Components.utils;
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
Cu.import('resource://gre/modules/Services.jsm');
@@ -12,7 +12,7 @@ Cu.import('resource://gre/modules/DOMRequestHelper.jsm');
XPCOMUtils.defineLazyModuleGetter(this, 'GlobalSimulatorScreen',
'resource://gre/modules/GlobalSimulatorScreen.jsm');
let DEBUG_PREFIX = 'SimulatorScreen.js - ';
var DEBUG_PREFIX = 'SimulatorScreen.js - ';
function debug() {
//dump(DEBUG_PREFIX + Array.slice(arguments) + '\n');
}
+48 -7
View File
@@ -11,10 +11,12 @@ Cu.import('resource://gre/modules/Services.jsm');
this.EXPORTED_SYMBOLS = ['SystemAppProxy'];
let SystemAppProxy = {
var SystemAppProxy = {
_frame: null,
_isLoaded: false,
_isReady: false,
_pendingEvents: [],
_pendingLoadedEvents: [],
_pendingReadyEvents: [],
_pendingListeners: [],
// To call when a new system app iframe is created
@@ -34,18 +36,38 @@ let SystemAppProxy = {
return this._frame;
},
// To call when the load event of the System app document is triggered.
// i.e. everything that is not lazily loaded are run and done.
setIsLoaded: function () {
if (this._isLoaded) {
Cu.reportError('SystemApp has already been declared as being loaded.');
}
this._isLoaded = true;
// Dispatch all events being queued while the system app was still loading
this._pendingLoadedEvents
.forEach(([type, details]) =>
this._sendCustomEvent(type, details, true));
this._pendingLoadedEvents = [];
},
// To call when it is ready to receive events
// i.e. when system-message-listener-ready mozContentEvent is sent.
setIsReady: function () {
if (!this._isLoaded) {
Cu.reportError('SystemApp.setIsLoaded() should be called before setIsReady().');
}
if (this._isReady) {
Cu.reportError('SystemApp has already been declared as being ready.');
}
this._isReady = true;
// Dispatch all events being queued while the system app was still loading
this._pendingEvents
// Dispatch all events being queued while the system app was still not ready
this._pendingReadyEvents
.forEach(([type, details]) =>
this._sendCustomEvent(type, details));
this._pendingEvents = [];
this._pendingReadyEvents = [];
},
/*
@@ -62,6 +84,9 @@ let SystemAppProxy = {
* @param details The event details.
* @param noPending Set to true to emit this event even before the system
* app is ready.
* Event is always pending if the app is not loaded yet.
*
* @returns event? Dispatched event, or null if the event is pending.
*/
_sendCustomEvent: function systemApp_sendCustomEvent(type,
details,
@@ -69,10 +94,22 @@ let SystemAppProxy = {
target) {
let content = this._frame ? this._frame.contentWindow : null;
// If the system app isn't loaded yet,
// queue events until someone calls setIsLoaded
if (!content || !this._isLoaded) {
if (noPending) {
this._pendingLoadedEvents.push([type, details]);
} else {
this._pendingReadyEvents.push([type, details]);
}
return null;
}
// If the system app isn't ready yet,
// queue events until someone calls setIsReady
if (!content || (!this._isReady && !noPending)) {
this._pendingEvents.push([type, details]);
if (!this._isReady && !noPending) {
this._pendingReadyEvents.push([type, details]);
return null;
}
@@ -87,6 +124,10 @@ let SystemAppProxy = {
payload = details ? Cu.cloneInto(details, content) : {};
}
if ((target || content) === this._frame.contentWindow) {
dump('XXX FIXME : Dispatch a ' + type + ': ' + details.type + "\n");
}
event.initCustomEvent(type, true, false, payload);
(target || content).dispatchEvent(event);
+1 -1
View File
@@ -15,7 +15,7 @@ Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/WebappsUpdater.jsm");
const VERBOSE = 1;
let log =
var log =
VERBOSE ?
function log_dump(msg) { dump("UpdatePrompt: "+ msg +"\n"); } :
function log_noop(msg) { };
+7
View File
@@ -18,6 +18,7 @@ EXTRA_COMPONENTS += [
'FxAccountsUIGlue.js',
'HelperAppDialog.js',
'InterAppCommUIGlue.js',
'KillSwitch.js',
'MailtoProtocolHandler.js',
'MobileIdentityUIGlue.js',
'OMAContentHandler.js',
@@ -65,7 +66,9 @@ EXTRA_JS_MODULES += [
'LogCapture.jsm',
'LogParser.jsm',
'LogShake.jsm',
'MultiscreenHandler.jsm',
'OrientationChangeHandler.jsm',
'SafeMode.jsm',
'Screenshot.jsm',
'SignInToWebsite.jsm',
'SystemAppProxy.jsm',
@@ -73,6 +76,10 @@ EXTRA_JS_MODULES += [
'WebappsUpdater.jsm',
]
EXTRA_PP_JS_MODULES += [
'KillSwitchMain.jsm'
]
if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'gonk':
EXTRA_JS_MODULES += [
'GlobalSimulatorScreen.jsm'
@@ -0,0 +1,67 @@
let Ci = Components.interfaces;
let Cc = Components.classes;
let Cu = Components.utils;
// Stolen from SpecialPowers, since at this point we don't know we're in a test.
let isMainProcess = function() {
try {
return Cc["@mozilla.org/xre/app-info;1"]
.getService(Ci.nsIXULRuntime)
.processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
} catch (e) { }
return true;
};
var fakeLibcUtils = {
_props_: {},
property_set: function(name, value) {
dump("property_set('" + name + "', '" + value+ "' [" + (typeof value) + "]);\n");
this._props_[name] = value;
},
property_get: function(name, defaultValue) {
dump("property_get('" + name + "', '" + defaultValue+ "');\n");
if (Object.keys(this._props_).indexOf(name) !== -1) {
return this._props_[name];
} else {
return defaultValue;
}
}
};
var kUserValues;
function installUserValues(next) {
var fakeValues = {
settings: {
"lockscreen.locked": false,
"lockscreen.lock-immediately": false
},
prefs: {
"b2g.killswitch.test": false
},
properties: {
"dalvik.vm.heapmaxfree": "32m",
"dalvik.vm.isa.arm.features": "fdiv",
"dalvik.vm.lockprof.threshold": "5000",
"net.bt.name": "BTAndroid",
"dalvik.vm.stack-trace-file": "/data/anr/stack-traces.txt"
}
};
OS.File.writeAtomic(kUserValues,
JSON.stringify(fakeValues)).then(() => {
next();
});
}
if (isMainProcess()) {
Cu.import("resource://gre/modules/SettingsRequestManager.jsm");
Cu.import("resource://gre/modules/osfile.jsm");
Cu.import("resource://gre/modules/KillSwitchMain.jsm");
kUserValues = OS.Path.join(OS.Constants.Path.profileDir, "killswitch.json");
installUserValues(() => {
KillSwitchMain._libcutils = fakeLibcUtils;
});
}
+149
View File
@@ -0,0 +1,149 @@
/* 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";
function setupSettings(target) {
ok((Object.keys(initialSettingsValues).length > 0), "Has at least one setting");
Object.keys(initialSettingsValues).forEach(k => {
ok(Object.keys(target).indexOf(k) !== -1, "Same settings set");
});
var lock = navigator.mozSettings.createLock();
lock.set(initialSettingsValues);
}
function testSettingsInitial(next) {
var promises = [];
for (var setting in initialSettingsValues) {
promises.push(navigator.mozSettings.createLock().get(setting));
}
Promise.all(promises).then(values => {
values.forEach(set => {
var key = Object.keys(set)[0];
var value = set[key];
is(value, initialSettingsValues[key], "Value of " + key + " is initial one");
});
next();
});
}
function testSettingsExpected(target, next) {
var promises = [];
for (var setting in initialSettingsValues) {
promises.push(navigator.mozSettings.createLock().get(setting));
}
Promise.all(promises).then(values => {
values.forEach(set => {
var key = Object.keys(set)[0];
var value = set[key];
is(value, target[key], "Value of " + key + " is expected one");
});
next();
});
}
function testSetPrefValue(prefName, prefValue) {
switch (typeof prefValue) {
case "boolean":
SpecialPowers.setBoolPref(prefName, prefValue);
break;
case "number":
SpecialPowers.setIntPref(prefName, prefValue);
break;
case "string":
SpecialPowers.setCharPref(prefName, prefValue);
break;
default:
is(false, "Unexpected pref type");
break;
}
}
function testGetPrefValue(prefName, prefValue) {
var rv = undefined;
switch (typeof prefValue) {
case "boolean":
rv = SpecialPowers.getBoolPref(prefName);
break;
case "number":
rv = SpecialPowers.getIntPref(prefName);
break;
case "string":
rv = SpecialPowers.getCharPref(prefName);
break;
default:
is(false, "Unexpected pref type");
break;
}
return rv;
}
function setupPrefs(target) {
ok((Object.keys(initialPrefsValues).length > 0), "Has at least one pref");
Object.keys(initialPrefsValues).forEach(k => {
ok(Object.keys(target).indexOf(k) !== -1, "Same pref set");
});
Object.keys(initialPrefsValues).forEach(key => {
testSetPrefValue(key, initialPrefsValues[key]);
});
}
function testPrefsInitial() {
Object.keys(initialPrefsValues).forEach(key => {
var value = testGetPrefValue(key, initialPrefsValues[key]);
is(value, initialPrefsValues[key], "Value of " + key + " is initial one");
});
}
function testPrefsExpected(target) {
Object.keys(target).forEach(key => {
var value = testGetPrefValue(key, target[key]);
is(value, target[key], "Value of " + key + " is initial one");
});
}
function finish() {
SpecialPowers.removePermission("killswitch", document);
SimpleTest.finish();
}
function addPermissions() {
if (SpecialPowers.hasPermission("killswitch", document)) {
startTests();
} else {
var allow = SpecialPowers.Ci.nsIPermissionManager.ALLOW_ACTION;
[ "killswitch", "settings-api-read", "settings-api-write",
"settings-read", "settings-write", "settings-clear"
].forEach(perm => {
SpecialPowers.addPermission(perm, allow, document);
});
window.location.reload();
}
}
function loadSettings() {
var url = SimpleTest.getTestFileURL("file_loadserver.js");
var script = SpecialPowers.loadChromeScript(url);
}
function addPrefs() {
SpecialPowers.pushPrefEnv({"set": [
["dom.ignore_webidl_scope_checks", true],
["dom.mozKillSwitch.enabled", true],
]}, addPermissions);
}
+13 -1
View File
@@ -1,5 +1,4 @@
[DEFAULT]
skip-if = toolkit != "gonk"
support-files =
permission_handler_chrome.js
SandboxPromptTest.html
@@ -8,14 +7,27 @@ support-files =
systemapp_helper.js
presentation_prompt_handler_chrome.js
presentation_ui_glue_handler_chrome.js
file_loadserver.js
killswitch.js
[test_filepicker_path.html]
skip-if = toolkit != "gonk"
[test_permission_deny.html]
skip-if = toolkit != "gonk"
[test_permission_gum_remember.html]
skip-if = true # Bug 1019572 - frequent timeouts
[test_sandbox_permission.html]
skip-if = toolkit != "gonk"
[test_screenshot.html]
skip-if = toolkit != "gonk"
[test_systemapp.html]
skip-if = toolkit != "gonk"
[test_presentation_device_prompt.html]
skip-if = toolkit != "gonk"
[test_permission_visibilitychange.html]
skip-if = toolkit != "gonk"
[test_presentation_request_ui_glue.html]
skip-if = toolkit != "gonk"
[test_killswitch_basics.html]
[test_killswitch_disable.html]
[test_killswitch_enable.html]
@@ -3,14 +3,14 @@ const Cu = Components.utils;
const { Services } = Cu.import("resource://gre/modules/Services.jsm");
// Load a duplicated copy of the jsm to prevent messing with the currently running one
let scope = {};
var scope = {};
Services.scriptloader.loadSubScript("resource://gre/modules/SystemAppProxy.jsm", scope);
const { SystemAppProxy } = scope;
let frame;
let customEventTarget;
var frame;
var customEventTarget;
let index = -1;
var index = -1;
function next() {
index++;
if (index >= steps.length) {
@@ -26,11 +26,12 @@ function next() {
// Listen for events received by the system app document
// to ensure that we receive all of them, in an expected order and time
let isLoaded = false;
let n = 0;
var isLoaded = false;
var isReady = false;
var n = 0;
function listener(event) {
if (!isLoaded) {
assert.ok(false, "Received event before the iframe is ready");
assert.ok(false, "Received event before the iframe is loaded");
return;
}
n++;
@@ -41,16 +42,34 @@ function listener(event) {
assert.equal(event.type, "custom");
assert.equal(event.detail.name, "second");
next(); // call checkEventDispatching
next(); // call checkEventPendingBeforeLoad
} else if (n == 3) {
if (!isReady) {
assert.ok(false, "Received event before the iframe is loaded");
return;
}
assert.equal(event.type, "custom");
assert.equal(event.detail.name, "third");
} else if (n == 4) {
if (!isReady) {
assert.ok(false, "Received event before the iframe is loaded");
return;
}
assert.equal(event.type, "mozChromeEvent");
assert.equal(event.detail.name, "fourth");
next(); // call checkEventDispatching
} else if (n == 5) {
assert.equal(event.type, "custom");
assert.equal(event.detail.name, "fifth");
} else if (n === 6) {
assert.equal(event.type, "mozChromeEvent");
assert.equal(event.detail.name, "sixth");
} else if (n === 7) {
assert.equal(event.type, "custom");
assert.equal(event.detail.name, "seventh");
assert.equal(event.target, customEventTarget);
next(); // call checkEventListening();
@@ -60,7 +79,7 @@ function listener(event) {
}
let steps = [
var steps = [
function waitForWebapps() {
// We are using webapps API later in this test and we need to ensure
// it is fully initialized before trying to use it
@@ -72,8 +91,8 @@ let steps = [
function earlyEvents() {
// Immediately try to send events
SystemAppProxy.dispatchEvent({ name: "first" });
SystemAppProxy._sendCustomEvent("custom", { name: "second" });
SystemAppProxy._sendCustomEvent("mozChromeEvent", { name: "first" }, true);
SystemAppProxy._sendCustomEvent("custom", { name: "second" }, true);
next();
},
@@ -110,7 +129,7 @@ let steps = [
// Declare that the iframe is now loaded.
// That should dispatch early events
isLoaded = true;
SystemAppProxy.setIsReady();
SystemAppProxy.setIsLoaded();
assert.ok(true, "Frame declared as loaded");
let gotFrame = SystemAppProxy.getFrame();
@@ -123,13 +142,24 @@ let steps = [
frame.setAttribute("src", "data:text/html,system app");
},
function checkEventPendingBeforeLoad() {
// Frame is loaded but not ready,
// these events should queue before the System app is ready.
SystemAppProxy._sendCustomEvent("custom", { name: "third" });
SystemAppProxy.dispatchEvent({ name: "fourth" });
isReady = true;
SystemAppProxy.setIsReady();
// Once this 4th event is received, we will run checkEventDispatching
},
function checkEventDispatching() {
// Send events after the iframe is ready,
// they should be dispatched right away
SystemAppProxy._sendCustomEvent("custom", { name: "third" });
SystemAppProxy.dispatchEvent({ name: "fourth" });
SystemAppProxy._sendCustomEvent("custom", { name: "fifth" }, false, customEventTarget);
// Once this 5th event is received, we will run checkEventListening
SystemAppProxy._sendCustomEvent("custom", { name: "fifth" });
SystemAppProxy.dispatchEvent({ name: "sixth" });
SystemAppProxy._sendCustomEvent("custom", { name: "seventh" }, false, customEventTarget);
// Once this 7th event is received, we will run checkEventListening
},
function checkEventListening() {
@@ -0,0 +1,92 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Enabling of killswitch feature</title>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js">
</script>
</head>
<body>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">
<script type="application/javascript">
"use strict";
function testNotExposed(check) {
ok(!("mozKillSwitch" in navigator),
"mozKillSwitch not exposed in navigator: " + check);
}
function testIsExposed() {
ok(("mozKillSwitch" in navigator), "mozKillSwitch is exposed in navigator");
ok(("enable" in navigator.mozKillSwitch), "mozKillSwitch has |enable|");
ok(("disable" in navigator.mozKillSwitch), "mozKillSwitch has |disable|");
}
function continueTests() {
// Now we should have it!
testIsExposed();
finish();
}
function finish() {
SpecialPowers.removePermission("killswitch", document);
SimpleTest.finish();
}
function alreadyHasPermission() {
return SpecialPowers.hasPermission("killswitch", document);
}
function addPermission() {
var allow = SpecialPowers.Ci.nsIPermissionManager.ALLOW_ACTION;
SpecialPowers.addPermission("killswitch", allow, document);
window.location.reload();
}
function addPrefIgnoreWebIDL(next) {
SpecialPowers.pushPrefEnv({"set": [
["dom.ignore_webidl_scope_checks", true],
]}, next);
}
function addPrefKillSwitch(next) {
SpecialPowers.pushPrefEnv({"set": [
["dom.mozKillSwitch.enabled", true],
]}, next);
}
function startTests() {
if (alreadyHasPermission()) {
continueTests();
} else {
// Make sure it's not exposed
testNotExposed("webidl, pref, perm");
// Expose certified APIs
addPrefIgnoreWebIDL(() => {
// Still not exposed because not perm and pref
testNotExposed("pref, perm");
// Add the kill switch pref
addPrefKillSwitch(() => {
// Still not exposed because not perm
testNotExposed("perm");
// Will reload the page
addPermission();
});
});
}
}
SimpleTest.waitForExplicitFinish();
startTests();
</script>
</pre>
</body>
</html>
@@ -0,0 +1,65 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Disabling of killswitch feature</title>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js">
</script>
<script type="application/javascript" src="killswitch.js"></script>
</head>
<body>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">
<script type="application/javascript">
"use strict";
var initialSettingsValues = {
"lockscreen.locked": true,
"lockscreen.lock-immediately": true
};
var initialPrefsValues = {
"b2g.killswitch.test": true
};
var disabledSettingsExpected = {
"lockscreen.locked": false,
"lockscreen.lock-immediately": false
};
var disabledPrefsExpected = {
"b2g.killswitch.test": false
};
function testDoAction() {
return navigator.mozKillSwitch.disable();
}
function startTests() {
setupSettings(disabledSettingsExpected);
setupPrefs(initialPrefsValues);
testSettingsInitial(() => {
testPrefsInitial();
testDoAction().then(() => {
testSettingsExpected(disabledSettingsExpected, () => {
testPrefsExpected(disabledPrefsExpected);
finish();
});
}).catch(() => {
ok(false, "KillSwitch promise failed");
finish();
});
});
}
SimpleTest.waitForExplicitFinish();
loadSettings();
addPrefs();
</script>
</pre>
</body>
</html>
@@ -0,0 +1,65 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Enabling of killswitch feature</title>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js">
</script>
<script type="application/javascript" src="killswitch.js"></script>
</head>
<body>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">
<script type="application/javascript">
"use strict";
var initialSettingsValues = {
"lockscreen.locked": false,
"lockscreen.lock-immediately": false
};
var initialPrefsValues = {
"b2g.killswitch.test": false
};
var enabledSettingsExpected = {
"lockscreen.locked": true,
"lockscreen.lock-immediately": true
};
var enabledPrefsExpected = {
"b2g.killswitch.test": true
};
function testDoAction() {
return navigator.mozKillSwitch.enable();
}
function startTests() {
setupSettings(enabledSettingsExpected);
setupPrefs(initialPrefsValues);
testSettingsInitial(() => {
testPrefsInitial();
testDoAction().then(() => {
testSettingsExpected(enabledSettingsExpected, () => {
testPrefsExpected(enabledPrefsExpected);
finish();
});
}).catch(() => {
ok(false, "KillSwitch promise failed");
finish();
});
});
}
SimpleTest.waitForExplicitFinish();
loadSettings();
addPrefs();
</script>
</pre>
</body>
</html>
+425
View File
@@ -0,0 +1,425 @@
/* 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 {classes: Cc, interfaces: Ci, utils: Cu} = Components;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/NetUtil.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm");
let kUserValues;
function run_test() {
do_get_profile();
Cu.import("resource://gre/modules/KillSwitchMain.jsm");
check_enabledValues();
run_next_test();
}
function check_enabledValues() {
let expected = {
settings: {
"debugger.remote-mode": "disabled",
"developer.menu.enabled": false,
"devtools.unrestricted": false,
"lockscreen.enabled": true,
"lockscreen.locked": true,
"lockscreen.lock-immediately": true,
"tethering.usb.enabled": false,
"tethering.wifi.enabled": false,
"ums.enabled": false
},
prefs: {
"b2g.killswitch.test": true
},
properties: {
"persist.sys.usb.config": "none"
},
services: {
"adbd": "stop"
}
};
for (let key of Object.keys(KillSwitchMain._enabledValues.settings)) {
strictEqual(expected.settings[key],
KillSwitchMain._enabledValues.settings[key],
"setting " + key);
}
for (let key of Object.keys(KillSwitchMain._enabledValues.prefs)) {
strictEqual(expected.prefs[key],
KillSwitchMain._enabledValues.prefs[key],
"pref " + key);
}
for (let key of Object.keys(KillSwitchMain._enabledValues.properties)) {
strictEqual(expected.properties[key],
KillSwitchMain._enabledValues.properties[key],
"proprety " + key);
}
for (let key of Object.keys(KillSwitchMain._enabledValues.services)) {
strictEqual(expected.services[key],
KillSwitchMain._enabledValues.services[key],
"service " + key);
}
}
add_test(function test_prepareTestValues() {
if (("adbd" in KillSwitchMain._enabledValues.services)) {
strictEqual(KillSwitchMain._enabledValues.services["adbd"], "stop");
// We replace those for the test because on Gonk we will loose the control
KillSwitchMain._enabledValues.settings = {
"lockscreen.locked": true,
"lockscreen.lock-immediately": true
};
KillSwitchMain._enabledValues.services = {};
KillSwitchMain._enabledValues.properties = {
"dalvik.vm.heapmaxfree": "8m",
"dalvik.vm.isa.arm.features": "div",
"dalvik.vm.lockprof.threshold": "500",
"net.bt.name": "Android",
"dalvik.vm.stack-trace-file": "/data/anr/traces.txt"
}
}
kUserValues = OS.Path.join(OS.Constants.Path.profileDir, "killswitch.json");
run_next_test();
});
function reset_status() {
KillSwitchMain._ksState = undefined;
KillSwitchMain._libcutils.property_set("persist.moz.killswitch", "undefined");
}
function install_common_tests() {
add_test(function test_readStateProperty() {
KillSwitchMain._libcutils.property_set("persist.moz.killswitch", "false");
KillSwitchMain.readStateProperty();
strictEqual(KillSwitchMain._ksState, false);
KillSwitchMain._libcutils.property_set("persist.moz.killswitch", "true");
KillSwitchMain.readStateProperty();
strictEqual(KillSwitchMain._ksState, true);
run_next_test();
});
add_test(function test_writeStateProperty() {
KillSwitchMain._ksState = false;
KillSwitchMain.writeStateProperty();
let state = KillSwitchMain._libcutils.property_get("persist.moz.killswitch");
strictEqual(state, "false");
KillSwitchMain._ksState = true;
KillSwitchMain.writeStateProperty();
state = KillSwitchMain._libcutils.property_get("persist.moz.killswitch");
strictEqual(state, "true");
run_next_test();
});
add_test(function test_doEnable() {
reset_status();
let enable = KillSwitchMain.doEnable();
ok(enable, "should have a Promise");
enable.then(() => {
strictEqual(KillSwitchMain._ksState, true);
let state = KillSwitchMain._libcutils.property_get("persist.moz.killswitch");
strictEqual(state, "true");
run_next_test();
}).catch(err => {
ok(false, "should have succeeded");
run_next_test();
});
});
add_test(function test_enableMessage() {
reset_status();
let listener = {
assertPermission: function() {
return true;
},
sendAsyncMessage: function(name, data) {
strictEqual(name, "KillSwitch:Enable:OK");
strictEqual(data.requestID, 1);
let state = KillSwitchMain._libcutils.property_get("persist.moz.killswitch");
strictEqual(state, "true");
run_next_test();
}
};
KillSwitchMain.receiveMessage({
name: "KillSwitch:Enable",
target: listener,
data: {
requestID: 1
}
});
});
add_test(function test_saveUserValues_prepare() {
reset_status();
OS.File.exists(kUserValues).then(e => {
if (e) {
OS.File.remove(kUserValues).then(() => {
run_next_test();
}).catch(err => {
ok(false, "should have succeeded");
run_next_test();
});
} else {
run_next_test();
}
}).catch(err => {
ok(false, "should have succeeded");
run_next_test();
});
});
add_test(function test_saveUserValues() {
reset_status();
let expectedValues = Object.assign({}, KillSwitchMain._enabledValues);
// Reset _enabledValues so we check properly that the dumped state
// is the current device state
KillSwitchMain._enabledValues = {
settings: {
"lockscreen.locked": false,
"lockscreen.lock-immediately": false
},
prefs: {
"b2g.killswitch.test": false
},
properties: {
"dalvik.vm.heapmaxfree": "32m",
"dalvik.vm.isa.arm.features": "fdiv",
"dalvik.vm.lockprof.threshold": "5000",
"net.bt.name": "BTAndroid",
"dalvik.vm.stack-trace-file": "/data/anr/stack-traces.txt"
},
services: {}
};
KillSwitchMain.saveUserValues().then(e => {
ok(e, "should have succeeded");
OS.File.read(kUserValues, { encoding: "utf-8" }).then(content => {
let obj = JSON.parse(content);
deepEqual(obj.settings, expectedValues.settings);
notDeepEqual(obj.settings, KillSwitchMain._enabledValues.settings);
deepEqual(obj.prefs, expectedValues.prefs);
notDeepEqual(obj.prefs, KillSwitchMain._enabledValues.prefs);
deepEqual(obj.properties, expectedValues.properties);
notDeepEqual(obj.properties, KillSwitchMain._enabledValues.properties);
run_next_test();
}).catch(err => {
ok(false, "should have succeeded");
run_next_test();
});
}).catch(err => {
ok(false, "should have succeeded");
run_next_test();
});
});
add_test(function test_saveUserValues_cleaup() {
reset_status();
OS.File.exists(kUserValues).then(e => {
if (e) {
OS.File.remove(kUserValues).then(() => {
ok(true, "should have had a file");
run_next_test();
}).catch(err => {
ok(false, "should have succeeded");
run_next_test();
});
} else {
ok(false, "should have had a file");
run_next_test();
}
}).catch(err => {
ok(false, "should have succeeded");
run_next_test();
});
});
add_test(function test_restoreUserValues_prepare() {
reset_status();
let fakeValues = {
settings: {
"lockscreen.locked": false,
"lockscreen.lock-immediately": false
},
prefs: {
"b2g.killswitch.test": false
},
properties: {
"dalvik.vm.heapmaxfree": "32m",
"dalvik.vm.isa.arm.features": "fdiv",
"dalvik.vm.lockprof.threshold": "5000",
"net.bt.name": "BTAndroid",
"dalvik.vm.stack-trace-file": "/data/anr/stack-traces.txt"
}
};
OS.File.exists(kUserValues).then(e => {
if (!e) {
OS.File.writeAtomic(kUserValues,
JSON.stringify(fakeValues)).then(() => {
ok(true, "success writing file");
run_next_test();
}, err => {
ok(false, "error writing file");
run_next_test();
});
} else {
ok(false, "file should not have been there");
run_next_test();
}
}).catch(err => {
ok(false, "should have succeeded");
run_next_test();
});
});
add_test(function test_restoreUserValues() {
reset_status();
KillSwitchMain.restoreUserValues().then(e => {
ok(e, "should have succeeded");
strictEqual(e.settings["lockscreen.locked"], false);
strictEqual(e.settings["lockscreen.lock-immediately"], false);
strictEqual(e.prefs["b2g.killswitch.test"], false);
strictEqual(
e.properties["dalvik.vm.heapmaxfree"],
"32m");
strictEqual(
e.properties["dalvik.vm.isa.arm.features"],
"fdiv");
strictEqual(
e.properties["dalvik.vm.lockprof.threshold"],
"5000");
strictEqual(
e.properties["net.bt.name"],
"BTAndroid");
strictEqual(
e.properties["dalvik.vm.stack-trace-file"],
"/data/anr/stack-traces.txt");
strictEqual(
KillSwitchMain._libcutils.property_get("dalvik.vm.heapmaxfree"),
"32m");
strictEqual(
KillSwitchMain._libcutils.property_get("dalvik.vm.isa.arm.features"),
"fdiv");
strictEqual(
KillSwitchMain._libcutils.property_get("dalvik.vm.lockprof.threshold"),
"5000");
strictEqual(
KillSwitchMain._libcutils.property_get("net.bt.name"),
"BTAndroid");
strictEqual(
KillSwitchMain._libcutils.property_get("dalvik.vm.stack-trace-file"),
"/data/anr/stack-traces.txt");
run_next_test();
}).catch(err => {
ok(false, "should not have had an error");
run_next_test();
});
});
add_test(function test_doDisable() {
reset_status();
let disable = KillSwitchMain.doDisable()
ok(disable, "should have a Promise");
disable.then(() => {
strictEqual(KillSwitchMain._ksState, false);
let state = KillSwitchMain._libcutils.property_get("persist.moz.killswitch");
strictEqual(state, "false");
run_next_test();
}).catch(err => {
ok(false, "should have succeeded");
run_next_test();
});
});
add_test(function test_disableMessage() {
reset_status();
let listener = {
assertPermission: function() {
return true;
},
sendAsyncMessage: function(name, data) {
strictEqual(name, "KillSwitch:Disable:OK");
strictEqual(data.requestID, 2);
let state = KillSwitchMain._libcutils.property_get("persist.moz.killswitch");
strictEqual(state, "false");
run_next_test();
}
};
KillSwitchMain.receiveMessage({
name: "KillSwitch:Disable",
target: listener,
data: {
requestID: 2
}
});
});
add_test(function test_doEnable_only_once() {
reset_status();
let firstEnable = KillSwitchMain.doEnable();
ok(firstEnable, "should have a first Promise");
firstEnable.then(() => {
let secondEnable = KillSwitchMain.doEnable();
ok(secondEnable, "should have a second Promise");
secondEnable.then(() => {
ok(false, "second enable should have not succeeded");
run_next_test();
}).catch(err => {
strictEqual(err, true, "second enable should reject(true);");
run_next_test();
});
}).catch(err => {
ok(false, "first enable should have succeeded");
run_next_test();
});
});
}
@@ -0,0 +1,58 @@
/**
* Boostrap LogShake's tests that need gonk support.
* This is creating a fake sdcard for LogShake tests and importing LogShake and
* osfile
*/
/* jshint moz: true */
/* global Components, LogCapture, LogShake, ok, add_test, run_next_test, dump,
do_get_profile, OS, volumeService, equal, XPCOMUtils */
/* exported setup_logshake_mocks */
/* disable use strict warning */
/* jshint -W097 */
"use strict";
const Cu = Components.utils;
const Ci = Components.interfaces;
const Cc = Components.classes;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/osfile.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "volumeService",
"@mozilla.org/telephony/volume-service;1",
"nsIVolumeService");
let sdcard;
function setup_logshake_mocks() {
do_get_profile();
setup_fs();
}
function setup_fs() {
OS.File.makeDir("/data/local/tmp/sdcard/", {from: "/data"}).then(function() {
setup_sdcard();
});
}
function setup_sdcard() {
let volName = "sdcard";
let mountPoint = "/data/local/tmp/sdcard";
volumeService.createFakeVolume(volName, mountPoint);
let vol = volumeService.getVolumeByName(volName);
ok(vol, "volume shouldn't be null");
equal(volName, vol.name, "name");
equal(Ci.nsIVolume.STATE_MOUNTED, vol.state, "state");
ensure_sdcard();
}
function ensure_sdcard() {
sdcard = volumeService.getVolumeByName("sdcard").mountPoint;
ok(sdcard, "Should have a valid sdcard mountpoint");
run_next_test();
}
+108
View File
@@ -0,0 +1,108 @@
/* 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 {results: Cr} = Components;
// Trivial test just to make sure we have no syntax error
add_test(function test_ksm_ok() {
ok(KillSwitchMain, "KillSwitchMain object exists");
run_next_test();
});
let aMessageNoPerm = {
name: "KillSwitch:Enable",
target: {
assertPermission: function() {
return false;
},
killChild: function() { }
}
};
let aMessageWithPerm = {
name: "KillSwitch:Enable",
target: {
assertPermission: function() {
return true;
}
},
data: {
requestID: 0
}
};
add_test(function test_sendMessageWithoutPerm() {
try {
KillSwitchMain.receiveMessage(aMessageNoPerm);
ok(false, "Should have failed");
} catch (ex) {
// strictEqual(ex, Cr.NS_ERROR_NOT_AVAILABLE);
}
run_next_test();
});
add_test(function test_sendMessageWithPerm() {
let rv = KillSwitchMain.receiveMessage(aMessageWithPerm);
strictEqual(rv, undefined);
run_next_test();
});
let uMessage = {
name: "KillSwitch:WTF",
target: {
assertPermission: function() {
return true;
},
killChild: function() { }
}
};
add_test(function test_sendUnknownMessage() {
try {
KillSwitchMain.receiveMessage(uMessage);
ok(false, "Should have failed");
} catch (ex) {
strictEqual(ex, Cr.NS_ERROR_ILLEGAL_VALUE);
}
run_next_test();
});
let fakeLibcUtils = {
_props_: {},
property_set: function(name, value) {
dump("property_set('" + name + "', '" + value+ "' [" + (typeof value) + "]);\n");
this._props_[name] = value;
},
property_get: function(name, defaultValue) {
dump("property_get('" + name + "', '" + defaultValue+ "');\n");
if (Object.keys(this._props_).indexOf(name) !== -1) {
return this._props_[name];
} else {
return defaultValue;
}
}
};
add_test(function test_nolibcutils() {
KillSwitchMain._libcutils = null;
try {
KillSwitchMain.checkLibcUtils();
ok(false, "Should have failed");
} catch (ex) {
strictEqual(ex, Cr.NS_ERROR_NO_INTERFACE);
}
run_next_test();
});
add_test(function test_install_fakelibcutils() {
KillSwitchMain._libcutils = fakeLibcUtils;
let rv = KillSwitchMain.checkLibcUtils();
strictEqual(rv, true);
run_next_test();
});
install_common_tests();
@@ -0,0 +1,42 @@
/* 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/. */
Cu.import("resource://gre/modules/NetUtil.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyGetter(this, "libcutils", function () {
Cu.import("resource://gre/modules/systemlibs.js");
return libcutils;
});
// Trivial test just to make sure we have no syntax error
add_test(function test_ksm_ok() {
ok(KillSwitchMain, "KillSwitchMain object exists");
run_next_test();
});
add_test(function test_has_libcutils() {
let rv = KillSwitchMain.checkLibcUtils();
strictEqual(rv, true);
run_next_test();
});
add_test(function test_libcutils_works() {
KillSwitchMain._libcutils.property_set("ro.moz.ks_test", "wesh");
let rv_ks_get = KillSwitchMain._libcutils.property_get("ro.moz.ks_test");
strictEqual(rv_ks_get, "wesh")
let rv_sys_get = libcutils.property_get("ro.moz.ks_test")
strictEqual(rv_sys_get, "wesh")
KillSwitchMain._libcutils.property_set("ro.moz.ks_test2", "123456789");
rv_ks_get = KillSwitchMain._libcutils.property_get("ro.moz.ks_test2");
strictEqual(rv_ks_get, "123456789")
rv_sys_get = libcutils.property_get("ro.moz.ks_test2")
strictEqual(rv_sys_get, "123456789")
run_next_test();
});
install_common_tests();
+2 -1
View File
@@ -54,7 +54,8 @@ add_test(function test_print_properties() {
"sys.usb.state": "diag,serial_smd,serial_tty,rmnet_bam,mass_storage,adb"
};
let logMessages = LogParser.prettyPrintPropertiesArray(properties);
let logMessagesRaw = LogParser.prettyPrintPropertiesArray(properties);
let logMessages = new TextDecoder("utf-8").decode(logMessagesRaw);
let logMessagesArray = logMessages.split("\n");
ok(logMessagesArray.length === 3, "There should be 3 lines in the log.");
+78 -44
View File
@@ -15,9 +15,14 @@ const Cu = Components.utils;
Cu.import("resource://gre/modules/LogCapture.jsm");
Cu.import("resource://gre/modules/LogShake.jsm");
// Force logshake to handle a device motion event with given components
// Does not use SystemAppProxy because event needs special
// accelerationIncludingGravity property
const EVENTS_PER_SECOND = 6.25;
const GRAVITY = 9.8;
/**
* Force logshake to handle a device motion event with given components.
* Does not use SystemAppProxy because event needs special
* accelerationIncludingGravity property.
*/
function sendDeviceMotionEvent(x, y, z) {
let event = {
type: "devicemotion",
@@ -30,8 +35,10 @@ function sendDeviceMotionEvent(x, y, z) {
LogShake.handleEvent(event);
}
// Send a screen change event directly, does not use SystemAppProxy due to race
// conditions.
/**
* Send a screen change event directly, does not use SystemAppProxy due to race
* conditions.
*/
function sendScreenChangeEvent(screenEnabled) {
let event = {
type: "screenchange",
@@ -42,23 +49,41 @@ function sendScreenChangeEvent(screenEnabled) {
LogShake.handleEvent(event);
}
function debug(msg) {
var timestamp = Date.now();
dump("LogShake: " + timestamp + ": " + msg);
/**
* Mock the readLogFile function of LogCapture.
* Used to detect whether LogShake activates.
* @return {Array<String>} Locations that LogShake tries to read
*/
function mockReadLogFile() {
let readLocations = [];
LogCapture.readLogFile = function(loc) {
readLocations.push(loc);
return null; // we don't want to provide invalid data to a parser
};
// Allow inspection of readLocations by caller
return readLocations;
}
/**
* Send a series of events that corresponds to a shake
*/
function sendSustainedShake() {
// Fire a series of devicemotion events that are of shake magnitude
for (let i = 0; i < 2 * EVENTS_PER_SECOND; i++) {
sendDeviceMotionEvent(0, 2 * GRAVITY, 2 * GRAVITY);
}
}
add_test(function test_do_log_capture_after_shaking() {
// Enable LogShake
LogShake.init();
let readLocations = [];
LogCapture.readLogFile = function(loc) {
readLocations.push(loc);
return null; // we don't want to provide invalid data to a parser
};
let readLocations = mockReadLogFile();
// Fire a devicemotion event that is of shake magnitude
sendDeviceMotionEvent(9001, 9001, 9001);
sendSustainedShake();
ok(readLocations.length > 0,
"LogShake should attempt to read at least one log");
@@ -71,36 +96,28 @@ add_test(function test_do_nothing_when_resting() {
// Enable LogShake
LogShake.init();
let readLocations = [];
LogCapture.readLogFile = function(loc) {
readLocations.push(loc);
return null; // we don't want to provide invalid data to a parser
};
let readLocations = mockReadLogFile();
// Fire a devicemotion event that is relatively tiny
sendDeviceMotionEvent(0, 9.8, 9.8);
// Fire several devicemotion events that are relatively tiny
for (let i = 0; i < 2 * EVENTS_PER_SECOND; i++) {
sendDeviceMotionEvent(0, GRAVITY, GRAVITY);
}
ok(readLocations.length === 0,
"LogShake should not read any logs");
debug("test_do_nothing_when_resting: stop");
LogShake.uninit();
run_next_test();
});
add_test(function test_do_nothing_when_disabled() {
debug("test_do_nothing_when_disabled: start");
// Disable LogShake
LogShake.uninit();
let readLocations = [];
LogCapture.readLogFile = function(loc) {
readLocations.push(loc);
return null; // we don't want to provide invalid data to a parser
};
let readLocations = mockReadLogFile();
// Fire a devicemotion event that would normally be a shake
sendDeviceMotionEvent(0, 9001, 9001);
// Fire a series of events that would normally be a shake
sendSustainedShake();
ok(readLocations.length === 0,
"LogShake should not read any logs");
@@ -112,18 +129,13 @@ add_test(function test_do_nothing_when_screen_off() {
// Enable LogShake
LogShake.init();
// Send an event as if the screen has been turned off
sendScreenChangeEvent(false);
let readLocations = [];
LogCapture.readLogFile = function(loc) {
readLocations.push(loc);
return null; // we don't want to provide invalid data to a parser
};
let readLocations = mockReadLogFile();
// Fire a devicemotion event that would normally be a shake
sendDeviceMotionEvent(0, 9001, 9001);
// Fire a series of events that would normally be a shake
sendSustainedShake();
ok(readLocations.length === 0,
"LogShake should not read any logs");
@@ -145,8 +157,8 @@ add_test(function test_do_log_capture_resilient_readLogFile() {
throw new Error("Exception during readLogFile for: " + loc);
};
// Fire a devicemotion event that is of shake magnitude
sendDeviceMotionEvent(9001, 9001, 9001);
// Fire a series of events that would normally be a shake
sendSustainedShake();
ok(readLocations.length > 0,
"LogShake should attempt to read at least one log");
@@ -168,8 +180,8 @@ add_test(function test_do_log_capture_resilient_parseLog() {
return null;
};
// Fire a devicemotion event that is of shake magnitude
sendDeviceMotionEvent(9001, 9001, 9001);
// Fire a series of events that would normally be a shake
sendSustainedShake();
ok(readLocations.length > 0,
"LogShake should attempt to read at least one log");
@@ -178,7 +190,29 @@ add_test(function test_do_log_capture_resilient_parseLog() {
run_next_test();
});
add_test(function test_do_nothing_when_dropped() {
// Enable LogShake
LogShake.init();
let readLocations = mockReadLogFile();
// We want a series of spikes to be ignored by LogShake. This roughly
// corresponds to the compare_stairs_sock graph on bug #1101994
for (let i = 0; i < 10 * EVENTS_PER_SECOND; i++) {
// Fire a devicemotion event that is at rest
sendDeviceMotionEvent(0, 0, GRAVITY);
// Fire a spike of motion
sendDeviceMotionEvent(0, 2 * GRAVITY, 2 * GRAVITY);
}
ok(readLocations.length === 0,
"LogShake should not read any logs");
LogShake.uninit();
run_next_test();
});
function run_test() {
debug("Starting");
run_next_test();
}
+10 -68
View File
@@ -3,8 +3,9 @@
* for Gonk-specific parts
*/
/* jshint moz: true */
/* global Components, LogCapture, LogShake, ok, add_test, run_next_test, dump */
/* jshint moz: true, esnext: true */
/* global Cu, LogCapture, LogShake, ok, add_test, run_next_test, dump,
setup_logshake_mocks, OS, sdcard */
/* exported run_test */
/* disable use strict warning */
@@ -12,77 +13,14 @@
"use strict";
const Cu = Components.utils;
const Ci = Components.interfaces;
const Cc = Components.classes;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "volumeService",
"@mozilla.org/telephony/volume-service;1",
"nsIVolumeService");
let sdcard;
Cu.import("resource://gre/modules/Promise.jsm");
function run_test() {
Cu.import("resource://gre/modules/LogShake.jsm");
Cu.import("resource://gre/modules/Promise.jsm");
Cu.import("resource://gre/modules/osfile.jsm");
do_get_profile();
debug("Starting");
run_next_test();
}
function debug(msg) {
var timestamp = Date.now();
dump("LogShake: " + timestamp + ": " + msg + "\n");
}
add_test(function setup_fs() {
OS.File.makeDir("/data/local/tmp/sdcard/", {from: "/data"}).then(function() {
run_next_test();
});
});
add_test(function setup_sdcard() {
let volName = "sdcard";
let mountPoint = "/data/local/tmp/sdcard";
volumeService.createFakeVolume(volName, mountPoint);
let vol = volumeService.getVolumeByName(volName);
ok(vol, "volume shouldn't be null");
equal(volName, vol.name, "name");
equal(Ci.nsIVolume.STATE_MOUNTED, vol.state, "state");
run_next_test();
});
add_test(function test_ensure_sdcard() {
sdcard = volumeService.getVolumeByName("sdcard").mountPoint;
ok(sdcard, "Should have a valid sdcard mountpoint");
run_next_test();
});
add_test(function test_logShake_captureLogs_returns() {
// Enable LogShake
LogShake.init();
LogShake.captureLogs().then(logResults => {
LogShake.uninit();
ok(logResults.logFilenames.length > 0, "Should have filenames");
ok(logResults.logPrefix.length > 0, "Should have prefix");
run_next_test();
},
error => {
LogShake.uninit();
ok(false, "Should not have received error: " + error);
run_next_test();
});
});
add_test(setup_logshake_mocks);
add_test(function test_logShake_captureLogs_writes() {
// Enable LogShake
@@ -93,7 +31,11 @@ add_test(function test_logShake_captureLogs_writes() {
LogShake.captureLogs().then(logResults => {
LogShake.uninit();
logResults.logFilenames.forEach(f => {
ok(logResults.logFilenames.length > 0, "Should have filenames");
ok(logResults.logPaths.length > 0, "Should have paths");
ok(!logResults.compressed, "Should not be compressed");
logResults.logPaths.forEach(f => {
let p = OS.Path.join(sdcard, f);
ok(p, "Should have a valid result path: " + p);
@@ -0,0 +1,76 @@
/**
* Test the log capturing capabilities of LogShake.jsm, checking
* for Gonk-specific parts
*/
/* jshint moz: true, esnext: true */
/* global Cc, Ci, Cu, LogCapture, LogShake, ok, add_test, run_next_test, dump,
setup_logshake_mocks, OS, sdcard, FileUtils */
/* exported run_test */
/* disable use strict warning */
/* jshint -W097 */
"use strict";
Cu.import("resource://gre/modules/Promise.jsm");
Cu.import("resource://gre/modules/FileUtils.jsm");
function run_test() {
Cu.import("resource://gre/modules/LogShake.jsm");
run_next_test();
}
add_test(setup_logshake_mocks);
add_test(function test_logShake_captureLogs_writes_zip() {
// Enable LogShake
LogShake.init();
let expectedFiles = [];
LogShake.enableQAMode();
LogShake.captureLogs().then(logResults => {
LogShake.uninit();
ok(logResults.logPaths.length === 1, "Should have zip path");
ok(logResults.logFilenames.length >= 1, "Should have log filenames");
ok(logResults.compressed, "Log files should be compressed");
let zipPath = OS.Path.join(sdcard, logResults.logPaths[0]);
ok(zipPath, "Should have a valid archive path: " + zipPath);
let zipFile = new FileUtils.File(zipPath);
ok(zipFile, "Should have a valid archive file: " + zipFile);
let zipReader = Cc["@mozilla.org/libjar/zip-reader;1"]
.createInstance(Ci.nsIZipReader);
zipReader.open(zipFile);
let logFilenamesSeen = {};
let zipEntries = zipReader.findEntries(null); // Find all entries
while (zipEntries.hasMore()) {
let entryName = zipEntries.getNext();
let entry = zipReader.getEntry(entryName);
logFilenamesSeen[entryName] = true;
ok(!entry.isDirectory, "Archive entry " + entryName + " should be a file");
}
zipReader.close();
// TODO: Verify archive contents
logResults.logFilenames.forEach(filename => {
ok(logFilenamesSeen[filename], "File " + filename + " should be present in archive");
});
run_next_test();
},
error => {
LogShake.uninit();
ok(false, "Should not have received error: " + error);
run_next_test();
});
});
@@ -0,0 +1,65 @@
/**
* Test the log capturing capabilities of LogShake.jsm under conditions that
* could cause races
*/
/* jshint moz: true, esnext: true */
/* global Cu, LogCapture, LogShake, ok, add_test, run_next_test, dump,
XPCOMUtils, do_get_profile, OS, volumeService, Promise, equal,
setup_logshake_mocks */
/* exported run_test */
/* disable use strict warning */
/* jshint -W097 */
"use strict";
function run_test() {
Cu.import("resource://gre/modules/LogShake.jsm");
run_next_test();
}
add_test(setup_logshake_mocks);
add_test(function test_logShake_captureLogs_waits_to_read() {
// Enable LogShake
LogShake.init();
// Save no logs synchronously (except properties)
LogShake.LOGS_WITH_PARSERS = {};
LogShake.captureLogs().then(logResults => {
LogShake.uninit();
ok(logResults.logFilenames.length > 0, "Should have filenames");
ok(logResults.logPaths.length > 0, "Should have paths");
ok(!logResults.compressed, "Should not be compressed");
// This assumes that the about:memory reading will only fail under abnormal
// circumstances. It does not check for screenshot.png because
// systemAppFrame is unavailable during xpcshell tests.
let hasAboutMemory = false;
logResults.logFilenames.forEach(filename => {
// Because the about:memory log's filename has the PID in it we can not
// use simple equality but instead search for the "about_memory" part of
// the filename which will look like logshake-about_memory-{PID}.json.gz
if (filename.indexOf("about_memory") < 0) {
return;
}
hasAboutMemory = true;
});
ok(hasAboutMemory,
"LogShake's asynchronous read of about:memory should have succeeded.");
run_next_test();
},
error => {
LogShake.uninit();
ok(false, "Should not have received error: " + error);
run_next_test();
});
});
+21 -1
View File
@@ -26,7 +26,27 @@ skip-if = toolkit != "gonk"
[test_logshake.js]
[test_logshake_gonk.js]
head = head_logshake_gonk.js
# only run on b2g builds due to requiring b2g-specific log files to exist
skip-if = ((toolkit != "gonk") || ((toolkit == "gonk") && (debug == true))) # bug 1125989: disabled because of race condition in OS.File.makeDir
skip-if = (toolkit != "gonk")
[test_logshake_gonk_compression.js]
head = head_logshake_gonk.js
# only run on b2g builds due to requiring b2g-specific log files to exist
skip-if = (toolkit != "gonk")
[test_logshake_readLog_gonk.js]
head = head_logshake_gonk.js
# only run on b2g builds due to requiring b2g-specific log files to exist
skip-if = (toolkit != "gonk")
[test_aboutserviceworkers.js]
[test_killswitch.js]
head = file_killswitch.js
skip-if = (toolkit == "gonk")
[test_killswitch_gonk.js]
head = file_killswitch.js
# Bug 1193677: disable on B2G ICS Emulator for intermittent failures with IndexedDB
skip-if = ((toolkit != "gonk") || (toolkit == "gonk" && debug))
+1
View File
@@ -942,6 +942,7 @@ bin/libfreebl_32int64_3.so
@RESPATH@/components/B2GAppMigrator.js
@RESPATH@/components/B2GPresentationDevicePrompt.js
@RESPATH@/components/PresentationRequestUIGlue.js
@RESPATH@/components/KillSwitch.js
#ifndef MOZ_WIDGET_GONK
@RESPATH@/components/SimulatorScreen.js
+10
View File
@@ -66,6 +66,13 @@ function init(aEvent)
#ifdef MOZ_UPDATER
gAppUpdater = new appUpdater();
let defaults = Services.prefs.getDefaultBranch("");
let channelLabel = document.getElementById("currentChannel");
let currentChannelText = document.getElementById("currentChannelText");
channelLabel.value = UpdateUtils.UpdateChannel;
if (/^release($|\-)/.test(channelLabel.value))
currentChannelText.hidden = true;
#endif
#ifdef XP_MACOSX
@@ -89,6 +96,9 @@ Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
Components.utils.import("resource://gre/modules/DownloadUtils.jsm");
Components.utils.import("resource://gre/modules/AddonManager.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
"resource://gre/modules/UpdateUtils.jsm");
var gAppUpdater;
function onUnload(aEvent) {
+1 -1
View File
@@ -19,7 +19,7 @@
* it changes the sidebar's visibility.
* - group this attribute must be set to "sidebar".
*/
let SidebarUI = {
var SidebarUI = {
browser: null,
_box: null,
+3 -3
View File
@@ -34,8 +34,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "Log",
"resource://gre/modules/Log.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "AppConstants",
"resource://gre/modules/AppConstants.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
"resource://gre/modules/UpdateChannel.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
"resource://gre/modules/UpdateUtils.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "Favicons",
"@mozilla.org/browser/favicon-service;1",
"mozIAsyncFavicons");
@@ -2749,7 +2749,7 @@ var BrowserOnClick = {
version: 1,
build: gAppInfo.appBuildID,
product: gAppInfo.name,
channel: UpdateChannel.get()
channel: UpdateUtils.UpdateChannel
}
let reportURL = Services.prefs.getCharPref("security.ssl.errorReporting.url");
+3 -2
View File
@@ -239,7 +239,7 @@ Cc["@mozilla.org/eventlistenerservice;1"]
.getService(Ci.nsIEventListenerService)
.addSystemEventListener(global, "contextmenu", handleContentContextMenu, false);
let AboutNetErrorListener = {
var AboutNetErrorListener = {
init: function(chromeGlobal) {
chromeGlobal.addEventListener('AboutNetErrorLoad', this, false, true);
chromeGlobal.addEventListener('AboutNetErrorSetAutomatic', this, false, true);
@@ -354,7 +354,8 @@ let AboutNetErrorListener = {
AboutNetErrorListener.init(this);
let ClickEventHandler = {
var ClickEventHandler = {
init: function init() {
Cc["@mozilla.org/eventlistenerservice;1"]
.getService(Ci.nsIEventListenerService)
+1 -1
View File
@@ -140,7 +140,7 @@ var gDrag = {
// drag image with its default opacity.
let dragElement = document.createElementNS(HTML_NAMESPACE, "div");
dragElement.classList.add("newtab-drag");
let scrollbox = document.getElementById("newtab-scrollbox");
let scrollbox = document.getElementById("newtab-vertical-margin");
scrollbox.appendChild(dragElement);
dt.setDragImage(dragElement, 0, 0);
@@ -4,7 +4,7 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#endif
let gDragDataHelper = {
var gDragDataHelper = {
get mimeType() {
return "text/x-moz-url";
},
+1 -1
View File
@@ -11,7 +11,7 @@ const DELAY_REARRANGE_MS = 100;
/**
* This singleton implements site dropping functionality.
*/
let gDrop = {
var gDrop = {
/**
* The last drop target.
*/
+1 -1
View File
@@ -9,7 +9,7 @@
* indicate the transformation that results from dropping a cell at a certain
* position.
*/
let gDropPreview = {
var gDropPreview = {
/**
* Rearranges the sites currently contained in the grid when a site would be
* dropped onto the given cell.
@@ -9,7 +9,7 @@
* the default DnD target detection relies on the cursor's position. We want
* to pick a drop target based on the dragged site's position.
*/
let gDropTargetShim = {
var gDropTargetShim = {
/**
* Cache for the position of all cells, cleaned after drag finished.
*/
+8 -1
View File
@@ -4,10 +4,17 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#endif
/**
* Define various fixed dimensions
*/
const GRID_BOTTOM_EXTRA = 7; // title's line-height extends 7px past the margin
const GRID_WIDTH_EXTRA = 1; // provide 1px buffer to allow for rounding error
const SPONSORED_TAG_BUFFER = 2; // 2px buffer to clip off top of sponsored tag
/**
* This singleton represents the grid that contains all sites.
*/
let gGrid = {
var gGrid = {
/**
* The DOM node of the grid.
*/
+147 -25
View File
@@ -2,26 +2,36 @@
* 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/. */
input[type=button] {
cursor: pointer;
html {
width: 100%;
height: 100%;
}
/* SCROLLBOX */
#newtab-scrollbox {
body {
font: message-box;
width: 100%;
height: 100%;
padding: 0;
margin: 0;
background-color: #F9F9F9;
display: -moz-box;
position: relative;
-moz-box-flex: 1;
-moz-user-focus: normal;
}
#newtab-scrollbox:not([page-disabled]) {
overflow: auto;
input {
font: message-box !important;
font-size: 16px !important;
}
input[type=button] {
cursor: pointer;
}
/* UNDO */
#newtab-undo-container {
transition: opacity 100ms ease-out;
display: -moz-box;
-moz-box-align: center;
-moz-box-pack: center;
}
@@ -38,7 +48,7 @@ input[type=button] {
right: 12px;
}
#newtab-toggle:-moz-locale-dir(rtl) {
#newtab-toggle:-moz-dir(rtl) {
left: 12px;
right: auto;
}
@@ -117,7 +127,9 @@ input[type=button] {
/* CELLS */
.newtab-cell {
display: -moz-box;
-moz-box-flex: 1;
height: 210px;
margin: 20px 10px 35px;
width: 290px;
}
/* SITES */
@@ -148,18 +160,127 @@ input[type=button] {
bottom: 0;
}
.newtab-thumbnail {
opacity: .8;
transition: opacity 100ms ease-out;
}
.newtab-thumbnail[dragged],
.newtab-link:-moz-focusring > .newtab-thumbnail,
.newtab-site:hover > .newtab-link > .newtab-thumbnail {
opacity: 1;
}
/* TITLES */
.newtab-sponsored,
.newtab-title,
.newtab-suggested {
overflow: hidden;
position: absolute;
right: 0;
text-align: center;
}
.newtab-sponsored,
.newtab-title {
bottom: 0;
white-space: nowrap;
text-overflow: ellipsis;
font-size: 13px;
line-height: 30px;
vertical-align: middle;
background-color: #F2F2F2;
}
.newtab-suggested {
border: 1px solid transparent;
border-radius: 2px;
font-size: 12px;
height: 17px;
line-height: 17px;
margin-bottom: -1px;
padding: 2px 8px;
display: none;
margin-left: auto;
margin-right: auto;
left: 0;
top: 215px;
-moz-user-select: none;
}
.newtab-suggested-bounds {
max-height: 34px; /* 34 / 17 = 2 lines maximum */
}
.newtab-title {
left: 0;
padding: 0 4px;
border: 1px solid #FFFFFF;
border-radius: 0px 0px 8px 8px;
}
.newtab-sponsored {
background-color: #FFFFFF;
border: 1px solid #E2E2E2;
border-radius: 3px;
color: #4A4A4A;
cursor: pointer;
display: none;
font-family: Arial;
font-size: 9px;
height: 17px;
left: 0;
line-height: 6px;
padding: 4px;
right: auto;
top: -15px;
}
.newtab-site[suggested=true] > .newtab-sponsored {
background-color: #E2E2E2;
border: none;
}
.newtab-site > .newtab-sponsored:-moz-any(:hover, [active]) {
background-color: #4A90E2;
border: 0;
color: white;
}
.newtab-site > .newtab-sponsored[active] {
background-color: #000000;
}
.newtab-sponsored:-moz-dir(rtl) {
right: 0;
left: auto;
}
.newtab-site:-moz-any([type=enhanced], [type=sponsored], [suggested]) .newtab-sponsored {
display: block;
}
.newtab-site[suggested] .newtab-suggested {
display: table;
}
.sponsored-explain,
.sponsored-explain a,
.suggested-explain,
.suggested-explain a {
color: white;
}
.sponsored-explain,
.suggested-explain {
background-color: rgba(51, 51, 51, 0.95);
bottom: 30px;
line-height: 20px;
padding: 15px 10px;
position: absolute;
text-align: start;
}
.sponsored-explain input,
.suggested-explain input {
background-size: 18px;
height: 18px;
opacity: 1;
pointer-events: none;
position: static;
width: 18px;
}
.newtab-title {
position: absolute;
left: 0;
@@ -193,13 +314,13 @@ input[type=button] {
}
}
.newtab-control-pin:-moz-locale-dir(ltr),
.newtab-control-block:-moz-locale-dir(rtl) {
.newtab-control-pin:-moz-dir(ltr),
.newtab-control-block:-moz-dir(rtl) {
left: 4px;
}
.newtab-control-block:-moz-locale-dir(ltr),
.newtab-control-pin:-moz-locale-dir(rtl) {
.newtab-control-block:-moz-dir(ltr),
.newtab-control-pin:-moz-dir(rtl) {
right: 4px;
}
@@ -226,8 +347,8 @@ input[type=button] {
#newtab-search-container {
display: -moz-box;
position: relative;
-moz-box-align: center;
-moz-box-pack: center;
margin: 40px 0 15px;
}
#newtab-search-container[page-disabled] {
@@ -330,6 +451,7 @@ input[type=button] {
}
#newtab-search-text:focus + #newtab-search-submit,
#newtab-search-text[keepfocus] + #newtab-search-submit,
#newtab-search-text[autofocus] + #newtab-search-submit {
background-image: linear-gradient(#4cb1ff, #1793e5);
box-shadow: 0 1px 0 hsla(0,0%,100%,.2) inset,
+10 -2
View File
@@ -34,15 +34,23 @@ XPCOMUtils.defineLazyGetter(this, "gStringBundle", function() {
createBundle("chrome://browser/locale/newTab.properties");
});
function newTabString(name) gStringBundle.GetStringFromName('newtab.' + name);
function newTabString(name, args) {
let stringName = "newtab." + name;
if (!args) {
return gStringBundle.GetStringFromName(stringName);
}
return gStringBundle.formatStringFromName(stringName, args, args.length);
}
function inPrivateBrowsingMode() {
return PrivateBrowsingUtils.isWindowPrivate(window);
return PrivateBrowsingUtils.isContentWindowPrivate(window);
}
const HTML_NAMESPACE = "http://www.w3.org/1999/xhtml";
const XUL_NAMESPACE = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
const TILES_EXPLAIN_LINK = "https://support.mozilla.org/kb/how-do-tiles-work-firefox";
#include transformations.js
#include page.js
#include grid.js
+72
View File
@@ -0,0 +1,72 @@
<?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/. -->
<!DOCTYPE html [
<!ENTITY % newTabDTD SYSTEM "chrome://browser/locale/newTab.dtd">
%newTabDTD;
<!ENTITY % searchBarDTD SYSTEM "chrome://browser/locale/searchbar.dtd">
%searchBarDTD;
<!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd">
%browserDTD;
<!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd">
%globalDTD;
]>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>&newtab.pageTitle;</title>
<link rel="stylesheet" type="text/css" media="all" href="chrome://global/skin/" />
<link rel="stylesheet" type="text/css" media="all" href="chrome://browser/content/searchSuggestionUI.css" />
<link rel="stylesheet" type="text/css" media="all" href="chrome://browser/content/newtab/newTab.css" />
<link rel="stylesheet" type="text/css" media="all" href="chrome://browser/skin/newtab/newTab.css" />
</head>
<body dir="&locale.dir;">
<div id="newtab-customize-overlay"></div>
<div id="newtab-scrollbox">
<div id="newtab-vertical-margin">
<div id="newtab-margin-top"/>
<div id="newtab-margin-undo-container">
<div id="newtab-undo-container" undo-disabled="true">
<label id="newtab-undo-label">&newtab.undo.removedLabel;</label>
<button id="newtab-undo-button" tabindex="-1"
class="newtab-undo-button">&newtab.undo.undoButton;</button>
<button id="newtab-undo-restore-button" tabindex="-1"
class="newtab-undo-button">&newtab.undo.restoreButton;</button>
<button id="newtab-undo-close-button" tabindex="-1" title="&newtab.undo.closeTooltip;"/>
</div>
</div>
<div id="newtab-search-container">
<form id="newtab-search-form" name="searchForm">
<div id="newtab-search-logo"/>
<input type="text" name="q" value="" id="newtab-search-text"
maxlength="256" dir="auto"/>
<input id="newtab-search-submit" type="submit"
value="&searchEndCap.label;"/>
</form>
</div>
<div id="newtab-horizontal-margin">
<div class="newtab-side-margin"/>
<div id="newtab-grid">
</div>
<div class="newtab-side-margin"/>
</div>
<div id="newtab-margin-bottom"/>
</div>
<input id="newtab-toggle" type="button"/>
</div>
</body>
<script type="text/javascript;version=1.8" src="chrome://browser/content/searchSuggestionUI.js"/>
<script type="text/javascript;version=1.8" src="chrome://browser/content/newtab/newTab.js"/>
</html>
-79
View File
@@ -1,79 +0,0 @@
<?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/. -->
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://browser/content/searchSuggestionUI.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/content/newtab/newTab.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/newtab/newTab.css" type="text/css"?>
<!DOCTYPE window [
<!ENTITY % newTabDTD SYSTEM "chrome://browser/locale/newTab.dtd">
%newTabDTD;
<!ENTITY % searchBarDTD SYSTEM "chrome://browser/locale/searchbar.dtd">
%searchBarDTD;
<!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd">
%browserDTD;
]>
<xul:window id="newtab-window" xmlns="http://www.w3.org/1999/xhtml"
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
title="&newtab.pageTitle;">
<xul:panel id="newtab-search-panel" orient="vertical" type="arrow"
noautohide="true">
<xul:hbox id="newtab-search-manage" class="newtab-search-panel-engine">
<xul:label>&changeSearchSettings.button;</xul:label>
</xul:hbox>
</xul:panel>
<div id="newtab-scrollbox">
<div id="newtab-vertical-margin">
<div id="newtab-margin-top">
<div id="newtab-undo-container" undo-disabled="true">
<xul:label id="newtab-undo-label"
value="&newtab.undo.removedLabel;" />
<xul:button id="newtab-undo-button" tabindex="-1"
label="&newtab.undo.undoButton;"
class="newtab-undo-button" />
<xul:button id="newtab-undo-restore-button" tabindex="-1"
label="&newtab.undo.restoreButton;"
class="newtab-undo-button" />
<xul:toolbarbutton id="newtab-undo-close-button" tabindex="-1"
class="close-icon"
tooltiptext="&newtab.undo.closeTooltip;" />
</div>
</div>
<div id="newtab-search-container">
<form id="newtab-search-form" name="searchForm">
<div id="newtab-search-logo"/>
<input type="text" name="q" value="" id="newtab-search-text"
maxlength="256" dir="auto"/>
<input id="newtab-search-submit" type="submit"
value="&searchEndCap.label;"/>
</form>
</div>
<div id="newtab-horizontal-margin">
<div class="newtab-side-margin"/>
<div id="newtab-grid">
</div>
<div class="newtab-side-margin"/>
</div>
<div id="newtab-margin-bottom"/>
</div>
<input id="newtab-toggle" type="button"/>
</div>
<xul:script type="text/javascript;version=1.8"
src="chrome://browser/content/newtab/newTab.js"/>
<xul:script type="text/javascript;version=1.8"
src="chrome://browser/content/searchSuggestionUI.js"/>
</xul:window>
+3 -3
View File
@@ -8,7 +8,7 @@
* This singleton represents the whole 'New Tab Page' and takes care of
* initializing all its components.
*/
let gPage = {
var gPage = {
/**
* Initializes the page.
*/
@@ -100,7 +100,7 @@ let gPage = {
*/
_updateAttributes: function Page_updateAttributes(aValue) {
// Set the nodes' states.
let nodeSelector = "#newtab-scrollbox, #newtab-toggle, #newtab-grid, #newtab-search-container";
let nodeSelector = "#newtab-grid, #newtab-search-container";
for (let node of document.querySelectorAll(nodeSelector)) {
if (aValue)
node.removeAttribute("page-disabled");
@@ -111,7 +111,7 @@ let gPage = {
// Enables/disables the control and link elements.
let inputSelector = ".newtab-control, .newtab-link";
for (let input of document.querySelectorAll(inputSelector)) {
if (aValue)
if (aValue)
input.removeAttribute("tabindex");
else
input.setAttribute("tabindex", "-1");
+164 -14
View File
@@ -37,7 +37,7 @@ Site.prototype = {
/**
* The title of the site's link.
*/
get title() { return this.link.title; },
get title() { return this.link.title || this.link.url; },
/**
* The site's parent cell.
@@ -50,13 +50,19 @@ Site.prototype = {
/**
* Pins the site on its current or a given index.
* @param aIndex The pinned index (optional).
* @return true if link changed type after pin
*/
pin: function Site_pin(aIndex) {
if (typeof aIndex == "undefined")
aIndex = this.cell.index;
this._updateAttributes(true);
gPinnedLinks.pin(this._link, aIndex);
let changed = gPinnedLinks.pin(this._link, aIndex);
if (changed) {
// render site again to remove suggested/sponsored tags
this._render();
}
return changed;
},
/**
@@ -108,14 +114,34 @@ Site.prototype = {
let control = this._querySelector(".newtab-control-pin");
if (aPinned) {
control.setAttribute("pinned", true);
this.node.setAttribute("pinned", true);
control.setAttribute("title", newTabString("unpin"));
} else {
control.removeAttribute("pinned");
this.node.removeAttribute("pinned");
control.setAttribute("title", newTabString("pin"));
}
},
_newTabString: function(str, substrArr) {
let regExp = /%[0-9]\$S/g;
let matches;
while (matches = regExp.exec(str)) {
let match = matches[0];
let index = match.charAt(1); // Get the digit in the regExp.
str = str.replace(match, substrArr[index - 1]);
}
return str;
},
_getSuggestedTileExplanation: function() {
let targetedName = `<strong> ${this.link.targetedName} </strong>`;
let targetedSite = `<strong> ${this.link.targetedSite} </strong>`;
if (this.link.explanation) {
return this._newTabString(this.link.explanation, [targetedName, targetedSite]);
}
return newTabString("suggested.button", [targetedName]);
},
/**
* Checks for and modifies link at campaign end time
*/
@@ -144,20 +170,45 @@ Site.prototype = {
// setup display variables
let enhanced = gAllPages.enhanced && DirectoryLinksProvider.getEnhancedLink(this.link);
let url = this.url;
let title = this.title || url;
let tooltip = (title == url ? title : title + "\n" + url);
let title = enhanced && enhanced.title ? enhanced.title :
this.link.type == "history" ? this.link.baseDomain :
this.title;
let tooltip = (this.title == url ? this.title : this.title + "\n" + url);
let link = this._querySelector(".newtab-link");
link.setAttribute("title", tooltip);
link.setAttribute("href", url);
this._querySelector(".newtab-title").textContent = title;
this.node.setAttribute("type", this.link.type);
let titleNode = this._querySelector(".newtab-title");
titleNode.textContent = title;
if (this.link.titleBgColor) {
titleNode.style.backgroundColor = this.link.titleBgColor;
}
// remove "suggested" attribute to avoid showing "suggested" tag
// after site was pinned or dropped
this.node.removeAttribute("suggested");
if (this.link.targetedSite) {
if (this.node.getAttribute("type") != "sponsored") {
this._querySelector(".newtab-sponsored").textContent =
newTabString("suggested.tag");
}
this.node.setAttribute("suggested", true);
let explanation = this._getSuggestedTileExplanation();
this._querySelector(".newtab-suggested").innerHTML =
`<div class='newtab-suggested-bounds'> ${explanation} </div>`;
}
if (this.isPinned())
this._updateAttributes(true);
let thumbnailURL = PageThumbs.getThumbnailURL(this.url);
let thumbnail = this._querySelector(".newtab-thumbnail");
thumbnail.style.backgroundImage = "url(" + thumbnailURL + ")";
// Capture the page if the thumbnail is missing, which will cause page.js
// to be notified and call our refreshThumbnail() method.
this.captureIfMissing();
// but still display whatever thumbnail might be available now.
this.refreshThumbnail();
},
/**
@@ -230,9 +281,12 @@ Site.prototype = {
this._node.addEventListener("dragend", this, false);
this._node.addEventListener("mouseover", this, false);
let controls = this.node.querySelectorAll(".newtab-control");
for (let i = 0; i < controls.length; i++)
controls[i].addEventListener("click", this, false);
// Specially treat the sponsored icon & suggested explanation
// text to prevent regular hover effects
let sponsored = this._querySelector(".newtab-sponsored");
let suggested = this._querySelector(".newtab-suggested");
this._ignoreHoverEvents(sponsored);
this._ignoreHoverEvents(suggested);
},
/**
@@ -244,6 +298,102 @@ Site.prototype = {
sc.speculativeConnect(uri, null);
},
/**
* Record interaction with site using telemetry.
*/
_recordSiteClicked: function Site_recordSiteClicked(aIndex) {
if (Services.prefs.prefHasUserValue("browser.newtabpage.rows") ||
Services.prefs.prefHasUserValue("browser.newtabpage.columns") ||
aIndex > 8) {
// We only want to get indices for the default configuration, everything
// else goes in the same bucket.
aIndex = 9;
}
Services.telemetry.getHistogramById("NEWTAB_PAGE_SITE_CLICKED")
.add(aIndex);
},
_toggleLegalText: function(buttonClass, explanationTextClass) {
let button = this._querySelector(buttonClass);
if (button.hasAttribute("active")) {
let explain = this._querySelector(explanationTextClass);
explain.parentNode.removeChild(explain);
button.removeAttribute("active");
}
else {
let explain = document.createElementNS(HTML_NAMESPACE, "div");
explain.className = explanationTextClass.slice(1); // Slice off the first character, '.'
this.node.appendChild(explain);
let link = '<a href="' + TILES_EXPLAIN_LINK + '">' +
newTabString("learn.link") + "</a>";
let type = (this.node.getAttribute("suggested") && this.node.getAttribute("type") == "affiliate") ?
"suggested" : this.node.getAttribute("type");
let icon = '<input type="button" class="newtab-control newtab-' +
(type == "enhanced" ? "customize" : "control-block") + '"/>';
explain.innerHTML = newTabString(type + (type == "sponsored" ? ".explain2" : ".explain"), [icon, link]);
button.setAttribute("active", "true");
}
},
/**
* Handles site click events.
*/
onClick: function Site_onClick(aEvent) {
let action;
let pinned = this.isPinned();
let tileIndex = this.cell.index;
let {button, target} = aEvent;
// Handle tile/thumbnail link click
if (target.classList.contains("newtab-link") ||
target.parentElement.classList.contains("newtab-link")) {
// Record for primary and middle clicks
if (button == 0 || button == 1) {
this._recordSiteClicked(tileIndex);
action = "click";
}
}
// Handle sponsored explanation link click
else if (target.parentElement.classList.contains("sponsored-explain")) {
action = "sponsored_link";
}
else if (target.parentElement.classList.contains("suggested-explain")) {
action = "suggested_link";
}
// Only handle primary clicks for the remaining targets
else if (button == 0) {
aEvent.preventDefault();
if (target.classList.contains("newtab-control-block")) {
this.block();
action = "block";
}
else if (target.classList.contains("sponsored-explain") ||
target.classList.contains("newtab-sponsored")) {
this._toggleLegalText(".newtab-sponsored", ".sponsored-explain");
action = "sponsored";
}
else if (pinned && target.classList.contains("newtab-control-pin")) {
this.unpin();
action = "unpin";
}
else if (!pinned && target.classList.contains("newtab-control-pin")) {
if (this.pin()) {
// suggested link has changed - update rest of the pages
gAllPages.update(gPage);
}
action = "pin";
}
}
// Report all link click actions
if (action) {
DirectoryLinksProvider.reportSitesAction(gGrid.sites, action, tileIndex);
}
},
/**
* Handles all site events.
*/
+30 -25
View File
@@ -9,7 +9,7 @@
* in the DOM and by showing or hiding the node. It additionally provides
* convenience methods to work with a site's DOM node.
*/
let gTransformation = {
var gTransformation = {
/**
* Returns the width of the left and top border of a cell. We need to take it
* into account when measuring and comparing site and cell positions.
@@ -103,7 +103,7 @@ let gTransformation = {
let style = aSite.node.style;
let comp = getComputedStyle(aSite.node, null);
style.width = comp.getPropertyValue("width")
style.width = comp.getPropertyValue("width");
style.height = comp.getPropertyValue("height");
aSite.node.setAttribute("frozen", "true");
@@ -156,7 +156,7 @@ let gTransformation = {
finish();
} else {
this.setSitePosition(aSite, targetPosition);
this._whenTransitionEnded(aSite.node, finish);
this._whenTransitionEnded(aSite.node, ["left", "top"], finish);
}
},
@@ -179,38 +179,42 @@ let gTransformation = {
if (!aSite || aSite == gDrag.draggedSite)
return;
let deferred = Promise.defer();
batch.push(deferred.promise);
let cb = function () deferred.resolve();
if (!cells[aIndex])
// The site disappeared from the grid, hide it.
this.hideSite(aSite, cb);
else if (this._getNodeOpacity(aSite.node) != 1)
// The site disappeared before but is now back, show it.
this.showSite(aSite, cb);
else
// The site's position has changed, move it around.
this._moveSite(aSite, aIndex, {unfreeze: unfreeze, callback: cb});
batch.push(new Promise(resolve => {
if (!cells[aIndex]) {
// The site disappeared from the grid, hide it.
this.hideSite(aSite, resolve);
} else if (this._getNodeOpacity(aSite.node) != 1) {
// The site disappeared before but is now back, show it.
this.showSite(aSite, resolve);
} else {
// The site's position has changed, move it around.
this._moveSite(aSite, aIndex, {unfreeze: unfreeze, callback: resolve});
}
}));
}, this);
let wait = Promise.promised(function () callback && callback());
wait.apply(null, batch);
if (callback) {
Promise.all(batch).then(callback);
}
},
/**
* Listens for the 'transitionend' event on a given node and calls the given
* callback.
* @param aNode The node that is transitioned.
* @param aProperties The properties we'll wait to be transitioned.
* @param aCallback The callback to call when finished.
*/
_whenTransitionEnded:
function Transformation_whenTransitionEnded(aNode, aCallback) {
function Transformation_whenTransitionEnded(aNode, aProperties, aCallback) {
aNode.addEventListener("transitionend", function onEnd() {
aNode.removeEventListener("transitionend", onEnd, false);
aCallback();
}, false);
let props = new Set(aProperties);
aNode.addEventListener("transitionend", function onEnd(e) {
if (props.has(e.propertyName)) {
aNode.removeEventListener("transitionend", onEnd);
aCallback();
}
});
},
/**
@@ -236,8 +240,9 @@ let gTransformation = {
if (aCallback)
aCallback();
} else {
if (aCallback)
this._whenTransitionEnded(aNode, aCallback);
if (aCallback) {
this._whenTransitionEnded(aNode, ["opacity"], aCallback);
}
aNode.style.opacity = aOpacity;
}
+1 -1
View File
@@ -8,7 +8,7 @@
* Dialog allowing to undo the removal of single site or to completely restore
* the grid's original state.
*/
let gUndoDialog = {
var gUndoDialog = {
/**
* The undo dialog's timeout in miliseconds.
*/
+1 -1
View File
@@ -8,7 +8,7 @@
* This singleton provides functionality to update the current grid to a new
* set of pinned and blocked sites. It adds, moves and removes sites.
*/
let gUpdater = {
var gUpdater = {
/**
* Updates the current grid according to its pinned and blocked sites.
* This removes old, moves existing and creates new sites to fill gaps.
@@ -68,6 +68,12 @@
<richlistbox id="login-fill-list"/>
</vbox>
#ifdef E10S_TESTING_ONLY
<popupnotification id="enable-e10s-notification" hidden="true">
<popupnotificationcontent orient="vertical"/>
</popupnotification>
#endif
<popupnotification id="addon-progress-notification" hidden="true">
<popupnotificationcontent orient="vertical">
<progressmeter id="addon-progress-notification-progressmeter"/>
+4 -3
View File
@@ -1,6 +1,7 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/. */
* 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/. */
var Cc = Components.classes;
var Ci = Components.interfaces;
@@ -10,7 +11,7 @@ const appStartup = Services.startup;
Cu.import("resource://gre/modules/ResetProfile.jsm");
let defaultToReset = false;
var defaultToReset = false;
function restartApp() {
appStartup.quit(appStartup.eForceQuit | appStartup.eRestart);
+1 -1
View File
@@ -1,4 +1,4 @@
// -*- indent-tabs-mode: nil; js-indent-level: 4 -*-
// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+1 -1
View File
@@ -276,7 +276,7 @@ var gSanitizePromptDialog = {
this.showItemList();
else
this.hideItemList();
}
},
#ifdef CRH_DIALOG_TREE_VIEW
// A duration value; used in the same context as Sanitizer.TIMESPAN_HOUR,
+7 -7
View File
@@ -98,7 +98,7 @@ addMessageListener("SecondScreen:tab-mirror", function(message) {
}
});
let AboutHomeListener = {
var AboutHomeListener = {
init: function(chromeGlobal) {
chromeGlobal.addEventListener('AboutHomeLoad', this, false, true);
},
@@ -265,7 +265,7 @@ let AboutHomeListener = {
};
AboutHomeListener.init(this);
let AboutPrivateBrowsingListener = {
var AboutPrivateBrowsingListener = {
init(chromeGlobal) {
chromeGlobal.addEventListener("AboutPrivateBrowsingOpenWindow", this,
false, true);
@@ -288,7 +288,7 @@ let AboutPrivateBrowsingListener = {
};
AboutPrivateBrowsingListener.init(this);
let AboutReaderListener = {
var AboutReaderListener = {
_articlePromise: null,
@@ -403,7 +403,7 @@ let AboutReaderListener = {
AboutReaderListener.init();
let ContentSearchMediator = {
var ContentSearchMediator = {
whitelist: new Set([
"about:home",
@@ -458,7 +458,7 @@ let ContentSearchMediator = {
};
ContentSearchMediator.init(this);
let PageStyleHandler = {
var PageStyleHandler = {
init: function() {
addMessageListener("PageStyle:Switch", this);
addMessageListener("PageStyle:Disable", this);
@@ -587,7 +587,7 @@ addMessageListener("Browser:AppTab", function(message) {
}
});
let WebBrowserChrome = {
var WebBrowserChrome = {
onBeforeLinkTraversal: function(originalTarget, linkURI, linkNode, isAppTab) {
return BrowserUtils.onBeforeLinkTraversal(originalTarget, linkURI, linkNode, isAppTab);
},
@@ -610,7 +610,7 @@ if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
}
let DOMFullscreenHandler = {
var DOMFullscreenHandler = {
_fullscreenDoc: null,
init: function() {
@@ -0,0 +1,81 @@
/* 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/. */
var {WebChannel} = Cu.import("resource://gre/modules/WebChannel.jsm", {});
const TEST_URL_TAIL = "example.com/browser/browser/base/content/test/general/test_remoteTroubleshoot.html"
const TEST_URI_GOOD = Services.io.newURI("https://" + TEST_URL_TAIL, null, null);
const TEST_URI_BAD = Services.io.newURI("http://" + TEST_URL_TAIL, null, null);
// Creates a one-shot web-channel for the test data to be sent back from the test page.
function promiseChannelResponse(channelID, originOrPermission) {
return new Promise((resolve, reject) => {
let channel = new WebChannel(channelID, originOrPermission);
channel.listen((id, data, target) => {
channel.stopListening();
resolve(data);
});
});
};
// Loads the specified URI in a new tab and waits for it to send us data on our
// test web-channel and resolves with that data.
function promiseNewChannelResponse(uri) {
let channelPromise = promiseChannelResponse("test-remote-troubleshooting-backchannel",
uri);
let tab = gBrowser.loadOneTab(uri.spec, { inBackground: false });
return promiseTabLoaded(tab).then(
() => channelPromise
).then(data => {
gBrowser.removeTab(tab);
return data;
});
}
add_task(function*() {
// We haven't set a permission yet - so even the "good" URI should fail.
let got = yield promiseNewChannelResponse(TEST_URI_GOOD);
// Should have no data.
Assert.ok(got.message === undefined, "should have failed to get any data");
// Add a permission manager entry for our URI.
Services.perms.add(TEST_URI_GOOD,
"remote-troubleshooting",
Services.perms.ALLOW_ACTION);
registerCleanupFunction(() => {
Services.perms.remove(TEST_URI_GOOD, "remote-troubleshooting");
});
// Try again - now we are expecting a response with the actual data.
got = yield promiseNewChannelResponse(TEST_URI_GOOD);
// Check some keys we expect to always get.
Assert.ok(got.message.extensions, "should have extensions");
Assert.ok(got.message.graphics, "should have graphics");
// Check we have channel and build ID info:
Assert.equal(got.message.application.buildID, Services.appinfo.appBuildID,
"should have correct build ID");
let updateChannel = null;
try {
updateChannel = Cu.import("resource://gre/modules/UpdateUtils.jsm", {}).UpdateUtils.UpdateChannel;
} catch (ex) {}
if (!updateChannel) {
Assert.ok(!('updateChannel' in got.message.application),
"should not have update channel where not available.");
} else {
Assert.equal(got.message.application.updateChannel, updateChannel,
"should have correct update channel.");
}
// And check some keys we know we decline to return.
Assert.ok(!got.message.modifiedPreferences, "should not have a modifiedPreferences key");
Assert.ok(!got.message.crashes, "should not have crash info");
// Now a http:// URI - should get nothing even with the permission setup.
got = yield promiseNewChannelResponse(TEST_URI_BAD);
Assert.ok(got.message === undefined, "should have failed to get any data");
});
+1 -1
View File
@@ -62,7 +62,7 @@ browser.jar:
content/browser/content.js (content/content.js)
content/browser/defaultthemes/devedition.header.png (content/defaultthemes/devedition.header.png)
content/browser/defaultthemes/devedition.icon.png (content/defaultthemes/devedition.icon.png)
content/browser/newtab/newTab.xul (content/newtab/newTab.xul)
content/browser/newtab/newTab.xhtml (content/newtab/newTab.xhtml)
* content/browser/newtab/newTab.js (content/newtab/newTab.js)
content/browser/newtab/newTab.css (content/newtab/newTab.css)
* content/browser/pageinfo/pageInfo.xul (content/pageinfo/pageInfo.xul)
+1 -1
View File
@@ -82,7 +82,7 @@ static RedirEntry kRedirMap[] = {
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
nsIAboutModule::MAKE_LINKABLE |
nsIAboutModule::ALLOW_SCRIPT },
{ "newtab", "chrome://browser/content/newtab/newTab.xul",
{ "newtab", "chrome://browser/content/newtab/newTab.xhtml",
nsIAboutModule::ALLOW_SCRIPT },
{ "permissions", "chrome://browser/content/preferences/aboutPermissions.xul",
nsIAboutModule::ALLOW_SCRIPT },
@@ -2,7 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
let { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
var { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+23 -6
View File
@@ -97,6 +97,11 @@ XPCOMUtils.defineLazyModuleGetter(this, "SimpleServiceDiscovery",
XPCOMUtils.defineLazyModuleGetter(this, "ContentSearch",
"resource:///modules/ContentSearch.jsm");
#ifdef E10S_TESTING_ONLY
XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
"resource://gre/modules/UpdateUtils.jsm");
#endif
#ifdef MOZ_CRASHREPORTER
XPCOMUtils.defineLazyModuleGetter(this, "TabCrashReporter",
"resource:///modules/ContentCrashReporters.jsm");
@@ -2633,15 +2638,18 @@ var DefaultBrowserCheck = {
};
#ifdef E10S_TESTING_ONLY
let E10SUINotification = {
var E10SUINotification = {
// Increase this number each time we want to roll out an
// e10s testing period to Nightly users.
CURRENT_NOTICE_COUNT: 0,
CURRENT_NOTICE_COUNT: 1,
CURRENT_PROMPT_PREF: "browser.displayedE10SPrompt.1",
PREVIOUS_PROMPT_PREF: "browser.displayedE10SPrompt",
checkStatus: function() {
let skipE10sChecks = false;
try {
let updateChannel = UpdateChannel.get();
let updateChannel = UpdateUtils.UpdateChannel;
let channelAuthorized = updateChannel == "nightly" || updateChannel == "aurora";
skipE10sChecks = !channelAuthorized ||
@@ -2672,16 +2680,25 @@ let E10SUINotification = {
} else {
let e10sPromptShownCount = 0;
try {
e10sPromptShownCount = Services.prefs.getIntPref("browser.displayedE10SPrompt");
e10sPromptShownCount = Services.prefs.getIntPref(this.CURRENT_PROMPT_PREF);
} catch(e) {}
let isHardwareAccelerated = true;
try {
let win = RecentWindow.getMostRecentBrowserWindow();
let winutils = win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
isHardwareAccelerated = winutils.layerManagerType != "Basic";
} catch (e) {}
if (!Services.appinfo.inSafeMode &&
!Services.appinfo.accessibilityEnabled &&
isHardwareAccelerated &&
e10sPromptShownCount < 5) {
Services.tm.mainThread.dispatch(() => {
try {
this._showE10SPrompt();
Services.prefs.setIntPref("browser.displayedE10SPrompt", e10sPromptShownCount + 1);
Services.prefs.setIntPref(this.CURRENT_PROMPT_PREF, e10sPromptShownCount + 1);
Services.prefs.clearUserPref(this.PREVIOUS_PROMPT_PREF);
} catch (ex) {
Cu.reportError("Failed to show e10s prompt: " + ex);
}
@@ -2756,7 +2773,7 @@ let E10SUINotification = {
label: win.gNavigatorBundle.getString("e10s.offerPopup.noThanks.label"),
accessKey: win.gNavigatorBundle.getString("e10s.offerPopup.noThanks.accesskey"),
callback: function () {
Services.prefs.setIntPref("browser.displayedE10SPrompt", 5);
Services.prefs.setIntPref(E10SUINotification.CURRENT_PROMPT_PREF, 5);
}
}
];
@@ -2766,7 +2783,7 @@ let E10SUINotification = {
persistWhileVisible: true
};
win.PopupNotifications.show(browser, "enable_e10s", promptMessage, null, mainAction, secondaryActions, options);
win.PopupNotifications.show(browser, "enable-e10s", promptMessage, null, mainAction, secondaryActions, options);
let highlights = [
win.gNavigatorBundle.getString("e10s.offerPopup.highlight1"),
@@ -7,3 +7,16 @@ newtab.unpin=Unpin this site
newtab.block=Remove this site
newtab.show=Show the new tab page
newtab.hide=Hide the new tab page
# LOCALIZATION NOTE(newtab.sponsored.button): This text appears for sponsored
# and enhanced tiles on the same line as the tile's title, so prefer short
# strings to avoid overlap. This string should be uppercase.
newtab.sponsored.button=SPONSORED
# LOCALIZATION NOTE(newtab.sponsored.explain): %1$S will be replaced inline by
# the (X) block icon. %2$S will be replaced by an active link using string
# newtab.learn.link as text.
newtab.sponsored.explain=This tile is being shown to you on behalf of a Mozilla partner. You can remove it at any time by clicking the %1$S button. %2$S
# LOCALIZATION NOTE(newtab.enhanced.explain): %1$S will be replaced inline by
# the gear icon used to customize the new tab window. %2$S will be replaced by
# an active link using string newtab.learn.link as text.
newtab.enhanced.explain=A Mozilla partner has visually enhanced this tile, replacing the screenshot. You can turn off enhanced tiles by clicking the %1$S button for your preferences. %2$S
newtab.learn.link=Learn more…
+3 -3
View File
@@ -26,8 +26,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "OS",
"resource://gre/modules/osfile.jsm")
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
"resource://gre/modules/Promise.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
"resource://gre/modules/UpdateChannel.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
"resource://gre/modules/UpdateUtils.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "eTLD",
"@mozilla.org/network/effective-tld-service;1",
"nsIEffectiveTLDService");
@@ -280,7 +280,7 @@ var DirectoryLinksProvider = {
_fetchAndCacheLinks: function DirectoryLinksProvider_fetchAndCacheLinks(uri) {
// Replace with the same display locale used for selecting links data
uri = uri.replace("%LOCALE%", this.locale);
uri = uri.replace("%CHANNEL%", UpdateChannel.get());
uri = uri.replace("%CHANNEL%", UpdateUtils.UpdateChannel);
return this._downloadJsonData(uri).then(json => {
return OS.File.writeAtomic(this._directoryFilePath, json, {tmpPath: this._directoryFilePath + ".tmp"});
+1 -1
View File
@@ -25,7 +25,7 @@ Cu.import("resource://gre/modules/Services.jsm");
*/
const HANG_EXPIRATION_TIME = 10000;
let ProcessHangMonitor = {
var ProcessHangMonitor = {
/**
* Collection of hang reports that haven't expired or been dismissed
* by the user. The keys are nsIHangReports and values keys are
-1
View File
@@ -85,7 +85,6 @@ browser.jar:
skin/classic/browser/feeds/subscribe.css (feeds/subscribe.css)
skin/classic/browser/feeds/subscribe-ui.css (feeds/subscribe-ui.css)
skin/classic/browser/newtab/newTab.css (newtab/newTab.css)
skin/classic/browser/newtab/controls.png (newtab/controls.png)
skin/classic/browser/newtab/noise.png (newtab/noise.png)
skin/classic/browser/places/bookmarksMenu.png (places/bookmarksMenu.png)
skin/classic/browser/places/bookmarksToolbar.png (places/bookmarksToolbar.png)
-1
View File
@@ -105,7 +105,6 @@ browser.jar:
skin/classic/browser/feeds/subscribe.css (feeds/subscribe.css)
skin/classic/browser/feeds/subscribe-ui.css (feeds/subscribe-ui.css)
skin/classic/browser/newtab/newTab.css (newtab/newTab.css)
skin/classic/browser/newtab/controls.png (newtab/controls.png)
skin/classic/browser/newtab/noise.png (newtab/noise.png)
skin/classic/browser/places/places.css (places/places.css)
* skin/classic/browser/places/organizer.css (places/organizer.css)
+11
View File
@@ -19,6 +19,17 @@
skin/classic/browser/addons/addon-install-anchor.svg (../shared/addons/addon-install-anchor.svg)
skin/classic/browser/fullscreen/insecure.svg (../shared/fullscreen/insecure.svg)
skin/classic/browser/fullscreen/secure.svg (../shared/fullscreen/secure.svg)
skin/classic/browser/search-indicator.png (../shared/search/search-indicator.png)
skin/classic/browser/search-indicator@2x.png (../shared/search/search-indicator@2x.png)
skin/classic/browser/search-engine-placeholder.png (../shared/search/search-engine-placeholder.png)
skin/classic/browser/search-engine-placeholder@2x.png (../shared/search/search-engine-placeholder@2x.png)
skin/classic/browser/badge-add-engine.png (../shared/search/badge-add-engine.png)
skin/classic/browser/badge-add-engine@2x.png (../shared/search/badge-add-engine@2x.png)
skin/classic/browser/search-indicator-badge-add.png (../shared/search/search-indicator-badge-add.png)
skin/classic/browser/search-indicator-badge-add@2x.png (../shared/search/search-indicator-badge-add@2x.png)
skin/classic/browser/search-history-icon.svg (../shared/search/history-icon.svg)
skin/classic/browser/search-indicator-magnifying-glass.svg (../shared/search/search-indicator-magnifying-glass.svg)
skin/classic/browser/search-arrow-go.svg (../shared/search/search-arrow-go.svg)
skin/classic/browser/reader-mode-16.png (../shared/reader/reader-mode-16.png)
skin/classic/browser/session-restore.svg (../shared/incontent-icons/session-restore.svg)
skin/classic/browser/tab-crashed.svg (../shared/incontent-icons/tab-crashed.svg)
Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 425 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 888 B

@@ -0,0 +1,22 @@
<?xml version="1.0"?>
<!-- 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/. -->
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" viewBox="0 0 16 16">
<style>
use:not(:target) {
display: none;
}
use {
fill: graytext;
}
use[id$="-active"] {
fill: HighlightText;
}
</style>
<defs>
<path id="search-history-glyph" d="M8,1C4.1,1,1,4.1,1,8c0,3.9,3.1,7,7,7c3.9,0,7-3.1,7-7 C15,4.1,11.9,1,8,1z M8,13.3c-2.9,0-5.3-2.4-5.3-5.3S5.1,2.7,8,2.7c2.9,0,5.3,2.4,5.3,5.3S10.9,13.3,8,13.3z M10.5,7H9V5 c0-0.6-0.4-1-1-1S7,4.4,7,5v3c0,0.6,0.4,1,1,1h2.5c0.6,0,1-0.4,1-1C11.5,7.4,11.1,7,10.5,7z"/>
</defs>
<use id="search-history-icon" xlink:href="#search-history-glyph"/>
<use id="search-history-icon-active" xlink:href="#search-history-glyph"/>
</svg>

After

Width:  |  Height:  |  Size: 1000 B

@@ -0,0 +1,22 @@
<?xml version="1.0"?>
<!-- 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/. -->
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" viewBox="0 0 16 16">
<style>
use:not(:target) {
display: none;
}
use {
fill: #616366;
}
use[id$="-inverted"] {
fill: #fff;
}
</style>
<defs>
<path id="search-arrow-go-glyph" d="M1,7v2.2C1,9.8,1.4,10,2,10h7.5l-3,3.1c-0.4,0.3-0.4,1,0,1.4l0.8,0.8 c0.4,0.4,1,0.4,1.4,0l6.6-6.6c0.4-0.4,0.4-1,0-1.4L8.7,0.7c-0.4-0.4-1-0.4-1.4,0L6.5,1.6C6.1,2,6.1,2.6,6.5,3l3,3H2C1.4,6,1,6.4,1,7z"/>
</defs>
<use id="search-arrow-go" xlink:href="#search-arrow-go-glyph"/>
<use id="search-arrow-go-inverted" xlink:href="#search-arrow-go-glyph"/>
</svg>

After

Width:  |  Height:  |  Size: 926 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 252 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 461 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1000 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

@@ -0,0 +1,7 @@
<?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/. -->
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path fill="#808080" d="M21.7,20.3l-1.4,1.4l-5.4-5.4c-1.3,1-3,1.7-4.9,1.7 c-4.4,0-8-3.6-8-8c0-4.4,3.6-8,8-8c4.4,0,8,3.6,8,8c0,1.8-0.6,3.5-1.7,4.9L21.7,20.3z M10,4c-3.3,0-6,2.7-6,6s2.7,6,6,6s6-2.7,6-6 S13.3,4,10,4z"/>
</svg>

After

Width:  |  Height:  |  Size: 561 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 344 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 694 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

-1
View File
@@ -116,7 +116,6 @@ browser.jar:
skin/classic/browser/feeds/subscribe.css (feeds/subscribe.css)
skin/classic/browser/feeds/subscribe-ui.css (feeds/subscribe-ui.css)
skin/classic/browser/newtab/newTab.css (newtab/newTab.css)
skin/classic/browser/newtab/controls.png (newtab/controls.png)
skin/classic/browser/newtab/noise.png (newtab/noise.png)
skin/classic/browser/places/places.css (places/places.css)
skin/classic/browser/places/organizer.css (places/organizer.css)
+1 -1
View File
@@ -17,7 +17,7 @@ import traceback
import zipfile
from automation import Automation
from mozlog.structured import get_default_logger
from mozlog import get_default_logger
from mozprocess import ProcessHandlerMixin
+1 -1
View File
@@ -13,7 +13,7 @@ import sys
from automation import Automation
from devicemanager import DMError, DeviceManager
from mozlog.structured import get_default_logger
from mozlog import get_default_logger
import mozcrash
# signatures for logcat messages that we don't care about much
+11
View File
@@ -497,6 +497,17 @@ this.PermissionsTable = { geolocation: {
trusted: DENY_ACTION,
privileged: ALLOW_ACTION,
certified: ALLOW_ACTION
},
"system-app-only-audio-channels-in-app": {
app: DENY_ACTION,
privileged: DENY_ACTION,
certified: ALLOW_ACTION
},
"killswitch": {
app: DENY_ACTION,
trusted: DENY_ACTION,
privileged: DENY_ACTION,
certified: ALLOW_ACTION
}
};
+19
View File
@@ -4528,6 +4528,25 @@ this.DOMApplicationRegistry = {
}
},
// Returns a promise that resolves once all the add-ons are disabled.
disableAllAddons: function() {
for (let id in this.webapps) {
let app = this.webapps[id];
if (app.role == "addon" && app.enabled) {
app.enabled = false;
MessageBroadcaster.broadcastMessage("Webapps:UpdateState", {
app: app,
id: app.id
});
MessageBroadcaster.broadcastMessage("Webapps:SetEnabled:Return", app);
UserCustomizations.unregister(app);
}
}
return this._saveApps();
},
getManifestFor: function(aManifestURL, aEntryPoint) {
let id = this._appIdForManifestURL(aManifestURL);
let app = this.webapps[id];
@@ -45,6 +45,24 @@ NS_IMPL_CYCLE_COLLECTION_INHERITED(BrowserElementAudioChannel,
mTabParent,
mBrowserElementAPI)
/* static */ already_AddRefed<BrowserElementAudioChannel>
BrowserElementAudioChannel::Create(nsPIDOMWindow* aWindow,
nsIFrameLoader* aFrameLoader,
nsIBrowserElementAPI* aAPI,
AudioChannel aAudioChannel,
ErrorResult& aRv)
{
nsRefPtr<BrowserElementAudioChannel> ac =
new BrowserElementAudioChannel(aWindow, aFrameLoader, aAPI, aAudioChannel);
aRv = ac->Initialize();
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
return ac.forget();
}
BrowserElementAudioChannel::BrowserElementAudioChannel(
nsPIDOMWindow* aWindow,
nsIFrameLoader* aFrameLoader,
@@ -58,7 +76,6 @@ BrowserElementAudioChannel::BrowserElementAudioChannel(
{
MOZ_ASSERT(NS_IsMainThread());
AssertIsInMainProcess();
MOZ_ASSERT(mFrameLoader);
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (obs) {
@@ -94,6 +111,17 @@ BrowserElementAudioChannel::~BrowserElementAudioChannel()
nsresult
BrowserElementAudioChannel::Initialize()
{
if (!mFrameLoader) {
nsCOMPtr<nsPIDOMWindow> window = GetOwner();
if (!window) {
return NS_ERROR_FAILURE;
}
mFrameWindow = window->GetScriptableTop();
mFrameWindow = mFrameWindow->GetOuterWindow();
return NS_OK;
}
nsCOMPtr<nsIDocShell> docShell;
nsresult rv = mFrameLoader->GetDocShell(getter_AddRefs(docShell));
if (NS_WARN_IF(NS_FAILED(rv))) {

Some files were not shown because too many files have changed in this diff Show More