Reputation: 51
As a Chrome extension developer, I am developing an extension like 'Grammarly' and 'LanguageTool'. In a typical webpage rendered through DOM nodes, I can achieve the desired effect by utilizing the getClients()
API along with a positioning and overlay strategy. However, this approach proves ineffective on Google Docs pages, given that Google Docs is currently rendered using canvas.(discussion about this).
Google App Script can interact with Google Docs programmatically. However, those extension I mentioned before seem that can work without it, just through local javaScript script. I'm not sure about 'LanguageTool', you can see it even can add a toolbar item.
Even with Google App Script, I only find an API to add menu item. I really want to know how do they do it.
Upvotes: 5
Views: 376
Reputation: 1906
From the linked discussion; these extensions have whitelisted IDs, that must be injected into window._docs_annotate_canvas_by_ext
at document_start
. This will add some DOM elements containing the text/selection etc.
You can apply for your extension to get whitelisted, or you can use one of the many already approved IDs, a list of some of them can be found here.
From this comment, manifest.json
should be something like:
"content_scripts": [ { "matches": ["*://docs.google.com/document/*"], "run_at": "document_start", "js": ["gdocs.js"], "world": "MAIN" } ]
and gdocs.js
:
window._docs_annotate_canvas_by_ext = "npnbdojkgkbcdfdjlfdmplppdphlhhcf"; // or your/any whitelisted ID
As long as you inject the ID into global window object (MAIN world) and at document_start it should work even with a userscript:
// ==UserScript==
// @name gDocs injection
// @namespace http://tampermonkey.net/
// @version 0.1
// @description .
// @match https://docs.google.com/document/d/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=google.com
// @run-at document-start
// @grant none
// @noframes
// ==/UserScript==
window._docs_annotate_canvas_by_ext = "kbfnbcaeplbcioakkpcpgfkobkghlhen";
await new Promise(resolve => window.addEventListener('DOMContentLoaded', resolve));
const contentFrame = document.querySelector('.docs-texteventtarget-iframe').contentDocument;
function getText() {
const rects = document.querySelectorAll('div.kix-canvas-tile-content > svg > g > rect');
return Array.from(rects, r => r.ariaLabel).join('\n');
}
function getSelection() {
const contentDiv = contentFrame.querySelector('div[aria-label="Document content"]');
contentDiv.dispatchEvent(new Event('copy'));
const nodes = contentDiv.firstChild?.children || [];
return Array.from(nodes, c => c.innerText).join('\n');
}
function insertText(text) {
for (let i=0; i < text.length; i++){
const keyEvent = new KeyboardEvent("keypress", {charCode: text.codePointAt(i)})
contentFrame.dispatchEvent(keyEvent);
}
}
window.gDocs = {getText, getSelection, insertText};
Some extensions are doing more than this. Grammarly seems to have done some reverse-engineering. As per the other answer you can look at the source code yourself and try to make sense of it, also there are some great insights in the linked discussion.
Upvotes: 1
Reputation: 6355
You can download Grammarly or LanguageTool extension using following tool:
https://crx-downloader.com/
Then you'll need to open it with some text editor like (BBEdit or Notepad++) and remove everything from Cr24
that is at the begging of the file until PK
(which should remain as it is first bytes of zip archive).
Then rename .crx
file you downloaded to .zip
and unzip it. So you'll have a folder with source code of their extension (similar to yours).
Then you'll be able to find out what are missing permissions or functionality you need to make it within your extension.
Upvotes: 2