Publish Vulpes Preview 15 sources for desktop, Android and Tundra
Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
This commit is contained in:
parent
93e3a48004
commit
b38dea0597
25901 changed files with 9982925 additions and 369 deletions
196
gaia/tv_apps/smart-system/js/devtools/developer_hud.js
Executable file
196
gaia/tv_apps/smart-system/js/devtools/developer_hud.js
Executable file
|
|
@ -0,0 +1,196 @@
|
|||
/* global SettingsCache */
|
||||
|
||||
(function(exports) {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* The Developer HUD displays app metrics as an overlay on top of MozApps.
|
||||
* @class DeveloperHUD
|
||||
*/
|
||||
function DeveloperHUD() {
|
||||
SettingsCache.observe('devtools.overlay.system',
|
||||
false, this.toggleSystemHUD.bind(this));
|
||||
}
|
||||
|
||||
DeveloperHUD.prototype = {
|
||||
|
||||
start: function() {
|
||||
window.addEventListener('developer-hud-update', this);
|
||||
},
|
||||
|
||||
stop: function() {
|
||||
window.removeEventListener('developer-hud-update', this);
|
||||
},
|
||||
|
||||
_showSystemHUD: false,
|
||||
toggleSystemHUD: function(enabled) {
|
||||
if (!enabled) {
|
||||
this.display(window, {});
|
||||
}
|
||||
|
||||
this._showSystemHUD = enabled;
|
||||
},
|
||||
|
||||
handleEvent: function(e) {
|
||||
this.display(e.target, e.detail);
|
||||
e.preventDefault();
|
||||
},
|
||||
|
||||
/**
|
||||
* Display a HUD over an app to show its metrics' values.
|
||||
* @param {Object} target The iframe of the app being tracked.
|
||||
* @param {Object} data The metric values to display.
|
||||
*/
|
||||
display: function(target, data) {
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
|
||||
// For regular mozbrowser iframes use the div container.
|
||||
var appwindow = target.parentElement;
|
||||
|
||||
// For system messages, directly insert the hud into the #screen
|
||||
// wrapper if needed.
|
||||
if (!appwindow) {
|
||||
if (target === window && this._showSystemHUD) {
|
||||
appwindow = document.getElementById('screen');
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var overlay = appwindow.querySelector(':scope > .developer-hud');
|
||||
|
||||
if (!overlay) {
|
||||
overlay = document.createElement('div');
|
||||
overlay.classList.add('developer-hud');
|
||||
appwindow.appendChild(overlay);
|
||||
}
|
||||
|
||||
if (!data.metrics || data.metrics.length < 1) {
|
||||
overlay.remove();
|
||||
return;
|
||||
}
|
||||
|
||||
var canvas = overlay.querySelector('.widgets');
|
||||
if (!canvas) {
|
||||
canvas = document.createElement('canvas');
|
||||
canvas.classList.add('widgets');
|
||||
canvas.height = 30;
|
||||
overlay.appendChild(canvas);
|
||||
}
|
||||
|
||||
// The width is always set to the size of the screen in case the screen
|
||||
// has been rotated. This does not cause a reflow as long as the width
|
||||
// is the same.
|
||||
canvas.width = window.innerWidth;
|
||||
|
||||
var ctx = canvas.getContext('2d');
|
||||
ctx.font = '18px sans-serif';
|
||||
ctx.textBaseline = 'top';
|
||||
|
||||
ctx.save();
|
||||
|
||||
// Widgets are positioned starting from the right side of the screen.
|
||||
ctx.translate(canvas.width, 0);
|
||||
|
||||
data.metrics.reverse().forEach((function(metric) {
|
||||
var widget = this.widget(metric);
|
||||
if (!widget) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The size of a widget is comprise between 30px and the size of its
|
||||
// content. There is also an additional padding of 5px on each side.
|
||||
var textWidth = ctx.measureText(widget.value).width;
|
||||
var widgetWidth = Math.max(30, textWidth) + (5 * 2);
|
||||
|
||||
// Position the widget relatively to the last position on the left
|
||||
// of the screen.
|
||||
ctx.translate(-widgetWidth, 0);
|
||||
|
||||
// Fill widget background-color.
|
||||
ctx.fillStyle = widget.color;
|
||||
ctx.fillRect(0, 0, widgetWidth, canvas.height);
|
||||
|
||||
// Draw widget text centered both horizontally and vertically.
|
||||
ctx.fillStyle = 'white';
|
||||
ctx.fillText(widget.value,
|
||||
(widgetWidth - textWidth) / 2,
|
||||
canvas.height / 4);
|
||||
}).bind(this));
|
||||
|
||||
ctx.restore();
|
||||
},
|
||||
|
||||
widget: function(metric) {
|
||||
var value = metric.value;
|
||||
if (!value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var color;
|
||||
switch(metric.name) {
|
||||
case 'warnings':
|
||||
color = 'orange';
|
||||
break;
|
||||
|
||||
case 'errors':
|
||||
color = 'red';
|
||||
break;
|
||||
|
||||
case 'security':
|
||||
color = 'black';
|
||||
break;
|
||||
|
||||
case 'reflows':
|
||||
color = 'purple';
|
||||
break;
|
||||
|
||||
case 'jank':
|
||||
color = 'cornflowerblue';
|
||||
value += 'ms';
|
||||
break;
|
||||
|
||||
case 'uss':
|
||||
color = 'dimgrey';
|
||||
value = this.formatMemory(value);
|
||||
break;
|
||||
|
||||
case 'memory':
|
||||
color = 'lightslategrey';
|
||||
value = this.formatMemory(value);
|
||||
break;
|
||||
|
||||
default:
|
||||
color = this.colorHash(metric.name);
|
||||
break;
|
||||
}
|
||||
|
||||
return {
|
||||
'color': color,
|
||||
'value': value
|
||||
};
|
||||
},
|
||||
|
||||
colorHash: function(name) {
|
||||
var hue = 0;
|
||||
for (var i = 0; i < name.length; i++) {
|
||||
hue += name.charCodeAt(i);
|
||||
}
|
||||
return 'hsl(' + (hue % 360) + ', 75%, 50%)';
|
||||
},
|
||||
|
||||
formatMemory: function(bytes) {
|
||||
var prefix = ['','K','M','G','T','P','E','Z','Y'];
|
||||
var i = 0;
|
||||
for (; bytes > 1024 && i < prefix.length; ++i) {
|
||||
bytes /= 1024;
|
||||
}
|
||||
return (Math.round(bytes * 100) / 100) + ' ' + prefix[i] + 'B';
|
||||
}
|
||||
};
|
||||
|
||||
exports.DeveloperHUD = DeveloperHUD;
|
||||
|
||||
}(window));
|
||||
140
gaia/tv_apps/smart-system/js/devtools/logshake.js
Executable file
140
gaia/tv_apps/smart-system/js/devtools/logshake.js
Executable file
|
|
@ -0,0 +1,140 @@
|
|||
/* global MozActivity, dump, NotificationHelper */
|
||||
(function(exports) {
|
||||
'use strict';
|
||||
/**
|
||||
* This developer system module captures a snapshot of the current device
|
||||
* logs as displayed by logcat using DeviceStorage to persist the file for
|
||||
* future access. It communicates with gecko code running in
|
||||
* b2g/chrome/content/shell.js using a SystemAppProxy custom event based API.
|
||||
* It requires the preference 'devtools.logshake' to be enabled
|
||||
*
|
||||
* @class LogShake
|
||||
*/
|
||||
function LogShake() {
|
||||
}
|
||||
|
||||
function debug(str) {
|
||||
dump('LogShake: ' + str + '\n');
|
||||
}
|
||||
|
||||
LogShake.prototype = {
|
||||
/**
|
||||
* Start existing, observing for capture-logs events caused by Gecko
|
||||
* LogShake
|
||||
*/
|
||||
start: function() {
|
||||
this.startCaptureLogsListener();
|
||||
},
|
||||
|
||||
startCaptureLogsListener: function() {
|
||||
debug('starting captureLogs listener');
|
||||
window.addEventListener('capture-logs-success', this);
|
||||
window.addEventListener('capture-logs-error', this);
|
||||
},
|
||||
|
||||
stopCaptureLogsListener: function() {
|
||||
debug('stopping captureLogs listener');
|
||||
window.removeEventListener('capture-logs-success', this);
|
||||
window.removeEventListener('capture-logs-error', this);
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle a capture-logs-success or capture-logs-error event, dispatching
|
||||
* to the appropriate handler
|
||||
*/
|
||||
handleEvent: function(event) {
|
||||
debug('handling event ' + event.type);
|
||||
if (event.type === 'capture-logs-success') {
|
||||
this.handleCaptureLogsSuccess(event);
|
||||
} else if (event.type === 'capture-logs-error') {
|
||||
this.handleCaptureLogsError(event);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle an event of type capture-logs-success. event.detail.locations is
|
||||
* an array of absolute paths to the saved log files, and
|
||||
* event.detail.logFolder is the folder name where the logs are located.
|
||||
*/
|
||||
handleCaptureLogsSuccess: function(event) {
|
||||
debug('handling capture-logs-success');
|
||||
navigator.vibrate(100);
|
||||
this._notify('logsSaved', event.detail.logPrefix, function() {
|
||||
var logFilenames = event.detail.logFilenames;
|
||||
var logFiles = [];
|
||||
|
||||
var storage = navigator.getDeviceStorage('sdcard');
|
||||
|
||||
var requestsRemaining = logFilenames.length;
|
||||
var self = this;
|
||||
|
||||
function onSuccess() {
|
||||
/* jshint validthis: true */
|
||||
logFiles.push(this.result);
|
||||
requestsRemaining -= 1;
|
||||
if (requestsRemaining === 0) {
|
||||
var logNames = logFiles.map(function(file) {
|
||||
// For some reason file.name contains the full path despite
|
||||
// File's documentation explicitly stating that the opposite.
|
||||
var pathComponents = file.name.split('/');
|
||||
return pathComponents[pathComponents.length - 1];
|
||||
});
|
||||
/* jshint nonew: false */
|
||||
new MozActivity({
|
||||
name: 'new',
|
||||
data: {
|
||||
type: 'mail',
|
||||
blobs: logFiles,
|
||||
filenames: logNames
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function onError() {
|
||||
/* jshint validthis: true */
|
||||
self.handleCaptureLogsError({detail: {error: this.error}});
|
||||
}
|
||||
|
||||
for (var logFilename of logFilenames) {
|
||||
var req = storage.get(logFilename);
|
||||
req.onsuccess = onSuccess;
|
||||
req.onerror = onError;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
handleCaptureLogsError: function(event) {
|
||||
debug('handling capture logs error');
|
||||
var error = '';
|
||||
if (event) {
|
||||
error = event.detail.error;
|
||||
}
|
||||
this._notify('logsFailed', error);
|
||||
},
|
||||
|
||||
_notify: function(titleId, body, onclick) {
|
||||
|
||||
NotificationHelper.send(titleId, {
|
||||
'bodyL10n': body
|
||||
}).then(function(notification) {
|
||||
if(onclick) {
|
||||
notification.addEventListener('click', onclick);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Stop the component, removing all listeners if necessary
|
||||
*/
|
||||
stop: function() {
|
||||
this.stopCaptureLogsListener();
|
||||
}
|
||||
};
|
||||
|
||||
exports.LogShake = LogShake;
|
||||
|
||||
// XXX: See issue described in screenshot.js
|
||||
exports.logshake = new LogShake();
|
||||
exports.logshake.start();
|
||||
})(window);
|
||||
Loading…
Reference in a new issue