Skip to content
91 changes: 91 additions & 0 deletions README.MD
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,94 @@ these instructions:
https://developer.chrome.com/docs/extensions/mv3/getstarted/development-basics/#load-unpacked
- Microsoft Edge:
https://docs.microsoft.com/en-us/microsoft-edge/extensions-chromium/getting-started/extension-sideloading


# How does the extension work.

Extensions have two types of scripts: background scripts and content scripts.
- Background scripts run in the background of the browser and can be used to
listen to events and perform actions that are common to all tabs.
- Content scripts run in the context of a tab and can be used to interact with
the DOM of the page.

Here in our extension, we store the spaceId, apiKey, selectedCollection, and selectedDemo. Storing and retrieving these
values is done using the chrome.storage API. The background script does the storing and retrieving of these values.


Messaging between the background script and the content script is done using the chrome.runtime API.

# dodao-upload.js and dodao-content.js
All the content on the DOM is created in dodao-content.js. This is the content script. When any action is done on the
UI, we listen to the action, and pass a message to the background script.

For example

# Loading of the extension
When the extension icon is clicked, the background checks if the spaceId and apiKey exists, if it exists, it send a message
to the content script(dodao-content.js), to draw the bottom bar.

If the spaceId and apiKey does not exist, it sends a message to the content script to draw the modal to capture the spaceId
and apiKey.

The UI captures it and then sends a message to the background script to store the spaceId and apiKey.


# When we enter file name of capture
when user enters the "Html capture" file name, we call the background script and pass the filename.

Background script has the logic to see what needs to be done, and what UI needs to be shown next. In this case, the
background will initiate download of the file by calling `business.saveTabs` method and passing dodao specific config.


# Showing of extension
The extension is shown when the user clicks on the extension icon. The background script listens to the click event. Its
present in `ui-button.js`

```javascript
browser.action.onClicked.addListener(async tab => {
await business.extensionIconClicked(tab);
});
```

Earlier(base code of extension), used to call saveTab, which used to download the file. Now we show the bottom bar
or the spaceId and apiKey modal. `business.extensionIconClicked(tab)` this function was written by us.

# Important files
- `dodao-upload.js` - This is the background script. This is the script that listens to the messages from the content script
and does the necessary actions. This has the logic to decide what should be done next, i.e. flow of the extension.
- `dodao-content.js` - This is the content script. This is the script that interacts with the DOM of the page. This is the
script that creates the UI on the page. i.e. the bottom bar or the modals, or the success and error messages.
- `background.js` - This has the logic for post handler of download of the file. This in our flow is called from `content.js` (existing logic of the extension)
```javascript
const pageData = await processPage(options);
if (pageData) {
await download.downloadPage(pageData, options);
}
```
We just modify a couple of lines in download.js. This file then does the post handling of the download and send a message "downloads.download"

Here is some of the custom logic
```javascript
if (options.saveWithTidbitsHub) {
// we capture screenshot using options.content as it contains whole page content for the screesnhot
message.dodaoScreenshotBlobUrl = await getDodaoScreenshotBlobUrl(options.content);
}
```
- `download.js` - This is the file that handles the download of the file. This is the file that listens to the message "downloads.download"
We added some custom code to this file which looks like
```javascript
if (message.saveWithTidbitsHub) {
// adding the scripts to the content
message.pageData.content=injectScriptLinkTags(message.pageData.content)
}
```
This is the code that injects the scripts to the content. Like the dodao tooltip, selector, css, and tippy scripts

