Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Firefox support #1154

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
34 changes: 34 additions & 0 deletions examples/firefox-mv2/content.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
'use strict';

console.log('InboxSDK: content script loaded');

// Set InboxSDK inject method
InboxSDK.setInjectScriptImplementation(() => {
const script = document.createElement('script');
script.type = 'text/javascript';
script.src = browser.runtime.getURL('pageWorld.js');
document.documentElement.appendChild(script);

console.log('InboxSDK: pageWorld.js manually injected!');
});

InboxSDK.load(2, 'firefox-mv2-example', {
appName: 'Twitter',
appIconUrl:
'http://materialdesignblog.com/wp-content/uploads/2015/04/387b93c8b66379c32e1cc2b98dcf5197.png',
suppressAddonTitle: 'Streak',
}).then((sdk) => {
console.log('InboxSDK: has loaded', sdk);

sdk.Compose.registerComposeViewHandler((view) => {
view.addButton({
title: 'My button',
iconUrl: (globalThis.chrome || globalThis.browser).runtime.getURL(
'monkey.png',
),
onClick() {
alert('my button works!');
},
});
});
});
DaPotatoMan marked this conversation as resolved.
Show resolved Hide resolved
18 changes: 18 additions & 0 deletions examples/firefox-mv2/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"manifest_version": 2,
"name": "Firefox MV2",
"description": "InboxSDK example!",
"version": "1.0",
"permissions": ["scripting"],
"background": {
"scripts": ["background.js"]
},
"content_scripts": [
{
"matches": ["*://mail.google.com/*"],
"js": ["inboxsdk.js", "content.js"],
"run_at": "document_end"
}
],
"web_accessible_resources": ["pageWorld.js", "monkey.png"]
}
DaPotatoMan marked this conversation as resolved.
Show resolved Hide resolved
Binary file added examples/firefox-mv2/monkey.png
DaPotatoMan marked this conversation as resolved.
Show resolved Hide resolved
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
34 changes: 34 additions & 0 deletions examples/firefox-mv3/content.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
'use strict';

console.log('InboxSDK: content script loaded');

// Set InboxSDK inject method
InboxSDK.setInjectScriptImplementation(() => {
const script = document.createElement('script');
script.type = 'text/javascript';
script.src = browser.runtime.getURL('pageWorld.js');
document.documentElement.appendChild(script);

console.log('InboxSDK: pageWorld.js manually injected!');
});

InboxSDK.load(2, 'firefox-mv3-example', {
appName: 'Twitter',
appIconUrl:
'http://materialdesignblog.com/wp-content/uploads/2015/04/387b93c8b66379c32e1cc2b98dcf5197.png',
suppressAddonTitle: 'Streak',
}).then((sdk) => {
console.log('InboxSDK: has loaded', sdk);

sdk.Compose.registerComposeViewHandler((view) => {
view.addButton({
title: 'My button',
iconUrl: (globalThis.chrome || globalThis.browser).runtime.getURL(
'monkey.png',
),
onClick() {
alert('my button works!');
},
});
});
});
24 changes: 24 additions & 0 deletions examples/firefox-mv3/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"manifest_version": 3,
"name": "Firefox MV3",
"description": "InboxSDK example!",
"version": "1.0",
"host_permissions": ["https://mail.google.com/"],
"permissions": ["scripting"],
"background": {
"scripts": ["background.js"]
},
"content_scripts": [
{
"matches": ["*://mail.google.com/*"],
"js": ["inboxsdk.js", "content.js"],
"run_at": "document_end"
}
],
"web_accessible_resources": [
{
"matches": ["*://mail.google.com/*"],
"resources": ["pageWorld.js", "monkey.png"]
}
]
}
Binary file added examples/firefox-mv3/monkey.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 2 additions & 4 deletions examples/types.d.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { InboxSDK as InboxSDK_ } from '../src/inboxsdk';
import * as SDK from '../src/inboxsdk';

