Support content_capabilities
Categories
(WebExtensions :: Compatibility, enhancement)
Tracking
(Not tracked)
People
(Reporter: jrmuizel, Unassigned)
References
(Blocks 3 open bugs)
Details
(Keywords: webcompat:platform-bug)
User Story
user-impact-score:1350
The Google Docs Offline extension uses content_capabilities key in the extension manifest to grant the following permissions to Google docs: "permissions": [ "clipboardRead", "clipboardWrite", "unlimitedStorage" ]
Google docs uses execCommand('paste') to implement features like paste from markdown.
If we want to make this work it seems like we'll need content_capabilities or something like it. Google has an allowlist of extensions that are allowed to use content_capabilities so we also probably wouldn't want to make it broadly available.
Updated•6 months ago
|
Updated•6 months ago
|
Comment 1•6 months ago
|
||
content_capabilities is an undocumented feature, introduced a decade ago as a replacement for a removed legacy Chrome feature (Chrome app v1) in https://issues.chromium.org/issues/40382050
There are only a handful of allowlisted extensions, most of them added 10+ years ago, one other in 2018. The Google Docs Offline Chrome extension is the fourth item in the list (4895B1DBB92D52488F8D9FFDF9CC7B95C7258C9A) - this is the hex representation of the SHA-1 hash of the extension ID.
Firefox does not need to introduce new extension APIs for this functionality; if really desired an extension can declare the "clipboardRead" and "clipboardWrite" permissions, and have a content script that replaces the document.execCommand method with one that calls execCommand from the content script. clipboardWrite is not strictly necessary, as the web has evolved to allowing copying to the clipboard with transient user activation.
(Here is a unit test that verifies that these permissions work as expected from a content scripts: https://searchfox.org/firefox-main/rev/643d732886fe0de4e2a3eee3c5ed9bd0d47c77cf/toolkit/components/extensions/test/mochitest/test_ext_clipboard.html#112-183 ; implementation ends up here in execCommand for clipboardRead and here for clipboardWrite (called from execCommand here.)
For unlimitedStorage, there is a web API to request additional quota: https://developer.mozilla.org/en-US/docs/Web/API/StorageManager/persist
Overall, I don't see a point in copying the internal-only content_capabilities API from Chrome, when there are fitting alternatives.
| Reporter | ||
Comment 2•6 months ago
|
||
Ok, let's close this as WONTFIX for now. Ksenia has an add-on that hooks things up enough to work on gdocs if we spoof the UA: https://github.com/ksy36/gdocs-paste/. Does that code look reasonable to you?
Comment 3•6 months ago
|
||
That repository consists of two files: manifest.json and content.js (content script).
https://github.com/ksy36/gdocs-paste/blob/49f7abfeeef5055c0988a96c0b6989109e73545c/manifest.json
If this works for you, then it should be all right. From experience I know that many Google web apps use blank frames for functionality. If you really need to modify the contents of these frames, specify "match_about_blank": true as well.
(There is also "match_origin_as_fallback": true, which covers not only blank frames, but also blob:-URLs. I don't expect you to need this, but mentioning for completeness. When this flag is used, Chrome requires the path part of the match pattern to be a wildcard, Firefox does not)
https://github.com/ksy36/gdocs-paste/blob/49f7abfeeef5055c0988a96c0b6989109e73545c/content.js
I'm somewhat puzzled by the two ways to override the method - one directly on document and the other on Document.prototype. There can be valid reasons (e.g. if Google docs looks up prototype methods), but if that is not the case, I'd recommend the same unified approach.
The logic looks functional, but to minimize the chance of bugs, I recommend only calling the privileged execCommand method when pasting.
Secondly, while not obvious at all, when you override a method with wrappedJSObject or exportFunction, that wrapper will be destroyed when the extension unloads/reloads (e.g. by an extension update). If you need to account for this, a way to avoid the issue is by having two scripts: one in the MAIN world (that overwrites document.execCommand, etc) and another one in the default (ISOLATED) world that provides the privileged pasting functionality. For example:
// main.js with "world": "MAIN"
window._overrideExecCommand = function(tryPaste) {
delete window._overrideExecCommand;
const execCommand = Function.prototype.apply.bind(Document.prototype.execCommand);
Document.prototype.execCommand = function(cmd) {
try {
return tryPaste(this, cmd);
} catch {
// Not paste or content script unavailable, fall back to default method.
return execCommand(this, arguments);
}
};
};
// content.js
function tryPaste(doc, cmd) {
if (cmd.toLowerCase() === "paste") {
// If doc is somehow not a Document instance, this throws:
return Document.prototype.execCommand.call(doc, "paste");
}
throw "Not a paste";
}
window.wrappedJSObject._overrideExecCommand?.(exportFunction(tryPaste, window));
Comment 4•5 months ago
|
||
Thanks, for the feedback, Rob. I've added the changes you suggested. It doesn't appear that there are blank frames used at the moment, so I didn't use "match_about_blank": true .
Description
•