```javascript
else if (message.saveWithTidbitsHub) {
const blob = await (await fetch(blobURI)).blob();
const screenshotBlob = new Blob([await (await fetch(message.dodaoScreenshotBlobUrl)).blob()], { type: "image/png" });
await uploadFileToDodao(message.captureHtmlScreenFileName, blob, screenshotBlob);
}
```
This code send the message to dodao api to upload the html capture.
23 changes: 1 addition & 22 deletions manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,6 @@
"background": {
"service_worker": "lib/single-file-extension-background.js"
},
"side_panel": {
"default_path": "src/ui/pages/panel.html"
},
"options_ui": {
"browser_style": true,
"page": "src/ui/pages/options.html",
"open_in_tab": false
},
"action": {
"default_icon": {
"16": "src/ui/resources/icon_16.png",
Expand All @@ -71,20 +63,7 @@
},
"default_title": "__MSG_buttonDefaultTooltip__"
},
"commands": {
"save-selected-tabs": {
"suggested_key": {
"default": "Ctrl+Shift+Y"
},
"description": "__MSG_commandSaveSelectedTabs__"
},
"save-all-tabs": {
"suggested_key": {
"default": "Ctrl+Shift+U"
},
"description": "__MSG_commandSaveAllTabs__"
}
},
"commands": {},
"web_accessible_resources": [
{
"matches": [
Expand Down
40 changes: 14 additions & 26 deletions src/core/common/dodao-screenshot.js
Original file line number Diff line number Diff line change
@@ -1,32 +1,20 @@
import html2canvas from "html2canvas";
import * as ui from "./../../ui/content/content-ui.js";

export async function getDodaoScreenshotBlobUrl(content) {
const iframe = createIframeWithContent(content);
document.body.appendChild(iframe);
export async function getDodaoScreenshotBlobUrl(options) {
ui.setVisible(false);

const dodaoScreenshotBlobUrl = await new Promise((resolve, reject) => {
iframe.onload = async () => {
const iframeDocument = iframe.contentDocument;
const canvas = await html2canvas(iframeDocument.body, {
width: 1920,
height: 1080,
windowWidth: 1920,
windowHeight: 1080,
useCORS: true,
allowTaint: true,
});

const canvasBlob = await canvasToBlob(canvas);

const dodaoScreenshotBlobUrl = URL.createObjectURL(canvasBlob);
// remove the iframe after screesnhot is taken
document.body.removeChild(iframe);

resolve(dodaoScreenshotBlobUrl);
};
const screenshotBlobURI = await browser.runtime.sendMessage({
method: "tabs.getScreenshot",
width: window.innerWidth,
height: window.innerHeight,
innerHeight: globalThis.innerHeight,
});

return dodaoScreenshotBlobUrl;
ui.setVisible(true);
const embeddedImage = new Uint8Array(
await (await fetch(screenshotBlobURI)).arrayBuffer()
);
const blob = new Blob([embeddedImage], { type: "image/png" });
return URL.createObjectURL(blob);
}

function createIframeWithContent(htmlContent) {
Expand Down
31 changes: 29 additions & 2 deletions src/core/common/dodao-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,45 @@ export const CLIKABLE_FILES_HOST_URL = "https://dodao-prod-public-assets.s3.amaz

export function injectScriptLinkTags(htmlContent) {
console.log("Injecting script and link tags into HTML content");
const insertionIndex = findInsertionIndex(htmlContent);
// Add `allow-same-origin` to iframes
const updatedHtmlContent = addAllowSameOriginToIframes(htmlContent);
const insertionIndex = findInsertionIndex(updatedHtmlContent);

if (insertionIndex !== undefined) {
const tags = getScriptLinkTags();
const modifiedHtml = insertTagsIntoHtml(htmlContent, insertionIndex, tags);
const modifiedHtml = insertTagsIntoHtml(updatedHtmlContent, insertionIndex, tags);
return modifiedHtml;
} else {
console.warn("Unable to find opening style tag in HTML content");
return htmlContent; // Return unmodified content if the style tag is not found
}
}

function addAllowSameOriginToIframes(htmlContent) {
// Regular expression to find all <iframe> tags
return htmlContent.replace(/<iframe([^>]*)>/g, (match, attributes) => {
// Check if `sandbox` attribute exists
const sandboxMatch = attributes.match(/sandbox="([^"]*)"/);
if (sandboxMatch) {
// `sandbox` attribute exists, check if `allow-same-origin` is already present
const sandboxValue = sandboxMatch[1];
if (!sandboxValue.includes("allow-same-origin")) {
// Append `allow-same-origin` to existing `sandbox` value
const newSandboxValue = `sandbox="${sandboxValue} allow-same-origin"`;
return `<iframe${attributes.replace(
sandboxMatch[0],
newSandboxValue
)}>`;
}
} else {
// `sandbox` attribute does not exist, add it with `allow-same-origin`
return `<iframe${attributes} sandbox="allow-same-origin">`;
}
// If `allow-same-origin` is already present, return the original match
return match;
});
}

export function slugify(string) {
return string
.toString()
Expand Down
10 changes: 2 additions & 8 deletions src/core/common/download.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,7 @@
import * as yabson from "./../../lib/yabson/yabson.js";
import * as ui from "./../../ui/content/content-ui.js";
import { getSharePageBar, setLabels } from "./../../ui/common/common-content-ui.js";
import html2canvas from "html2canvas";
import {getDodaoScreenshotBlobUrl} from "./dodao-screenshot.js";
import { getDodaoScreenshotBlobUrl } from "./dodao-screenshot.js";

const MAX_CONTENT_SIZE = 16 * (1024 * 1024);

Expand Down Expand Up @@ -129,7 +128,6 @@ async function downloadPage(pageData, options) {
}
if (pageData.filename) {
if (options.saveWithTidbitsHub) {
// we capture screenshot using options.content as it contains whole page content for the screesnhot
message.dodaoScreenshotBlobUrl = await getDodaoScreenshotBlobUrl(options.content);
}
const blob = new Blob([await yabson.serialize(pageData)], { type: pageData.mimeType });
Expand Down Expand Up @@ -173,10 +171,6 @@ async function downloadPage(pageData, options) {
message.filename = pageData.filename = filename;
pageData.filename = options.captureHtmlScreenFileName;

if(options.saveWithTidbitsHub) {
message.dodaoScreenshotBlobUrl = await getDodaoScreenshotBlobUrl(pageData.content);
}

const blobURL = URL.createObjectURL(new Blob([pageData.content], { type: pageData.mimeType }));
message.blobURL = blobURL;
console.log('message in download.js', message);
Expand Down Expand Up @@ -272,4 +266,4 @@ function saveToClipboard(pageData) {
event.clipboardData.setData("text/plain", pageData.content);
event.preventDefault();
}
}
}
2 changes: 1 addition & 1 deletion src/core/content/content.js
Original file line number Diff line number Diff line change
Expand Up @@ -362,4 +362,4 @@ async function processPage(options) {
}
}
return page;
}
}
Loading