declare global {
var InboxSDK: {
load(version: string, moduleName: string): Promise<InboxSDK_>;
};
var InboxSDK: typeof SDK;
}
1 change: 1 addition & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ module.exports = {
globals: {
SDK_VERSION: 'beep',
},
setupFiles: ['./jest.setup.js'],
moduleNameMapper: {
'\\.css$': require.resolve('jest-css-modules'),
},
Expand Down
8 changes: 8 additions & 0 deletions jest.setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// Add support for webextension-polyfill in unit tests
if (!globalThis.chrome) {
globalThis.chrome = {
runtime: {
id: 'testid',
},
};
}
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"@types/sinon-test": "^2.4.0",
"@types/transducers.js": "^0.3.0",
"@types/vinyl-source-stream": "^0.0.30",
"@types/webextension-polyfill": "^0.10.7",
"@typescript-eslint/eslint-plugin": "^5.59.8",
"@typescript-eslint/parser": "^5.59.8",
"asap": "^2.0.3",
Expand Down Expand Up @@ -94,7 +95,8 @@
"typed-emitter": "^2.1.0",
"typescript": "^5.3.3",
"ud": "^3.3.1",
"ud-kefir": "^2.0.1"
"ud-kefir": "^2.0.1",
"webextension-polyfill": "^0.10.0"
},
"description": "Library for browser extensions to interact with Gmail.",
"license": "(MIT OR Apache-2.0)",
Expand Down
10 changes: 6 additions & 4 deletions packages/core/background.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
/* global chrome */
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
/** @type {import('webextension-polyfill').Browser} */
const browser = globalThis.chrome || globalThis.browser;

