Skip to content

Commit

Permalink
🚧 Implement browser_action manifest key backend
Browse files Browse the repository at this point in the history
  • Loading branch information
trickypr committed Apr 21, 2024
1 parent b9990c5 commit 496142b
Show file tree
Hide file tree
Showing 24 changed files with 507 additions and 48 deletions.
2 changes: 1 addition & 1 deletion apps/content/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"svelte-preprocess": "^5.1.3",
"svelte-sequential-preprocessor": "^2.0.1",
"ts-loader": "^9.5.1",
"typescript": "^5.3.3",
"typescript": "^5.4.5",
"webpack": "^5.89.0",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^4.15.1",
Expand Down
6 changes: 6 additions & 0 deletions apps/extensions/lib/ext-browser.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
{
"browserAction": {
"url": "chrome://bextensions/content/parent/ext-browserAction.js",
"schema": "chrome://bextensions/content/schemas/browser_action.json",
"scopes": ["addon_parent"],
"manifest": ["browser_action"]
},
"pageAction": {
"url": "chrome://bextensions/content/parent/ext-pageAction.js",
"schema": "chrome://extensions/content/schemas/page_action.json",
Expand Down
5 changes: 4 additions & 1 deletion apps/extensions/lib/parent/ext-browser.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const { lazyESModuleGetters } = typedImportUtils

const lazy = lazyESModuleGetters({
WindowTracker: 'resource://app/modules/BrowserWindowTracker.sys.mjs',
EBrowserActions: 'resource://app/modules/EBrowserActions.sys.mjs',
EPageActions: 'resource://app/modules/EPageActions.sys.mjs',
ExtensionParent: 'resource://gre/modules/ExtensionParent.sys.mjs',
})
Expand All @@ -36,8 +37,10 @@ class TabTracker extends TabTrackerBase {
}

/**
* @template {import('@browser/tabs').WindowTab | null} T
* @param {number} tabId
* @param {import('@browser/tabs').WindowTab} default_
* @param {T} default_
* @returns {T}
*/
getTab(tabId, default_) {
const { tab } = lazy.WindowTracker.getWindowWithBrowserId(tabId) || {
Expand Down
38 changes: 38 additions & 0 deletions apps/extensions/lib/parent/ext-browserAction.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// @ts-check
/// <reference path="../types/index.d.ts" />
/// <reference path="./ext-browser.js" />
/// <reference types="@browser/link" />

this.browserAction = class extends ExtensionAPIPersistent {
/** @type {import("resource://app/modules/EBrowserActions.sys.mjs").IBrowserAction | undefined} */
browserAction

async onManifestEntry() {
const { extension } = this
/** @type {browser_action__manifest.WebExtensionManifest__extended['browser_action']} */
const options = extension.manifest.browser_action

if (!options) {
return
}

this.browserAction = lazy.EBrowserActions.BrowserAction(extension.id, {
icons: lazy.ExtensionParent.IconDetails.normalize(
{
path: options.default_icon || extension.manifest.icons,
iconType: 'browserAction',
themeIcon: options.theme_icons,
},
extension,
),
title: options.default_title || extension.id,
popupUrl: options.default_popup,
})

lazy.EBrowserActions.actions.addKey(extension.id, this.browserAction)
}

onShutdown() {
lazy.EBrowserActions.actions.removeKey(this.extension.id)
}
}
6 changes: 5 additions & 1 deletion apps/extensions/lib/parent/ext-pageAction.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ this.pageAction = class extends ExtensionAPIPersistent {
const { extension } = this
const options = extension.manifest.page_action

if (!options) {
return
}

this.pageAction = new lazy.EPageActions.PageAction({
extensionId: extension.id,
tooltip: options.default_title,
Expand All @@ -27,7 +31,7 @@ this.pageAction = class extends ExtensionAPIPersistent {
{
path: options.default_icon || extension.manifest.icons,
iconType: 'browserAction',
themeIcon: options.theme_icons || extension.theme_icons,
themeIcon: options.theme_icons,
},
extension,
),
Expand Down
2 changes: 1 addition & 1 deletion apps/extensions/lib/parent/ext-tabs.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
// @ts-check
/// <reference path="../types/index.d.ts" />
/// <reference path="./ext-browser.js" />
/// <reference path="../schemaTypes/tabs.d.ts" />
/// <reference types="@browser/link" />

/**
* @param {tabs__tabs.QueryInfo} queryInfo
Expand Down
43 changes: 43 additions & 0 deletions apps/extensions/lib/schemas/browser_action.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
[
{
"namespace": "manifest",
"types": [
{
"$extend": "WebExtensionManifest",
"properties": {
"browser_action": {
"type": "object",
"optional": true,
"properties": {
"default_icon": {
"optional": true,
"$ref": "IconPath"
},
"default_popup": {
"optional": true,
"type": "string"
},
"default_title": {
"optional": true,
"type": "string"
},
"theme_icons": {
"optional": true,
"type": "array",
"items": { "$ref": "ThemeIcons" }
},
"browser_style": {
"optional": true,
"type": "boolean"
},
"default_area": {
"optional": true,
"type": "string"
}
}
}
}
}
]
}
]
1 change: 0 additions & 1 deletion apps/extensions/lib/schemas/tabs.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
"types": [
{
"$extend": "OptionalPermission",
"id": "ExtraPerms1",
"choices": [
{
"type": "string",
Expand Down
10 changes: 9 additions & 1 deletion apps/extensions/lib/types/utils.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,14 +155,22 @@ declare global {
[LISTENERS]: Map<any, any>;
[ONCE_MAP]: WeakMap<object, any>
}

class ExtensionData {
id: string

manifest: browser._manifest.WebExtensionManifest &
browser_action__manifest.WebExtensionManifest__extended
}

/**
* Base class for WebExtension APIs. Each API creates a new class
* that inherits from this class, the derived class is instantiated
* once for each extension that uses the API.
*/
class ExtensionAPI extends EventEmitter {
constructor(extension: any)
extension: any
extension: ExtensionData
destroy(): void
onManifestEntry(entry: any): void
getAPI(context: any): void
Expand Down
4 changes: 2 additions & 2 deletions apps/extensions/package.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"name": "extensions",
"name": "@browser/extensions",
"type": "module",
"version": "1.0.0",
"scripts": {
"build": "node ./scripts/buildTypes.js",
"dev": "watch 'pnpm build' ./lib/schemas/"
"dev": "watch 'pnpm build' ./lib/schemas/ ./scripts/"
},
"dependencies": {
"@browser/link": "workspace:*"
Expand Down
26 changes: 22 additions & 4 deletions apps/extensions/scripts/buildTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ const {
/**
* @typedef {object} StringType
* @property {'string'} type
* @property {{ name: string }[]} [enum]
* @property {({ name: string } | string)[]} [enum]
*/

/**
Expand All @@ -78,7 +78,12 @@ const {
*/

const schemaFolder = path.join(process.cwd(), 'lib', 'schemas')
const outFolder = path.join(process.cwd(), 'lib', 'schemaTypes')
const outFolder = path.join(
process.cwd(),
'../..',
'libs/link/types',
'schemaTypes',
)

const printer = createPrinter({
newLine: NewLineKind.LineFeed,
Expand All @@ -87,8 +92,10 @@ const printer = createPrinter({

const QUESTION_TOKEN = factory.createToken(SyntaxKind.QuestionToken)

const modules = []
for (const file of fs.readdirSync(schemaFolder)) {
const fileName = file.replace('.json', '')
modules.push(fileName)
let text = fs.readFileSync(path.join(schemaFolder, file), 'utf8')
const sourceFile = createSourceFile(file, text, ScriptTarget.Latest)

Expand Down Expand Up @@ -132,14 +139,23 @@ for (const file of fs.readdirSync(schemaFolder)) {
)
}

fs.writeFileSync(
path.join(outFolder, 'index.d.ts'),
'// @not-mpl \n' +
modules.map((m) => `/// <reference path="./${m}.d.ts" />`).join('\n'),
)

/**
* @param {Type[]} types
* @returns {import('typescript').TypeAliasDeclaration[]}
*/
function generateTypes(types) {
return types
.map((type) => {
if (type.$extend) return null
if (type.$extend) {
type.id = `${type.$extend}__extended`
type.type = 'object'
}

return factory.createTypeAliasDeclaration(
undefined,
Expand Down Expand Up @@ -256,7 +272,9 @@ function generateTypeNode(type) {
if (type.enum) {
return factory.createUnionTypeNode(
type.enum.map((e) =>
factory.createLiteralTypeNode(factory.createStringLiteral(e.name)),
factory.createLiteralTypeNode(
factory.createStringLiteral(typeof e === 'string' ? e : e.name),
),
),
)
}
Expand Down
98 changes: 98 additions & 0 deletions apps/modules/lib/EBrowserActions.sys.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// @ts-check
/// <reference types="@browser/link" />
import { map, writable } from 'resource://app/modules/SvelteStore.sys.mjs'
import mitt from 'resource://app/modules/mitt.sys.mjs'

import { derived } from './SvelteStore.sys.mjs'

/** @typedef {import('resource://app/modules/EBrowserActions.sys.mjs').IBrowserAction} IBrowserAction */
/** @implements {IBrowserAction} */
class BrowserAction {
id
/** @type {ReturnType<IBrowserAction['getEmmiter']>} */
emmiter = mitt()

/** @type{import('resource://app/modules/SvelteStore.sys.mjs').IWritable<Record<number, string>>} */
icons = writable({})
/** @type {import('resource://app/modules/SvelteStore.sys.mjs').IWritable<string>} */
title = writable('')
/** @type {import('resource://app/modules/SvelteStore.sys.mjs').IWritable<string | undefined>} */
popupUrl = writable(undefined)

/**
* @param {string} id
*/
constructor(id) {
this.id = id
}

getEmmiter() {
return this.emmiter
}

getExtensionId() {
return this.id
}

/**
* @param {Record<number, string>} icons
*/
setIcons(icons) {
this.icons.set(icons)
}
getIcons() {
return this.icons
}
/** @param {number} preferredSize */
getIcon(preferredSize) {
return derived([this.icons], (icon) => {
let bestSize

if (icon[preferredSize]) {
bestSize = preferredSize
} else if (icon[preferredSize * 2]) {
bestSize = preferredSize * 2
} else {
const sizes = Object.keys(icon)
.map((key) => parseInt(key, 10))
.sort((a, b) => a - b)
bestSize =
sizes.find((candidate) => candidate > preferredSize) ||
sizes.pop() ||
0
}

return icon[bestSize]
})
}

setTitle(title) {
this.title.set(title)
}
getTitle() {
return this.title
}

setPopupUrl(url) {
this.popupUrl.set(url)
}
getPopupUrl() {
return this.popupUrl
}
}

/** @type {typeof import('resource://app/modules/EBrowserActions.sys.mjs').EBrowserActions} */
export const EBrowserActions = {
BrowserAction: (id, options) => {
const action = new BrowserAction(id)

action.setIcons(options.icons)
action.setTitle(options.title)
action.setPopupUrl(options.popupUrl)

return action
},
actions: map({}),
}

EBrowserActions.actions.subscribe((a) => console.log(JSON.stringify(a)))
Loading

0 comments on commit 496142b

Please sign in to comment.