mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-08 02:14:43 +00:00
Bug 1197451 - Add clipboardWrite permission r=billm
MozReview-Commit-ID: 6d1mQSVWRPe --HG-- extra : rebase_source : 2f19bab5c9d6db25c60b2b19c06c7027384a04ca
This commit is contained in:
parent
aa5cceb394
commit
9069fff35f
@ -586,6 +586,22 @@ BasePrincipal::GetUnknownAppId(bool* aUnknownAppId)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
BasePrincipal::AddonHasPermission(const nsAString& aPerm)
|
||||
{
|
||||
if (mOriginAttributes.mAddonId.IsEmpty()) {
|
||||
return false;
|
||||
}
|
||||
nsCOMPtr<nsIAddonPolicyService> aps =
|
||||
do_GetService("@mozilla.org/addons/policy-service;1");
|
||||
NS_ENSURE_TRUE(aps, false);
|
||||
|
||||
bool retval = false;
|
||||
nsresult rv = aps->AddonHasPermission(mOriginAttributes.mAddonId, aPerm, &retval);
|
||||
NS_ENSURE_SUCCESS(rv, false);
|
||||
return retval;
|
||||
}
|
||||
|
||||
already_AddRefed<BasePrincipal>
|
||||
BasePrincipal::CreateCodebasePrincipal(nsIURI* aURI, const PrincipalOriginAttributes& aAttrs)
|
||||
{
|
||||
|
@ -266,6 +266,8 @@ public:
|
||||
NS_IMETHOD GetUserContextId(uint32_t* aUserContextId) final;
|
||||
NS_IMETHOD GetPrivateBrowsingId(uint32_t* aPrivateBrowsingId) final;
|
||||
|
||||
virtual bool AddonHasPermission(const nsAString& aPerm);
|
||||
|
||||
virtual bool IsOnCSSUnprefixingWhitelist() override { return false; }
|
||||
|
||||
virtual bool IsCodebasePrincipal() const { return false; };
|
||||
|
@ -40,6 +40,11 @@ interface nsIAddonPolicyService : nsISupports
|
||||
*/
|
||||
ACString getGeneratedBackgroundPageUrl(in ACString aAddonId);
|
||||
|
||||
/**
|
||||
* Returns true if the addon was granted the |aPerm| API permission.
|
||||
*/
|
||||
boolean addonHasPermission(in AString aAddonId, in AString aPerm);
|
||||
|
||||
/**
|
||||
* Returns true if unprivileged code associated with the given addon may load
|
||||
* data from |aURI|.
|
||||
|
@ -786,6 +786,17 @@ nsExpandedPrincipal::GetBaseDomain(nsACString& aBaseDomain)
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
bool
|
||||
nsExpandedPrincipal::AddonHasPermission(const nsAString& aPerm)
|
||||
{
|
||||
for (size_t i = 0; i < mPrincipals.Length(); ++i) {
|
||||
if (BasePrincipal::Cast(mPrincipals[i])->AddonHasPermission(aPerm)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
nsExpandedPrincipal::IsOnCSSUnprefixingWhitelist()
|
||||
{
|
||||
|
@ -79,6 +79,7 @@ public:
|
||||
NS_IMETHOD GetDomain(nsIURI** aDomain) override;
|
||||
NS_IMETHOD SetDomain(nsIURI* aDomain) override;
|
||||
NS_IMETHOD GetBaseDomain(nsACString& aBaseDomain) override;
|
||||
virtual bool AddonHasPermission(const nsAString& aPerm) override;
|
||||
virtual bool IsOnCSSUnprefixingWhitelist() override;
|
||||
virtual void GetScriptLocation(nsACString &aStr) override;
|
||||
nsresult GetOriginInternal(nsACString& aOrigin) override;
|
||||
|
@ -98,6 +98,7 @@
|
||||
#include "nsHostObjectProtocolHandler.h"
|
||||
#include "nsHtml5Module.h"
|
||||
#include "nsHtml5StringParser.h"
|
||||
#include "nsIAddonPolicyService.h"
|
||||
#include "nsIAsyncVerifyRedirectCallback.h"
|
||||
#include "nsICategoryManager.h"
|
||||
#include "nsIChannelEventSink.h"
|
||||
@ -6759,9 +6760,12 @@ nsContentUtils::IsRequestFullScreenAllowed()
|
||||
bool
|
||||
nsContentUtils::IsCutCopyAllowed()
|
||||
{
|
||||
return (!IsCutCopyRestricted() &&
|
||||
EventStateManager::IsHandlingUserInput()) ||
|
||||
IsCallerChrome();
|
||||
if ((!IsCutCopyRestricted() && EventStateManager::IsHandlingUserInput()) ||
|
||||
IsCallerChrome()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return BasePrincipal::Cast(SubjectPrincipal())->AddonHasPermission(NS_LITERAL_STRING("clipboardWrite"));
|
||||
}
|
||||
|
||||
/* static */
|
||||
|
@ -804,6 +804,10 @@ BrowserExtensionContent.prototype = {
|
||||
localize(...args) {
|
||||
return this.localeData.localize(...args);
|
||||
},
|
||||
|
||||
hasPermission(perm) {
|
||||
return this.permissions.has(perm);
|
||||
},
|
||||
};
|
||||
|
||||
ExtensionManager = {
|
||||
|
@ -166,6 +166,7 @@ var Service = {
|
||||
handler.setSubstitution(uuid, uri);
|
||||
|
||||
this.uuidMap.set(uuid, extension);
|
||||
this.aps.setAddonHasPermissionCallback(extension.id, extension.hasPermission.bind(extension));
|
||||
this.aps.setAddonLoadURICallback(extension.id, this.checkAddonMayLoad.bind(this, extension));
|
||||
this.aps.setAddonLocalizeCallback(extension.id, extension.localize.bind(extension));
|
||||
this.aps.setAddonCSP(extension.id, extension.manifest.content_security_policy);
|
||||
@ -176,6 +177,7 @@ var Service = {
|
||||
shutdownExtension(uuid) {
|
||||
let extension = this.uuidMap.get(uuid);
|
||||
this.uuidMap.delete(uuid);
|
||||
this.aps.setAddonHasPermissionCallback(extension.id, null);
|
||||
this.aps.setAddonLoadURICallback(extension.id, null);
|
||||
this.aps.setAddonLocalizeCallback(extension.id, null);
|
||||
this.aps.setAddonCSP(extension.id, null);
|
||||
|
@ -187,6 +187,7 @@
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"alarms",
|
||||
"clipboardWrite",
|
||||
"idle",
|
||||
"notifications",
|
||||
"storage"
|
||||
|
@ -35,6 +35,8 @@ support-files =
|
||||
file_ext_test_api_injection.js
|
||||
file_permission_xhr.html
|
||||
|
||||
[test_clipboard.html]
|
||||
# skip-if = # disabled test case with_permission_allow_copy, see inline comment.
|
||||
[test_ext_inIncognitoContext_window.html]
|
||||
skip-if = os == 'android' # Android does not currently support windows.
|
||||
[test_ext_geturl.html]
|
||||
|
140
toolkit/components/extensions/test/mochitest/test_clipboard.html
Normal file
140
toolkit/components/extensions/test/mochitest/test_clipboard.html
Normal file
@ -0,0 +1,140 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>clipboard permission test</title>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script src="/tests/SimpleTest/SpawnTask.js"></script>
|
||||
<script src="/tests/SimpleTest/ExtensionTestUtils.js"></script>
|
||||
<script src="head.js"></script>
|
||||
<link rel="stylesheet" href="/tests/SimpleTest/test.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<script>
|
||||
"use strict";
|
||||
|
||||
function doCopy(txt) {
|
||||
let field = document.createElement("textarea");
|
||||
document.body.appendChild(field);
|
||||
field.value = txt;
|
||||
field.select();
|
||||
return document.execCommand("copy");
|
||||
}
|
||||
|
||||
add_task(function* no_permission_deny_copy() {
|
||||
function backgroundScript() {
|
||||
browser.test.assertEq(false, doCopy("whatever"),
|
||||
"copy should be denied without permission");
|
||||
browser.test.sendMessage("ready");
|
||||
}
|
||||
let extensionData = {
|
||||
background: `${doCopy};(${backgroundScript})();`,
|
||||
};
|
||||
let extension = ExtensionTestUtils.loadExtension(extensionData);
|
||||
yield extension.startup();
|
||||
|
||||
yield extension.awaitMessage("ready");
|
||||
|
||||
yield extension.unload();
|
||||
});
|
||||
|
||||
/** Selecting text in a bg page is not possible, skip test until it's fixed.
|
||||
add_task(function* with_permission_allow_copy() {
|
||||
function backgroundScript() {
|
||||
browser.test.onMessage.addListener(txt => {
|
||||
browser.test.assertEq(true, doCopy(txt),
|
||||
"copy should be allowed with permission");
|
||||
});
|
||||
browser.test.sendMessage("ready");
|
||||
}
|
||||
let extensionData = {
|
||||
background: `${doCopy};(${backgroundScript})();`,
|
||||
manifest: {
|
||||
permissions: [
|
||||
"clipboardWrite",
|
||||
],
|
||||
},
|
||||
};
|
||||
let extension = ExtensionTestUtils.loadExtension(extensionData);
|
||||
yield extension.startup();
|
||||
yield extension.awaitMessage("ready");
|
||||
|
||||
const DUMMY_STR = "dummy string to copy";
|
||||
yield new Promise(resolve => {
|
||||
SimpleTest.waitForClipboard(DUMMY_STR, () => {
|
||||
extension.sendMessage(DUMMY_STR);
|
||||
}, resolve, resolve);
|
||||
});
|
||||
|
||||
yield extension.unload();
|
||||
}); */
|
||||
|
||||
add_task(function* content_script_no_permission_deny_copy() {
|
||||
function contentScript() {
|
||||
browser.test.assertEq(false, doCopy("whatever"),
|
||||
"copy should be denied without permission");
|
||||
browser.test.sendMessage("ready");
|
||||
}
|
||||
let extensionData = {
|
||||
manifest: {
|
||||
content_scripts: [{
|
||||
js: ["contentscript.js"],
|
||||
matches: ["http://mochi.test/*/file_sample.html"],
|
||||
}],
|
||||
},
|
||||
files: {
|
||||
"contentscript.js": `${doCopy};(${contentScript})();`,
|
||||
},
|
||||
};
|
||||
let extension = ExtensionTestUtils.loadExtension(extensionData);
|
||||
yield extension.startup();
|
||||
|
||||
let win = window.open("file_sample.html");
|
||||
yield extension.awaitMessage("ready");
|
||||
win.close();
|
||||
|
||||
yield extension.unload();
|
||||
});
|
||||
|
||||
add_task(function* content_script_with_permission_allow_copy() {
|
||||
function contentScript() {
|
||||
browser.test.onMessage.addListener(txt => {
|
||||
browser.test.assertEq(true, doCopy(txt),
|
||||
"copy should be allowed with permission");
|
||||
});
|
||||
browser.test.sendMessage("ready");
|
||||
}
|
||||
let extensionData = {
|
||||
manifest: {
|
||||
content_scripts: [{
|
||||
js: ["contentscript.js"],
|
||||
matches: ["http://mochi.test/*/file_sample.html"],
|
||||
}],
|
||||
permissions: [
|
||||
"clipboardWrite",
|
||||
],
|
||||
},
|
||||
files: {
|
||||
"contentscript.js": `${doCopy};(${contentScript})();`,
|
||||
},
|
||||
};
|
||||
let extension = ExtensionTestUtils.loadExtension(extensionData);
|
||||
yield extension.startup();
|
||||
|
||||
let win = window.open("file_sample.html");
|
||||
yield extension.awaitMessage("ready");
|
||||
|
||||
const DUMMY_STR = "dummy string to copy in content script";
|
||||
yield new Promise(resolve => {
|
||||
SimpleTest.waitForClipboard(DUMMY_STR, () => {
|
||||
extension.sendMessage(DUMMY_STR);
|
||||
}, resolve, resolve);
|
||||
});
|
||||
|
||||
win.close();
|
||||
|
||||
yield extension.unload();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -29,6 +29,7 @@ function AddonPolicyService()
|
||||
this.wrappedJSObject = this;
|
||||
this.cspStrings = new Map();
|
||||
this.backgroundPageUrlCallbacks = new Map();
|
||||
this.checkHasPermissionCallbacks = new Map();
|
||||
this.mayLoadURICallbacks = new Map();
|
||||
this.localizeCallbacks = new Map();
|
||||
|
||||
@ -66,6 +67,17 @@ AddonPolicyService.prototype = {
|
||||
return cb && cb(aAddonId) || '';
|
||||
},
|
||||
|
||||
/*
|
||||
* Invokes a callback (if any) associated with the addon to determine whether
|
||||
* the addon is granted the |aPerm| API permission.
|
||||
*
|
||||
* @see nsIAddonPolicyService.addonHasPermission
|
||||
*/
|
||||
addonHasPermission(aAddonId, aPerm) {
|
||||
let cb = this.checkHasPermissionCallbacks.get(aAddonId);
|
||||
return cb ? cb(aPerm) : false;
|
||||
},
|
||||
|
||||
/*
|
||||
* Invokes a callback (if any) associated with the addon to determine whether
|
||||
* unprivileged code running within the addon is allowed to perform loads from
|
||||
@ -119,6 +131,19 @@ AddonPolicyService.prototype = {
|
||||
return cb(aURI);
|
||||
},
|
||||
|
||||
/*
|
||||
* Sets the callbacks used in addonHasPermission above. Not accessible over
|
||||
* XPCOM - callers should use .wrappedJSObject on the service to call it
|
||||
* directly.
|
||||
*/
|
||||
setAddonHasPermissionCallback(aAddonId, aCallback) {
|
||||
if (aCallback) {
|
||||
this.checkHasPermissionCallbacks.set(aAddonId, aCallback);
|
||||
} else {
|
||||
this.checkHasPermissionCallbacks.delete(aAddonId);
|
||||
}
|
||||
},
|
||||
|
||||
/*
|
||||
* Sets the callbacks used in addonMayLoadURI above. Not accessible over
|
||||
* XPCOM - callers should use .wrappedJSObject on the service to call it
|
||||
|
Loading…
Reference in New Issue
Block a user