Merge fx-team to central, a=merge

This commit is contained in:
Wes Kocher 2015-07-15 16:33:58 -07:00
commit c0dd754465
88 changed files with 1606 additions and 678 deletions

View File

@ -271,7 +271,6 @@ pref("general.autoScroll", true);
pref("browser.shell.checkDefaultBrowser", true);
pref("browser.shell.shortcutFavicons",true);
pref("browser.shell.mostRecentDateSetAsDefault", "");
pref("browser.shell.windows10DefaultBrowserABTest", -1);
// 0 = blank, 1 = home (browser.startup.homepage), 2 = last visited page, 3 = resume previous browser session
// The behavior of option 3 is detailed at: http://wiki.mozilla.org/Session_Restore

View File

@ -397,58 +397,73 @@ const gXPInstallObserver = {
};
var LightWeightThemeWebInstaller = {
init: function () {
let mm = window.messageManager;
mm.addMessageListener("LightWeightThemeWebInstaller:Install", this);
mm.addMessageListener("LightWeightThemeWebInstaller:Preview", this);
mm.addMessageListener("LightWeightThemeWebInstaller:ResetPreview", this);
},
receiveMessage: function (message) {
// ignore requests from background tabs
if (message.target != gBrowser.selectedBrowser) {
return;
}
let data = message.data;
switch (message.name) {
case "LightWeightThemeWebInstaller:Install": {
this._installRequest(data.themeData, data.baseURI);
break;
}
case "LightWeightThemeWebInstaller:Preview": {
this._preview(data.themeData, data.baseURI);
break;
}
case "LightWeightThemeWebInstaller:ResetPreview": {
this._resetPreview(data && data.baseURI);
break;
}
}
},
handleEvent: function (event) {
switch (event.type) {
case "InstallBrowserTheme":
case "PreviewBrowserTheme":
case "ResetBrowserThemePreview":
// ignore requests from background tabs
if (event.target.ownerDocument.defaultView.top != content)
return;
}
switch (event.type) {
case "InstallBrowserTheme":
this._installRequest(event);
break;
case "PreviewBrowserTheme":
this._preview(event);
break;
case "ResetBrowserThemePreview":
this._resetPreview(event);
break;
case "pagehide":
case "TabSelect":
case "TabSelect": {
this._resetPreview();
break;
}
}
},
get _manager () {
var temp = {};
let temp = {};
Cu.import("resource://gre/modules/LightweightThemeManager.jsm", temp);
delete this._manager;
return this._manager = temp.LightweightThemeManager;
},
_installRequest: function (event) {
var node = event.target;
var data = this._getThemeFromNode(node);
if (!data)
return;
_installRequest: function (dataString, baseURI) {
let data = this._manager.parseTheme(dataString, baseURI);
if (this._isAllowed(node)) {
if (!data) {
return;
}
if (this._isAllowed(baseURI)) {
this._install(data);
return;
}
var allowButtonText =
let allowButtonText =
gNavigatorBundle.getString("lwthemeInstallRequest.allowButton");
var allowButtonAccesskey =
let allowButtonAccesskey =
gNavigatorBundle.getString("lwthemeInstallRequest.allowButton.accesskey");
var message =
let message =
gNavigatorBundle.getFormattedString("lwthemeInstallRequest.message",
[node.ownerDocument.location.host]);
var buttons = [{
[makeURI(baseURI).host]);
let buttons = [{
label: allowButtonText,
accessKey: allowButtonAccesskey,
callback: function () {
@ -458,8 +473,8 @@ var LightWeightThemeWebInstaller = {
this._removePreviousNotifications();
var notificationBox = gBrowser.getNotificationBox();
var notificationBar =
let notificationBox = gBrowser.getNotificationBox();
let notificationBar =
notificationBox.appendNotification(message, "lwtheme-install-request", "",
notificationBox.PRIORITY_INFO_MEDIUM,
buttons);
@ -467,12 +482,13 @@ var LightWeightThemeWebInstaller = {
},
_install: function (newLWTheme) {
var previousLWTheme = this._manager.currentTheme;
let previousLWTheme = this._manager.currentTheme;
var listener = {
let listener = {
onEnabling: function(aAddon, aRequiresRestart) {
if (!aRequiresRestart)
if (!aRequiresRestart) {
return;
}
let messageString = gNavigatorBundle.getFormattedString("lwthemeNeedsRestart.message",
[aAddon.name], 1);
@ -509,7 +525,7 @@ var LightWeightThemeWebInstaller = {
return gNavigatorBundle.getString("lwthemePostInstallNotification." + id);
}
var buttons = [{
let buttons = [{
label: text("undoButton"),
accessKey: text("undoButton.accesskey"),
callback: function () {
@ -526,8 +542,8 @@ var LightWeightThemeWebInstaller = {
this._removePreviousNotifications();
var notificationBox = gBrowser.getNotificationBox();
var notificationBar =
let notificationBox = gBrowser.getNotificationBox();
let notificationBar =
notificationBox.appendNotification(text("message"),
"lwtheme-install-notification", "",
notificationBox.PRIORITY_INFO_MEDIUM,
@ -537,62 +553,54 @@ var LightWeightThemeWebInstaller = {
},
_removePreviousNotifications: function () {
var box = gBrowser.getNotificationBox();
let box = gBrowser.getNotificationBox();
["lwtheme-install-request",
"lwtheme-install-notification"].forEach(function (value) {
var notification = box.getNotificationWithValue(value);
let notification = box.getNotificationWithValue(value);
if (notification)
box.removeNotification(notification);
});
},
_previewWindow: null,
_preview: function (event) {
if (!this._isAllowed(event.target))
_preview: function (dataString, baseURI) {
if (!this._isAllowed(baseURI))
return;
var data = this._getThemeFromNode(event.target);
let data = this._manager.parseTheme(dataString, baseURI);
if (!data)
return;
this._resetPreview();
this._previewWindow = event.target.ownerDocument.defaultView;
this._previewWindow.addEventListener("pagehide", this, true);
gBrowser.tabContainer.addEventListener("TabSelect", this, false);
this._manager.previewTheme(data);
},
_resetPreview: function (event) {
if (!this._previewWindow ||
event && !this._isAllowed(event.target))
_resetPreview: function (baseURI) {
if (baseURI && !this._isAllowed(baseURI))
return;
this._previewWindow.removeEventListener("pagehide", this, true);
this._previewWindow = null;
gBrowser.tabContainer.removeEventListener("TabSelect", this, false);
this._manager.resetPreview();
},
_isAllowed: function (node) {
var pm = Services.perms;
var uri = node.ownerDocument.documentURIObject;
if (!uri.schemeIs("https"))
_isAllowed: function (srcURIString) {
let uri;
try {
uri = makeURI(srcURIString);
}
catch(e) {
//makeURI fails if srcURIString is a nonsense URI
return false;
}
if (!uri.schemeIs("https")) {
return false;
}
let pm = Services.perms;
return pm.testPermission(uri, "install") == pm.ALLOW_ACTION;
},
_getThemeFromNode: function (node) {
return this._manager.parseTheme(node.getAttribute("data-browsertheme"),
node.baseURI);
}
}
};
/*
* Listen for Lightweight Theme styling changes and update the browser's theme accordingly.

View File

@ -1372,9 +1372,7 @@ var gBrowserInit = {
placesContext.addEventListener("popuphiding", updateEditUIVisibility, false);
#endif
gBrowser.mPanelContainer.addEventListener("InstallBrowserTheme", LightWeightThemeWebInstaller, false, true);
gBrowser.mPanelContainer.addEventListener("PreviewBrowserTheme", LightWeightThemeWebInstaller, false, true);
gBrowser.mPanelContainer.addEventListener("ResetBrowserThemePreview", LightWeightThemeWebInstaller, false, true);
LightWeightThemeWebInstaller.init();
if (Win7Features)
Win7Features.onOpenWindow();

View File

@ -717,6 +717,57 @@ addMessageListener("ContextMenu:SearchFieldBookmarkData", (message) => {
{ spec, title, description, postData, charset });
});
let LightWeightThemeWebInstallListener = {
_previewWindow: null,
init: function() {
addEventListener("InstallBrowserTheme", this, false, true);
addEventListener("PreviewBrowserTheme", this, false, true);
addEventListener("ResetBrowserThemePreview", this, false, true);
},
handleEvent: function (event) {
switch (event.type) {
case "InstallBrowserTheme": {
sendAsyncMessage("LightWeightThemeWebInstaller:Install", {
baseURI: event.target.baseURI,
themeData: event.target.getAttribute("data-browsertheme"),
});
break;
}
case "PreviewBrowserTheme": {
sendAsyncMessage("LightWeightThemeWebInstaller:Preview", {
baseURI: event.target.baseURI,
themeData: event.target.getAttribute("data-browsertheme"),
});
this._previewWindow = event.target.ownerDocument.defaultView;
this._previewWindow.addEventListener("pagehide", this, true);
break;
}
case "pagehide": {
sendAsyncMessage("LightWeightThemeWebInstaller:ResetPreview");
this._resetPreviewWindow();
break;
}
case "ResetBrowserThemePreview": {
if (this._previewWindow) {
sendAsyncMessage("LightWeightThemeWebInstaller:ResetPreview",
{baseURI: event.target.baseURI});
this._resetPreviewWindow();
}
break;
}
}
},
_resetPreviewWindow: function () {
this._previewWindow.removeEventListener("pagehide", this, true);
this._previewWindow = null;
}
};
LightWeightThemeWebInstallListener.init();
function disableSetDesktopBackground(aTarget) {
// Disable the Set as Desktop Background menu item if we're still trying
// to load the image or the load failed.

View File

@ -227,7 +227,6 @@ skip-if = toolkit != "cocoa"
[browser_bug585830.js]
[browser_bug590206.js]
[browser_bug592338.js]
skip-if = e10s # Bug 653065 - Make the lightweight theme web installer ready for e10s
[browser_bug594131.js]
[browser_bug595507.js]
[browser_bug596687.js]

View File

@ -30,8 +30,7 @@ function test_install_http() {
gBrowser.selectedBrowser.removeEventListener("pageshow", arguments.callee, false);
executeSoon(function() {
var link = gBrowser.contentDocument.getElementById("theme-install");
EventUtils.synthesizeMouse(link, 2, 2, {}, gBrowser.contentWindow);
BrowserTestUtils.synthesizeMouse("#theme-install", 2, 2, {}, gBrowser.selectedBrowser);
is(LightweightThemeManager.currentTheme, null, "Should not have installed the test theme");
@ -57,19 +56,21 @@ function test_install_lwtheme() {
gBrowser.selectedBrowser.removeEventListener("pageshow", arguments.callee, false);
executeSoon(function() {
var link = gBrowser.contentDocument.getElementById("theme-install");
EventUtils.synthesizeMouse(link, 2, 2, {}, gBrowser.contentWindow);
BrowserTestUtils.synthesizeMouse("#theme-install", 2, 2, {}, gBrowser.selectedBrowser);
let notification;
let notificationBox = gBrowser.getNotificationBox(gBrowser.selectedBrowser);
waitForCondition(
() => (notification = notificationBox.getNotificationWithValue("lwtheme-install-notification")),
() => {
is(LightweightThemeManager.currentTheme.id, "test", "Should have installed the test theme");
is(LightweightThemeManager.currentTheme.id, "test", "Should have installed the test theme");
LightweightThemeManager.currentTheme = null;
gBrowser.removeTab(gBrowser.selectedTab);
Services.perms.remove(makeURI("http://example.com/"), "install");
LightweightThemeManager.currentTheme = null;
gBrowser.removeTab(gBrowser.selectedTab);
Services.perms.remove(makeURI("http://example.com/"), "install");
runNextTest();
});
runNextTest();
}
);
}, false);
},
@ -92,7 +93,6 @@ function test_lwtheme_switch_theme() {
gBrowser.selectedBrowser.removeEventListener("pageshow", arguments.callee, false);
executeSoon(function() {
var link = gBrowser.contentDocument.getElementById("theme-install");
wait_for_notification(function(aPanel) {
is(LightweightThemeManager.currentTheme, null, "Should not have installed the test lwtheme");
ok(aAddon.isActive, "Test theme should still be active");
@ -110,7 +110,7 @@ function test_lwtheme_switch_theme() {
runNextTest();
});
EventUtils.synthesizeMouse(link, 2, 2, {}, gBrowser.contentWindow);
BrowserTestUtils.synthesizeMouse("#theme-install", 2, 2, {}, gBrowser.selectedBrowser);
});
}, false);
});

790
browser/components/loop/content/shared/libs/sdk.js Normal file → Executable file
View File

@ -1,12 +1,12 @@
/**
* @license OpenTok JavaScript Library v2.5.1 23265fa HEAD
* @license OpenTok JavaScript Library v2.5.2 f4508e1 2015Q1.patch.1
* http://www.tokbox.com/
*
* Copyright (c) 2014 TokBox, Inc.
* Released under the MIT license
* http://opensource.org/licenses/MIT
*
* Date: April 13 06:37:42 2015
* Date: July 13 05:38:08 2015
*/
@ -15,12 +15,12 @@
!(function(window, OTHelpers, undefined) {
/**
* @license Common JS Helpers on OpenTok 0.3.0 058dfa5 2015Q1
* @license Common JS Helpers on OpenTok 0.3.0 f151b47 HEAD
* http://www.tokbox.com/
*
* Copyright (c) 2015 TokBox, Inc.
*
* Date: April 13 06:37:28 2015
* Date: July 13 05:37:51 2015
*
*/
@ -3716,6 +3716,19 @@ OTHelpers.getCookie = function(key) {
return null;
};
/*jshint browser:true, smarttabs:true*/
// tb_require('../helpers.js')
OTHelpers.castToBoolean = function(value, defaultValue) {
if (value === undefined) return defaultValue;
return value === 'true' || value === true;
};
OTHelpers.roundFloat = function(value, places) {
return Number(value.toFixed(places));
};
// tb_require('../helpers.js')
/* jshint globalstrict: true, strict: false, undef: true, unused: true,
@ -3895,19 +3908,6 @@ OTHelpers.Collection = function(idField) {
};
/*jshint browser:true, smarttabs:true*/
// tb_require('../helpers.js')
OTHelpers.castToBoolean = function(value, defaultValue) {
if (value === undefined) return defaultValue;
return value === 'true' || value === true;
};
OTHelpers.roundFloat = function(value, places) {
return Number(value.toFixed(places));
};
/*jshint browser:true, smarttabs:true*/
// tb_require('../helpers.js')
@ -6105,12 +6105,12 @@ OTHelpers.post = function(url, options, callback) {
/**
* @license TB Plugin 0.4.0.10 01e58ad 2015Q1
* @license TB Plugin 0.4.0.10 6935b20 HEAD
* http://www.tokbox.com/
*
* Copyright (c) 2015 TokBox, Inc.
*
* Date: April 13 06:37:38 2015
* Date: July 13 05:38:06 2015
*
*/
@ -7424,96 +7424,6 @@ var MediaConstraints = function(userConstraints) {
};
};
// tb_require('./header.js')
// tb_require('./shims.js')
/* global scope:true */
/* exported createFrame */
var createFrame = function createFrame (bodyContent, callbackId, callback) {
var Proto = function Frame () {},
api = new Proto(),
domElement = scope.document.createElement('iframe');
domElement.id = 'OTPlugin_frame_' + $.uuid().replace(/\-+/g, '');
domElement.style.border = '0';
try {
domElement.style.backgroundColor = 'rgba(0,0,0,0)';
} catch (err) {
// Old IE browsers don't support rgba
domElement.style.backgroundColor = 'transparent';
domElement.setAttribute('allowTransparency', 'true');
}
domElement.scrolling = 'no';
domElement.setAttribute('scrolling', 'no');
// This is necessary for IE, as it will not inherit it's doctype from
// the parent frame.
var frameContent = '<!DOCTYPE html><html><head>' +
'<meta http-equiv="x-ua-compatible" content="IE=Edge">' +
'<meta http-equiv="Content-type" content="text/html; charset=utf-8">' +
'<title></title></head><body>' +
bodyContent +
'<script>window.parent["' + callbackId + '"](' +
'document.querySelector("object")' +
');</script></body></html>';
var wrappedCallback = function() {
OTPlugin.log('LOADED IFRAME');
var doc = domElement.contentDocument || domElement.contentWindow.document;
if ($.env.iframeNeedsLoad) {
doc.body.style.backgroundColor = 'transparent';
doc.body.style.border = 'none';
if ($.env.name !== 'IE') {
// Skip this for IE as we use the bookmarklet workaround
// for THAT browser.
doc.open();
doc.write(frameContent);
doc.close();
}
}
if (callback) {
callback(
api,
domElement.contentWindow,
doc
);
}
};
scope.document.body.appendChild(domElement);
if($.env.iframeNeedsLoad) {
if ($.env.name === 'IE') {
// This works around some issues with IE and document.write.
// Basically this works by slightly abusing the bookmarklet/scriptlet
// functionality that all browsers support.
domElement.contentWindow.contents = frameContent;
/*jshint scripturl:true*/
domElement.src = 'javascript:window["contents"]';
/*jshint scripturl:false*/
}
$.on(domElement, 'load', wrappedCallback);
} else {
setTimeout(wrappedCallback, 0);
}
api.reparent = function reparent (target) {
// document.adoptNode(domElement);
target.appendChild(domElement);
};
api.element = domElement;
return api;
};
// tb_require('./header.js')
// tb_require('./shims.js')
// tb_require('./plugin_proxies.js')
@ -7845,6 +7755,96 @@ var // jshint -W098
});
};
// tb_require('./header.js')
// tb_require('./shims.js')
/* global scope:true */
/* exported createFrame */
var createFrame = function createFrame (bodyContent, callbackId, callback) {
var Proto = function Frame () {},
api = new Proto(),
domElement = scope.document.createElement('iframe');
domElement.id = 'OTPlugin_frame_' + $.uuid().replace(/\-+/g, '');
domElement.style.border = '0';
try {
domElement.style.backgroundColor = 'rgba(0,0,0,0)';
} catch (err) {
// Old IE browsers don't support rgba
domElement.style.backgroundColor = 'transparent';
domElement.setAttribute('allowTransparency', 'true');
}
domElement.scrolling = 'no';
domElement.setAttribute('scrolling', 'no');
// This is necessary for IE, as it will not inherit it's doctype from
// the parent frame.
var frameContent = '<!DOCTYPE html><html><head>' +
'<meta http-equiv="x-ua-compatible" content="IE=Edge">' +
'<meta http-equiv="Content-type" content="text/html; charset=utf-8">' +
'<title></title></head><body>' +
bodyContent +
'<script>window.parent["' + callbackId + '"](' +
'document.querySelector("object")' +
');</script></body></html>';
var wrappedCallback = function() {
OTPlugin.log('LOADED IFRAME');
var doc = domElement.contentDocument || domElement.contentWindow.document;
if ($.env.iframeNeedsLoad) {
doc.body.style.backgroundColor = 'transparent';
doc.body.style.border = 'none';
if ($.env.name !== 'IE') {
// Skip this for IE as we use the bookmarklet workaround
// for THAT browser.
doc.open();
doc.write(frameContent);
doc.close();
}
}
if (callback) {
callback(
api,
domElement.contentWindow,
doc
);
}
};
scope.document.body.appendChild(domElement);
if($.env.iframeNeedsLoad) {
if ($.env.name === 'IE') {
// This works around some issues with IE and document.write.
// Basically this works by slightly abusing the bookmarklet/scriptlet
// functionality that all browsers support.
domElement.contentWindow.contents = frameContent;
/*jshint scripturl:true*/
domElement.src = 'javascript:window["contents"]';
/*jshint scripturl:false*/
}
$.on(domElement, 'load', wrappedCallback);
} else {
setTimeout(wrappedCallback, 0);
}
api.reparent = function reparent (target) {
// document.adoptNode(domElement);
target.appendChild(domElement);
};
api.element = domElement;
return api;
};
// tb_require('./header.js')
// tb_require('./shims.js')
// tb_require('./proxy.js')
@ -8065,8 +8065,8 @@ if (!window.TB) window.TB = OT;
// tb_require('../js/ot.js')
OT.properties = {
version: 'v2.5.1', // The current version (eg. v2.0.4) (This is replaced by gradle)
build: '23265fa', // The current build hash (This is replaced by gradle)
version: 'v2.5.2', // The current version (eg. v2.0.4) (This is replaced by gradle)
build: 'f4508e1', // The current build hash (This is replaced by gradle)
// Whether or not to turn on debug logging by default
debug: 'false',
@ -8253,122 +8253,6 @@ OT.$.userAgent = function() { return OT.$.env.userAgent; };
// tb_require('../../../helpers/helpers.js')
/* jshint globalstrict: true, strict: false, undef: true, unused: true,
trailing: true, browser: true, smarttabs:true */
/* global OT */
// Rumor Messaging for JS
//
// https://tbwiki.tokbox.com/index.php/Rumor_:_Messaging_FrameWork
//
// @todo Rumor {
// Add error codes for all the error cases
// Add Dependability commands
// }
OT.Rumor = {
MessageType: {
// This is used to subscribe to address/addresses. The address/addresses the
// client specifies here is registered on the server. Once any message is sent to
// that address/addresses, the client receives that message.
SUBSCRIBE: 0,
// This is used to unsubscribe to address / addresses. Once the client unsubscribe
// to an address, it will stop getting messages sent to that address.
UNSUBSCRIBE: 1,
// This is used to send messages to arbitrary address/ addresses. Messages can be
// anything and Rumor will not care about what is included.
MESSAGE: 2,
// This will be the first message that the client sends to the server. It includes
// the uniqueId for that client connection and a disconnect_notify address that will
// be notified once the client disconnects.
CONNECT: 3,
// This will be the message used by the server to notify an address that a
// client disconnected.
DISCONNECT: 4,
//Enhancements to support Keepalives
PING: 7,
PONG: 8,
STATUS: 9
}
};
// tb_require('../../../helpers/helpers.js')
// tb_require('./rumor.js')
/* jshint globalstrict: true, strict: false, undef: true, unused: true,
trailing: true, browser: true, smarttabs:true */
/* global OT, OTPlugin */
!(function() {
OT.Rumor.PluginSocket = function(messagingURL, events) {
var webSocket,
state = 'initializing';
OTPlugin.initRumorSocket(messagingURL, OT.$.bind(function(err, rumorSocket) {
if(err) {
state = 'closed';
events.onClose({ code: 4999 });
} else if(state === 'initializing') {
webSocket = rumorSocket;
webSocket.onOpen(function() {
state = 'open';
events.onOpen();
});
webSocket.onClose(function(error) {
state = 'closed'; /* CLOSED */
events.onClose({ code: error });
});
webSocket.onError(function(error) {
state = 'closed'; /* CLOSED */
events.onError(error);
/* native websockets seem to do this, so should we */
events.onClose({ code: error });
});
webSocket.onMessage(function(type, addresses, headers, payload) {
var msg = new OT.Rumor.Message(type, addresses, headers, payload);
events.onMessage(msg);
});
webSocket.open();
} else {
this.close();
}
}, this));
this.close = function() {
if(state === 'initializing' || state === 'closed') {
state = 'closed';
return;
}
webSocket.close(1000, '');
};
this.send = function(msg) {
if(state === 'open') {
webSocket.send(msg);
}
};
this.isClosed = function() {
return state === 'closed';
};
};
}(this));
// tb_require('../../../helpers/helpers.js')
// https://code.google.com/p/stringencoding/
// An implementation of http://encoding.spec.whatwg.org/#api
// Modified by TokBox to remove all encoding support except for utf-8
@ -9424,6 +9308,122 @@ OT.Rumor = {
}(this));
// tb_require('../../../helpers/helpers.js')
/* jshint globalstrict: true, strict: false, undef: true, unused: true,
trailing: true, browser: true, smarttabs:true */
/* global OT */
// Rumor Messaging for JS
//
// https://tbwiki.tokbox.com/index.php/Rumor_:_Messaging_FrameWork
//
// @todo Rumor {
// Add error codes for all the error cases
// Add Dependability commands
// }
OT.Rumor = {
MessageType: {
// This is used to subscribe to address/addresses. The address/addresses the
// client specifies here is registered on the server. Once any message is sent to
// that address/addresses, the client receives that message.
SUBSCRIBE: 0,
// This is used to unsubscribe to address / addresses. Once the client unsubscribe
// to an address, it will stop getting messages sent to that address.
UNSUBSCRIBE: 1,
// This is used to send messages to arbitrary address/ addresses. Messages can be
// anything and Rumor will not care about what is included.
MESSAGE: 2,
// This will be the first message that the client sends to the server. It includes
// the uniqueId for that client connection and a disconnect_notify address that will
// be notified once the client disconnects.
CONNECT: 3,
// This will be the message used by the server to notify an address that a
// client disconnected.
DISCONNECT: 4,
//Enhancements to support Keepalives
PING: 7,
PONG: 8,
STATUS: 9
}
};
// tb_require('../../../helpers/helpers.js')
// tb_require('./rumor.js')
/* jshint globalstrict: true, strict: false, undef: true, unused: true,
trailing: true, browser: true, smarttabs:true */
/* global OT, OTPlugin */
!(function() {
OT.Rumor.PluginSocket = function(messagingURL, events) {
var webSocket,
state = 'initializing';
OTPlugin.initRumorSocket(messagingURL, OT.$.bind(function(err, rumorSocket) {
if(err) {
state = 'closed';
events.onClose({ code: 4999 });
} else if(state === 'initializing') {
webSocket = rumorSocket;
webSocket.onOpen(function() {
state = 'open';
events.onOpen();
});
webSocket.onClose(function(error) {
state = 'closed'; /* CLOSED */
events.onClose({ code: error });
});
webSocket.onError(function(error) {
state = 'closed'; /* CLOSED */
events.onError(error);
/* native websockets seem to do this, so should we */
events.onClose({ code: error });
});
webSocket.onMessage(function(type, addresses, headers, payload) {
var msg = new OT.Rumor.Message(type, addresses, headers, payload);
events.onMessage(msg);
});
webSocket.open();
} else {
this.close();
}
}, this));
this.close = function() {
if(state === 'initializing' || state === 'closed') {
state = 'closed';
return;
}
webSocket.close(1000, '');
};
this.send = function(msg) {
if(state === 'open') {
webSocket.send(msg);
}
};
this.isClosed = function() {
return state === 'closed';
};
};
}(this));
// tb_require('../../../helpers/helpers.js')
// tb_require('./encoding.js')
// tb_require('./rumor.js')
@ -16557,6 +16557,140 @@ var loadCSS = function loadCSS(cssURL) {
head.appendChild(style);
};
// tb_require('../helpers.js')
/* jshint globalstrict: true, strict: false, undef: true, unused: true,
trailing: true, browser: true, smarttabs:true */
/* global OTPlugin, OT */
///
// Capabilities
//
// Support functions to query browser/client Media capabilities.
//
// Indicates whether this client supports the getUserMedia
// API.
//
OT.$.registerCapability('getUserMedia', function() {
if (OT.$.env === 'Node') return false;
return !!(navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ||
OTPlugin.isInstalled());
});
// TODO Remove all PeerConnection stuff, that belongs to the messaging layer not the Media layer.
// Indicates whether this client supports the PeerConnection
// API.
//
// Chrome Issues:
// * The explicit prototype.addStream check is because webkitRTCPeerConnection was
// partially implemented, but not functional, in Chrome 22.
//
// Firefox Issues:
// * No real support before Firefox 19
// * Firefox 19 has issues with generating Offers.
// * Firefox 20 doesn't interoperate with Chrome.
//
OT.$.registerCapability('PeerConnection', function() {
if (OT.$.env === 'Node') {
return false;
}
else if (typeof(window.webkitRTCPeerConnection) === 'function' &&
!!window.webkitRTCPeerConnection.prototype.addStream) {
return true;
} else if (typeof(window.mozRTCPeerConnection) === 'function' && OT.$.env.version > 20.0) {
return true;
} else {
return OTPlugin.isInstalled();
}
});
// Indicates whether this client supports WebRTC
//
// This is defined as: getUserMedia + PeerConnection + exceeds min browser version
//
OT.$.registerCapability('webrtc', function() {
if (OT.properties) {
var minimumVersions = OT.properties.minimumVersion || {},
minimumVersion = minimumVersions[OT.$.env.name.toLowerCase()];
if(minimumVersion && OT.$.env.versionGreaterThan(minimumVersion)) {
OT.debug('Support for', OT.$.env.name, 'is disabled because we require',
minimumVersion, 'but this is', OT.$.env.version);
return false;
}
}
if (OT.$.env === 'Node') {
// Node works, even though it doesn't have getUserMedia
return true;
}
return OT.$.hasCapabilities('getUserMedia', 'PeerConnection');
});
// TODO Remove all transport stuff, that belongs to the messaging layer not the Media layer.
// Indicates if the browser supports bundle
//
// Broadly:
// * Firefox doesn't support bundle
// * Chrome support bundle
// * OT Plugin supports bundle
// * We assume NodeJs supports bundle (e.g. 'you're on your own' mode)
//
OT.$.registerCapability('bundle', function() {
return OT.$.hasCapabilities('webrtc') &&
(OT.$.env.name === 'Chrome' ||
OT.$.env.name === 'Node' ||
OTPlugin.isInstalled());
});
// Indicates if the browser supports RTCP Mux
//
// Broadly:
// * Older versions of Firefox (<= 25) don't support RTCP Mux
// * Older versions of Firefox (>= 26) support RTCP Mux (not tested yet)
// * Chrome support RTCP Mux
// * OT Plugin supports RTCP Mux
// * We assume NodeJs supports RTCP Mux (e.g. 'you're on your own' mode)
//
OT.$.registerCapability('RTCPMux', function() {
return OT.$.hasCapabilities('webrtc') &&
(OT.$.env.name === 'Chrome' ||
OT.$.env.name === 'Node' ||
OTPlugin.isInstalled());
});
// Indicates whether this browser supports the getMediaDevices (getSources) API.
//
OT.$.registerCapability('getMediaDevices', function() {
return OT.$.isFunction(window.MediaStreamTrack) &&
OT.$.isFunction(window.MediaStreamTrack.getSources);
});
OT.$.registerCapability('audioOutputLevelStat', function() {
return OT.$.env.name === 'Chrome' || OT.$.env.name === 'IE';
});
OT.$.registerCapability('webAudioCapableRemoteStream', function() {
return OT.$.env.name === 'Firefox';
});
OT.$.registerCapability('webAudio', function() {
return 'AudioContext' in window;
});
// tb_require('../helpers.js')
// tb_require('./properties.js')
@ -16706,140 +16840,6 @@ OT.Config = (function() {
return _this;
})();
// tb_require('../helpers.js')
/* jshint globalstrict: true, strict: false, undef: true, unused: true,
trailing: true, browser: true, smarttabs:true */
/* global OTPlugin, OT */
///
// Capabilities
//
// Support functions to query browser/client Media capabilities.
//
// Indicates whether this client supports the getUserMedia
// API.
//
OT.$.registerCapability('getUserMedia', function() {
if (OT.$.env === 'Node') return false;
return !!(navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ||
OTPlugin.isInstalled());
});
// TODO Remove all PeerConnection stuff, that belongs to the messaging layer not the Media layer.
// Indicates whether this client supports the PeerConnection
// API.
//
// Chrome Issues:
// * The explicit prototype.addStream check is because webkitRTCPeerConnection was
// partially implemented, but not functional, in Chrome 22.
//
// Firefox Issues:
// * No real support before Firefox 19
// * Firefox 19 has issues with generating Offers.
// * Firefox 20 doesn't interoperate with Chrome.
//
OT.$.registerCapability('PeerConnection', function() {
if (OT.$.env === 'Node') {
return false;
}
else if (typeof(window.webkitRTCPeerConnection) === 'function' &&
!!window.webkitRTCPeerConnection.prototype.addStream) {
return true;
} else if (typeof(window.mozRTCPeerConnection) === 'function' && OT.$.env.version > 20.0) {
return true;
} else {
return OTPlugin.isInstalled();
}
});
// Indicates whether this client supports WebRTC
//
// This is defined as: getUserMedia + PeerConnection + exceeds min browser version
//
OT.$.registerCapability('webrtc', function() {
if (OT.properties) {
var minimumVersions = OT.properties.minimumVersion || {},
minimumVersion = minimumVersions[OT.$.env.name.toLowerCase()];
if(minimumVersion && OT.$.env.versionGreaterThan(minimumVersion)) {
OT.debug('Support for', OT.$.env.name, 'is disabled because we require',
minimumVersion, 'but this is', OT.$.env.version);
return false;
}
}
if (OT.$.env === 'Node') {
// Node works, even though it doesn't have getUserMedia
return true;
}
return OT.$.hasCapabilities('getUserMedia', 'PeerConnection');
});
// TODO Remove all transport stuff, that belongs to the messaging layer not the Media layer.
// Indicates if the browser supports bundle
//
// Broadly:
// * Firefox doesn't support bundle
// * Chrome support bundle
// * OT Plugin supports bundle
// * We assume NodeJs supports bundle (e.g. 'you're on your own' mode)
//
OT.$.registerCapability('bundle', function() {
return OT.$.hasCapabilities('webrtc') &&
(OT.$.env.name === 'Chrome' ||
OT.$.env.name === 'Node' ||
OTPlugin.isInstalled());
});
// Indicates if the browser supports RTCP Mux
//
// Broadly:
// * Older versions of Firefox (<= 25) don't support RTCP Mux
// * Older versions of Firefox (>= 26) support RTCP Mux (not tested yet)
// * Chrome support RTCP Mux
// * OT Plugin supports RTCP Mux
// * We assume NodeJs supports RTCP Mux (e.g. 'you're on your own' mode)
//
OT.$.registerCapability('RTCPMux', function() {
return OT.$.hasCapabilities('webrtc') &&
(OT.$.env.name === 'Chrome' ||
OT.$.env.name === 'Node' ||
OTPlugin.isInstalled());
});
// Indicates whether this browser supports the getMediaDevices (getSources) API.
//
OT.$.registerCapability('getMediaDevices', function() {
return OT.$.isFunction(window.MediaStreamTrack) &&
OT.$.isFunction(window.MediaStreamTrack.getSources);
});
OT.$.registerCapability('audioOutputLevelStat', function() {
return OT.$.env.name === 'Chrome' || OT.$.env.name === 'IE';
});
OT.$.registerCapability('webAudioCapableRemoteStream', function() {
return OT.$.env.name === 'Firefox';
});
OT.$.registerCapability('webAudio', function() {
return 'AudioContext' in window;
});
// tb_require('../helpers.js')
// tb_require('./config.js')
@ -25970,9 +25970,9 @@ OT.Publisher = function(options) {
var peerConnection = _peerConnections[connectionId];
peerConnection.getSenders().forEach(function(sender) {
if (sender.track.kind === 'audio' && newStream.getAudioTracks().length) {
replacePromises.push(sender.switchTracks(newStream.getAudioTracks()[0]));
replacePromises.push(sender.replaceTrack(newStream.getAudioTracks()[0]));
} else if (sender.track.kind === 'video' && newStream.getVideoTracks().length) {
replacePromises.push(sender.switchTracks(newStream.getVideoTracks()[0]));
replacePromises.push(sender.replaceTrack(newStream.getVideoTracks()[0]));
}
});
});
@ -26249,6 +26249,38 @@ OT.Publisher = function(options) {
// Helper function to generate unique publisher ids
OT.Publisher.nextId = OT.$.uuid;
// tb_require('./helpers/lib/css_loader.js')
// tb_require('./ot/system_requirements.js')
// tb_require('./ot/session.js')
// tb_require('./ot/publisher.js')
// tb_require('./ot/subscriber.js')
// tb_require('./ot/archive.js')
// tb_require('./ot/connection.js')
// tb_require('./ot/stream.js')
// We want this to be included at the end, just before footer.js
/* jshint globalstrict: true, strict: false, undef: true, unused: true,
trailing: true, browser: true, smarttabs:true */
/* global loadCSS, define */
// Tidy up everything on unload
OT.onUnload(function() {
OT.publishers.destroy();
OT.subscribers.destroy();
OT.sessions.destroy('unloaded');
});
loadCSS(OT.properties.cssURL);
// Register as a named AMD module, since TokBox could be concatenated with other
// files that may use define, but not via a proper concatenation script that
// understands anonymous AMD modules. A named AMD is safest and most robust
// way to register. Uppercase TB is used because AMD module names are
// derived from file names, and OpenTok is normally delivered in an uppercase
// file name.
if (typeof define === 'function' && define.amd) {
define( 'TB', [], function () { return TB; } );
}
// tb_require('../../conf/properties.js')
// tb_require('../ot.js')
// tb_require('./session.js')
@ -26948,38 +26980,6 @@ OT.components = {};
*/
// tb_require('./helpers/lib/css_loader.js')
// tb_require('./ot/system_requirements.js')
// tb_require('./ot/session.js')
// tb_require('./ot/publisher.js')
// tb_require('./ot/subscriber.js')
// tb_require('./ot/archive.js')
// tb_require('./ot/connection.js')
// tb_require('./ot/stream.js')
// We want this to be included at the end, just before footer.js
/* jshint globalstrict: true, strict: false, undef: true, unused: true,
trailing: true, browser: true, smarttabs:true */
/* global loadCSS, define */
// Tidy up everything on unload
OT.onUnload(function() {
OT.publishers.destroy();
OT.subscribers.destroy();
OT.sessions.destroy('unloaded');
});
loadCSS(OT.properties.cssURL);
// Register as a named AMD module, since TokBox could be concatenated with other
// files that may use define, but not via a proper concatenation script that
// understands anonymous AMD modules. A named AMD is safest and most robust
// way to register. Uppercase TB is used because AMD module names are
// derived from file names, and OpenTok is normally delivered in an uppercase
// file name.
if (typeof define === 'function' && define.amd) {
define( 'TB', [], function () { return TB; } );
}
// tb_require('./postscript.js')
/* jshint ignore:start */

View File

@ -166,9 +166,6 @@ XPCOMUtils.defineLazyModuleGetter(this, "AddonWatcher",
XPCOMUtils.defineLazyModuleGetter(this, "LightweightThemeManager",
"resource://gre/modules/LightweightThemeManager.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "AppConstants",
"resource://gre/modules/AppConstants.jsm");
const PREF_PLUGINS_NOTIFYUSER = "plugins.update.notifyUser";
const PREF_PLUGINS_UPDATEURL = "plugins.update.url";
@ -1189,17 +1186,6 @@ BrowserGlue.prototype = {
Services.prefs.setCharPref("browser.shell.mostRecentDateSetAsDefault", now);
}
if (Services.prefs.getIntPref("browser.shell.windows10DefaultBrowserABTest") == -1) {
let abTest = Math.round(Math.random());
Services.prefs.setIntPref("browser.shell.windows10DefaultBrowserABTest", abTest);
}
if (AppConstants.isPlatformAndVersionAtLeast("win", "10")) {
let abTest = Services.prefs.getIntPref("browser.shell.windows10DefaultBrowserABTest");
let result = abTest * 2 + Number(isDefault);
Services.telemetry.getHistogramById("WIN_10_DEFAULT_BROWSER_AB_TEST").add(result);
}
if (shouldCheck && !isDefault && !willRecoverSession) {
Services.tm.mainThread.dispatch(function() {
DefaultBrowserCheck.prompt(RecentWindow.getMostRecentBrowserWindow());

View File

@ -138,6 +138,13 @@ let gSyncPane = {
document.getElementById("fxaSaveChangeDeviceName").hidden = !editMode;
},
_focusComputerNameTextbox: function() {
let textbox = document.getElementById("fxaSyncComputerName");
let valLength = textbox.value.length;
textbox.focus();
textbox.setSelectionRange(valLength, valLength);
},
_updateComputerNameValue: function(save) {
let textbox = document.getElementById("fxaSyncComputerName");
if (save) {
@ -182,6 +189,7 @@ let gSyncPane = {
});
setEventListener("fxaChangeDeviceName", "click", function () {
this._toggleComputerNameControls(true);
this._focusComputerNameTextbox();
});
setEventListener("fxaCancelChangeDeviceName", "click", function () {
this._toggleComputerNameControls(false);
@ -253,6 +261,13 @@ let gSyncPane = {
let win = Services.wm.getMostRecentWindow("navigator:browser");
fxaMigrator.resendVerificationMail(win);
});
setEventListener("fxaSyncComputerName", "keypress", function (e) {
if (e.keyCode == KeyEvent.DOM_VK_RETURN) {
document.getElementById("fxaSaveChangeDeviceName").click();
} else if (e.keyCode == KeyEvent.DOM_VK_ESCAPE) {
document.getElementById("fxaCancelChangeDeviceName").click();
}
});
},
_initProfileImageUI: function () {

View File

@ -318,30 +318,32 @@
<spacer/>
</hbox>
</groupbox>
<vbox>
<caption>
<label accesskey="&syncDeviceName.accesskey;"
control="fxaSyncComputerName">
&fxaSyncDeviceName.label;
</label>
</caption>
<hbox id="fxaDeviceName">
<hbox flex="1">
<textbox id="fxaSyncComputerName" class="plain"
disabled="true" flex="1"/>
<groupbox>
<vbox>
<caption>
<label accesskey="&syncDeviceName.accesskey;"
control="fxaSyncComputerName">
&fxaSyncDeviceName.label;
</label>
</caption>
<hbox id="fxaDeviceName">
<hbox flex="1">
<textbox id="fxaSyncComputerName" class="plain"
disabled="true" flex="1"/>
</hbox>
<hbox>
<button id="fxaChangeDeviceName"
label="&changeSyncDeviceName.label;"/>
<button id="fxaCancelChangeDeviceName"
label="&cancelChangeSyncDeviceName.label;"
hidden="true"/>
<button id="fxaSaveChangeDeviceName"
label="&saveChangeSyncDeviceName.label;"
hidden="true"/>
</hbox>
</hbox>
<hbox>
<button id="fxaChangeDeviceName"
label="&changeSyncDeviceName.label;"/>
<button id="fxaCancelChangeDeviceName"
label="&cancelChangeSyncDeviceName.label;"
hidden="true"/>
<button id="fxaSaveChangeDeviceName"
label="&saveChangeSyncDeviceName.label;"
hidden="true"/>
</hbox>
</hbox>
</vbox>
</vbox>
</groupbox>
<spacer flex="1"/>
<hbox id="tosPP-small">
<label id="tosPP-small-ToS" class="text-link">

View File

@ -730,7 +730,6 @@ nsWindowsShellService::SetDefaultBrowser(bool aClaimAllTypes, bool aForAllUsers)
}
nsresult rv = LaunchHelper(appHelperPath);
nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
if (NS_SUCCEEDED(rv) && IsWin8OrLater()) {
if (aClaimAllTypes) {
if (IsWin10OrLater()) {
@ -751,15 +750,7 @@ nsWindowsShellService::SetDefaultBrowser(bool aClaimAllTypes, bool aForAllUsers)
// Windows 10 blocks attempts to load the
// HTTP Handler association dialog.
if (IsWin10OrLater()) {
if (prefs) {
int32_t abTest;
rv = prefs->GetIntPref("browser.shell.windows10DefaultBrowserABTest", &abTest);
if (NS_SUCCEEDED(rv) && abTest == 0) {
rv = InvokeHTTPOpenAsVerb();
} else {
rv = LaunchModernSettingsDialogDefaultApps();
}
}
rv = LaunchModernSettingsDialogDefaultApps();
} else {
rv = LaunchHTTPHandlerPane();
}
@ -767,15 +758,12 @@ nsWindowsShellService::SetDefaultBrowser(bool aClaimAllTypes, bool aForAllUsers)
// The above call should never really fail, but just in case
// fall back to showing control panel for all defaults
if (NS_FAILED(rv)) {
if (IsWin10OrLater()) {
rv = LaunchModernSettingsDialogDefaultApps();
} else {
rv = LaunchControlPanelDefaultsSelectionUI();
}
rv = LaunchControlPanelDefaultsSelectionUI();
}
}
}
nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
if (prefs) {
(void) prefs->SetBoolPref(PREF_CHECKDEFAULTBROWSER, true);
}

View File

@ -0,0 +1,102 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests if the waterfall collapsing logic works properly
* when filtering parents and children.
*/
function run_test() {
run_next_test();
}
add_task(function test() {
const WaterfallUtils = devtools.require("devtools/performance/waterfall-utils");
[
[["DOMEvent"], gExpectedOutputNoDOMEvent],
[["Javascript"], gExpectedOutputNoJS],
[["DOMEvent", "Javascript"], gExpectedOutputNoDOMEventOrJS],
].forEach(([filter, expected]) => {
let rootMarkerNode = WaterfallUtils.createParentNode({ name: "(root)" });
WaterfallUtils.collapseMarkersIntoNode({
rootNode: rootMarkerNode,
markersList: gTestMarkers,
filter
});
compare(rootMarkerNode, expected);
});
function compare (marker, expected) {
for (let prop in expected) {
if (prop === "submarkers") {
for (let i = 0; i < expected.submarkers.length; i++) {
compare(marker.submarkers[i], expected.submarkers[i]);
}
} else if (prop !== "uid") {
equal(marker[prop], expected[prop], `${expected.name} matches ${prop}`);
}
}
}
});
const gTestMarkers = [
{ start: 1, end: 18, name: "DOMEvent" },
// Test that JS markers can fold in DOM events and have marker children
{ start: 2, end: 16, name: "Javascript" },
// Test all these markers can be children
{ start: 3, end: 4, name: "Paint" },
{ start: 5, end: 6, name: "Reflow" },
{ start: 7, end: 8, name: "Styles" },
{ start: 9, end: 9, name: "TimeStamp" },
{ start: 10, end: 11, name: "Parse HTML" },
{ start: 12, end: 13, name: "Parse XML" },
{ start: 14, end: 15, name: "GarbageCollection" },
// Test that JS markers can be parents without being a child of DOM events
{ start: 25, end: 30, name: "Javascript" },
{ start: 26, end: 27, name: "Paint" },
];
const gExpectedOutputNoJS = {
name: "(root)", submarkers: [
{ start: 1, end: 18, name: "DOMEvent", submarkers: [
{ start: 3, end: 4, name: "Paint" },
{ start: 5, end: 6, name: "Reflow" },
{ start: 7, end: 8, name: "Styles" },
{ start: 9, end: 9, name: "TimeStamp" },
{ start: 10, end: 11, name: "Parse HTML" },
{ start: 12, end: 13, name: "Parse XML" },
{ start: 14, end: 15, name: "GarbageCollection" },
]},
{ start: 26, end: 27, name: "Paint" },
]};
const gExpectedOutputNoDOMEvent = {
name: "(root)", submarkers: [
{ start: 2, end: 16, name: "Javascript", submarkers: [
{ start: 3, end: 4, name: "Paint" },
{ start: 5, end: 6, name: "Reflow" },
{ start: 7, end: 8, name: "Styles" },
{ start: 9, end: 9, name: "TimeStamp" },
{ start: 10, end: 11, name: "Parse HTML" },
{ start: 12, end: 13, name: "Parse XML" },
{ start: 14, end: 15, name: "GarbageCollection" },
]},
{ start: 25, end: 30, name: "Javascript", submarkers: [
{ start: 26, end: 27, name: "Paint" },
]}
]};
const gExpectedOutputNoDOMEventOrJS = {
name: "(root)", submarkers: [
{ start: 3, end: 4, name: "Paint" },
{ start: 5, end: 6, name: "Reflow" },
{ start: 7, end: 8, name: "Styles" },
{ start: 9, end: 9, name: "TimeStamp" },
{ start: 10, end: 11, name: "Parse HTML" },
{ start: 12, end: 13, name: "Parse XML" },
{ start: 14, end: 15, name: "GarbageCollection" },
{ start: 26, end: 27, name: "Paint" },
]};

View File

@ -27,3 +27,4 @@ skip-if = toolkit == 'android' || toolkit == 'gonk'
[test_waterfall-utils-collapse-01.js]
[test_waterfall-utils-collapse-02.js]
[test_waterfall-utils-collapse-03.js]
[test_waterfall-utils-collapse-04.js]

View File

@ -1681,7 +1681,7 @@ toolbarbutton[constrain-size="true"][cui-areatype="toolbar"] > .toolbarbutton-ba
}
#notification-popup-box:not([hidden]) + #identity-box {
-moz-padding-start: 10px;
-moz-padding-start: 10px !important;
border-radius: 0;
}

View File

@ -392,8 +392,8 @@ description > html|a {
margin-right: 80px;
}
#fxaDeviceName {
margin: 14px 0px;
#fxaSyncComputerName {
margin-left: 0px;
}
#fxaSyncComputerName.plain {

View File

@ -184,10 +184,12 @@ browser.jar:
skin/classic/browser/loop/toolbar-aero@2x.png (loop/toolbar-aero@2x.png)
skin/classic/browser/loop/toolbar-inverted.png (loop/toolbar-inverted.png)
skin/classic/browser/loop/toolbar-inverted@2x.png (loop/toolbar-inverted@2x.png)
skin/classic/browser/loop/toolbar-XP.png (loop/toolbar-XP.png)
skin/classic/browser/loop/toolbar-XP@2x.png (loop/toolbar-XP@2x.png)
skin/classic/browser/loop/toolbar-lunaSilver.png (loop/toolbar-lunaSilver.png)
skin/classic/browser/loop/toolbar-lunaSilver@2x.png (loop/toolbar-lunaSilver@2x.png)
skin/classic/browser/loop/toolbar-win8.png (loop/toolbar-win8.png)
skin/classic/browser/loop/toolbar-win8@2x.png (loop/toolbar-win8@2x.png)
skin/classic/browser/loop/toolbar-XP.png (loop/toolbar-XP.png)
skin/classic/browser/loop/toolbar-XP@2x.png (loop/toolbar-XP@2x.png)
* skin/classic/browser/controlcenter/panel.css (controlcenter/panel.css)
skin/classic/browser/controlcenter/arrow-subview.svg (../shared/controlcenter/arrow-subview.svg)
skin/classic/browser/controlcenter/conn-not-secure.svg (../shared/controlcenter/conn-not-secure.svg)
@ -669,9 +671,13 @@ browser.jar:
% override chrome://browser/skin/loop/toolbar.png chrome://browser/skin/loop/toolbar-XP.png os=WINNT osversion<6
% override chrome://browser/skin/loop/toolbar.png chrome://browser/skin/loop/toolbar-aero.png os=WINNT osversion=6
% override chrome://browser/skin/loop/toolbar.png chrome://browser/skin/loop/toolbar-aero.png os=WINNT osversion=6.1
% override chrome://browser/skin/loop/toolbar.png chrome://browser/skin/loop/toolbar-win8.png os=WINNT osversion=6.2
% override chrome://browser/skin/loop/toolbar.png chrome://browser/skin/loop/toolbar-win8.png os=WINNT osversion=6.3
% override chrome://browser/skin/loop/toolbar@2x.png chrome://browser/skin/loop/toolbar-XP@2x.png os=WINNT osversion<6
% override chrome://browser/skin/loop/toolbar@2x.png chrome://browser/skin/loop/toolbar-aero@2x.png os=WINNT osversion=6
% override chrome://browser/skin/loop/toolbar@2x.png chrome://browser/skin/loop/toolbar-aero@2x.png os=WINNT osversion=6.1
% override chrome://browser/skin/loop/toolbar@2x.png chrome://browser/skin/loop/toolbar-win8@2x.png os=WINNT osversion=6.2
% override chrome://browser/skin/loop/toolbar@2x.png chrome://browser/skin/loop/toolbar-win8@2x.png os=WINNT osversion=6.3
% override chrome://browser/skin/preferences/checkbox.png chrome://browser/skin/preferences/checkbox-aero.png os=WINNT osversion=6
% override chrome://browser/skin/preferences/checkbox.png chrome://browser/skin/preferences/checkbox-aero.png os=WINNT osversion=6.1
% override chrome://browser/skin/preferences/checkbox.png chrome://browser/skin/preferences/checkbox-xp.png os=WINNT osversion<6

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 751 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -23,7 +23,7 @@ import android.os.SystemClock;
import android.text.TextUtils;
import android.view.KeyEvent;
import com.jayway.android.robotium.solo.Solo;
import com.robotium.solo.Solo;
public class FennecNativeActions implements Actions {
private static final String LOGTAG = "FennecNativeActions";
@ -367,7 +367,7 @@ public class FennecNativeActions implements Actions {
if (keyCode <= 0 || keyCode > KeyEvent.getMaxKeyCode()) {
mAsserter.ok(false, "sendKeyCode", "Unknown keyCode " + keyCode);
}
mInstr.sendCharacterSync(keyCode);
mSolo.sendKey(keyCode);
}
@Override

View File

@ -28,7 +28,7 @@ import android.app.Activity;
import android.util.Log;
import android.view.View;
import com.jayway.android.robotium.solo.Solo;
import com.robotium.solo.Solo;
public class FennecNativeDriver implements Driver {
private static final int FRAME_TIME_THRESHOLD = 25; // allow 25ms per frame (40fps)

View File

@ -8,7 +8,7 @@ TESTPATH := $(topsrcdir)/$(mobile-tests)
ANDROID_APK_NAME := robocop-debug
ANDROID_EXTRA_JARS += \
$(srcdir)/robotium-solo-4.3.1.jar \
$(srcdir)/robotium-solo-5.4.1.jar \
$(NULL)
ANDROID_ASSETS_DIR := $(TESTPATH)/assets

Binary file not shown.

View File

@ -42,6 +42,7 @@
#include "WindowNamedPropertiesHandler.h"
#include "nsFrameSelection.h"
#include "nsNetUtil.h"
#include "nsIConsoleService.h"
// Helper Classes
#include "nsJSUtils.h"
@ -1562,6 +1563,12 @@ nsGlobalWindow::FreeInnerObjects()
{
NS_ASSERTION(IsInnerWindow(), "Don't free inner objects on an outer window");
// Prune messages related to this window in the console cache
nsCOMPtr<nsIConsoleService> console(do_GetService("@mozilla.org/consoleservice;1"));
if (console) {
console->ClearMessagesForWindowID(mWindowID);
}
// Make sure that this is called before we null out the document and
// other members that the window destroyed observers could
// re-create.

View File

@ -424,7 +424,20 @@ public:
}
if (status != nsEventStatus_eConsumeNoDefault) {
mReport->LogToConsole();
if (mError.isObject()) {
AutoJSAPI jsapi;
if (NS_WARN_IF(!jsapi.Init(mError.toObjectOrNull()))) {
mReport->LogToConsole();
return NS_OK;
}
JSContext* cx = jsapi.cx();
JS::Rooted<JSObject*> exObj(cx, mError.toObjectOrNull());
JS::RootedObject stack(cx, ExceptionStackOrNull(cx, exObj));
mReport->LogToConsoleWithStack(stack);
} else {
mReport->LogToConsole();
}
}
return NS_OK;
@ -502,7 +515,14 @@ SystemErrorReporter(JSContext *cx, const char *message, JSErrorReport *report)
if (!win || JSREPORT_IS_WARNING(xpcReport->mFlags) ||
report->errorNumber == JSMSG_OUT_OF_MEMORY)
{
xpcReport->LogToConsole();
if (exception.isObject()) {
JS::RootedObject exObj(cx, exception.toObjectOrNull());
JSAutoCompartment ac(cx, exObj);
JS::RootedObject stackVal(cx, ExceptionStackOrNull(cx, exObj));
xpcReport->LogToConsoleWithStack(stackVal);
} else {
xpcReport->LogToConsole();
}
return;
}

View File

@ -4915,6 +4915,9 @@ JS_DropExceptionState(JSContext* cx, JSExceptionState* state);
extern JS_PUBLIC_API(JSErrorReport*)
JS_ErrorFromException(JSContext* cx, JS::HandleObject obj);
extern JS_PUBLIC_API(JSObject*)
ExceptionStackOrNull(JSContext* cx, JS::HandleObject obj);
/*
* Throws a StopIteration exception on cx.
*/

View File

@ -323,6 +323,20 @@ js::ErrorFromException(JSContext* cx, HandleObject objArg)
return obj->as<ErrorObject>().getOrCreateErrorReport(cx);
}
JS_PUBLIC_API(JSObject*)
ExceptionStackOrNull(JSContext* cx, HandleObject objArg)
{
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
assertSameCompartment(cx, objArg);
RootedObject obj(cx, CheckedUnwrap(objArg));
if (!obj || !obj->is<ErrorObject>()) {
return nullptr;
}
return obj->as<ErrorObject>().stack();
}
bool
Error(JSContext* cx, unsigned argc, Value* vp)
{

View File

@ -15,7 +15,7 @@
#include "nsStringGlue.h" // for nsDependentCString
%}
[scriptable, uuid(248b2c94-2736-4d29-bfdf-bc64a2e60d35)]
[scriptable, uuid(18bdefde-e57b-11e4-832a-000c29a57fff)]
interface nsIScriptError : nsIConsoleMessage
{
/** pseudo-flag for default case */
@ -66,6 +66,8 @@ interface nsIScriptError : nsIConsoleMessage
readonly attribute boolean isFromPrivateWindow;
attribute jsval stack;
void init(in AString message,
in AString sourceName,
in AString sourceLine,

View File

@ -15,6 +15,7 @@ EXPORTS += [
UNIFIED_SOURCES += [
'ExportHelpers.cpp',
'nsScriptError.cpp',
'nsScriptErrorWithStack.cpp',
'nsXPConnect.cpp',
'Sandbox.cpp',
'XPCCallContext.cpp',

View File

@ -142,6 +142,17 @@ nsScriptError::GetCategory(char** result) {
return NS_OK;
}
NS_IMETHODIMP
nsScriptError::GetStack(JS::MutableHandleValue aStack) {
aStack.setUndefined();
return NS_OK;
}
NS_IMETHODIMP
nsScriptError::SetStack(JS::HandleValue aStack) {
return NS_OK;
}
NS_IMETHODIMP
nsScriptError::Init(const nsAString& message,
const nsAString& sourceName,

View File

@ -0,0 +1,69 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set ts=8 sts=4 et sw=4 tw=99: */
/* 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/. */
/*
* nsScriptErrorWithStack implementation.
* a main-thread-only, cycle-collected subclass of nsScriptError
* that can store a SavedFrame stack trace object.
*/
#include "xpcprivate.h"
#include "MainThreadUtils.h"
#include "mozilla/Assertions.h"
#include "nsGlobalWindow.h"
#include "nsCycleCollectionParticipant.h"
NS_IMPL_CYCLE_COLLECTION_CLASS(nsScriptErrorWithStack)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsScriptErrorWithStack)
tmp->mStack = nullptr;
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsScriptErrorWithStack)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsScriptErrorWithStack)
NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mStack)
NS_IMPL_CYCLE_COLLECTION_TRACE_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsScriptErrorWithStack)
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsScriptErrorWithStack)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsScriptErrorWithStack)
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_ENTRY(nsIConsoleMessage)
NS_INTERFACE_MAP_ENTRY(nsIScriptError)
NS_INTERFACE_MAP_END
nsScriptErrorWithStack::nsScriptErrorWithStack(JS::HandleObject aStack)
: nsScriptError(),
mStack(aStack)
{
mozilla::HoldJSObjects(this);
}
nsScriptErrorWithStack::~nsScriptErrorWithStack() {
mozilla::DropJSObjects(this);
}
NS_IMETHODIMP
nsScriptErrorWithStack::Init(const nsAString& message,
const nsAString& sourceName,
const nsAString& sourceLine,
uint32_t lineNumber,
uint32_t columnNumber,
uint32_t flags,
const char* category)
{
MOZ_CRASH("nsScriptErrorWithStack requires to be initialized with a document, by using InitWithWindowID");
}
NS_IMETHODIMP
nsScriptErrorWithStack::GetStack(JS::MutableHandleValue aStack) {
aStack.setObjectOrNull(mStack);
return NS_OK;
}

View File

@ -217,6 +217,11 @@ static PRLogModuleInfo* gJSDiagnostics;
void
xpc::ErrorReport::LogToConsole()
{
LogToConsoleWithStack(nullptr);
}
void
xpc::ErrorReport::LogToConsoleWithStack(JS::HandleObject aStack)
{
// Log to stdout.
if (nsContentUtils::DOMWindowDumpEnabled()) {
@ -253,8 +258,17 @@ xpc::ErrorReport::LogToConsole()
// mechanisms.
nsCOMPtr<nsIConsoleService> consoleService =
do_GetService(NS_CONSOLESERVICE_CONTRACTID);
nsCOMPtr<nsIScriptError> errorObject =
do_CreateInstance("@mozilla.org/scripterror;1");
nsCOMPtr<nsIScriptError> errorObject;
if (mWindowID && aStack) {
// Only set stack on messages related to a document
// As we cache messages in the console service,
// we have to ensure not leaking them after the related
// context is destroyed and we only track document lifecycle for now.
errorObject = new nsScriptErrorWithStack(aStack);
} else {
errorObject = new nsScriptError();
}
NS_ENSURE_TRUE_VOID(consoleService && errorObject);
nsresult rv = errorObject->InitWithWindowID(mErrorMsg, mFileName, mSourceLine,

View File

@ -2995,7 +2995,7 @@ xpc_PrintJSStack(JSContext* cx, bool showArgs, bool showLocals,
// Definition of nsScriptError, defined here because we lack a place to put
// XPCOM objects associated with the JavaScript engine.
class nsScriptError final : public nsIScriptError {
class nsScriptError : public nsIScriptError {
public:
nsScriptError();
@ -3005,7 +3005,7 @@ public:
NS_DECL_NSICONSOLEMESSAGE
NS_DECL_NSISCRIPTERROR
private:
protected:
virtual ~nsScriptError();
void
@ -3028,6 +3028,30 @@ private:
bool mIsFromPrivateWindow;
};
class nsScriptErrorWithStack : public nsScriptError {
public:
explicit nsScriptErrorWithStack(JS::HandleObject);
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsScriptErrorWithStack)
NS_IMETHOD Init(const nsAString& message,
const nsAString& sourceName,
const nsAString& sourceLine,
uint32_t lineNumber,
uint32_t columnNumber,
uint32_t flags,
const char* category) override;
NS_IMETHOD GetStack(JS::MutableHandleValue);
private:
virtual ~nsScriptErrorWithStack();
// Complete stackframe where the error happened.
// Must be SavedFrame object.
JS::Heap<JSObject*> mStack;
};
/******************************************************************************
* Handles pre/post script processing.
*/

View File

@ -510,6 +510,7 @@ class ErrorReport {
void Init(JSErrorReport* aReport, const char* aFallbackMessage,
bool aIsChrome, uint64_t aWindowID);
void LogToConsole();
void LogToConsoleWithStack(JS::HandleObject aStack);
public:

View File

@ -108,3 +108,4 @@ skip-if = buildapp == 'mulet'
# Disabled until this test gets updated to test the new proxy based wrappers.
skip-if = true
[test_watchpoints.xul]
[test_nsScriptErrorWithStack.html]

View File

@ -0,0 +1,63 @@
<!DOCTYPE html>
<meta charset=utf-8>
<title>Test for 814497</title>
<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<div id="log"></div>
<script>
var c = Components.classes;
var Ci = Components.interfaces;
var Cu = Components.utils;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
SimpleTest.waitForExplicitFinish();
SimpleTest.expectUncaughtException();
// /!\ Line number is important in this test,
// we are asserting the following functions line #
function failingStack() {
nestedFunction();
}
function nestedFunction() {
doesntExistsAndThrow();
}
var TestObserver = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
observe: function test_observe(aSubject)
{
if (!(aSubject instanceof Ci.nsIScriptError)) {
return;
}
dump("stack: "+aSubject.stack+"\n");
// Main assertions
var s = aSubject.stack;
ok(!!s, "has first frame");
ok(s.source.indexOf("test_nsScriptErrorWithStack.html") !== -1, "source is correct");
is(s.line, 23, "line is correct");
is(s.column, 5, "column is correct");
is(s.functionDisplayName, "nestedFunction");
s = s.parent;
ok(!!s, "has second frame");
ok(s.source.indexOf("test_nsScriptErrorWithStack.html") !== -1, "source is correct");
is(s.line, 20, "line is correct");
is(s.column, 5, "column is correct");
is(s.functionDisplayName, "failingStack");
// We shouldn't have any more frame as we used setTimeout
ok(!s.parent, "has no more frames");
// Cleanup
Services.console.unregisterListener(TestObserver);
SimpleTest.finish();
}
};
Services.console.registerListener(TestObserver);
// use setTimeout in order to prevent throwing from test frame
// and so have a clean stack frame with just our method calls
setTimeout(failingStack, 0);
</script>

View File

@ -47,7 +47,7 @@ dependencies {
compile project(':base')
compile project(':omnijar')
// Including the Robotium JAR directly can cause issues with dexing.
androidTestCompile 'com.jayway.android.robotium:robotium-solo:4.3.1'
androidTestCompile 'com.jayway.android.robotium:robotium-solo:5.4.1'
}
/**

View File

@ -18,7 +18,8 @@ import android.widget.ListView;
import android.widget.TabWidget;
import android.widget.TextView;
import com.jayway.android.robotium.solo.Condition;
import com.robotium.solo.Condition;
/**
* This class is an extension of BaseTest that helps with interaction with about:home

View File

@ -12,7 +12,7 @@ import android.test.ActivityInstrumentationTestCase2;
import android.text.TextUtils;
import android.util.Log;
import com.jayway.android.robotium.solo.Solo;
import com.robotium.solo.Solo;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;

View File

@ -19,7 +19,6 @@ import org.json.JSONObject;
import org.mozilla.gecko.Actions;
import org.mozilla.gecko.Element;
import org.mozilla.gecko.GeckoAppShell;
import org.mozilla.gecko.GeckoEvent;
import org.mozilla.gecko.GeckoProfile;
import org.mozilla.gecko.GeckoThread;
import org.mozilla.gecko.GeckoThread.LaunchState;
@ -30,10 +29,8 @@ import org.mozilla.gecko.Tabs;
import android.app.Activity;
import android.content.ContentValues;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.database.Cursor;
import android.os.Build;
import android.os.SystemClock;
@ -50,9 +47,9 @@ import android.widget.EditText;
import android.widget.ListAdapter;
import android.widget.TextView;
import com.jayway.android.robotium.solo.Condition;
import com.jayway.android.robotium.solo.Solo;
import com.jayway.android.robotium.solo.Timeout;
import com.robotium.solo.Condition;
import com.robotium.solo.Solo;
import com.robotium.solo.Timeout;
/**
* A convenient base class suitable for most Robocop tests.

View File

@ -9,7 +9,7 @@ import org.mozilla.gecko.util.Clipboard;
import android.util.DisplayMetrics;
import com.jayway.android.robotium.solo.Condition;
import com.robotium.solo.Condition;
/**
* This class covers interactions with the context menu opened from web content

View File

@ -6,10 +6,7 @@ package org.mozilla.gecko.tests;
import org.mozilla.gecko.Actions;
import org.mozilla.gecko.Assert;
import org.mozilla.gecko.BrowserApp;
import org.mozilla.gecko.Driver;
import org.mozilla.gecko.GeckoAppShell;
import org.mozilla.gecko.GeckoEvent;
import org.mozilla.gecko.tests.components.AboutHomeComponent;
import org.mozilla.gecko.tests.components.AppMenuComponent;
import org.mozilla.gecko.tests.components.BaseComponent;
@ -18,11 +15,7 @@ import org.mozilla.gecko.tests.components.TabStripComponent;
import org.mozilla.gecko.tests.components.ToolbarComponent;
import org.mozilla.gecko.tests.helpers.HelperInitializer;
import android.content.Intent;
import android.content.res.Resources;
import android.text.TextUtils;
import com.jayway.android.robotium.solo.Solo;
import com.robotium.solo.Solo;
/**
* A base test class for Robocop (UI-centric) tests. This and the related classes attempt to

View File

@ -12,7 +12,7 @@ import org.mozilla.gecko.tests.components.BaseComponent;
import android.app.Activity;
import android.app.Instrumentation;
import com.jayway.android.robotium.solo.Solo;
import com.robotium.solo.Solo;
/**
* Interface to the global information about a UITest environment.

View File

@ -22,8 +22,8 @@ import android.support.v4.view.ViewPager;
import android.view.View;
import android.widget.TextView;
import com.jayway.android.robotium.solo.Condition;
import com.jayway.android.robotium.solo.Solo;
import com.robotium.solo.Condition;
import com.robotium.solo.Solo;
/**
* A class representing any interactions that take place on the Awesomescreen.

View File

@ -23,9 +23,9 @@ import android.text.TextUtils;
import android.view.View;
import android.widget.TextView;
import com.jayway.android.robotium.solo.Condition;
import com.jayway.android.robotium.solo.RobotiumUtils;
import com.jayway.android.robotium.solo.Solo;
import com.robotium.solo.Condition;
import com.robotium.solo.RobotiumUtils;
import com.robotium.solo.Solo;
/**
* A class representing any interactions that take place on the app menu.

View File

@ -10,7 +10,7 @@ import org.mozilla.gecko.tests.UITestContext;
import android.app.Activity;
import com.jayway.android.robotium.solo.Solo;
import com.robotium.solo.Solo;
/**
* A base class for constructing components - an abstraction over small bits of Firefox

View File

@ -22,7 +22,7 @@ import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
import com.jayway.android.robotium.solo.Condition;
import com.robotium.solo.Condition;
/**
* A class representing any interactions that take place on GeckoView.

View File

@ -2,7 +2,7 @@ package org.mozilla.gecko.tests.components;
import android.view.View;
import com.jayway.android.robotium.solo.Condition;
import com.robotium.solo.Condition;
import org.mozilla.gecko.tests.UITestContext;
import org.mozilla.gecko.tests.helpers.DeviceHelper;

View File

@ -10,7 +10,6 @@ import static org.mozilla.gecko.tests.helpers.AssertionHelper.fAssertNotNull;
import static org.mozilla.gecko.tests.helpers.AssertionHelper.fAssertTrue;
import org.mozilla.gecko.R;
import org.mozilla.gecko.tests.StringHelper;
import org.mozilla.gecko.tests.UITestContext;
import org.mozilla.gecko.tests.helpers.DeviceHelper;
import org.mozilla.gecko.tests.helpers.NavigationHelper;
@ -22,8 +21,8 @@ import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.TextView;
import com.jayway.android.robotium.solo.Condition;
import com.jayway.android.robotium.solo.Solo;
import com.robotium.solo.Condition;
import com.robotium.solo.Solo;
/**
* A class representing any interactions that take place on the Toolbar.

View File

@ -13,7 +13,7 @@ import android.app.Activity;
import android.os.Build;
import android.util.DisplayMetrics;
import com.jayway.android.robotium.solo.Solo;
import com.robotium.solo.Solo;
/**
* Provides general hardware (ex: configuration) and software (ex: version) information

View File

@ -3,7 +3,7 @@ package org.mozilla.gecko.tests.helpers;
import android.app.Activity;
import android.util.DisplayMetrics;
import com.jayway.android.robotium.solo.Solo;
import com.robotium.solo.Solo;
import org.mozilla.gecko.Driver;
import org.mozilla.gecko.tests.StringHelper;

View File

@ -11,7 +11,7 @@ import org.mozilla.gecko.tests.UITestContext.ComponentType;
import org.mozilla.gecko.tests.components.AppMenuComponent;
import org.mozilla.gecko.tests.components.ToolbarComponent;
import com.jayway.android.robotium.solo.Solo;
import com.robotium.solo.Solo;
/**
* Provides helper functionality for navigating around the Firefox UI. These functions will often

View File

@ -16,8 +16,8 @@ import org.mozilla.gecko.tests.UITestContext;
import org.mozilla.gecko.tests.UITestContext.ComponentType;
import org.mozilla.gecko.tests.components.ToolbarComponent;
import com.jayway.android.robotium.solo.Condition;
import com.jayway.android.robotium.solo.Solo;
import com.robotium.solo.Condition;
import com.robotium.solo.Solo;
/**
* Provides functionality related to waiting on certain events to happen.

View File

@ -9,7 +9,7 @@ import org.mozilla.gecko.AppConstants;
import android.content.Context;
import android.content.Intent;
import com.jayway.android.robotium.solo.Condition;
import com.robotium.solo.Condition;
import java.io.File;
import java.io.FileReader;

View File

@ -19,7 +19,7 @@ import org.mozilla.gecko.R;
import android.widget.ImageView;
import android.widget.ListView;
import com.jayway.android.robotium.solo.Condition;
import com.robotium.solo.Condition;
/**
* Test adding a search engine from an input field context menu.

View File

@ -4,7 +4,7 @@
package org.mozilla.gecko.tests;
import com.jayway.android.robotium.solo.Condition;
import com.robotium.solo.Condition;
public class testBookmark extends AboutHomeTest {
private static String BOOKMARK_URL;

View File

@ -15,7 +15,7 @@ import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.TextView;
import com.jayway.android.robotium.solo.Condition;
import com.robotium.solo.Condition;
public class testBookmarkFolders extends AboutHomeTest {
private static String DESKTOP_BOOKMARK_URL;

View File

@ -10,7 +10,7 @@ import org.mozilla.gecko.home.HomePager;
import android.database.Cursor;
import android.widget.ListView;
import com.jayway.android.robotium.solo.Condition;
import com.robotium.solo.Condition;
public class testBookmarklets extends AboutHomeTest {

View File

@ -8,7 +8,7 @@ import android.support.v4.app.Fragment;
import android.view.KeyEvent;
import android.view.View;
import com.jayway.android.robotium.solo.Condition;
import com.robotium.solo.Condition;
/**
* Test for browser search visibility.

View File

@ -6,7 +6,7 @@ package org.mozilla.gecko.tests;
import org.mozilla.gecko.R;
import com.jayway.android.robotium.solo.Condition;
import com.robotium.solo.Condition;
import android.view.View;
/**

View File

@ -6,7 +6,7 @@ package org.mozilla.gecko.tests;
import android.widget.CheckBox;
import android.view.View;
import com.jayway.android.robotium.solo.Condition;
import com.robotium.solo.Condition;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

View File

@ -15,7 +15,7 @@ import org.mozilla.gecko.util.GeckoEventListener;
import org.json.JSONObject;
import com.jayway.android.robotium.solo.Condition;
import com.robotium.solo.Condition;
public class testFindInPage extends JavascriptTest implements GeckoEventListener {
private static final int WAIT_FOR_CONDITION_MS = 3000;

View File

@ -6,7 +6,7 @@ package org.mozilla.gecko.tests;
import java.util.concurrent.atomic.AtomicBoolean;
import com.jayway.android.robotium.solo.Condition;
import com.robotium.solo.Condition;
import org.mozilla.gecko.GeckoAppShell;
import org.mozilla.gecko.tests.helpers.AssertionHelper;

View File

@ -9,7 +9,7 @@ import org.mozilla.gecko.AppConstants;
import android.widget.Spinner;
import android.view.View;
import com.jayway.android.robotium.solo.Condition;
import com.robotium.solo.Condition;
import android.hardware.Camera;
import android.os.Build;

View File

@ -10,7 +10,7 @@ import android.widget.ListView;
import org.mozilla.gecko.home.HomePager;
import com.jayway.android.robotium.solo.Condition;
import com.robotium.solo.Condition;
public class testHistory extends AboutHomeTest {
private View mFirstChild;

View File

@ -15,7 +15,7 @@ import android.database.Cursor;
import android.net.Uri;
import android.provider.Browser;
import com.jayway.android.robotium.solo.Condition;
import com.robotium.solo.Condition;
/**
* This test covers the Import from Android feature

View File

@ -8,9 +8,8 @@ import org.mozilla.gecko.Element;
import org.mozilla.gecko.R;
import android.app.Activity;
import android.view.View;
import com.jayway.android.robotium.solo.Condition;
import com.robotium.solo.Condition;
/* A simple test that creates 2 new tabs and checks that the tab count increases. */
public class testNewTab extends BaseTest {

View File

@ -12,7 +12,7 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.jayway.android.robotium.solo.Condition;
import com.robotium.solo.Condition;
/**
* Test for search suggestions.

View File

@ -2,7 +2,7 @@ package org.mozilla.gecko.tests;
import org.mozilla.gecko.Actions;
import com.jayway.android.robotium.solo.Condition;
import com.robotium.solo.Condition;
/**
* Tests session OOM save behavior.

View File

@ -18,7 +18,7 @@ import android.widget.GridView;
import android.widget.ListView;
import android.widget.TextView;
import com.jayway.android.robotium.solo.Condition;
import com.robotium.solo.Condition;
/**
* This test covers the opening and content of the Share Link pop-up list

View File

@ -4,14 +4,13 @@
package org.mozilla.gecko.tests;
import org.mozilla.gecko.AppConstants;
import org.mozilla.gecko.preferences.GeckoPreferences;
import org.mozilla.mozstumbler.service.AppGlobals;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import com.jayway.android.robotium.solo.Condition;
import com.robotium.solo.Condition;
/*
* This test enables (checkbox checked) the Fennec setting to contribute to MLS, then waits for

View File

@ -5,7 +5,7 @@ import org.mozilla.gecko.db.BrowserDB;
import android.content.ContentResolver;
import android.graphics.Color;
import com.jayway.android.robotium.solo.Condition;
import com.robotium.solo.Condition;
/**
* Test for thumbnail updates.

View File

@ -86,7 +86,10 @@ let AutoUpdate = {
};
let State = {
_monitor: PerformanceStats.getMonitor(["jank", "cpow", "ticks"]),
_monitor: PerformanceStats.getMonitor([
"jank", "cpow", "ticks",
"jank-content", "cpow-content", "ticks-content",
]),
/**
* @type{PerformanceData}
@ -244,7 +247,7 @@ let updateLiveData = Task.async(function*() {
let headerElt = document.createElement("tr");
dataElt.appendChild(headerElt);
headerElt.classList.add("header");
for (let column of [...MEASURES, {key: "name", name: ""}]) {
for (let column of [...MEASURES, {key: "name", name: ""}, {key: "process", name: ""}]) {
let el = document.createElement("td");
el.classList.add(column.key);
el.textContent = column.label;
@ -280,20 +283,37 @@ let updateLiveData = Task.async(function*() {
el.textContent = value;
}
// Name
let el = document.createElement("td");
let id = item.id;
el.classList.add("contents");
el.classList.add("name");
row.appendChild(el);
if (item.addonId) {
let _el = el;
let _item = item;
AddonManager.getAddonByID(item.addonId, a => {
_el.textContent = a ? a.name : _item.name
});
} else {
el.textContent = item.title || item.name;
{
// Name
let el = document.createElement("td");
let id = item.id;
el.classList.add("contents");
el.classList.add("name");
row.appendChild(el);
if (item.addonId) {
let _el = el;
let _item = item;
AddonManager.getAddonByID(item.addonId, a => {
_el.textContent = a ? a.name : _item.name
});
} else {
el.textContent = item.title || item.name;
}
}
{
// Process information.
let el = document.createElement("td");
el.classList.add("contents");
el.classList.add("process");
row.appendChild(el);
if (item.isChildProcess) {
el.textContent = "(child)";
row.classList.add("child");
} else {
el.textContent = "(parent)";
row.classList.add("parent");
}
}
}
} catch (ex) {

View File

@ -6,4 +6,3 @@ support-files =
browser_compartments_script.js
[browser_aboutperformance.js]
skip-if = e10s # Feature not implemented yet bug 1140310

View File

@ -0,0 +1,144 @@
/* 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/. */
/**
* A proxy implementing communication between the PerformanceStats.jsm modules
* of the parent and children processes.
*
* This script is loaded in all processes but is essentially a NOOP in the
* parent process.
*/
"use strict";
const { utils: Cu, classes: Cc, interfaces: Ci } = Components;
const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
const { XPCOMUtils } = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {});
XPCOMUtils.defineLazyModuleGetter(this, "PerformanceStats",
"resource://gre/modules/PerformanceStats.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Task",
"resource://gre/modules/Task.jsm");
/**
* A global performance monitor used by this process.
*
* For the sake of simplicity, rather than attempting to map each PerformanceMonitor
* of the parent to a PerformanceMonitor in each child process, we maintain a single
* PerformanceMonitor in each child process. Probes activation/deactivation for this
* monitor is controlled by the activation/deactivation of probes marked as "-content"
* in the parent.
*
* In the parent, this is always an empty monitor.
*/
let gMonitor = PerformanceStats.getMonitor([]);
/**
* `true` if this is a content process, `false` otherwise.
*/
let isContent = Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT;
/**
* Handle message `performance-stats-service-acquire`: ensure that the global
* monitor has a given probe. This message must be sent by the parent process
* whenever a probe is activated application-wide.
*
* Note that we may miss acquire messages if they are sent before this process is
* launched. For this reason, `performance-stats-service-collect` automatically
* re-acquires probes if it realizes that they are missing.
*
* This operation is a NOOP on the parent process.
*
* @param {{payload: Array<string>}} msg.data The message received. `payload`
* must be an array of probe names.
*/
Services.cpmm.addMessageListener("performance-stats-service-acquire", function(msg) {
if (!isContent) {
return;
}
let name = msg.data.payload;
ensureAcquired(name);
});
/**
* Handle message `performance-stats-service-release`: release a given probe
* from the global monitor. This message must be sent by the parent process
* whenever a probe is deactivated application-wide.
*
* Note that we may miss release messages if they are sent before this process is
* launched. This is ok, as probes are inactive by default: if we miss the release
* message, we have already missed the acquire message, and the effect of both
* messages together is to reset to the default state.
*
* This operation is a NOOP on the parent process.
*
* @param {{payload: Array<string>}} msg.data The message received. `payload`
* must be an array of probe names.
*/
Services.cpmm.addMessageListener("performance-stats-service-release", function(msg) {
if (!isContent) {
return;
}
// Keep only the probes that do not appear in the payload
let probes = gMonitor.getProbeNames
.filter(x => msg.data.payload.indexOf(x) == -1);
gMonitor = PerformanceStats.getMonitor(probes);
});
/**
* Ensure that this process has all the probes it needs.
*
* @param {Array<string>} probeNames The name of all probes needed by the
* process.
*/
function ensureAcquired(probeNames) {
let alreadyAcquired = gMonitor.probeNames;
// Algorithm is O(n^2) because we expect that n ≤ 3.
let shouldAcquire = [];
for (let probeName of probeNames) {
if (alreadyAcquired.indexOf(probeName) == -1) {
shouldAcquire.push(probeName)
}
}
if (shouldAcquire.length == 0) {
return;
}
gMonitor = PerformanceStats.getMonitor([...alreadyAcquired, ...shouldAcquire]);
}
/**
* Handle message `performance-stats-service-collected`: collect the data
* obtained by the monitor. This message must be sent by the parent process
* whenever we grab a performance snapshot of the application.
*
* This operation provides `null` on the parent process.
*
* @param {{data: {payload: Array<string>}}} msg The message received. `payload`
* must be an array of probe names.
*/
Services.cpmm.addMessageListener("performance-stats-service-collect", Task.async(function*(msg) {
let {id, payload: {probeNames}} = msg.data;
if (!isContent) {
// This message was sent by the parent process to itself.
// As per protocol, respond `null`.
Services.cpmm.sendAsyncMessage("performance-stats-service-collect", {
id,
data: null
});
return;
}
// We may have missed acquire messages if the process was loaded too late.
// Catch up now.
ensureAcquired(probeNames);
// Collect and return data.
let data = yield gMonitor.promiseSnapshot({probeNames});
Services.cpmm.sendAsyncMessage("performance-stats-service-collect", {
id,
data
});
}));

View File

@ -23,6 +23,14 @@ const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
Cu.import("resource://gre/modules/Services.jsm", this);
Cu.import("resource://gre/modules/Task.jsm", this);
XPCOMUtils.defineLazyModuleGetter(this, "PromiseUtils",
"resource://gre/modules/PromiseUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "setTimeout",
"resource://gre/modules/Timer.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "clearTimeout",
"resource://gre/modules/Timer.jsm");
// The nsIPerformanceStatsService provides lower-level
// access to SpiderMonkey and the probes.
@ -37,14 +45,17 @@ XPCOMUtils.defineLazyServiceGetter(this, "finalizer",
Ci.nsIFinalizationWitnessService
);
// The topic used to notify that a PerformanceMonitor has been garbage-collected
// and that we can release/close the probes it holds.
const FINALIZATION_TOPIC = "performancemonitor-finalize";
const PROPERTIES_META_IMMUTABLE = ["name", "addonId", "isSystem", "groupId"];
const PROPERTIES_META = [...PROPERTIES_META_IMMUTABLE, "windowId", "title"];
const PROPERTIES_META_IMMUTABLE = ["addonId", "isSystem", "isChildProcess", "groupId"];
const PROPERTIES_META = [...PROPERTIES_META_IMMUTABLE, "windowId", "title", "name"];
// How long we wait for children processes to respond.
const MAX_WAIT_FOR_CHILD_PROCESS_MS = 5000;
let isContent = Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT;
/**
* Access to a low-level performance probe.
*
@ -125,14 +136,14 @@ Probe.prototype = {
* @return {object} An object representing `a - b`. If `b` is
* `null`, this is `a`.
*/
substract: function(a, b) {
subtract: function(a, b) {
if (a == null) {
throw new TypeError();
}
if (b == null) {
return a;
}
return this._impl.substract(a, b);
return this._impl.subtract(a, b);
},
/**
@ -204,7 +215,7 @@ let Probes = {
}
return true;
},
substract: function(a, b) {
subtract: function(a, b) {
// invariant: `a` and `b` are both non-null
let result = {
totalUserTime: a.totalUserTime - b.totalUserTime,
@ -243,7 +254,7 @@ let Probes = {
isEqual: function(a, b) {
return a.totalCPOWTime == b.totalCPOWTime;
},
substract: function(a, b) {
subtract: function(a, b) {
return {
totalCPOWTime: a.totalCPOWTime - b.totalCPOWTime
};
@ -272,12 +283,78 @@ let Probes = {
isEqual: function(a, b) {
return a.ticks == b.ticks;
},
substract: function(a, b) {
subtract: function(a, b) {
return {
ticks: a.ticks - b.ticks
};
}
}),
"jank-content": new Probe("jank-content", {
_isActive: false,
set isActive(x) {
this._isActive = x;
if (x) {
Process.broadcast("acquire", ["jank"]);
} else {
Process.broadcast("release", ["jank"]);
}
},
get isActive() {
return this._isActive;
},
extract: function(xpcom) {
return {};
},
isEqual: function(a, b) {
return true;
},
subtract: function(a, b) {
return null;
}
}),
"cpow-content": new Probe("cpow-content", {
_isActive: false,
set isActive(x) {
this._isActive = x;
if (x) {
Process.broadcast("acquire", ["cpow"]);
} else {
Process.broadcast("release", ["cpow"]);
}
},
get isActive() {
return this._isActive;
},
extract: function(xpcom) {
return {};
},
isEqual: function(a, b) {
return true;
},
subtract: function(a, b) {
return null;
}
}),
"ticks-content": new Probe("ticks-content", {
set isActive(x) {
// Ignore: This probe is always active.
},
get isActive() {
return true;
},
extract: function(xpcom) {
return {};
},
isEqual: function(a, b) {
return true;
},
subtract: function(a, b) {
return null;
}
}),
};
@ -305,6 +382,13 @@ function PerformanceMonitor(probes) {
PerformanceMonitor._monitors.set(this._id, probes);
}
PerformanceMonitor.prototype = {
/**
* The names of probes activated in this monitor.
*/
get probeNames() {
return [for (probe of this._probes) probe.name];
},
/**
* Return asynchronously a snapshot with the data
* for each probe monitored by this PerformanceMonitor.
@ -317,7 +401,7 @@ PerformanceMonitor.prototype = {
* will return a `Snapshot` in which all values are 0. For most uses,
* the appropriate scenario is to perform a first call to `promiseSnapshot()`
* to obtain a baseline, and then watch evolution of the values by calling
* `promiseSnapshot()` and `substract()`.
* `promiseSnapshot()` and `subtract()`.
*
* On the other hand, numeric values are also monotonic across several instances
* of a PerformanceMonitor with the same probes.
@ -330,18 +414,45 @@ PerformanceMonitor.prototype = {
*
* // all values of `snapshot2` are greater or equal to values of `snapshot1`.
*
* @param {object} options If provided, an object that may contain the following
* fields:
* {Array<string>} probeNames The subset of probes to use for this snapshot.
* These probes must be a subset of the probes active in the monitor.
*
* @return {Promise}
* @resolve {Snapshot}
*/
promiseSnapshot: function() {
promiseSnapshot: function(options = null) {
if (!this._finalizer) {
throw new Error("dispose() has already been called, this PerformanceMonitor is not usable anymore");
}
// Current implementation is actually synchronous.
return Promise.resolve().then(() => new Snapshot({
xpcom: performanceStatsService.getSnapshot(),
probes: this._probes
}));
let probes;
if (options && options.probeNames || undefined) {
if (!Array.isArray(options.probeNames)) {
throw new TypeError();
}
// Make sure that we only request probes that we have
for (let probeName of options.probeNames) {
let probe = this._probes.find(probe => probe.name == probeName);
if (!probe) {
throw new TypeError(`I need probe ${probeName} but I only have ${this.probeNames}`);
}
if (!probes) {
probes = [];
}
probes.push(probe);
}
} else {
probes = this._probes;
}
return Task.spawn(function*() {
let collected = yield Process.broadcastAndCollect("collect", {probeNames: [for (probe of probes) probe.name]});
return new Snapshot({
xpcom: performanceStatsService.getSnapshot(),
childProcesses: collected,
probes
});
});
},
/**
@ -381,7 +492,7 @@ PerformanceMonitor.make = function(probeNames) {
let probes = [];
for (let probeName of probeNames) {
if (!(probeName in Probes)) {
throw new TypeError("Probe not implemented: " + k);
throw new TypeError("Probe not implemented: " + probeName);
}
probes.push(Probes[probeName]);
}
@ -467,12 +578,24 @@ this.PerformanceStats = {
* @field {object|undefined} cpow See the documentation of probe "cpow".
* `undefined` if this probe is not active.
*/
function PerformanceData({xpcom, probes}) {
for (let k of PROPERTIES_META) {
this[k] = xpcom[k];
function PerformanceData({xpcom, json, probes}) {
if (xpcom && json) {
throw new TypeError("Cannot import both xpcom and json data");
}
for (let probe of probes) {
this[probe.name] = probe.extract(xpcom);
let source = xpcom || json;
for (let k of PROPERTIES_META) {
this[k] = source[k];
}
if (xpcom) {
for (let probe of probes) {
this[probe.name] = probe.extract(xpcom);
}
this.isChildProcess = false;
} else {
for (let probe of probes) {
this[probe.name] = json[probe.name];
}
this.isChildProcess = true;
}
}
PerformanceData.prototype = {
@ -519,20 +642,161 @@ function PerformanceDiff(current, old = null) {
for (let probeName of Object.keys(Probes)) {
let other = old ? old[probeName] : null;
if (probeName in current) {
this[probeName] = Probes[probeName].substract(current[probeName], other);
this[probeName] = Probes[probeName].subtract(current[probeName], other);
}
}
}
/**
* A snapshot of the performance usage of the process.
* A snapshot of the performance usage of the application.
*
* @param {nsIPerformanceSnapshot} xpcom The data acquired from this process.
* @param {Array<Object>} childProcesses The data acquired from children processes.
* @param {Array<Probe>} probes The active probes.
*/
function Snapshot({xpcom, probes}) {
function Snapshot({xpcom, childProcesses, probes}) {
this.componentsData = [];
let enumeration = xpcom.getComponentsData().enumerate();
while (enumeration.hasMoreElements()) {
let stat = enumeration.getNext().QueryInterface(Ci.nsIPerformanceStats);
this.componentsData.push(new PerformanceData({xpcom: stat, probes}));
// Parent process
if (xpcom) {
let enumeration = xpcom.getComponentsData().enumerate();
while (enumeration.hasMoreElements()) {
let xpcom = enumeration.getNext().QueryInterface(Ci.nsIPerformanceStats);
this.componentsData.push(new PerformanceData({xpcom, probes}));
}
}
// Content processes
if (childProcesses) {
for (let {componentsData} of childProcesses) {
// We are only interested in `componentsData` for the time being.
for (let json of componentsData) {
this.componentsData.push(new PerformanceData({json, probes}));
}
}
}
this.processData = new PerformanceData({xpcom: xpcom.getProcessData(), probes});
}
/**
* Communication with other processes
*/
let Process = {
// `true` once communications have been initialized
_initialized: false,
// the message manager
_loader: null,
// a counter used to match responses to requests
_idcounter: 0,
/**
* If we are in a child process, return `null`.
* Otherwise, return the global parent process message manager
* and load the script to connect to children processes.
*/
get loader() {
if (this._initialized) {
return this._loader;
}
this._initialized = true;
this._loader = Services.ppmm;
if (!this._loader) {
// We are in a child process.
return null;
}
this._loader.loadProcessScript("resource://gre/modules/PerformanceStats-content.js",
true/*including future processes*/);
return this._loader;
},
/**
* Broadcast a message to all children processes.
*
* NOOP if we are in a child process.
*/
broadcast: function(topic, payload) {
if (!this.loader) {
return;
}
this.loader.broadcastAsyncMessage("performance-stats-service-" + topic, {payload});
},
/**
* Brodcast a message to all children processes and wait for answer.
*
* NOOP if we are in a child process, or if we have no children processes,
* in which case we return `undefined`.
*
* @return {undefined} If we have no children processes, in particular
* if we are in a child process.
* @return {Promise<Array<Object>>} If we have children processes, an
* array of objects with a structure similar to PerformanceData. Note
* that the array may be empty if no child process responded.
*/
broadcastAndCollect: Task.async(function*(topic, payload) {
if (!this.loader || this.loader.childCount == 1) {
return undefined;
}
const TOPIC = "performance-stats-service-" + topic;
let id = this._idcounter++;
// The number of responses we are expecting. Note that we may
// not receive all responses if a process is too long to respond.
let expecting = this.loader.childCount;
// The responses we have collected, in arbitrary order.
let collected = [];
let deferred = PromiseUtils.defer();
// The content script may be loaded more than once (bug 1184115).
// To avoid double-responses, we keep track of who has already responded.
// Note that we could it on the other end, at the expense of implementing
// an additional .jsm just for that purpose.
let responders = new Set();
let observer = function({data, target}) {
if (data.id != id) {
// Collision between two collections,
// ignore the other one.
return;
}
if (responders.has(target)) {
return;
}
responders.add(target);
if (data.data) {
collected.push(data.data)
}
if (--expecting > 0) {
// We are still waiting for at least one response.
return;
}
deferred.resolve();
};
this.loader.addMessageListener(TOPIC, observer);
this.loader.broadcastAsyncMessage(
TOPIC,
{id, payload}
);
// Processes can die/freeze/be busy loading a page..., so don't expect
// that they will always respond.
let timeout = setTimeout(() => {
if (expecting == 0) {
return;
}
deferred.resolve();
}, MAX_WAIT_FOR_CHILD_PROCESS_MS);
deferred.promise.then(() => {
clearTimeout(timeout);
});
yield deferred.promise;
this.loader.removeMessageListener(TOPIC, observer);
return collected;
})
};

View File

@ -13,6 +13,7 @@ XPIDL_MODULE = 'toolkit_perfmonitoring'
EXTRA_JS_MODULES += [
'AddonWatcher.jsm',
'PerformanceStats-content.js',
'PerformanceStats.jsm',
]

View File

@ -260,7 +260,7 @@ nsPerformanceSnapshot::GetGroupId(JSContext* cx,
groupId.AssignLiteral("process: ");
groupId.AppendInt(mProcessId);
groupId.AssignLiteral(", thread: ");
groupId.AppendLiteral(", thread: ");
groupId.AppendInt(runtimeId);
groupId.AppendLiteral(", group: ");
groupId.AppendInt(uid);

View File

@ -86,7 +86,7 @@ let SilentAssert = {
}
};
let isShuttingDown = false;
function monotinicity_tester(source, testName) {
// In the background, check invariants:
// - numeric data can only ever increase;
@ -130,6 +130,10 @@ function monotinicity_tester(source, testName) {
};
let iteration = 0;
let frameCheck = Task.async(function*() {
if (isShuttingDown) {
window.clearInterval(interval);
return;
}
let name = `${testName}: ${iteration++}`;
let snapshot = yield source();
if (!snapshot) {
@ -159,7 +163,10 @@ function monotinicity_tester(source, testName) {
}
let key = item.groupId;
SilentAssert.ok(!map.has(key), "The component hasn't been seen yet.");
if (map.has(key)) {
let old = map.get(key);
Assert.ok(false, `Component ${key} has already been seen. Latest: ${item.title||item.addonId||item.name}, previous: ${old.title||old.addonId||old.name}`);
}
map.set(key, item);
}
for (let [key, item] of map) {
@ -236,13 +243,19 @@ add_task(function* test() {
info("Searching by title, we didn't find the main frame");
continue;
}
info("Found the main frame");
if (skipTotalUserTime || parent.jank.totalUserTime > 1000) {
if (skipTotalUserTime) {
info("Not looking for total user time on this platform, we're done");
} else if (parent.jank.totalUserTime > 1000) {
info("Enough CPU time detected, we're done");
break;
} else {
info(`Not enough CPU time detected: ${parent.jank.totalUserTime}`)
info(`Not enough CPU time detected: ${parent.jank.totalUserTime}`);
info(`Details: ${JSON.stringify(parent, null, "\t")}`);
}
}
isShuttingDown = true;
// Cleanup
gBrowser.removeTab(newTab);

View File

@ -7036,13 +7036,6 @@
"kind": "boolean",
"description": "The result of the startup default desktop browser check."
},
"WIN_10_DEFAULT_BROWSER_AB_TEST": {
"alert_emails": ["jwein@mozilla.com"],
"expires_in_version": "45",
"kind": "enumerated",
"n_values": 4,
"description": "A/B test of different default browser dialogs on Windows 10 (0=openas-notdefault, 1=openas-default, 2=modernsettings-notdefault, 3=modernsettings-default)."
},
"BROWSER_IS_ASSIST_DEFAULT": {
"expires_in_version": "never",
"kind": "boolean",

View File

@ -77,19 +77,21 @@ let StyleSheetsActor = exports.StyleSheetsActor = protocol.ActorClass({
* all the style sheets in this document.
*/
getStyleSheets: method(Task.async(function* () {
let documents = [this.document];
// Iframe document can change during load (bug 1171919). Track their windows
// instead.
let windows = [this.window];
let actors = [];
for (let doc of documents) {
let sheets = yield this._addStyleSheets(doc);
for (let win of windows) {
let sheets = yield this._addStyleSheets(win);
actors = actors.concat(sheets);
// Recursively handle style sheets of the documents in iframes.
for (let iframe of doc.querySelectorAll("iframe, browser, frame")) {
if (iframe.contentDocument) {
for (let iframe of win.document.querySelectorAll("iframe, browser, frame")) {
if (iframe.contentDocument && iframe.contentWindow) {
// Sometimes, iframes don't have any document, like the
// one that are over deeply nested (bug 285395)
documents.push(iframe.contentDocument);
windows.push(iframe.contentWindow);
}
}
}
@ -122,21 +124,29 @@ let StyleSheetsActor = exports.StyleSheetsActor = protocol.ActorClass({
},
/**
* Add all the stylesheets for this document to the map and create an actor
* for each one if not already created.
* Add all the stylesheets for the document in this window to the map and
* create an actor for each one if not already created.
*
* @param {Document} doc
* Document for which to add stylesheets
* @param {Window} win
* Window for which to add stylesheets
*
* @return {Promise}
* Promise that resolves to an array of StyleSheetActors
*/
_addStyleSheets: function(doc)
_addStyleSheets: function(win)
{
return Task.spawn(function*() {
if (doc.readyState === "loading") {
let doc = win.document;
// readyState can be uninitialized if an iframe has just been created but
// it has not started to load yet.
if (doc.readyState === "loading" || doc.readyState === "uninitialized") {
// Wait for the document to load first.
yield listenOnce(doc.defaultView, "DOMContentLoaded", true);
yield listenOnce(win, "DOMContentLoaded", true);
// Make sure we have the actual document for this window. If the
// readyState was initially uninitialized, the initial dummy document
// was replaced with the actual document (bug 1171919).
doc = win.document;
}
let isChrome = Services.scriptSecurityManager.isSystemPrincipal(doc.nodePrincipal);

View File

@ -12,6 +12,7 @@ support-files =
storage-unsecured-iframe.html
storage-updates.html
storage-secured-iframe.html
stylesheets-nested-iframes.html
timeline-iframe-child.html
timeline-iframe-parent.html
director-script-target.html
@ -42,6 +43,7 @@ support-files =
[browser_storage_dynamic_windows.js]
[browser_storage_listings.js]
[browser_storage_updates.js]
[browser_stylesheets_nested-iframes.js]
[browser_timeline.js]
skip-if = buildapp == 'mulet'
[browser_timeline_actors.js]

View File

@ -0,0 +1,39 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that StyleSheetsActor.getStyleSheets() works if an iframe does not have
// a content document.
const {StyleSheetsFront} = require("devtools/server/actors/stylesheets");
add_task(function*() {
let doc = yield addTab(MAIN_DOMAIN + "stylesheets-nested-iframes.html");
info("Initialising the debugger server and client.");
initDebuggerServer();
let client = new DebuggerClient(DebuggerServer.connectPipe());
let form = yield connectDebuggerClient(client);
info("Attaching to the active tab.");
yield new Promise(resolve => {
client.attachTab(form.actor, resolve);
});
let front = StyleSheetsFront(client, form);
ok(front, "The StyleSheetsFront was created.");
let sheets = yield front.getStyleSheets();
ok(sheets, "getStyleSheets() succeeded even with documentless iframes.");
// Bug 285395 limits the number of nested iframes to 10. There's one sheet per
// frame so we should get 10 sheets. However, the limit might change in the
// future so it's better not to rely on the limit. Asserting > 2 ensures that
// the test page is actually loading nested iframes and this test is doing
// something sensible (if we got this far, the test has served its purpose).
ok(sheets.length > 2, sheets.length + " sheets found (expected 3 or more).");
yield closeDebuggerClient(client);
});

View File

@ -0,0 +1,25 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>StyleSheetsActor iframe test</title>
<style>
p {
padding: 1em;
}
</style>
</head>
<body>
<p>A test page with nested iframes</p>
<iframe></iframe>
<script type="application/javascript;version=1.8">
let iframe = document.querySelector("iframe");
let i = parseInt(location.href.split("?")[1]) || 1;
// The frame can't have the same src URL as any of its ancestors.
// This will not infinitely recurse because a frame won't get a content
// document once it's nested deeply enough.
iframe.src = location.href.split("?")[0] + "?" + (++i);
</script>
</body>
</html>

View File

@ -20,6 +20,7 @@
#include "nsIClassInfoImpl.h"
#include "nsIConsoleListener.h"
#include "nsPrintfCString.h"
#include "nsIScriptError.h"
#include "mozilla/Preferences.h"
@ -66,6 +67,45 @@ nsConsoleService::nsConsoleService()
mBufferSize = 250;
}
NS_IMETHODIMP
nsConsoleService::ClearMessagesForWindowID(const uint64_t innerID)
{
// Remove the messages related to this window
for (uint32_t i = 0; i < mBufferSize && mMessages[i]; i++) {
// Only messages implementing nsIScriptError interface exposes the inner window ID
nsCOMPtr<nsIScriptError> scriptError = do_QueryInterface(mMessages[i]);
if (!scriptError) {
continue;
}
uint64_t innerWindowID;
nsresult rv = scriptError->GetInnerWindowID(&innerWindowID);
if (NS_FAILED(rv) || innerWindowID != innerID) {
continue;
}
// Free this matching message!
NS_RELEASE(mMessages[i]);
uint32_t j = i;
// Now shift all the following messages
for (; j < mBufferSize - 1 && mMessages[j + 1]; j++) {
mMessages[j] = mMessages[j + 1];
}
// Nullify the current slot
mMessages[j] = nullptr;
mCurrent = j;
// The array is no longer full
mFull = false;
// Ensure the next iteration handles the messages we just shifted down
i--;
}
return NS_OK;
}
nsConsoleService::~nsConsoleService()
{
uint32_t i = 0;

View File

@ -8,7 +8,7 @@
interface nsIConsoleListener;
interface nsIConsoleMessage;
[scriptable, uuid(0eb81d20-c37e-42d4-82a8-ca9ae96bdf52)]
[scriptable, uuid(2436031e-2167-4307-9f1c-a3f38b55a224)]
interface nsIConsoleService : nsISupports
{
void logMessage(in nsIConsoleMessage message);
@ -44,6 +44,11 @@ interface nsIConsoleService : nsISupports
* Clear the message buffer (e.g. for privacy reasons).
*/
void reset();
/**
* Clear the message buffer for a given inner window.
*/
void clearMessagesForWindowID(in uint64_t innerWindowID);
};