mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge fx-team to central, a=merge
This commit is contained in:
commit
c0dd754465
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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();
|
||||
|
@ -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.
|
||||
|
@ -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]
|
||||
|
@ -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
790
browser/components/loop/content/shared/libs/sdk.js
Normal file → Executable 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 */
|
||||
|
@ -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());
|
||||
|
@ -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 () {
|
||||
|
@ -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">
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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" },
|
||||
]};
|
@ -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]
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -392,8 +392,8 @@ description > html|a {
|
||||
margin-right: 80px;
|
||||
}
|
||||
|
||||
#fxaDeviceName {
|
||||
margin: 14px 0px;
|
||||
#fxaSyncComputerName {
|
||||
margin-left: 0px;
|
||||
}
|
||||
|
||||
#fxaSyncComputerName.plain {
|
||||
|
@ -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
|
||||
|
BIN
browser/themes/windows/loop/toolbar-win8.png
Normal file
BIN
browser/themes/windows/loop/toolbar-win8.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
BIN
browser/themes/windows/loop/toolbar-win8@2x.png
Normal file
BIN
browser/themes/windows/loop/toolbar-win8@2x.png
Normal file
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 |
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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.
BIN
build/mobile/robocop/robotium-solo-5.4.1.jar
Normal file
BIN
build/mobile/robocop/robotium-solo-5.4.1.jar
Normal file
Binary file not shown.
@ -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.
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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,
|
||||
|
@ -15,6 +15,7 @@ EXPORTS += [
|
||||
UNIFIED_SOURCES += [
|
||||
'ExportHelpers.cpp',
|
||||
'nsScriptError.cpp',
|
||||
'nsScriptErrorWithStack.cpp',
|
||||
'nsXPConnect.cpp',
|
||||
'Sandbox.cpp',
|
||||
'XPCCallContext.cpp',
|
||||
|
@ -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,
|
||||
|
69
js/xpconnect/src/nsScriptErrorWithStack.cpp
Normal file
69
js/xpconnect/src/nsScriptErrorWithStack.cpp
Normal 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;
|
||||
}
|
@ -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,
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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:
|
||||
|
||||
|
@ -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]
|
||||
|
63
js/xpconnect/tests/chrome/test_nsScriptErrorWithStack.html
Normal file
63
js/xpconnect/tests/chrome/test_nsScriptErrorWithStack.html
Normal 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>
|
@ -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'
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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) {
|
||||
|
@ -6,4 +6,3 @@ support-files =
|
||||
browser_compartments_script.js
|
||||
|
||||
[browser_aboutperformance.js]
|
||||
skip-if = e10s # Feature not implemented yet – bug 1140310
|
||||
|
144
toolkit/components/perfmonitoring/PerformanceStats-content.js
Normal file
144
toolkit/components/perfmonitoring/PerformanceStats-content.js
Normal 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
|
||||
});
|
||||
}));
|
@ -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;
|
||||
})
|
||||
};
|
||||
|
@ -13,6 +13,7 @@ XPIDL_MODULE = 'toolkit_perfmonitoring'
|
||||
|
||||
EXTRA_JS_MODULES += [
|
||||
'AddonWatcher.jsm',
|
||||
'PerformanceStats-content.js',
|
||||
'PerformanceStats.jsm',
|
||||
]
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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",
|
||||
|
@ -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);
|
||||
|
@ -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]
|
||||
|
@ -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);
|
||||
});
|
@ -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>
|
@ -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;
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user