browser.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.type === 'inboxsdk__injectPageWorld' && sender.tab) {
if (chrome.scripting) {
if (browser.scripting) {
// MV3
chrome.scripting.executeScript({
browser.scripting.executeScript({
target: { tabId: sender.tab.id },
world: 'MAIN',
files: ['pageWorld.js'],
Expand Down
10 changes: 7 additions & 3 deletions src/common/get-extension-id.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import browser from 'webextension-polyfill';

export default function getExtensionId(): string | null {
const chrome: any = (global as any).chrome;
if (chrome && chrome.extension && chrome.extension.getURL) {
return chrome.extension.getURL('');
try {
return browser.runtime.id;
} catch (error) {
console.error('Failed to get extension id');
}
DaPotatoMan marked this conversation as resolved.
Show resolved Hide resolved

return null;
}
7 changes: 4 additions & 3 deletions src/common/load-script.ts
DaPotatoMan marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ import ajax from './ajax';
import delay from 'pdelay';

const isContentScript: () => boolean = once(function () {
if ((global as any).chrome && (global as any).chrome.extension) return true;
if ((global as any).safari && (global as any).safari.extension) return true;
return false;
const ctx = global as any;
const env = ['chrome', 'browser', 'safari'] as const;

return env.some((key) => ctx[key] && !!ctx[key].extension);
});

function addScriptToPage(url: string, cors: boolean): Promise<void> {
Expand Down
2 changes: 2 additions & 0 deletions src/inboxsdk-js/inboxsdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { BUILD_VERSION } from '../common/version';
import { loadScript as _loadScript } from './load-script-proxy';
import type { LoadScriptOptions } from '../common/load-script';

export { setInjectScriptImplementation } from '../platform-implementation-js/lib/inject-script';

declare global {
var __test_origin: string | undefined;
}
Expand Down
2 changes: 2 additions & 0 deletions src/inboxsdk.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ import type ListRouteView from './platform-implementation-js/views/route-view/li
import type SectionView from './platform-implementation-js/views/section-view';
import type { RouteParams } from './platform-implementation-js/namespaces/router';

export { setInjectScriptImplementation } from './platform-implementation-js/lib/inject-script';

export type { User };

export const LOADER_VERSION: string;
Expand Down
54 changes: 35 additions & 19 deletions src/platform-implementation-js/lib/inject-script.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import browser from 'webextension-polyfill';
import once from 'lodash/once';
import Kefir from 'kefir';
import makeMutationObserverChunkedStream from './dom/make-mutation-observer-chunked-stream';
Expand All @@ -8,26 +9,26 @@ declare global {
}

let injectScriptImplementation: () => void = () => {
(window as any).chrome.runtime.sendMessage(
{ type: 'inboxsdk__injectPageWorld' },
(didExecute: boolean) => {
if (!didExecute) {
if (NPM_MV2_SUPPORT) {
// MV2 support.
// Removed from regular MV3 NPM builds to not falsely set off Chrome Web Store
// about dynamically-loaded code.
const scr = document.createElement('script');
scr.type = 'text/javascript';
scr.src = (window as any).chrome.runtime.getURL('pageWorld.js');
document.documentElement.appendChild(scr);
} else {
throw new Error(
"Couldn't inject pageWorld.js. Check that the extension is using MV3 and has the correct permissions and host_permissions in its manifest.",
);
}
browser.runtime
.sendMessage({ type: 'inboxsdk__injectPageWorld' })
.then((didExecute: boolean) => {
/** pageWorld was injected successfully */
if (didExecute) return;

if (NPM_MV2_SUPPORT) {
// MV2 support.
// Removed from regular MV3 NPM builds to not falsely set off Chrome Web Store
// about dynamically-loaded code.
const scr = document.createElement('script');
scr.type = 'text/javascript';
scr.src = browser.runtime.getURL('pageWorld.js');
document.documentElement.appendChild(scr);
} else {
throw new Error(
"Couldn't inject pageWorld.js. Check that the extension is using MV3 and has the correct permissions and host_permissions in its manifest.",
);
}
},
);
});
};

// Returns a promise that resolves once the injected script has been injected
Expand All @@ -53,6 +54,21 @@ export const injectScript = once((): Promise<null> => {
.toPromise();
});

/** This function can be used override the logic for injecting pageWorld.js to Gmail page.
* Useful for firefox browser or mv2
*
* @example
* ```ts
* InboxSDK.setInjectScriptImplementation(() => {
* const script = document.createElement('script');
* script.type = 'text/javascript';
* script.src = browser.runtime.getURL('pageWorld.js');
* document.documentElement.appendChild(script);
* })
*
* InboxSDK.load(2, ...);
* ```
*/
export function setInjectScriptImplementation(fn: () => void) {
injectScriptImplementation = fn;
}
16 changes: 16 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2548,6 +2548,13 @@ __metadata:
languageName: node
linkType: hard

"@types/webextension-polyfill@npm:^0.10.7":
version: 0.10.7
resolution: "@types/webextension-polyfill@npm:0.10.7"
checksum: b2d9b4f4e7ebafb299e8dfc75ac2c3cc6cc29bebce5f590c01533388b0d22d4a9fa8e7cb547cb69d1092c3a8d573675df3355d46c64de77d690b30ae641eeb77
languageName: node
linkType: hard

"@types/yargs-parser@npm:*":
version: 20.2.0
resolution: "@types/yargs-parser@npm:20.2.0"
Expand Down Expand Up @@ -6722,6 +6729,7 @@ __metadata:
"@types/sinon-test": "npm:^2.4.0"
"@types/transducers.js": "npm:^0.3.0"
"@types/vinyl-source-stream": "npm:^0.0.30"
"@types/webextension-polyfill": "npm:^0.10.7"
"@typescript-eslint/eslint-plugin": "npm:^5.59.8"
"@typescript-eslint/parser": "npm:^5.59.8"
asap: "npm:^2.0.3"
Expand Down Expand Up @@ -6796,6 +6804,7 @@ __metadata:
typescript: "npm:^5.3.3"
ud: "npm:^3.3.1"
ud-kefir: "npm:^2.0.1"
webextension-polyfill: "npm:^0.10.0"
webpack: "npm:^5.76.3"
languageName: unknown
linkType: soft
Expand Down Expand Up @@ -12539,6 +12548,13 @@ __metadata:
languageName: node
linkType: hard

"webextension-polyfill@npm:^0.10.0":
version: 0.10.0
resolution: "webextension-polyfill@npm:0.10.0"
checksum: 51ff30ebed4b1aa802b7f0347f05021b2fe492078bb1a597223d43995fcee96e2da8f914a2f6e36f988c1877ed5ab36ca7077f2f3ab828955151a59e4c01bf7e
languageName: node
linkType: hard

"webidl-conversions@npm:^7.0.0":
version: 7.0.0
resolution: "webidl-conversions@npm:7.0.0"
Expand Down