mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-25 22:01:30 +00:00
Bug 787767 - Implement runtime performance warnings for Worker API abuse. r=felipe
This commit is contained in:
parent
7ef6e8b80f
commit
faf749e67e
@ -275,6 +275,7 @@ _BROWSER_FILES = \
|
||||
browser_social_mozSocial_API.js \
|
||||
browser_social_isVisible.js \
|
||||
browser_social_chatwindow.js \
|
||||
browser_social_usageMonitor.js \
|
||||
social_panel.html \
|
||||
social_share_image.png \
|
||||
social_sidebar.html \
|
||||
|
121
browser/base/content/test/browser_social_usageMonitor.js
Normal file
121
browser/base/content/test/browser_social_usageMonitor.js
Normal file
@ -0,0 +1,121 @@
|
||||
/* 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 mock notifications server. Based on:
|
||||
// dom/tests/mochitest/notification/notification_common.js
|
||||
const FAKE_CID = Cc["@mozilla.org/uuid-generator;1"].
|
||||
getService(Ci.nsIUUIDGenerator).generateUUID();
|
||||
|
||||
const ALERTS_SERVICE_CONTRACT_ID = "@mozilla.org/alerts-service;1";
|
||||
const ALERTS_SERVICE_CID = Components.ID(Cc[ALERTS_SERVICE_CONTRACT_ID].number);
|
||||
|
||||
function MockAlertsService() {}
|
||||
|
||||
MockAlertsService.prototype = {
|
||||
|
||||
showAlertNotification: function(imageUrl, title, text, textClickable,
|
||||
cookie, alertListener, name) {
|
||||
let obData = JSON.stringify({
|
||||
imageUrl: imageUrl,
|
||||
title: title,
|
||||
text: text,
|
||||
textClickable: textClickable,
|
||||
cookie: cookie,
|
||||
name: name
|
||||
});
|
||||
Services.obs.notifyObservers(null, "social-test:notification-alert", obData);
|
||||
},
|
||||
|
||||
QueryInterface: function(aIID) {
|
||||
if (aIID.equals(Ci.nsISupports) ||
|
||||
aIID.equals(Ci.nsIAlertsService))
|
||||
return this;
|
||||
throw Cr.NS_ERROR_NO_INTERFACE;
|
||||
}
|
||||
};
|
||||
|
||||
var factory = {
|
||||
createInstance: function(aOuter, aIID) {
|
||||
if (aOuter != null)
|
||||
throw Cr.NS_ERROR_NO_AGGREGATION;
|
||||
return new MockAlertsService().QueryInterface(aIID);
|
||||
}
|
||||
};
|
||||
|
||||
function replacePromptService() {
|
||||
Components.manager.QueryInterface(Ci.nsIComponentRegistrar)
|
||||
.registerFactory(FAKE_CID, "",
|
||||
ALERTS_SERVICE_CONTRACT_ID,
|
||||
factory)
|
||||
}
|
||||
|
||||
function restorePromptService() {
|
||||
Components.manager.QueryInterface(Ci.nsIComponentRegistrar)
|
||||
.registerFactory(ALERTS_SERVICE_CID, "",
|
||||
ALERTS_SERVICE_CONTRACT_ID,
|
||||
null);
|
||||
}
|
||||
// end of alerts service mock.
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
let manifest = { // normal provider
|
||||
name: "provider 1",
|
||||
origin: "https://example.com",
|
||||
sidebarURL: "https://example.com/browser/browser/base/content/test/social_sidebar.html",
|
||||
workerURL: "https://example.com/browser/browser/base/content/test/social_worker.js",
|
||||
iconURL: "https://example.com/browser/browser/base/content/test/moz.png"
|
||||
};
|
||||
Services.prefs.setBoolPref("social.debug.monitorUsage", true);
|
||||
Services.prefs.setIntPref("social.debug.monitorUsageTimeLimitMS", 1000);
|
||||
replacePromptService();
|
||||
registerCleanupFunction(function() {
|
||||
Services.prefs.clearUserPref("social.debug.monitorUsage");
|
||||
Services.prefs.clearUserPref("social.debug.monitorUsageTimeLimitMS");
|
||||
restorePromptService();
|
||||
});
|
||||
|
||||
runSocialTestWithProvider(manifest, function (finishcb) {
|
||||
runSocialTests(tests, undefined, undefined, finishcb);
|
||||
});
|
||||
}
|
||||
|
||||
var tests = {
|
||||
testWorkerAPIAbuse: function(next) {
|
||||
let port = Social.provider.getWorkerPort();
|
||||
ok(port, "provider has a port");
|
||||
Services.obs.addObserver(function abuseObserver(subject, topic, data) {
|
||||
Services.obs.removeObserver(abuseObserver, "social-test:notification-alert");
|
||||
data = JSON.parse(data);
|
||||
is(data.title, "provider 1", "Abusive provider name should match");
|
||||
is(data.text,
|
||||
"Social API performance warning: More than 10 calls to social.cookies-get in less than 10 seconds.",
|
||||
"Usage warning should mention social.cookies-get");
|
||||
next();
|
||||
}, "social-test:notification-alert", false);
|
||||
|
||||
for (let i = 0; i < 15; i++)
|
||||
port.postMessage({topic: "test-worker-spam-message"});
|
||||
},
|
||||
testTimeBetweenFirstAndLastMoreThanLimit: function(next) {
|
||||
let port = Social.provider.getWorkerPort();
|
||||
ok(port, "provider has a port");
|
||||
Services.obs.addObserver(function abuseObserver(subject, topic, data) {
|
||||
Services.obs.removeObserver(abuseObserver, "social-test:notification-alert");
|
||||
data = JSON.parse(data);
|
||||
is(data.title, "provider 1", "Abusive provider name should match");
|
||||
is(data.text,
|
||||
"Social API performance warning: More than 10 calls to social.cookies-get in less than 10 seconds.",
|
||||
"Usage warning should mention social.cookies-get");
|
||||
next();
|
||||
}, "social-test:notification-alert", false);
|
||||
|
||||
port.postMessage({topic: "test-worker-spam-message"});
|
||||
setTimeout(function() {
|
||||
for (let i = 0; i < 15; i++)
|
||||
port.postMessage({topic: "test-worker-spam-message"});
|
||||
}, 2000);
|
||||
}
|
||||
}
|
@ -76,6 +76,10 @@ onconnect = function(e) {
|
||||
case "test-worker-chat":
|
||||
apiPort.postMessage({topic: "social.request-chat", data: event.data.data });
|
||||
break;
|
||||
case "test-worker-spam-message":
|
||||
// Just use a random api message, but one that has little side-effects.
|
||||
apiPort.postMessage({topic: "social.cookies-get"});
|
||||
break;
|
||||
case "social.initialize":
|
||||
// This is the workerAPI port, respond and set up a notification icon.
|
||||
apiPort = port;
|
||||
|
@ -3780,6 +3780,8 @@ pref("memory.low_memory_notification_interval_ms", 10000);
|
||||
pref("memory.ghost_window_timeout_seconds", 60);
|
||||
|
||||
pref("social.enabled", false);
|
||||
pref("social.debug.monitorUsage", false);
|
||||
pref("social.debug.monitorUsageTimeThresholdMS", 10000);
|
||||
|
||||
// Disable idle observer fuzz, because only privileged content can access idle
|
||||
// observers (bug 780507).
|
||||
|
@ -22,6 +22,9 @@ function WorkerAPI(provider, port) {
|
||||
this._provider = provider;
|
||||
this._port = port;
|
||||
this._port.onmessage = this._handleMessage.bind(this);
|
||||
this._usageMonitor = Services.prefs.getBoolPref("social.debug.monitorUsage") ?
|
||||
new WorkerAPIUsageMonitor(provider) :
|
||||
null;
|
||||
|
||||
// Send an "intro" message so the worker knows this is the port
|
||||
// used for the api.
|
||||
@ -42,6 +45,8 @@ WorkerAPI.prototype = {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (this._usageMonitor)
|
||||
this._usageMonitor.logMessage(topic);
|
||||
handler.call(this, data);
|
||||
} catch (ex) {
|
||||
Cu.reportError("WorkerAPI: failed to handle message '" + topic + "': " + ex);
|
||||
@ -69,7 +74,7 @@ WorkerAPI.prototype = {
|
||||
cookies.forEach(function(aCookie) {
|
||||
let [name, value] = aCookie.split("=");
|
||||
results.push({name: unescape(name.trim()),
|
||||
value: unescape(value.trim())});
|
||||
value: value ? unescape(value.trim()) : ""});
|
||||
});
|
||||
this._port.postMessage({topic: "social.cookies-get-response",
|
||||
data: results});
|
||||
@ -130,3 +135,35 @@ WorkerAPI.prototype = {
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
function WorkerAPIUsageMonitor(provider) {
|
||||
if (!provider)
|
||||
throw new Error("Can't initialize WorkerAPIUsageMonitor with a null provider");
|
||||
this._providerName = provider.name;
|
||||
this.TIME_THRESHOLD_MS = Services.prefs.getIntPref("social.debug.monitorUsageTimeThresholdMS");
|
||||
this._messages = {};
|
||||
}
|
||||
|
||||
WorkerAPIUsageMonitor.prototype = {
|
||||
logMessage: function WorkerAPIUsage_logMessage(aMessage) {
|
||||
if (!(aMessage in this._messages)) {
|
||||
this._messages[aMessage] = [];
|
||||
}
|
||||
let messageList = this._messages[aMessage];
|
||||
messageList.push(Date.now());
|
||||
if (messageList.length > 10) {
|
||||
if (messageList[9] - messageList[0] < this.TIME_THRESHOLD_MS) {
|
||||
let alertsService = Cc["@mozilla.org/alerts-service;1"]
|
||||
.getService(Ci.nsIAlertsService);
|
||||
const SOCIAL_BUNDLE = "chrome://global/locale/social.properties";
|
||||
let socialBundle = Services.strings.createBundle(SOCIAL_BUNDLE);
|
||||
let seconds = (this.TIME_THRESHOLD_MS / 1000).toString();
|
||||
let text = socialBundle.formatStringFromName("social.usageAbuse",
|
||||
[aMessage, seconds], 2);
|
||||
alertsService.showAlertNotification("chrome://branding/content/icon48.png",
|
||||
this._providerName, text);
|
||||
}
|
||||
messageList.shift();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -58,6 +58,7 @@
|
||||
locale/@AB_CD@/global/printProgress.dtd (%chrome/global/printProgress.dtd)
|
||||
locale/@AB_CD@/global/regionNames.properties (%chrome/global/regionNames.properties)
|
||||
locale/@AB_CD@/global/resetProfile.dtd (%chrome/global/resetProfile.dtd)
|
||||
locale/@AB_CD@/global/social.properties (%chrome/global/social.properties)
|
||||
locale/@AB_CD@/global/dialog.properties (%chrome/global/dialog.properties)
|
||||
locale/@AB_CD@/global/tree.dtd (%chrome/global/tree.dtd)
|
||||
locale/@AB_CD@/global/textcontext.dtd (%chrome/global/textcontext.dtd)
|
||||
|
Loading…
Reference in New Issue
Block a user