diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
index 5bee6f7..eafcc8a 100644
--- a/.github/FUNDING.yml
+++ b/.github/FUNDING.yml
@@ -1,3 +1,3 @@
# These are supported funding model platforms
-custom: https://bank.hackclub.com/donations/start/linkscape
+custom: https://linkscape.app/donate
diff --git a/Language/ChineseSimplified.isl b/ChineseSimplified.isl
similarity index 100%
rename from Language/ChineseSimplified.isl
rename to ChineseSimplified.isl
diff --git a/ContextSearch-webext/Chrome/contextsearch_webext.json b/ContextSearch-webext/Chrome/contextsearch_webext.json
deleted file mode 100644
index ff3bfcd..0000000
--- a/ContextSearch-webext/Chrome/contextsearch_webext.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "name": "contextsearch_webext",
- "description": "Launches external application",
- "type": "stdio",
- "allowed_origins": [
- "chrome-extension://lnojieghgnojlhmnfpmeidigmjpkppep/",
- "chrome-extension://ddippghibegbgpjcaaijbacfhjjeafjh/"
- ],
- "path": "..\\ContextSearch.bat"
-}
\ No newline at end of file
diff --git a/ContextSearch-webext/Chromium/contextsearch_webext.json b/ContextSearch-webext/Chromium/contextsearch_webext.json
deleted file mode 100644
index ff3bfcd..0000000
--- a/ContextSearch-webext/Chromium/contextsearch_webext.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "name": "contextsearch_webext",
- "description": "Launches external application",
- "type": "stdio",
- "allowed_origins": [
- "chrome-extension://lnojieghgnojlhmnfpmeidigmjpkppep/",
- "chrome-extension://ddippghibegbgpjcaaijbacfhjjeafjh/"
- ],
- "path": "..\\ContextSearch.bat"
-}
\ No newline at end of file
diff --git a/ContextSearch-webext/ContextSearch.bat b/ContextSearch-webext/ContextSearch.bat
deleted file mode 100644
index c6f0367..0000000
--- a/ContextSearch-webext/ContextSearch.bat
+++ /dev/null
@@ -1,2 +0,0 @@
-@echo off
-"C:\Python311\python.exe" -u ContextSearch.py
diff --git a/ContextSearch-webext/ContextSearch.py b/ContextSearch-webext/ContextSearch.py
deleted file mode 100644
index e429cc2..0000000
--- a/ContextSearch-webext/ContextSearch.py
+++ /dev/null
@@ -1,138 +0,0 @@
-#!/usr/bin/python3 -u
-
-# Note that running python with the `-u` flag is required on Windows,
-# in order to ensure that stdin and stdout are opened in binary, rather
-# than text, mode.
-
-import json
-import sys
-import struct
-import os
-
-__version__ = "2.18"
-
-BINARY_URL = "https://raw.githubusercontent.com/ssborbis/ContextSearch-Native-App/master/ContextSearch.py"
-VERSION_URL = "https://raw.githubusercontent.com/ssborbis/ContextSearch-Native-App/master/version.json"
-
-# Read a message from stdin and decode it.
-def get_message():
- raw_length = sys.stdin.buffer.read(4)
-
- if not raw_length:
- sys.exit(0)
-
- message_length = struct.unpack('=I', raw_length)[0]
- message = sys.stdin.buffer.read(message_length).decode("utf-8")
- return json.loads(message)
-
-# Encode a message for transmission, given its content.
-def encode_message(message_content):
- encoded_content = json.dumps(message_content).encode("utf-8")
- encoded_length = struct.pack('=I', len(encoded_content))
- # use struct.pack("10s", bytes), to pack a string of the length of 10 characters
- return {'length': encoded_length, 'content': struct.pack(str(len(encoded_content))+"s",encoded_content)}
-
-# Send an encoded message to stdout.
-def send_message(encoded_message):
- sys.stdout.buffer.write(encoded_message['length'])
- sys.stdout.buffer.write(encoded_message['content'])
- sys.stdout.buffer.flush()
-
-def check_for_update():
- import urllib.request
- response = urllib.request.urlopen(VERSION_URL)
- js = json.loads(response.read().decode("utf-8"))
- latest_version = js["version"]
-
- if ( float(latest_version) > float(__version__)):
- return latest_version
- else:
- return False
-
-def update():
- import urllib.request
- response = urllib.request.urlopen(BINARY_URL)
- remote_script = response.read().decode("utf-8")
-
- with open(os.path.realpath(__file__), 'w') as f:
- f.write(remote_script);
-
-def download(url, dest):
- from urllib.request import urlopen
- from urllib.request import urlretrieve
- import cgi
-
- remotefile = urlopen(url)
- content = remotefile.info()['Content-Disposition']
- if not content is None:
- value, params = cgi.parse_header(content)
- filename = os.path.join(dest, params["filename"])
- else:
- filename = os.path.join(dest, os.path.basename(url))
-
- urlretrieve(url, filename)
-
- return filename
-
-message = get_message()
-
-if not message.get("verify") is None:
- send_message(encode_message(True))
- sys.exit(0)
-
-if not message.get("version") is None:
- send_message(encode_message(__version__))
- sys.exit(0)
-
-if not message.get("checkForUpdate") is None:
- send_message(encode_message(check_for_update()))
- sys.exit(0)
-
-if not message.get("update") is None:
- update()
- send_message(encode_message(True))
- sys.exit(0)
-
-if not message.get("downloadURL") is None:
- tmpdir = None
-
- if not message.get("downloadFolder") is None and os.path.isdir(os.path.expanduser(message.get("downloadFolder"))):
- tmpdir = os.path.expanduser(message.get("downloadFolder"))
- else:
- import tempfile
- tmpdir = tempfile.gettempdir()
-
- filename = download(message.get("downloadURL"), tmpdir)
- message["path"] = message["path"].replace("{download_url}", filename)
-
-if not message.get("path") is None:
-
- import subprocess
-
- cwd = message.get("cwd") or os.getcwd()
- cwd = os.path.expanduser(cwd)
-
- import shlex
- cmd = shlex.split(message["path"])
-
- if message["return_stdout"]:
- output = subprocess.check_output(message["path"], cwd=cwd, shell=True).decode()
- send_message(encode_message(output))
- else:
-
- if sys.platform == "win32":
-
- CREATE_NEW_PROCESS_GROUP = 0x00000200
- DETACHED_PROCESS = 0x00000008
- CREATE_NEW_CONSOLE = 0x00000010
- CREATE_BREAKAWAY_FROM_JOB = 0x01000000
-
- subprocess.run(message["path"], cwd=cwd, shell=True, creationflags=CREATE_BREAKAWAY_FROM_JOB )
-
- else:
- subprocess.run(message["path"], cwd=cwd, shell=True)
-
- sys.exit(0)
-
-send_message(encode_message(False))
-sys.exit(1)
\ No newline at end of file
diff --git a/ContextSearch-webext/Edge/contextsearch_webext.json b/ContextSearch-webext/Edge/contextsearch_webext.json
deleted file mode 100644
index ff3bfcd..0000000
--- a/ContextSearch-webext/Edge/contextsearch_webext.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "name": "contextsearch_webext",
- "description": "Launches external application",
- "type": "stdio",
- "allowed_origins": [
- "chrome-extension://lnojieghgnojlhmnfpmeidigmjpkppep/",
- "chrome-extension://ddippghibegbgpjcaaijbacfhjjeafjh/"
- ],
- "path": "..\\ContextSearch.bat"
-}
\ No newline at end of file
diff --git a/ContextSearch-webext/Firefox/contextsearch_webext.json b/ContextSearch-webext/Firefox/contextsearch_webext.json
deleted file mode 100644
index 5322793..0000000
--- a/ContextSearch-webext/Firefox/contextsearch_webext.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "name": "contextsearch_webext",
- "description": "Launches external application",
- "type": "stdio",
- "allowed_extensions": [
- "{5dd73bb9-e728-4d1e-990b-c77d8e03670f}"
- ],
- "path": "..\\ContextSearch.bat"
-}
\ No newline at end of file
diff --git a/ContextSearch-webext/Vivaldi/contextsearch_webext.json b/ContextSearch-webext/Vivaldi/contextsearch_webext.json
deleted file mode 100644
index ff3bfcd..0000000
--- a/ContextSearch-webext/Vivaldi/contextsearch_webext.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "name": "contextsearch_webext",
- "description": "Launches external application",
- "type": "stdio",
- "allowed_origins": [
- "chrome-extension://lnojieghgnojlhmnfpmeidigmjpkppep/",
- "chrome-extension://ddippghibegbgpjcaaijbacfhjjeafjh/"
- ],
- "path": "..\\ContextSearch.bat"
-}
\ No newline at end of file
diff --git a/LinkDown/chrome.json b/LinkDown/chrome.json
new file mode 100644
index 0000000..6ad6a44
--- /dev/null
+++ b/LinkDown/chrome.json
@@ -0,0 +1,9 @@
+{
+ "name": "linkdown",
+ "description": "Launches external video download softwares",
+ "type": "stdio",
+ "allowed_origins": [
+ "chrome-extension://lnckamlbboggdkkgnkaocibpnilhemhc/"
+ ],
+ "path": "nativehost.bat"
+}
\ No newline at end of file
diff --git a/LinkDown/firefox.json b/LinkDown/firefox.json
new file mode 100644
index 0000000..5242560
--- /dev/null
+++ b/LinkDown/firefox.json
@@ -0,0 +1,9 @@
+{
+ "name": "linkdown",
+ "description": "Launches external video download softwares",
+ "type": "stdio",
+ "allowed_extensions": [
+ "linkdown_for_browsers@linkscape.app"
+ ],
+ "path": "nativehost.bat"
+}
\ No newline at end of file
diff --git a/LinkDown/lux.exe b/LinkDown/lux.exe
deleted file mode 100644
index 9b99a73..0000000
Binary files a/LinkDown/lux.exe and /dev/null differ
diff --git a/LinkDown/nativehost.bat b/LinkDown/nativehost.bat
new file mode 100644
index 0000000..0cd3534
--- /dev/null
+++ b/LinkDown/nativehost.bat
@@ -0,0 +1,2 @@
+@echo off
+python -u nativehost.py
\ No newline at end of file
diff --git a/LinkDown/nativehost.py b/LinkDown/nativehost.py
new file mode 100644
index 0000000..379d790
--- /dev/null
+++ b/LinkDown/nativehost.py
@@ -0,0 +1,37 @@
+import json
+import subprocess
+import sys
+
+# Read a message from stdin and decode it
+def read_message():
+ raw_length = sys.stdin.buffer.read(4)
+ if not raw_length:
+ sys.exit(0)
+ message_length = int.from_bytes(raw_length, byteorder='little')
+ message = sys.stdin.buffer.read(message_length).decode('utf-8')
+ return json.loads(message)
+
+# Encode a message and send it to stdout
+def send_message(message):
+ encoded_message = json.dumps(message).encode('utf-8')
+ encoded_length = len(encoded_message).to_bytes(4, byteorder='little')
+ sys.stdout.buffer.write(encoded_length)
+ sys.stdout.buffer.write(encoded_message)
+ sys.stdout.buffer.flush()
+
+# Main loop
+while True:
+ # Read a message from stdin
+ message = read_message()
+
+ # Check if the message contains a command
+ if 'command' not in message:
+ send_message({'error': 'No command specified'})
+ continue
+
+ # Execute the command using subprocess
+ try:
+ output = subprocess.check_output(message['command'], shell=True, stderr=subprocess.STDOUT)
+ send_message({'output': output.decode('utf-8')})
+ except subprocess.CalledProcessError as e:
+ send_message({'error': e.output.decode('utf-8')})
diff --git a/LinkDown/yt-dlp.exe b/LinkDown/yt-dlp.exe
index 64b32ee..74c4876 100644
Binary files a/LinkDown/yt-dlp.exe and b/LinkDown/yt-dlp.exe differ
diff --git a/LinkDown_Wizard.iss b/LinkDown_Wizard.iss
index 92c7f29..cfed512 100644
--- a/LinkDown_Wizard.iss
+++ b/LinkDown_Wizard.iss
@@ -2,7 +2,7 @@
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
#define MyAppName "LinkDown"
-#define MyAppVersion "0.1.1"
+#define MyAppVersion "0.2"
#define MyAppPublisher "LinkScape"
[Setup]
@@ -13,7 +13,7 @@ AppName={#MyAppName}
AppVersion={#MyAppVersion}
;AppVerName={#MyAppName} {#MyAppVersion}
AppPublisher={#MyAppPublisher}
-DefaultDirName={autopf}\{#MyAppName}
+DefaultDirName={autopf}\LinkScape\{#MyAppName}
DisableDirPage=yes
DefaultGroupName={#MyAppName}
DisableProgramGroupPage=yes
@@ -33,7 +33,7 @@ Name: modifypath; Description: Add LinkDown to PATH; Flags: checkablealone
[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"
-Name: "chinesesimplified"; MessagesFile: "{src}\Languages\ChineseSimplified.isl"
+Name: "chinesesimplified"; MessagesFile: "ChineseSimplified.isl"
[Code]
const
@@ -49,9 +49,8 @@ end;
[Files]
Source: "LinkDown\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
-Source: "ContextSearch-webext\*"; DestDir: "{userappdata}\ContextSearch-webext"; Flags: ignoreversion recursesubdirs createallsubdirs
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
[Registry]
-Root: HKCR; Subkey: "Software\Mozilla\NativeMessagingHosts\contextsearch_webext"; ValueType: String; ValueData: "{userappdata}\ContextSearch-webext\Firefox\contextsearch_webext.json"; MinVersion: 0.0,6.0;
-Root: HKCR; Subkey: "Software\Google\Chrome\NativeMessagingHosts\contextsearch_webext"; ValueType: String; ValueData: "{userappdata}\ContextSearch-webext\Chrome\contextsearch_webext.json"; MinVersion: 0.0,6.0;
+Root: HKCU; Subkey: "Software\Mozilla\NativeMessagingHosts\linkdown"; ValueType: String; ValueData: "{app}\firefox.json"; MinVersion: 0.0,6.0;
+Root: HKCU; Subkey: "Software\Google\Chrome\NativeMessagingHosts\linkdown"; ValueType: String; ValueData: "{app}\chrome.json"; MinVersion: 0.0,6.0;
diff --git a/README.md b/README.md
index 1dbb7a0..dc7b8e0 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
LinkDown
-A Video Downloader. Easier than easy.
+A simple link, a easier life.
@@ -12,11 +12,12 @@
+
English | [简体中文](https://github.com/LinkScapeFoundation/LinkDown/blob/main/README_CN.md)
## Donate
-Donate via Stripe: [here](https://bank.hackclub.com/donations/start/linkscape)
+Donate via Stripe: [here](https://linkscape.app/donate)
[ ](https://hackclub.com/bank)
@@ -24,72 +25,54 @@ Donate via Stripe: [here](https://bank.hackclub.com/donations/start/linkscape)
Discord: https://discord.gg/WDdvabyKaH
-Telegram: https://t.me/LinkScapeChat
-
-## Download link:
+## Download Link
-https://github.com/LinkScapeOfficial/LinkDown/releases/latest
+https://github.com/LinkScapeOfficial/LinkDown/releases/latest/download/linkdown_setup.exe
------
-Software develop:Thomas Wu [(@thomaswcy)](https://github.com/thomaswcy)
+Software developed by **Thomas Wu** ([@thomaswcy](https://github.com/thomaswcy), Founder of LinkScape)
-Product planning & English translation:Meli Liu [(@MeliLXT)](https://github.com/MeliLXT)
+Translted to English by **Meli Liu** ([@MeliLXT](https://github.com/MeliLXT), Founder of LinkScape)
-Tutorials:RunningCheese [(@RunningCheese)](https://github.com/RunningCheese)
+Logo designed by **Tzu-Yun Hsiao** ([@Powerlean](https://github.com/Powerlean), Head of Designs at LinkScape)
-Logo design:MemeCat [(@Powerlean)](https://github.com/Powerlean)
+Tutorials written by RunningCheese [(@RunningCheese)](https://github.com/RunningCheese)
------
-## Software description
-
-This software is the integration of lux,yt-dlp,ffmpeg,python,ContextSearch-Native-App, with automatic configuration meanwhile discarding the sophisticated steps for users.
+## Description
-This software is developed based on Inno Setup, with source files in repo.
-
-Install Inno Setup, Download the repo, and run LinkDown_Wizard.iss
-
-## Tutorial
+This software is a integration of you-get, yt-dlp, ffmpeg, and python with automatic configuration, including a easy-to-use browser add-on, discarding the sophisticated steps for users.
-### I) Install browser extension
+## Development
-Install the browser extension **ContextSearch web-ext**.
+This software is developed based on Inno Setup, with the source file in repository, run `LinkDown_Wizzard.iss` to compile.
-Address: [Firefox](https://addons.mozilla.org/en-US/firefox/addon/contextsearch-web-ext)、[Chrome](https://chrome.google.com/webstore/detail/ddippghibegbgpjcaaijbacfhjjeafjh)
+The source code of the Firefox Manifest V2 version of LinkDown for Browsers is at the `webext-firefox` folder in this repository; The source code of the Chrome Manifest V3 of LinkDown for Browsers is at the `webext-chrome` folder in this repository.
-### II) Importing extended configuration
+*P.S. Manifest V2 is still used for Firefox for there is still no support of Manifest V3 for the current ESR version; Manifest V3 will be used after the release of Firefox 114 ESR on June 6th, 2023.*
-Open **Setting Page** of ContextSearch web-ext, and import configuration **ContextSearchOptions.json**. (Make sure to select the version.)
-
-Configuration downloading:[Firefox version](https://fastly.jsdelivr.net/gh/runningcheese/RunningCheese-Firefox/Restore/ContextSearchOptions_Firefox.json)、[Chrome version](https://fastly.jsdelivr.net/gh/runningcheese/RunningCheese-Firefox/Restore/ContextSearchOptions_Chrome.json)、[Local downloads](https://www.lanzouh.com/b07nx1b4f)
-
-
-
-### III) Enabling permission
-
-**“Messenger app is not connected”** might appear when downloading videos after importing configurations.
+## Tutorial
-In this case, choose and click “**Request Permission** in the extended permission settings, then **Restart Browser**
+### I) Install Application
-**Complete shutdown** is a successful restart; restart fails if the browser is still running in the background.
+Install and run `linkdown_setup.exe` at the [Download Link](#Download Link) of LinkDown.
-
+### II) Install Add-on
-### IV) Using
+Install the browser add-on **LinkDown for Browsers**. Link: [Firefox](https://addons.mozilla.org/zh-CN/firefox/addon/linkdown-for-browsers/), [Chrome](https://chrome.google.com/webstore/detail/linkdown-for-browsers/lnckamlbboggdkkgnkaocibpnilhemhc).
-Right click in the downloading page, and see the following image:
+### III) Usage
-
+Right-click on the webpage, and choose the `Download Video` option in the ContextMenu:
-lux is for Chinese Websites, BBDown is for bilibili, yt-dlp is for all other websites.
+
-Final program:
-
------
-## Citation
+## References
-A large amount of softwares is cited in this program, link: [REFERENCE.md](https://github.com/LinkScapeFoundation/LinkDown/blob/main/REFERENCE.md)
+A number of softwares were used in project, see [REFERENCE.md](REFERENCE.md) for details.
diff --git a/README_CN.md b/README_CN.md
index 28f435a..75315e5 100644
--- a/README_CN.md
+++ b/README_CN.md
@@ -1,22 +1,22 @@
-
+
LinkDown
-A Video Downloader. Easier than easy.
+鏈一程 益萬人
-
-
+
+
[English](https://github.com/LinkScapeOfficial/LinkDown/blob/main/README.md) | 简体中文
## 捐款
-通过 Stripe 捐款:[链接](https://bank.hackclub.com/donations/start/linkscape)
+通过 Stripe 捐款:[链接](https://linkscape.app/donate)
[ ](https://hackclub.com/bank)
@@ -24,66 +24,52 @@
Discord: https://discord.gg/WDdvabyKaH
-Telegram: https://t.me/LinkScapeChat
-
## 下载链接
-https://github.com/LinkScapeOfficial/LinkDown/releases/latest
+https://github.com/LinkScapeOfficial/LinkDown/releases/latest/download/linkdown_setup.exe
------
-软件开发:Thomas Wu [(@thomaswcy)](https://github.com/thomaswcy)
+软件开发:**吳承泳**([@thomaswcy](https://github.com/thomaswcy),LinkScape 创始人)
-产品规划 & 英语翻译:Meli Liu [(@MeliLXT)](https://github.com/MeliLXT)
+英语翻译:**劉欣恬**([@MeliLXT](https://github.com/MeliLXT),LinkScape 创始人)
-教程编写:RunningCheese [(@RunningCheese)](https://github.com/RunningCheese)
+商标设计:**Tzu-Yun Hsiao**([@Powerlean](https://github.com/Powerlean),LinkScape 首席设计师)
-商标设计:MemeCat [(@Powerlean)](https://github.com/Powerlean)
+教程编写:奔跑中的奶酪([@RunningCheese](https://github.com/RunningCheese))
------
## 软件概述
-本软件集成了 lux,yt-dlp,ffmpeg,python,ContextSearch-Native-App 五款软件并且会自动配置,省去各种繁琐步骤。
-
-本软件基于 Inno Setup 开发,源文件在 repo 中,下载项目后运行 LinkDown_Wizzard.iss
-
-## 使用教程
-
-### 一、安装扩展
+本软件集成了 you-get,yt-dlp,ffmpeg,python 四款软件进行自动配置,并且编写了一个简单易用的浏览器附加组件,使用户下载视频时省去各种繁琐步骤。
-安装扩展 **ContextSearch web-ext**。地址:[Firefox](https://addons.mozilla.org/zh-CN/firefox/addon/contextsearch-web-ext)、[Chrome](https://chrome.google.com/webstore/detail/ddippghibegbgpjcaaijbacfhjjeafjh)、[代理下载](https://www.crxsoso.com/webstore/detail/ddippghibegbgpjcaaijbacfhjjeafjh)
+## 软件开发
-### 二、导入扩展配置
+本软件基于 Inno Setup 开发,源文件在 repo 中,下载项目后运行 `LinkDown_Wizzard.iss` 即可编译。
-打开扩展 ContextSearch web-ext 的**设置页面**,导入配置 **ContextSearchOptions.json**。(注意选择版本)
+LinkDown for Browsers 的 Firefox Manifest V2 版本源代码在repo中的 `webext-firefox` 目录中;Chrome Manifest V3 版本源代码在repo中的 `webext-chrome` 目录中。
-配置下载:[Firefox 版本](https://fastly.jsdelivr.net/gh/runningcheese/RunningCheese-Firefox/Restore/ContextSearchOptions_Firefox.json)、[Chrome 版本](https://fastly.jsdelivr.net/gh/runningcheese/RunningCheese-Firefox/Restore/ContextSearchOptions_Chrome.json)、[本地一键下载](https://www.lanzouh.com/b07nx1b4f)
+*注:Firefox 由于当前 ESR 版本尚不支持 Manifest V3,在 Firefox 114 ESR 于 2023年6月7日发布后将会改用 Manifest V3。*
-
-
-### 三、开启权限
-
-导入配置后,点击下载视频时,可能会出现“**Messenger app is not connected”**的提示。
-
-这时需要在扩展的权限设置里,勾选“**Request Permission**”,然后**重启一下浏览器**即可。
+## 使用教程
-注意要**完全关闭**才算是重启,如果浏览器在后台还在运行的话,就不能算是重启。
+### 一、安装程序
-
+在 [下载链接](#下载链接) 下载并运行 `linkdown_setup.exe`。
-### 四、使用
+### 二、安装附加组件
-在下载页面右键,然后按照图片内容操作:
+安装附加组件 **LinkDown for Browsers**。地址:[Firefox](https://addons.mozilla.org/zh-CN/firefox/addon/linkdown-for-browsers/)、[Chrome](https://chrome.google.com/webstore/detail/linkdown-for-browsers/lnckamlbboggdkkgnkaocibpnilhemhc)、[代理下载](https://cdn.linkscape.app/linkdown_webext.crx)。
-
+### 三、使用
-最后成品如下:
+在网页上右键,然后选择 `下载视频` 选项,如下图所示:
-
+
------
## 引用
-本项目引用了大量软件,链接在 [REFERENCE.md](https://github.com/LinkScapeFoudation/LinkDown/blob/main/REFERENCE.md)
+本项目引用了大量软件,详情请查看 [REFERENCE.md](https://github.com/LinkScapeFoudation/LinkDown/blob/main/REFERENCE.md).
diff --git a/REFERENCE.md b/REFERENCE.md
index 2795b13..2706234 100644
--- a/REFERENCE.md
+++ b/REFERENCE.md
@@ -1,8 +1,10 @@
# REFERENCE
-## lux
+## you-get
-https://github.com/iawia002/lux/
+Source Code: https://github.com/soimort/you-get
+
+Binary Files: https://github.com/LussacZheng/you-get.exe/
## yt-dlp
@@ -26,8 +28,8 @@ Inno Setup: https://jrsoftware.org/isinfo.php/
modpath.iss: http://www.legroom.net/software/
-Inno-Setup-Chinese-Simplified-Translation: https://github.com/kira-96/Inno-Setup-Chinese-Simplified-Translation/
+Inno Setup Translation (Chinese Simplified): https://github.com/kira-96/Inno-Setup-Chinese-Simplified-Translation/
-## ContextSearch-Native-App
+## webextension-polyfill
-https://github.com/ssborbis/ContextSearch-Native-App/
+https://www.npmjs.com/package/webextension-polyfill
diff --git a/Tutorial1.jpg b/Tutorial1.jpg
deleted file mode 100644
index 8a681de..0000000
Binary files a/Tutorial1.jpg and /dev/null differ
diff --git a/Tutorial2.jpg b/Tutorial2.jpg
deleted file mode 100644
index 8bf2b1b..0000000
Binary files a/Tutorial2.jpg and /dev/null differ
diff --git a/Tutorial3.jpg b/Tutorial3.jpg
deleted file mode 100644
index 8bf2b1b..0000000
Binary files a/Tutorial3.jpg and /dev/null differ
diff --git a/Tutorial5.gif b/Tutorial5.gif
deleted file mode 100644
index 0dc3e0a..0000000
Binary files a/Tutorial5.gif and /dev/null differ
diff --git a/Tutotial4.png b/Tutotial4.png
deleted file mode 100644
index 7bbb953..0000000
Binary files a/Tutotial4.png and /dev/null differ
diff --git a/webext-chrome/_locales/en/messages.json b/webext-chrome/_locales/en/messages.json
new file mode 100644
index 0000000..fd9d7fe
--- /dev/null
+++ b/webext-chrome/_locales/en/messages.json
@@ -0,0 +1,5 @@
+{
+ "download_video": {
+ "message": "Download Video"
+ }
+}
diff --git a/webext-chrome/_locales/zh_CN/messages.json b/webext-chrome/_locales/zh_CN/messages.json
new file mode 100644
index 0000000..0e743c8
--- /dev/null
+++ b/webext-chrome/_locales/zh_CN/messages.json
@@ -0,0 +1,5 @@
+{
+ "download_video": {
+ "message": "下载视频"
+ }
+}
diff --git a/webext-chrome/_locales/zh_HK/messages.json b/webext-chrome/_locales/zh_HK/messages.json
new file mode 100644
index 0000000..07f1e0f
--- /dev/null
+++ b/webext-chrome/_locales/zh_HK/messages.json
@@ -0,0 +1,5 @@
+{
+ "download_video": {
+ "message": "下載影片"
+ }
+}
diff --git a/webext-chrome/_locales/zh_TW/messages.json b/webext-chrome/_locales/zh_TW/messages.json
new file mode 100644
index 0000000..07f1e0f
--- /dev/null
+++ b/webext-chrome/_locales/zh_TW/messages.json
@@ -0,0 +1,5 @@
+{
+ "download_video": {
+ "message": "下載影片"
+ }
+}
diff --git a/webext-chrome/background.js b/webext-chrome/background.js
new file mode 100644
index 0000000..a4c864f
--- /dev/null
+++ b/webext-chrome/background.js
@@ -0,0 +1,85 @@
+if (typeof browser === "undefined") {
+ browser = chrome;
+}
+
+console.log("LinkDown for Browsers: I am successfully triggered!");
+// Create Context Menu Item
+
+browser.contextMenus.create({
+ id: "download-video",
+ title: browser.i18n.getMessage("download_video"),
+ contexts: ["all"],
+});
+
+// Listen for Clicks on Context Menu Item
+browser.contextMenus.onClicked.addListener(async function(info, tab) {
+ console.log(info);
+ // Get URL
+ var url = info.linkUrl || info.srcUrl || info.pageUrl;
+ // Determine to Use BBDown / you-get / yt-dlp and Execute Commands
+ var command;
+ if (url.indexOf("music.163.com") !== -1 ||
+ url.indexOf("56.com") !== -1 ||
+ url.indexOf("acfun.cn") !== -1 ||
+ url.indexOf("baidu.com") !== -1 ||
+ url.indexOf("baomihua.com") !== -1 ||
+ url.indexOf("douyutv.com") !== -1 ||
+ url.indexOf("ifeng.com") !== -1 ||
+ url.indexOf("fun.tv") !== -1 ||
+ url.indexOf("iqiyi.com") !== -1 ||
+ url.indexOf("kou.cn") !== -1 ||
+ url.indexOf("ku6.com") !== -1 ||
+ url.indexOf("kugou.com") !== -1 ||
+ url.indexOf("kuwo.cn") !== -1 ||
+ url.indexOf("le.com") !== -1 ||
+ url.indexOf("lizhi.fm") !== -1 ||
+ url.indexOf("lrts.me") !== -1 ||
+ url.indexOf("miaopai.com") !== -1 ||
+ url.indexOf("miomio.tv") !== -1 ||
+ url.indexOf("missevan.com") !== -1 ||
+ url.indexOf("pixnet.net") !== -1 ||
+ url.indexOf("pptv.com") !== -1 ||
+ url.indexOf("iqilu.com") !== -1 ||
+ url.indexOf("qq.com") !== -1 ||
+ url.indexOf("sina.com.cn") !== -1 ||
+ url.indexOf("weibo.com") !== -1 ||
+ url.indexOf("sohu.com") !== -1 ||
+ url.indexOf("tudou.com") !== -1 ||
+ url.indexOf("isuntv.com") !== -1 ||
+ url.indexOf("youku.com") !== -1 ||
+ url.indexOf("zhanqi.tv") !== -1 ||
+ url.indexOf("cntv.cn") !== -1 ||
+ url.indexOf("mgtv.com") !== -1 ||
+ url.indexOf("huomao.com") !== -1 ||
+ url.indexOf("356yg.com") !== -1 ||
+ url.indexOf("ixigua.com") !== -1 ||
+ url.indexOf("xinpianchang.com") !== -1 ||
+ url.indexOf("kuaishou.com") !== -1 ||
+ url.indexOf("douyin.com") !== -1 ||
+ url.indexOf("zhibo.tv") !== -1 ||
+ url.indexOf("zhihu.com") !== -1) {
+ command =
+ 'start you-get.exe -o "%HOMEPATH%/Downloads/Video" ' + url;
+ } else if (url.indexOf("bilibili.com") !== -1) {
+ var cookies = await browser.cookies.getAll({ url: "https://www.bilibili.com" });
+ var cookie = cookies.map(cookie => `${cookie.name}=${cookie.value}`).join('; ');
+ if (cookies.length > 0) {
+ command =
+ `start bbdown.exe ${url} --work-dir "%HOMEPATH%/Downloads/Video" -tv --cookie "${cookie}"`;
+ } else {
+ command =
+ `start bbdown.exe ${url} --work-dir "%HOMEPATH%/Downloads/Video"`;
+ }
+ } else {
+ command =
+ 'start yt-dlp.exe --output "../../../Downloads/Video/%(title)s.%(ext)s" --merge-output-format mp4 ' +
+ url;
+ }
+ try {
+ await browser.runtime.sendNativeMessage("linkdown", { command });
+ console.log("LinkDown for Browsers: Command Sent: " + command);
+ } catch (error) {
+ console.error(error);
+ console.error("LinkDown for Browsers: Failed to Send Command: " + command);
+ }
+});
diff --git a/webext-chrome/browser-polyfill.js b/webext-chrome/browser-polyfill.js
new file mode 100644
index 0000000..8e732a2
--- /dev/null
+++ b/webext-chrome/browser-polyfill.js
@@ -0,0 +1,1269 @@
+(function (global, factory) {
+ if (typeof define === "function" && define.amd) {
+ define("webextension-polyfill", ["module"], factory);
+ } else if (typeof exports !== "undefined") {
+ factory(module);
+ } else {
+ var mod = {
+ exports: {}
+ };
+ factory(mod);
+ global.browser = mod.exports;
+ }
+})(typeof globalThis !== "undefined" ? globalThis : typeof self !== "undefined" ? self : this, function (module) {
+ /* webextension-polyfill - v0.10.0 - Fri Aug 12 2022 19:42:44 */
+
+ /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+
+ /* vim: set sts=2 sw=2 et tw=80: */
+
+ /* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ "use strict";
+
+ if (!globalThis.chrome?.runtime?.id) {
+ throw new Error("This script should only be loaded in a browser extension.");
+ }
+
+ if (typeof globalThis.browser === "undefined" || Object.getPrototypeOf(globalThis.browser) !== Object.prototype) {
+ const CHROME_SEND_MESSAGE_CALLBACK_NO_RESPONSE_MESSAGE = "The message port closed before a response was received."; // Wrapping the bulk of this polyfill in a one-time-use function is a minor
+ // optimization for Firefox. Since Spidermonkey does not fully parse the
+ // contents of a function until the first time it's called, and since it will
+ // never actually need to be called, this allows the polyfill to be included
+ // in Firefox nearly for free.
+
+ const wrapAPIs = extensionAPIs => {
+ // NOTE: apiMetadata is associated to the content of the api-metadata.json file
+ // at build time by replacing the following "include" with the content of the
+ // JSON file.
+ const apiMetadata = {
+ "alarms": {
+ "clear": {
+ "minArgs": 0,
+ "maxArgs": 1
+ },
+ "clearAll": {
+ "minArgs": 0,
+ "maxArgs": 0
+ },
+ "get": {
+ "minArgs": 0,
+ "maxArgs": 1
+ },
+ "getAll": {
+ "minArgs": 0,
+ "maxArgs": 0
+ }
+ },
+ "bookmarks": {
+ "create": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "get": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "getChildren": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "getRecent": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "getSubTree": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "getTree": {
+ "minArgs": 0,
+ "maxArgs": 0
+ },
+ "move": {
+ "minArgs": 2,
+ "maxArgs": 2
+ },
+ "remove": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "removeTree": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "search": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "update": {
+ "minArgs": 2,
+ "maxArgs": 2
+ }
+ },
+ "browserAction": {
+ "disable": {
+ "minArgs": 0,
+ "maxArgs": 1,
+ "fallbackToNoCallback": true
+ },
+ "enable": {
+ "minArgs": 0,
+ "maxArgs": 1,
+ "fallbackToNoCallback": true
+ },
+ "getBadgeBackgroundColor": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "getBadgeText": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "getPopup": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "getTitle": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "openPopup": {
+ "minArgs": 0,
+ "maxArgs": 0
+ },
+ "setBadgeBackgroundColor": {
+ "minArgs": 1,
+ "maxArgs": 1,
+ "fallbackToNoCallback": true
+ },
+ "setBadgeText": {
+ "minArgs": 1,
+ "maxArgs": 1,
+ "fallbackToNoCallback": true
+ },
+ "setIcon": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "setPopup": {
+ "minArgs": 1,
+ "maxArgs": 1,
+ "fallbackToNoCallback": true
+ },
+ "setTitle": {
+ "minArgs": 1,
+ "maxArgs": 1,
+ "fallbackToNoCallback": true
+ }
+ },
+ "browsingData": {
+ "remove": {
+ "minArgs": 2,
+ "maxArgs": 2
+ },
+ "removeCache": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "removeCookies": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "removeDownloads": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "removeFormData": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "removeHistory": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "removeLocalStorage": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "removePasswords": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "removePluginData": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "settings": {
+ "minArgs": 0,
+ "maxArgs": 0
+ }
+ },
+ "commands": {
+ "getAll": {
+ "minArgs": 0,
+ "maxArgs": 0
+ }
+ },
+ "contextMenus": {
+ "remove": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "removeAll": {
+ "minArgs": 0,
+ "maxArgs": 0
+ },
+ "update": {
+ "minArgs": 2,
+ "maxArgs": 2
+ }
+ },
+ "cookies": {
+ "get": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "getAll": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "getAllCookieStores": {
+ "minArgs": 0,
+ "maxArgs": 0
+ },
+ "remove": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "set": {
+ "minArgs": 1,
+ "maxArgs": 1
+ }
+ },
+ "devtools": {
+ "inspectedWindow": {
+ "eval": {
+ "minArgs": 1,
+ "maxArgs": 2,
+ "singleCallbackArg": false
+ }
+ },
+ "panels": {
+ "create": {
+ "minArgs": 3,
+ "maxArgs": 3,
+ "singleCallbackArg": true
+ },
+ "elements": {
+ "createSidebarPane": {
+ "minArgs": 1,
+ "maxArgs": 1
+ }
+ }
+ }
+ },
+ "downloads": {
+ "cancel": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "download": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "erase": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "getFileIcon": {
+ "minArgs": 1,
+ "maxArgs": 2
+ },
+ "open": {
+ "minArgs": 1,
+ "maxArgs": 1,
+ "fallbackToNoCallback": true
+ },
+ "pause": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "removeFile": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "resume": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "search": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "show": {
+ "minArgs": 1,
+ "maxArgs": 1,
+ "fallbackToNoCallback": true
+ }
+ },
+ "extension": {
+ "isAllowedFileSchemeAccess": {
+ "minArgs": 0,
+ "maxArgs": 0
+ },
+ "isAllowedIncognitoAccess": {
+ "minArgs": 0,
+ "maxArgs": 0
+ }
+ },
+ "history": {
+ "addUrl": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "deleteAll": {
+ "minArgs": 0,
+ "maxArgs": 0
+ },
+ "deleteRange": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "deleteUrl": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "getVisits": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "search": {
+ "minArgs": 1,
+ "maxArgs": 1
+ }
+ },
+ "i18n": {
+ "detectLanguage": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "getAcceptLanguages": {
+ "minArgs": 0,
+ "maxArgs": 0
+ }
+ },
+ "identity": {
+ "launchWebAuthFlow": {
+ "minArgs": 1,
+ "maxArgs": 1
+ }
+ },
+ "idle": {
+ "queryState": {
+ "minArgs": 1,
+ "maxArgs": 1
+ }
+ },
+ "management": {
+ "get": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "getAll": {
+ "minArgs": 0,
+ "maxArgs": 0
+ },
+ "getSelf": {
+ "minArgs": 0,
+ "maxArgs": 0
+ },
+ "setEnabled": {
+ "minArgs": 2,
+ "maxArgs": 2
+ },
+ "uninstallSelf": {
+ "minArgs": 0,
+ "maxArgs": 1
+ }
+ },
+ "notifications": {
+ "clear": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "create": {
+ "minArgs": 1,
+ "maxArgs": 2
+ },
+ "getAll": {
+ "minArgs": 0,
+ "maxArgs": 0
+ },
+ "getPermissionLevel": {
+ "minArgs": 0,
+ "maxArgs": 0
+ },
+ "update": {
+ "minArgs": 2,
+ "maxArgs": 2
+ }
+ },
+ "pageAction": {
+ "getPopup": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "getTitle": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "hide": {
+ "minArgs": 1,
+ "maxArgs": 1,
+ "fallbackToNoCallback": true
+ },
+ "setIcon": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "setPopup": {
+ "minArgs": 1,
+ "maxArgs": 1,
+ "fallbackToNoCallback": true
+ },
+ "setTitle": {
+ "minArgs": 1,
+ "maxArgs": 1,
+ "fallbackToNoCallback": true
+ },
+ "show": {
+ "minArgs": 1,
+ "maxArgs": 1,
+ "fallbackToNoCallback": true
+ }
+ },
+ "permissions": {
+ "contains": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "getAll": {
+ "minArgs": 0,
+ "maxArgs": 0
+ },
+ "remove": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "request": {
+ "minArgs": 1,
+ "maxArgs": 1
+ }
+ },
+ "runtime": {
+ "getBackgroundPage": {
+ "minArgs": 0,
+ "maxArgs": 0
+ },
+ "getPlatformInfo": {
+ "minArgs": 0,
+ "maxArgs": 0
+ },
+ "openOptionsPage": {
+ "minArgs": 0,
+ "maxArgs": 0
+ },
+ "requestUpdateCheck": {
+ "minArgs": 0,
+ "maxArgs": 0
+ },
+ "sendMessage": {
+ "minArgs": 1,
+ "maxArgs": 3
+ },
+ "sendNativeMessage": {
+ "minArgs": 2,
+ "maxArgs": 2
+ },
+ "setUninstallURL": {
+ "minArgs": 1,
+ "maxArgs": 1
+ }
+ },
+ "sessions": {
+ "getDevices": {
+ "minArgs": 0,
+ "maxArgs": 1
+ },
+ "getRecentlyClosed": {
+ "minArgs": 0,
+ "maxArgs": 1
+ },
+ "restore": {
+ "minArgs": 0,
+ "maxArgs": 1
+ }
+ },
+ "storage": {
+ "local": {
+ "clear": {
+ "minArgs": 0,
+ "maxArgs": 0
+ },
+ "get": {
+ "minArgs": 0,
+ "maxArgs": 1
+ },
+ "getBytesInUse": {
+ "minArgs": 0,
+ "maxArgs": 1
+ },
+ "remove": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "set": {
+ "minArgs": 1,
+ "maxArgs": 1
+ }
+ },
+ "managed": {
+ "get": {
+ "minArgs": 0,
+ "maxArgs": 1
+ },
+ "getBytesInUse": {
+ "minArgs": 0,
+ "maxArgs": 1
+ }
+ },
+ "sync": {
+ "clear": {
+ "minArgs": 0,
+ "maxArgs": 0
+ },
+ "get": {
+ "minArgs": 0,
+ "maxArgs": 1
+ },
+ "getBytesInUse": {
+ "minArgs": 0,
+ "maxArgs": 1
+ },
+ "remove": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "set": {
+ "minArgs": 1,
+ "maxArgs": 1
+ }
+ }
+ },
+ "tabs": {
+ "captureVisibleTab": {
+ "minArgs": 0,
+ "maxArgs": 2
+ },
+ "create": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "detectLanguage": {
+ "minArgs": 0,
+ "maxArgs": 1
+ },
+ "discard": {
+ "minArgs": 0,
+ "maxArgs": 1
+ },
+ "duplicate": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "executeScript": {
+ "minArgs": 1,
+ "maxArgs": 2
+ },
+ "get": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "getCurrent": {
+ "minArgs": 0,
+ "maxArgs": 0
+ },
+ "getZoom": {
+ "minArgs": 0,
+ "maxArgs": 1
+ },
+ "getZoomSettings": {
+ "minArgs": 0,
+ "maxArgs": 1
+ },
+ "goBack": {
+ "minArgs": 0,
+ "maxArgs": 1
+ },
+ "goForward": {
+ "minArgs": 0,
+ "maxArgs": 1
+ },
+ "highlight": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "insertCSS": {
+ "minArgs": 1,
+ "maxArgs": 2
+ },
+ "move": {
+ "minArgs": 2,
+ "maxArgs": 2
+ },
+ "query": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "reload": {
+ "minArgs": 0,
+ "maxArgs": 2
+ },
+ "remove": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "removeCSS": {
+ "minArgs": 1,
+ "maxArgs": 2
+ },
+ "sendMessage": {
+ "minArgs": 2,
+ "maxArgs": 3
+ },
+ "setZoom": {
+ "minArgs": 1,
+ "maxArgs": 2
+ },
+ "setZoomSettings": {
+ "minArgs": 1,
+ "maxArgs": 2
+ },
+ "update": {
+ "minArgs": 1,
+ "maxArgs": 2
+ }
+ },
+ "topSites": {
+ "get": {
+ "minArgs": 0,
+ "maxArgs": 0
+ }
+ },
+ "webNavigation": {
+ "getAllFrames": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "getFrame": {
+ "minArgs": 1,
+ "maxArgs": 1
+ }
+ },
+ "webRequest": {
+ "handlerBehaviorChanged": {
+ "minArgs": 0,
+ "maxArgs": 0
+ }
+ },
+ "windows": {
+ "create": {
+ "minArgs": 0,
+ "maxArgs": 1
+ },
+ "get": {
+ "minArgs": 1,
+ "maxArgs": 2
+ },
+ "getAll": {
+ "minArgs": 0,
+ "maxArgs": 1
+ },
+ "getCurrent": {
+ "minArgs": 0,
+ "maxArgs": 1
+ },
+ "getLastFocused": {
+ "minArgs": 0,
+ "maxArgs": 1
+ },
+ "remove": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "update": {
+ "minArgs": 2,
+ "maxArgs": 2
+ }
+ }
+ };
+
+ if (Object.keys(apiMetadata).length === 0) {
+ throw new Error("api-metadata.json has not been included in browser-polyfill");
+ }
+ /**
+ * A WeakMap subclass which creates and stores a value for any key which does
+ * not exist when accessed, but behaves exactly as an ordinary WeakMap
+ * otherwise.
+ *
+ * @param {function} createItem
+ * A function which will be called in order to create the value for any
+ * key which does not exist, the first time it is accessed. The
+ * function receives, as its only argument, the key being created.
+ */
+
+
+ class DefaultWeakMap extends WeakMap {
+ constructor(createItem, items = undefined) {
+ super(items);
+ this.createItem = createItem;
+ }
+
+ get(key) {
+ if (!this.has(key)) {
+ this.set(key, this.createItem(key));
+ }
+
+ return super.get(key);
+ }
+
+ }
+ /**
+ * Returns true if the given object is an object with a `then` method, and can
+ * therefore be assumed to behave as a Promise.
+ *
+ * @param {*} value The value to test.
+ * @returns {boolean} True if the value is thenable.
+ */
+
+
+ const isThenable = value => {
+ return value && typeof value === "object" && typeof value.then === "function";
+ };
+ /**
+ * Creates and returns a function which, when called, will resolve or reject
+ * the given promise based on how it is called:
+ *
+ * - If, when called, `chrome.runtime.lastError` contains a non-null object,
+ * the promise is rejected with that value.
+ * - If the function is called with exactly one argument, the promise is
+ * resolved to that value.
+ * - Otherwise, the promise is resolved to an array containing all of the
+ * function's arguments.
+ *
+ * @param {object} promise
+ * An object containing the resolution and rejection functions of a
+ * promise.
+ * @param {function} promise.resolve
+ * The promise's resolution function.
+ * @param {function} promise.reject
+ * The promise's rejection function.
+ * @param {object} metadata
+ * Metadata about the wrapped method which has created the callback.
+ * @param {boolean} metadata.singleCallbackArg
+ * Whether or not the promise is resolved with only the first
+ * argument of the callback, alternatively an array of all the
+ * callback arguments is resolved. By default, if the callback
+ * function is invoked with only a single argument, that will be
+ * resolved to the promise, while all arguments will be resolved as
+ * an array if multiple are given.
+ *
+ * @returns {function}
+ * The generated callback function.
+ */
+
+
+ const makeCallback = (promise, metadata) => {
+ return (...callbackArgs) => {
+ if (extensionAPIs.runtime.lastError) {
+ promise.reject(new Error(extensionAPIs.runtime.lastError.message));
+ } else if (metadata.singleCallbackArg || callbackArgs.length <= 1 && metadata.singleCallbackArg !== false) {
+ promise.resolve(callbackArgs[0]);
+ } else {
+ promise.resolve(callbackArgs);
+ }
+ };
+ };
+
+ const pluralizeArguments = numArgs => numArgs == 1 ? "argument" : "arguments";
+ /**
+ * Creates a wrapper function for a method with the given name and metadata.
+ *
+ * @param {string} name
+ * The name of the method which is being wrapped.
+ * @param {object} metadata
+ * Metadata about the method being wrapped.
+ * @param {integer} metadata.minArgs
+ * The minimum number of arguments which must be passed to the
+ * function. If called with fewer than this number of arguments, the
+ * wrapper will raise an exception.
+ * @param {integer} metadata.maxArgs
+ * The maximum number of arguments which may be passed to the
+ * function. If called with more than this number of arguments, the
+ * wrapper will raise an exception.
+ * @param {boolean} metadata.singleCallbackArg
+ * Whether or not the promise is resolved with only the first
+ * argument of the callback, alternatively an array of all the
+ * callback arguments is resolved. By default, if the callback
+ * function is invoked with only a single argument, that will be
+ * resolved to the promise, while all arguments will be resolved as
+ * an array if multiple are given.
+ *
+ * @returns {function(object, ...*)}
+ * The generated wrapper function.
+ */
+
+
+ const wrapAsyncFunction = (name, metadata) => {
+ return function asyncFunctionWrapper(target, ...args) {
+ if (args.length < metadata.minArgs) {
+ throw new Error(`Expected at least ${metadata.minArgs} ${pluralizeArguments(metadata.minArgs)} for ${name}(), got ${args.length}`);
+ }
+
+ if (args.length > metadata.maxArgs) {
+ throw new Error(`Expected at most ${metadata.maxArgs} ${pluralizeArguments(metadata.maxArgs)} for ${name}(), got ${args.length}`);
+ }
+
+ return new Promise((resolve, reject) => {
+ if (metadata.fallbackToNoCallback) {
+ // This API method has currently no callback on Chrome, but it return a promise on Firefox,
+ // and so the polyfill will try to call it with a callback first, and it will fallback
+ // to not passing the callback if the first call fails.
+ try {
+ target[name](...args, makeCallback({
+ resolve,
+ reject
+ }, metadata));
+ } catch (cbError) {
+ console.warn(`${name} API method doesn't seem to support the callback parameter, ` + "falling back to call it without a callback: ", cbError);
+ target[name](...args); // Update the API method metadata, so that the next API calls will not try to
+ // use the unsupported callback anymore.
+
+ metadata.fallbackToNoCallback = false;
+ metadata.noCallback = true;
+ resolve();
+ }
+ } else if (metadata.noCallback) {
+ target[name](...args);
+ resolve();
+ } else {
+ target[name](...args, makeCallback({
+ resolve,
+ reject
+ }, metadata));
+ }
+ });
+ };
+ };
+ /**
+ * Wraps an existing method of the target object, so that calls to it are
+ * intercepted by the given wrapper function. The wrapper function receives,
+ * as its first argument, the original `target` object, followed by each of
+ * the arguments passed to the original method.
+ *
+ * @param {object} target
+ * The original target object that the wrapped method belongs to.
+ * @param {function} method
+ * The method being wrapped. This is used as the target of the Proxy
+ * object which is created to wrap the method.
+ * @param {function} wrapper
+ * The wrapper function which is called in place of a direct invocation
+ * of the wrapped method.
+ *
+ * @returns {Proxy}
+ * A Proxy object for the given method, which invokes the given wrapper
+ * method in its place.
+ */
+
+
+ const wrapMethod = (target, method, wrapper) => {
+ return new Proxy(method, {
+ apply(targetMethod, thisObj, args) {
+ return wrapper.call(thisObj, target, ...args);
+ }
+
+ });
+ };
+
+ let hasOwnProperty = Function.call.bind(Object.prototype.hasOwnProperty);
+ /**
+ * Wraps an object in a Proxy which intercepts and wraps certain methods
+ * based on the given `wrappers` and `metadata` objects.
+ *
+ * @param {object} target
+ * The target object to wrap.
+ *
+ * @param {object} [wrappers = {}]
+ * An object tree containing wrapper functions for special cases. Any
+ * function present in this object tree is called in place of the
+ * method in the same location in the `target` object tree. These
+ * wrapper methods are invoked as described in {@see wrapMethod}.
+ *
+ * @param {object} [metadata = {}]
+ * An object tree containing metadata used to automatically generate
+ * Promise-based wrapper functions for asynchronous. Any function in
+ * the `target` object tree which has a corresponding metadata object
+ * in the same location in the `metadata` tree is replaced with an
+ * automatically-generated wrapper function, as described in
+ * {@see wrapAsyncFunction}
+ *
+ * @returns {Proxy}
+ */
+
+ const wrapObject = (target, wrappers = {}, metadata = {}) => {
+ let cache = Object.create(null);
+ let handlers = {
+ has(proxyTarget, prop) {
+ return prop in target || prop in cache;
+ },
+
+ get(proxyTarget, prop, receiver) {
+ if (prop in cache) {
+ return cache[prop];
+ }
+
+ if (!(prop in target)) {
+ return undefined;
+ }
+
+ let value = target[prop];
+
+ if (typeof value === "function") {
+ // This is a method on the underlying object. Check if we need to do
+ // any wrapping.
+ if (typeof wrappers[prop] === "function") {
+ // We have a special-case wrapper for this method.
+ value = wrapMethod(target, target[prop], wrappers[prop]);
+ } else if (hasOwnProperty(metadata, prop)) {
+ // This is an async method that we have metadata for. Create a
+ // Promise wrapper for it.
+ let wrapper = wrapAsyncFunction(prop, metadata[prop]);
+ value = wrapMethod(target, target[prop], wrapper);
+ } else {
+ // This is a method that we don't know or care about. Return the
+ // original method, bound to the underlying object.
+ value = value.bind(target);
+ }
+ } else if (typeof value === "object" && value !== null && (hasOwnProperty(wrappers, prop) || hasOwnProperty(metadata, prop))) {
+ // This is an object that we need to do some wrapping for the children
+ // of. Create a sub-object wrapper for it with the appropriate child
+ // metadata.
+ value = wrapObject(value, wrappers[prop], metadata[prop]);
+ } else if (hasOwnProperty(metadata, "*")) {
+ // Wrap all properties in * namespace.
+ value = wrapObject(value, wrappers[prop], metadata["*"]);
+ } else {
+ // We don't need to do any wrapping for this property,
+ // so just forward all access to the underlying object.
+ Object.defineProperty(cache, prop, {
+ configurable: true,
+ enumerable: true,
+
+ get() {
+ return target[prop];
+ },
+
+ set(value) {
+ target[prop] = value;
+ }
+
+ });
+ return value;
+ }
+
+ cache[prop] = value;
+ return value;
+ },
+
+ set(proxyTarget, prop, value, receiver) {
+ if (prop in cache) {
+ cache[prop] = value;
+ } else {
+ target[prop] = value;
+ }
+
+ return true;
+ },
+
+ defineProperty(proxyTarget, prop, desc) {
+ return Reflect.defineProperty(cache, prop, desc);
+ },
+
+ deleteProperty(proxyTarget, prop) {
+ return Reflect.deleteProperty(cache, prop);
+ }
+
+ }; // Per contract of the Proxy API, the "get" proxy handler must return the
+ // original value of the target if that value is declared read-only and
+ // non-configurable. For this reason, we create an object with the
+ // prototype set to `target` instead of using `target` directly.
+ // Otherwise we cannot return a custom object for APIs that
+ // are declared read-only and non-configurable, such as `chrome.devtools`.
+ //
+ // The proxy handlers themselves will still use the original `target`
+ // instead of the `proxyTarget`, so that the methods and properties are
+ // dereferenced via the original targets.
+
+ let proxyTarget = Object.create(target);
+ return new Proxy(proxyTarget, handlers);
+ };
+ /**
+ * Creates a set of wrapper functions for an event object, which handles
+ * wrapping of listener functions that those messages are passed.
+ *
+ * A single wrapper is created for each listener function, and stored in a
+ * map. Subsequent calls to `addListener`, `hasListener`, or `removeListener`
+ * retrieve the original wrapper, so that attempts to remove a
+ * previously-added listener work as expected.
+ *
+ * @param {DefaultWeakMap} wrapperMap
+ * A DefaultWeakMap object which will create the appropriate wrapper
+ * for a given listener function when one does not exist, and retrieve
+ * an existing one when it does.
+ *
+ * @returns {object}
+ */
+
+
+ const wrapEvent = wrapperMap => ({
+ addListener(target, listener, ...args) {
+ target.addListener(wrapperMap.get(listener), ...args);
+ },
+
+ hasListener(target, listener) {
+ return target.hasListener(wrapperMap.get(listener));
+ },
+
+ removeListener(target, listener) {
+ target.removeListener(wrapperMap.get(listener));
+ }
+
+ });
+
+ const onRequestFinishedWrappers = new DefaultWeakMap(listener => {
+ if (typeof listener !== "function") {
+ return listener;
+ }
+ /**
+ * Wraps an onRequestFinished listener function so that it will return a
+ * `getContent()` property which returns a `Promise` rather than using a
+ * callback API.
+ *
+ * @param {object} req
+ * The HAR entry object representing the network request.
+ */
+
+
+ return function onRequestFinished(req) {
+ const wrappedReq = wrapObject(req, {}
+ /* wrappers */
+ , {
+ getContent: {
+ minArgs: 0,
+ maxArgs: 0
+ }
+ });
+ listener(wrappedReq);
+ };
+ });
+ const onMessageWrappers = new DefaultWeakMap(listener => {
+ if (typeof listener !== "function") {
+ return listener;
+ }
+ /**
+ * Wraps a message listener function so that it may send responses based on
+ * its return value, rather than by returning a sentinel value and calling a
+ * callback. If the listener function returns a Promise, the response is
+ * sent when the promise either resolves or rejects.
+ *
+ * @param {*} message
+ * The message sent by the other end of the channel.
+ * @param {object} sender
+ * Details about the sender of the message.
+ * @param {function(*)} sendResponse
+ * A callback which, when called with an arbitrary argument, sends
+ * that value as a response.
+ * @returns {boolean}
+ * True if the wrapped listener returned a Promise, which will later
+ * yield a response. False otherwise.
+ */
+
+
+ return function onMessage(message, sender, sendResponse) {
+ let didCallSendResponse = false;
+ let wrappedSendResponse;
+ let sendResponsePromise = new Promise(resolve => {
+ wrappedSendResponse = function (response) {
+ didCallSendResponse = true;
+ resolve(response);
+ };
+ });
+ let result;
+
+ try {
+ result = listener(message, sender, wrappedSendResponse);
+ } catch (err) {
+ result = Promise.reject(err);
+ }
+
+ const isResultThenable = result !== true && isThenable(result); // If the listener didn't returned true or a Promise, or called
+ // wrappedSendResponse synchronously, we can exit earlier
+ // because there will be no response sent from this listener.
+
+ if (result !== true && !isResultThenable && !didCallSendResponse) {
+ return false;
+ } // A small helper to send the message if the promise resolves
+ // and an error if the promise rejects (a wrapped sendMessage has
+ // to translate the message into a resolved promise or a rejected
+ // promise).
+
+
+ const sendPromisedResult = promise => {
+ promise.then(msg => {
+ // send the message value.
+ sendResponse(msg);
+ }, error => {
+ // Send a JSON representation of the error if the rejected value
+ // is an instance of error, or the object itself otherwise.
+ let message;
+
+ if (error && (error instanceof Error || typeof error.message === "string")) {
+ message = error.message;
+ } else {
+ message = "An unexpected error occurred";
+ }
+
+ sendResponse({
+ __mozWebExtensionPolyfillReject__: true,
+ message
+ });
+ }).catch(err => {
+ // Print an error on the console if unable to send the response.
+ console.error("Failed to send onMessage rejected reply", err);
+ });
+ }; // If the listener returned a Promise, send the resolved value as a
+ // result, otherwise wait the promise related to the wrappedSendResponse
+ // callback to resolve and send it as a response.
+
+
+ if (isResultThenable) {
+ sendPromisedResult(result);
+ } else {
+ sendPromisedResult(sendResponsePromise);
+ } // Let Chrome know that the listener is replying.
+
+
+ return true;
+ };
+ });
+
+ const wrappedSendMessageCallback = ({
+ reject,
+ resolve
+ }, reply) => {
+ if (extensionAPIs.runtime.lastError) {
+ // Detect when none of the listeners replied to the sendMessage call and resolve
+ // the promise to undefined as in Firefox.
+ // See https://github.com/mozilla/webextension-polyfill/issues/130
+ if (extensionAPIs.runtime.lastError.message === CHROME_SEND_MESSAGE_CALLBACK_NO_RESPONSE_MESSAGE) {
+ resolve();
+ } else {
+ reject(new Error(extensionAPIs.runtime.lastError.message));
+ }
+ } else if (reply && reply.__mozWebExtensionPolyfillReject__) {
+ // Convert back the JSON representation of the error into
+ // an Error instance.
+ reject(new Error(reply.message));
+ } else {
+ resolve(reply);
+ }
+ };
+
+ const wrappedSendMessage = (name, metadata, apiNamespaceObj, ...args) => {
+ if (args.length < metadata.minArgs) {
+ throw new Error(`Expected at least ${metadata.minArgs} ${pluralizeArguments(metadata.minArgs)} for ${name}(), got ${args.length}`);
+ }
+
+ if (args.length > metadata.maxArgs) {
+ throw new Error(`Expected at most ${metadata.maxArgs} ${pluralizeArguments(metadata.maxArgs)} for ${name}(), got ${args.length}`);
+ }
+
+ return new Promise((resolve, reject) => {
+ const wrappedCb = wrappedSendMessageCallback.bind(null, {
+ resolve,
+ reject
+ });
+ args.push(wrappedCb);
+ apiNamespaceObj.sendMessage(...args);
+ });
+ };
+
+ const staticWrappers = {
+ devtools: {
+ network: {
+ onRequestFinished: wrapEvent(onRequestFinishedWrappers)
+ }
+ },
+ runtime: {
+ onMessage: wrapEvent(onMessageWrappers),
+ onMessageExternal: wrapEvent(onMessageWrappers),
+ sendMessage: wrappedSendMessage.bind(null, "sendMessage", {
+ minArgs: 1,
+ maxArgs: 3
+ })
+ },
+ tabs: {
+ sendMessage: wrappedSendMessage.bind(null, "sendMessage", {
+ minArgs: 2,
+ maxArgs: 3
+ })
+ }
+ };
+ const settingMetadata = {
+ clear: {
+ minArgs: 1,
+ maxArgs: 1
+ },
+ get: {
+ minArgs: 1,
+ maxArgs: 1
+ },
+ set: {
+ minArgs: 1,
+ maxArgs: 1
+ }
+ };
+ apiMetadata.privacy = {
+ network: {
+ "*": settingMetadata
+ },
+ services: {
+ "*": settingMetadata
+ },
+ websites: {
+ "*": settingMetadata
+ }
+ };
+ return wrapObject(extensionAPIs, staticWrappers, apiMetadata);
+ }; // The build process adds a UMD wrapper around this file, which makes the
+ // `module` variable available.
+
+
+ module.exports = wrapAPIs(chrome);
+ } else {
+ module.exports = globalThis.browser;
+ }
+});
+//# sourceMappingURL=browser-polyfill.js.map
diff --git a/webext-chrome/browser-polyfill.js.map b/webext-chrome/browser-polyfill.js.map
new file mode 100644
index 0000000..d5bd275
--- /dev/null
+++ b/webext-chrome/browser-polyfill.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"browser-polyfill.js","names":["globalThis","chrome","runtime","id","Error","browser","Object","getPrototypeOf","prototype","CHROME_SEND_MESSAGE_CALLBACK_NO_RESPONSE_MESSAGE","wrapAPIs","extensionAPIs","apiMetadata","keys","length","DefaultWeakMap","WeakMap","constructor","createItem","items","undefined","get","key","has","set","isThenable","value","then","makeCallback","promise","metadata","callbackArgs","lastError","reject","message","singleCallbackArg","resolve","pluralizeArguments","numArgs","wrapAsyncFunction","name","asyncFunctionWrapper","target","args","minArgs","maxArgs","Promise","fallbackToNoCallback","cbError","console","warn","noCallback","wrapMethod","method","wrapper","Proxy","apply","targetMethod","thisObj","call","hasOwnProperty","Function","bind","wrapObject","wrappers","cache","create","handlers","proxyTarget","prop","receiver","defineProperty","configurable","enumerable","desc","Reflect","deleteProperty","wrapEvent","wrapperMap","addListener","listener","hasListener","removeListener","onRequestFinishedWrappers","onRequestFinished","req","wrappedReq","getContent","onMessageWrappers","onMessage","sender","sendResponse","didCallSendResponse","wrappedSendResponse","sendResponsePromise","response","result","err","isResultThenable","sendPromisedResult","msg","error","__mozWebExtensionPolyfillReject__","catch","wrappedSendMessageCallback","reply","wrappedSendMessage","apiNamespaceObj","wrappedCb","push","sendMessage","staticWrappers","devtools","network","onMessageExternal","tabs","settingMetadata","clear","privacy","services","websites","module","exports"],"sources":["browser-polyfill.js"],"sourcesContent":["/* webextension-polyfill - v0.10.0 - Fri Aug 12 2022 19:42:44 */\n/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */\n/* vim: set sts=2 sw=2 et tw=80: */\n/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at http://mozilla.org/MPL/2.0/. */\n\"use strict\";\n\nif (!globalThis.chrome?.runtime?.id) {\n throw new Error(\"This script should only be loaded in a browser extension.\");\n}\n\nif (typeof globalThis.browser === \"undefined\" || Object.getPrototypeOf(globalThis.browser) !== Object.prototype) {\n const CHROME_SEND_MESSAGE_CALLBACK_NO_RESPONSE_MESSAGE = \"The message port closed before a response was received.\";\n\n // Wrapping the bulk of this polyfill in a one-time-use function is a minor\n // optimization for Firefox. Since Spidermonkey does not fully parse the\n // contents of a function until the first time it's called, and since it will\n // never actually need to be called, this allows the polyfill to be included\n // in Firefox nearly for free.\n const wrapAPIs = extensionAPIs => {\n // NOTE: apiMetadata is associated to the content of the api-metadata.json file\n // at build time by replacing the following \"include\" with the content of the\n // JSON file.\n const apiMetadata = {\n \"alarms\": {\n \"clear\": {\n \"minArgs\": 0,\n \"maxArgs\": 1\n },\n \"clearAll\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n },\n \"get\": {\n \"minArgs\": 0,\n \"maxArgs\": 1\n },\n \"getAll\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n }\n },\n \"bookmarks\": {\n \"create\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"get\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"getChildren\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"getRecent\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"getSubTree\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"getTree\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n },\n \"move\": {\n \"minArgs\": 2,\n \"maxArgs\": 2\n },\n \"remove\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"removeTree\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"search\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"update\": {\n \"minArgs\": 2,\n \"maxArgs\": 2\n }\n },\n \"browserAction\": {\n \"disable\": {\n \"minArgs\": 0,\n \"maxArgs\": 1,\n \"fallbackToNoCallback\": true\n },\n \"enable\": {\n \"minArgs\": 0,\n \"maxArgs\": 1,\n \"fallbackToNoCallback\": true\n },\n \"getBadgeBackgroundColor\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"getBadgeText\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"getPopup\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"getTitle\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"openPopup\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n },\n \"setBadgeBackgroundColor\": {\n \"minArgs\": 1,\n \"maxArgs\": 1,\n \"fallbackToNoCallback\": true\n },\n \"setBadgeText\": {\n \"minArgs\": 1,\n \"maxArgs\": 1,\n \"fallbackToNoCallback\": true\n },\n \"setIcon\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"setPopup\": {\n \"minArgs\": 1,\n \"maxArgs\": 1,\n \"fallbackToNoCallback\": true\n },\n \"setTitle\": {\n \"minArgs\": 1,\n \"maxArgs\": 1,\n \"fallbackToNoCallback\": true\n }\n },\n \"browsingData\": {\n \"remove\": {\n \"minArgs\": 2,\n \"maxArgs\": 2\n },\n \"removeCache\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"removeCookies\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"removeDownloads\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"removeFormData\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"removeHistory\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"removeLocalStorage\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"removePasswords\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"removePluginData\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"settings\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n }\n },\n \"commands\": {\n \"getAll\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n }\n },\n \"contextMenus\": {\n \"remove\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"removeAll\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n },\n \"update\": {\n \"minArgs\": 2,\n \"maxArgs\": 2\n }\n },\n \"cookies\": {\n \"get\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"getAll\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"getAllCookieStores\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n },\n \"remove\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"set\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n }\n },\n \"devtools\": {\n \"inspectedWindow\": {\n \"eval\": {\n \"minArgs\": 1,\n \"maxArgs\": 2,\n \"singleCallbackArg\": false\n }\n },\n \"panels\": {\n \"create\": {\n \"minArgs\": 3,\n \"maxArgs\": 3,\n \"singleCallbackArg\": true\n },\n \"elements\": {\n \"createSidebarPane\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n }\n }\n }\n },\n \"downloads\": {\n \"cancel\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"download\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"erase\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"getFileIcon\": {\n \"minArgs\": 1,\n \"maxArgs\": 2\n },\n \"open\": {\n \"minArgs\": 1,\n \"maxArgs\": 1,\n \"fallbackToNoCallback\": true\n },\n \"pause\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"removeFile\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"resume\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"search\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"show\": {\n \"minArgs\": 1,\n \"maxArgs\": 1,\n \"fallbackToNoCallback\": true\n }\n },\n \"extension\": {\n \"isAllowedFileSchemeAccess\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n },\n \"isAllowedIncognitoAccess\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n }\n },\n \"history\": {\n \"addUrl\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"deleteAll\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n },\n \"deleteRange\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"deleteUrl\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"getVisits\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"search\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n }\n },\n \"i18n\": {\n \"detectLanguage\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"getAcceptLanguages\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n }\n },\n \"identity\": {\n \"launchWebAuthFlow\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n }\n },\n \"idle\": {\n \"queryState\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n }\n },\n \"management\": {\n \"get\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"getAll\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n },\n \"getSelf\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n },\n \"setEnabled\": {\n \"minArgs\": 2,\n \"maxArgs\": 2\n },\n \"uninstallSelf\": {\n \"minArgs\": 0,\n \"maxArgs\": 1\n }\n },\n \"notifications\": {\n \"clear\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"create\": {\n \"minArgs\": 1,\n \"maxArgs\": 2\n },\n \"getAll\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n },\n \"getPermissionLevel\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n },\n \"update\": {\n \"minArgs\": 2,\n \"maxArgs\": 2\n }\n },\n \"pageAction\": {\n \"getPopup\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"getTitle\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"hide\": {\n \"minArgs\": 1,\n \"maxArgs\": 1,\n \"fallbackToNoCallback\": true\n },\n \"setIcon\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"setPopup\": {\n \"minArgs\": 1,\n \"maxArgs\": 1,\n \"fallbackToNoCallback\": true\n },\n \"setTitle\": {\n \"minArgs\": 1,\n \"maxArgs\": 1,\n \"fallbackToNoCallback\": true\n },\n \"show\": {\n \"minArgs\": 1,\n \"maxArgs\": 1,\n \"fallbackToNoCallback\": true\n }\n },\n \"permissions\": {\n \"contains\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"getAll\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n },\n \"remove\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"request\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n }\n },\n \"runtime\": {\n \"getBackgroundPage\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n },\n \"getPlatformInfo\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n },\n \"openOptionsPage\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n },\n \"requestUpdateCheck\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n },\n \"sendMessage\": {\n \"minArgs\": 1,\n \"maxArgs\": 3\n },\n \"sendNativeMessage\": {\n \"minArgs\": 2,\n \"maxArgs\": 2\n },\n \"setUninstallURL\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n }\n },\n \"sessions\": {\n \"getDevices\": {\n \"minArgs\": 0,\n \"maxArgs\": 1\n },\n \"getRecentlyClosed\": {\n \"minArgs\": 0,\n \"maxArgs\": 1\n },\n \"restore\": {\n \"minArgs\": 0,\n \"maxArgs\": 1\n }\n },\n \"storage\": {\n \"local\": {\n \"clear\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n },\n \"get\": {\n \"minArgs\": 0,\n \"maxArgs\": 1\n },\n \"getBytesInUse\": {\n \"minArgs\": 0,\n \"maxArgs\": 1\n },\n \"remove\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"set\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n }\n },\n \"managed\": {\n \"get\": {\n \"minArgs\": 0,\n \"maxArgs\": 1\n },\n \"getBytesInUse\": {\n \"minArgs\": 0,\n \"maxArgs\": 1\n }\n },\n \"sync\": {\n \"clear\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n },\n \"get\": {\n \"minArgs\": 0,\n \"maxArgs\": 1\n },\n \"getBytesInUse\": {\n \"minArgs\": 0,\n \"maxArgs\": 1\n },\n \"remove\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"set\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n }\n }\n },\n \"tabs\": {\n \"captureVisibleTab\": {\n \"minArgs\": 0,\n \"maxArgs\": 2\n },\n \"create\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"detectLanguage\": {\n \"minArgs\": 0,\n \"maxArgs\": 1\n },\n \"discard\": {\n \"minArgs\": 0,\n \"maxArgs\": 1\n },\n \"duplicate\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"executeScript\": {\n \"minArgs\": 1,\n \"maxArgs\": 2\n },\n \"get\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"getCurrent\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n },\n \"getZoom\": {\n \"minArgs\": 0,\n \"maxArgs\": 1\n },\n \"getZoomSettings\": {\n \"minArgs\": 0,\n \"maxArgs\": 1\n },\n \"goBack\": {\n \"minArgs\": 0,\n \"maxArgs\": 1\n },\n \"goForward\": {\n \"minArgs\": 0,\n \"maxArgs\": 1\n },\n \"highlight\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"insertCSS\": {\n \"minArgs\": 1,\n \"maxArgs\": 2\n },\n \"move\": {\n \"minArgs\": 2,\n \"maxArgs\": 2\n },\n \"query\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"reload\": {\n \"minArgs\": 0,\n \"maxArgs\": 2\n },\n \"remove\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"removeCSS\": {\n \"minArgs\": 1,\n \"maxArgs\": 2\n },\n \"sendMessage\": {\n \"minArgs\": 2,\n \"maxArgs\": 3\n },\n \"setZoom\": {\n \"minArgs\": 1,\n \"maxArgs\": 2\n },\n \"setZoomSettings\": {\n \"minArgs\": 1,\n \"maxArgs\": 2\n },\n \"update\": {\n \"minArgs\": 1,\n \"maxArgs\": 2\n }\n },\n \"topSites\": {\n \"get\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n }\n },\n \"webNavigation\": {\n \"getAllFrames\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"getFrame\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n }\n },\n \"webRequest\": {\n \"handlerBehaviorChanged\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n }\n },\n \"windows\": {\n \"create\": {\n \"minArgs\": 0,\n \"maxArgs\": 1\n },\n \"get\": {\n \"minArgs\": 1,\n \"maxArgs\": 2\n },\n \"getAll\": {\n \"minArgs\": 0,\n \"maxArgs\": 1\n },\n \"getCurrent\": {\n \"minArgs\": 0,\n \"maxArgs\": 1\n },\n \"getLastFocused\": {\n \"minArgs\": 0,\n \"maxArgs\": 1\n },\n \"remove\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"update\": {\n \"minArgs\": 2,\n \"maxArgs\": 2\n }\n }\n };\n\n if (Object.keys(apiMetadata).length === 0) {\n throw new Error(\"api-metadata.json has not been included in browser-polyfill\");\n }\n\n /**\n * A WeakMap subclass which creates and stores a value for any key which does\n * not exist when accessed, but behaves exactly as an ordinary WeakMap\n * otherwise.\n *\n * @param {function} createItem\n * A function which will be called in order to create the value for any\n * key which does not exist, the first time it is accessed. The\n * function receives, as its only argument, the key being created.\n */\n class DefaultWeakMap extends WeakMap {\n constructor(createItem, items = undefined) {\n super(items);\n this.createItem = createItem;\n }\n\n get(key) {\n if (!this.has(key)) {\n this.set(key, this.createItem(key));\n }\n\n return super.get(key);\n }\n }\n\n /**\n * Returns true if the given object is an object with a `then` method, and can\n * therefore be assumed to behave as a Promise.\n *\n * @param {*} value The value to test.\n * @returns {boolean} True if the value is thenable.\n */\n const isThenable = value => {\n return value && typeof value === \"object\" && typeof value.then === \"function\";\n };\n\n /**\n * Creates and returns a function which, when called, will resolve or reject\n * the given promise based on how it is called:\n *\n * - If, when called, `chrome.runtime.lastError` contains a non-null object,\n * the promise is rejected with that value.\n * - If the function is called with exactly one argument, the promise is\n * resolved to that value.\n * - Otherwise, the promise is resolved to an array containing all of the\n * function's arguments.\n *\n * @param {object} promise\n * An object containing the resolution and rejection functions of a\n * promise.\n * @param {function} promise.resolve\n * The promise's resolution function.\n * @param {function} promise.reject\n * The promise's rejection function.\n * @param {object} metadata\n * Metadata about the wrapped method which has created the callback.\n * @param {boolean} metadata.singleCallbackArg\n * Whether or not the promise is resolved with only the first\n * argument of the callback, alternatively an array of all the\n * callback arguments is resolved. By default, if the callback\n * function is invoked with only a single argument, that will be\n * resolved to the promise, while all arguments will be resolved as\n * an array if multiple are given.\n *\n * @returns {function}\n * The generated callback function.\n */\n const makeCallback = (promise, metadata) => {\n return (...callbackArgs) => {\n if (extensionAPIs.runtime.lastError) {\n promise.reject(new Error(extensionAPIs.runtime.lastError.message));\n } else if (metadata.singleCallbackArg ||\n (callbackArgs.length <= 1 && metadata.singleCallbackArg !== false)) {\n promise.resolve(callbackArgs[0]);\n } else {\n promise.resolve(callbackArgs);\n }\n };\n };\n\n const pluralizeArguments = (numArgs) => numArgs == 1 ? \"argument\" : \"arguments\";\n\n /**\n * Creates a wrapper function for a method with the given name and metadata.\n *\n * @param {string} name\n * The name of the method which is being wrapped.\n * @param {object} metadata\n * Metadata about the method being wrapped.\n * @param {integer} metadata.minArgs\n * The minimum number of arguments which must be passed to the\n * function. If called with fewer than this number of arguments, the\n * wrapper will raise an exception.\n * @param {integer} metadata.maxArgs\n * The maximum number of arguments which may be passed to the\n * function. If called with more than this number of arguments, the\n * wrapper will raise an exception.\n * @param {boolean} metadata.singleCallbackArg\n * Whether or not the promise is resolved with only the first\n * argument of the callback, alternatively an array of all the\n * callback arguments is resolved. By default, if the callback\n * function is invoked with only a single argument, that will be\n * resolved to the promise, while all arguments will be resolved as\n * an array if multiple are given.\n *\n * @returns {function(object, ...*)}\n * The generated wrapper function.\n */\n const wrapAsyncFunction = (name, metadata) => {\n return function asyncFunctionWrapper(target, ...args) {\n if (args.length < metadata.minArgs) {\n throw new Error(`Expected at least ${metadata.minArgs} ${pluralizeArguments(metadata.minArgs)} for ${name}(), got ${args.length}`);\n }\n\n if (args.length > metadata.maxArgs) {\n throw new Error(`Expected at most ${metadata.maxArgs} ${pluralizeArguments(metadata.maxArgs)} for ${name}(), got ${args.length}`);\n }\n\n return new Promise((resolve, reject) => {\n if (metadata.fallbackToNoCallback) {\n // This API method has currently no callback on Chrome, but it return a promise on Firefox,\n // and so the polyfill will try to call it with a callback first, and it will fallback\n // to not passing the callback if the first call fails.\n try {\n target[name](...args, makeCallback({resolve, reject}, metadata));\n } catch (cbError) {\n console.warn(`${name} API method doesn't seem to support the callback parameter, ` +\n \"falling back to call it without a callback: \", cbError);\n\n target[name](...args);\n\n // Update the API method metadata, so that the next API calls will not try to\n // use the unsupported callback anymore.\n metadata.fallbackToNoCallback = false;\n metadata.noCallback = true;\n\n resolve();\n }\n } else if (metadata.noCallback) {\n target[name](...args);\n resolve();\n } else {\n target[name](...args, makeCallback({resolve, reject}, metadata));\n }\n });\n };\n };\n\n /**\n * Wraps an existing method of the target object, so that calls to it are\n * intercepted by the given wrapper function. The wrapper function receives,\n * as its first argument, the original `target` object, followed by each of\n * the arguments passed to the original method.\n *\n * @param {object} target\n * The original target object that the wrapped method belongs to.\n * @param {function} method\n * The method being wrapped. This is used as the target of the Proxy\n * object which is created to wrap the method.\n * @param {function} wrapper\n * The wrapper function which is called in place of a direct invocation\n * of the wrapped method.\n *\n * @returns {Proxy}\n * A Proxy object for the given method, which invokes the given wrapper\n * method in its place.\n */\n const wrapMethod = (target, method, wrapper) => {\n return new Proxy(method, {\n apply(targetMethod, thisObj, args) {\n return wrapper.call(thisObj, target, ...args);\n },\n });\n };\n\n let hasOwnProperty = Function.call.bind(Object.prototype.hasOwnProperty);\n\n /**\n * Wraps an object in a Proxy which intercepts and wraps certain methods\n * based on the given `wrappers` and `metadata` objects.\n *\n * @param {object} target\n * The target object to wrap.\n *\n * @param {object} [wrappers = {}]\n * An object tree containing wrapper functions for special cases. Any\n * function present in this object tree is called in place of the\n * method in the same location in the `target` object tree. These\n * wrapper methods are invoked as described in {@see wrapMethod}.\n *\n * @param {object} [metadata = {}]\n * An object tree containing metadata used to automatically generate\n * Promise-based wrapper functions for asynchronous. Any function in\n * the `target` object tree which has a corresponding metadata object\n * in the same location in the `metadata` tree is replaced with an\n * automatically-generated wrapper function, as described in\n * {@see wrapAsyncFunction}\n *\n * @returns {Proxy}\n */\n const wrapObject = (target, wrappers = {}, metadata = {}) => {\n let cache = Object.create(null);\n let handlers = {\n has(proxyTarget, prop) {\n return prop in target || prop in cache;\n },\n\n get(proxyTarget, prop, receiver) {\n if (prop in cache) {\n return cache[prop];\n }\n\n if (!(prop in target)) {\n return undefined;\n }\n\n let value = target[prop];\n\n if (typeof value === \"function\") {\n // This is a method on the underlying object. Check if we need to do\n // any wrapping.\n\n if (typeof wrappers[prop] === \"function\") {\n // We have a special-case wrapper for this method.\n value = wrapMethod(target, target[prop], wrappers[prop]);\n } else if (hasOwnProperty(metadata, prop)) {\n // This is an async method that we have metadata for. Create a\n // Promise wrapper for it.\n let wrapper = wrapAsyncFunction(prop, metadata[prop]);\n value = wrapMethod(target, target[prop], wrapper);\n } else {\n // This is a method that we don't know or care about. Return the\n // original method, bound to the underlying object.\n value = value.bind(target);\n }\n } else if (typeof value === \"object\" && value !== null &&\n (hasOwnProperty(wrappers, prop) ||\n hasOwnProperty(metadata, prop))) {\n // This is an object that we need to do some wrapping for the children\n // of. Create a sub-object wrapper for it with the appropriate child\n // metadata.\n value = wrapObject(value, wrappers[prop], metadata[prop]);\n } else if (hasOwnProperty(metadata, \"*\")) {\n // Wrap all properties in * namespace.\n value = wrapObject(value, wrappers[prop], metadata[\"*\"]);\n } else {\n // We don't need to do any wrapping for this property,\n // so just forward all access to the underlying object.\n Object.defineProperty(cache, prop, {\n configurable: true,\n enumerable: true,\n get() {\n return target[prop];\n },\n set(value) {\n target[prop] = value;\n },\n });\n\n return value;\n }\n\n cache[prop] = value;\n return value;\n },\n\n set(proxyTarget, prop, value, receiver) {\n if (prop in cache) {\n cache[prop] = value;\n } else {\n target[prop] = value;\n }\n return true;\n },\n\n defineProperty(proxyTarget, prop, desc) {\n return Reflect.defineProperty(cache, prop, desc);\n },\n\n deleteProperty(proxyTarget, prop) {\n return Reflect.deleteProperty(cache, prop);\n },\n };\n\n // Per contract of the Proxy API, the \"get\" proxy handler must return the\n // original value of the target if that value is declared read-only and\n // non-configurable. For this reason, we create an object with the\n // prototype set to `target` instead of using `target` directly.\n // Otherwise we cannot return a custom object for APIs that\n // are declared read-only and non-configurable, such as `chrome.devtools`.\n //\n // The proxy handlers themselves will still use the original `target`\n // instead of the `proxyTarget`, so that the methods and properties are\n // dereferenced via the original targets.\n let proxyTarget = Object.create(target);\n return new Proxy(proxyTarget, handlers);\n };\n\n /**\n * Creates a set of wrapper functions for an event object, which handles\n * wrapping of listener functions that those messages are passed.\n *\n * A single wrapper is created for each listener function, and stored in a\n * map. Subsequent calls to `addListener`, `hasListener`, or `removeListener`\n * retrieve the original wrapper, so that attempts to remove a\n * previously-added listener work as expected.\n *\n * @param {DefaultWeakMap} wrapperMap\n * A DefaultWeakMap object which will create the appropriate wrapper\n * for a given listener function when one does not exist, and retrieve\n * an existing one when it does.\n *\n * @returns {object}\n */\n const wrapEvent = wrapperMap => ({\n addListener(target, listener, ...args) {\n target.addListener(wrapperMap.get(listener), ...args);\n },\n\n hasListener(target, listener) {\n return target.hasListener(wrapperMap.get(listener));\n },\n\n removeListener(target, listener) {\n target.removeListener(wrapperMap.get(listener));\n },\n });\n\n const onRequestFinishedWrappers = new DefaultWeakMap(listener => {\n if (typeof listener !== \"function\") {\n return listener;\n }\n\n /**\n * Wraps an onRequestFinished listener function so that it will return a\n * `getContent()` property which returns a `Promise` rather than using a\n * callback API.\n *\n * @param {object} req\n * The HAR entry object representing the network request.\n */\n return function onRequestFinished(req) {\n const wrappedReq = wrapObject(req, {} /* wrappers */, {\n getContent: {\n minArgs: 0,\n maxArgs: 0,\n },\n });\n listener(wrappedReq);\n };\n });\n\n const onMessageWrappers = new DefaultWeakMap(listener => {\n if (typeof listener !== \"function\") {\n return listener;\n }\n\n /**\n * Wraps a message listener function so that it may send responses based on\n * its return value, rather than by returning a sentinel value and calling a\n * callback. If the listener function returns a Promise, the response is\n * sent when the promise either resolves or rejects.\n *\n * @param {*} message\n * The message sent by the other end of the channel.\n * @param {object} sender\n * Details about the sender of the message.\n * @param {function(*)} sendResponse\n * A callback which, when called with an arbitrary argument, sends\n * that value as a response.\n * @returns {boolean}\n * True if the wrapped listener returned a Promise, which will later\n * yield a response. False otherwise.\n */\n return function onMessage(message, sender, sendResponse) {\n let didCallSendResponse = false;\n\n let wrappedSendResponse;\n let sendResponsePromise = new Promise(resolve => {\n wrappedSendResponse = function(response) {\n didCallSendResponse = true;\n resolve(response);\n };\n });\n\n let result;\n try {\n result = listener(message, sender, wrappedSendResponse);\n } catch (err) {\n result = Promise.reject(err);\n }\n\n const isResultThenable = result !== true && isThenable(result);\n\n // If the listener didn't returned true or a Promise, or called\n // wrappedSendResponse synchronously, we can exit earlier\n // because there will be no response sent from this listener.\n if (result !== true && !isResultThenable && !didCallSendResponse) {\n return false;\n }\n\n // A small helper to send the message if the promise resolves\n // and an error if the promise rejects (a wrapped sendMessage has\n // to translate the message into a resolved promise or a rejected\n // promise).\n const sendPromisedResult = (promise) => {\n promise.then(msg => {\n // send the message value.\n sendResponse(msg);\n }, error => {\n // Send a JSON representation of the error if the rejected value\n // is an instance of error, or the object itself otherwise.\n let message;\n if (error && (error instanceof Error ||\n typeof error.message === \"string\")) {\n message = error.message;\n } else {\n message = \"An unexpected error occurred\";\n }\n\n sendResponse({\n __mozWebExtensionPolyfillReject__: true,\n message,\n });\n }).catch(err => {\n // Print an error on the console if unable to send the response.\n console.error(\"Failed to send onMessage rejected reply\", err);\n });\n };\n\n // If the listener returned a Promise, send the resolved value as a\n // result, otherwise wait the promise related to the wrappedSendResponse\n // callback to resolve and send it as a response.\n if (isResultThenable) {\n sendPromisedResult(result);\n } else {\n sendPromisedResult(sendResponsePromise);\n }\n\n // Let Chrome know that the listener is replying.\n return true;\n };\n });\n\n const wrappedSendMessageCallback = ({reject, resolve}, reply) => {\n if (extensionAPIs.runtime.lastError) {\n // Detect when none of the listeners replied to the sendMessage call and resolve\n // the promise to undefined as in Firefox.\n // See https://github.com/mozilla/webextension-polyfill/issues/130\n if (extensionAPIs.runtime.lastError.message === CHROME_SEND_MESSAGE_CALLBACK_NO_RESPONSE_MESSAGE) {\n resolve();\n } else {\n reject(new Error(extensionAPIs.runtime.lastError.message));\n }\n } else if (reply && reply.__mozWebExtensionPolyfillReject__) {\n // Convert back the JSON representation of the error into\n // an Error instance.\n reject(new Error(reply.message));\n } else {\n resolve(reply);\n }\n };\n\n const wrappedSendMessage = (name, metadata, apiNamespaceObj, ...args) => {\n if (args.length < metadata.minArgs) {\n throw new Error(`Expected at least ${metadata.minArgs} ${pluralizeArguments(metadata.minArgs)} for ${name}(), got ${args.length}`);\n }\n\n if (args.length > metadata.maxArgs) {\n throw new Error(`Expected at most ${metadata.maxArgs} ${pluralizeArguments(metadata.maxArgs)} for ${name}(), got ${args.length}`);\n }\n\n return new Promise((resolve, reject) => {\n const wrappedCb = wrappedSendMessageCallback.bind(null, {resolve, reject});\n args.push(wrappedCb);\n apiNamespaceObj.sendMessage(...args);\n });\n };\n\n const staticWrappers = {\n devtools: {\n network: {\n onRequestFinished: wrapEvent(onRequestFinishedWrappers),\n },\n },\n runtime: {\n onMessage: wrapEvent(onMessageWrappers),\n onMessageExternal: wrapEvent(onMessageWrappers),\n sendMessage: wrappedSendMessage.bind(null, \"sendMessage\", {minArgs: 1, maxArgs: 3}),\n },\n tabs: {\n sendMessage: wrappedSendMessage.bind(null, \"sendMessage\", {minArgs: 2, maxArgs: 3}),\n },\n };\n const settingMetadata = {\n clear: {minArgs: 1, maxArgs: 1},\n get: {minArgs: 1, maxArgs: 1},\n set: {minArgs: 1, maxArgs: 1},\n };\n apiMetadata.privacy = {\n network: {\"*\": settingMetadata},\n services: {\"*\": settingMetadata},\n websites: {\"*\": settingMetadata},\n };\n\n return wrapObject(extensionAPIs, staticWrappers, apiMetadata);\n };\n\n // The build process adds a UMD wrapper around this file, which makes the\n // `module` variable available.\n module.exports = wrapAPIs(chrome);\n} else {\n module.exports = globalThis.browser;\n}\n"],"mappings":"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"}
\ No newline at end of file
diff --git a/webext-chrome/icons/icon48.png b/webext-chrome/icons/icon48.png
new file mode 100644
index 0000000..6808983
Binary files /dev/null and b/webext-chrome/icons/icon48.png differ
diff --git a/webext-chrome/icons/icon96.png b/webext-chrome/icons/icon96.png
new file mode 100644
index 0000000..a32d706
Binary files /dev/null and b/webext-chrome/icons/icon96.png differ
diff --git a/webext-chrome/manifest.json b/webext-chrome/manifest.json
new file mode 100644
index 0000000..1ea4d7d
--- /dev/null
+++ b/webext-chrome/manifest.json
@@ -0,0 +1,31 @@
+{
+ "manifest_version": 3,
+ "name": "LinkDown for Browsers",
+ "description": "LinkDown's Add-on for browsers, download the favourite videos you like within a click!",
+ "version": "0.2",
+ "icons": {
+ "48": "icons/icon48.png",
+ "96": "icons/icon96.png"
+ },
+ "default_locale": "en",
+ "permissions": ["contextMenus", "cookies", "nativeMessaging"],
+ "host_permissions": [
+ "*://*/*"
+ ],
+ "content_security_policy": {
+ "extension_pages": "script-src 'self'; object-src 'self'"
+ },
+ "background": {
+ "service_worker": "background.js"
+ },
+ "content_scripts": [
+ {
+ "matches": [""],
+ "js": ["browser-polyfill.js", "background.js"]
+ }
+ ],
+ "action": {
+ "default_icon": "icons/icon48.png",
+ "default_title": "LinkDown for Browsers"
+ }
+}
diff --git a/webext-chrome/webext-chrome.zip b/webext-chrome/webext-chrome.zip
new file mode 100644
index 0000000..4dc501c
Binary files /dev/null and b/webext-chrome/webext-chrome.zip differ
diff --git a/webext-firefox/_locales/en/messages.json b/webext-firefox/_locales/en/messages.json
new file mode 100644
index 0000000..fd9d7fe
--- /dev/null
+++ b/webext-firefox/_locales/en/messages.json
@@ -0,0 +1,5 @@
+{
+ "download_video": {
+ "message": "Download Video"
+ }
+}
diff --git a/webext-firefox/_locales/zh_CN/messages.json b/webext-firefox/_locales/zh_CN/messages.json
new file mode 100644
index 0000000..0e743c8
--- /dev/null
+++ b/webext-firefox/_locales/zh_CN/messages.json
@@ -0,0 +1,5 @@
+{
+ "download_video": {
+ "message": "下载视频"
+ }
+}
diff --git a/webext-firefox/_locales/zh_HK/messages.json b/webext-firefox/_locales/zh_HK/messages.json
new file mode 100644
index 0000000..07f1e0f
--- /dev/null
+++ b/webext-firefox/_locales/zh_HK/messages.json
@@ -0,0 +1,5 @@
+{
+ "download_video": {
+ "message": "下載影片"
+ }
+}
diff --git a/webext-firefox/_locales/zh_TW/messages.json b/webext-firefox/_locales/zh_TW/messages.json
new file mode 100644
index 0000000..07f1e0f
--- /dev/null
+++ b/webext-firefox/_locales/zh_TW/messages.json
@@ -0,0 +1,5 @@
+{
+ "download_video": {
+ "message": "下載影片"
+ }
+}
diff --git a/webext-firefox/_metadata b/webext-firefox/_metadata
new file mode 100644
index 0000000..f4946ad
--- /dev/null
+++ b/webext-firefox/_metadata
@@ -0,0 +1,3 @@
+{
+ "id": "ebc3c64a-3bee-49f1-a38f-378633577de1"
+}
diff --git a/webext-firefox/background.js b/webext-firefox/background.js
new file mode 100644
index 0000000..a4c864f
--- /dev/null
+++ b/webext-firefox/background.js
@@ -0,0 +1,85 @@
+if (typeof browser === "undefined") {
+ browser = chrome;
+}
+
+console.log("LinkDown for Browsers: I am successfully triggered!");
+// Create Context Menu Item
+
+browser.contextMenus.create({
+ id: "download-video",
+ title: browser.i18n.getMessage("download_video"),
+ contexts: ["all"],
+});
+
+// Listen for Clicks on Context Menu Item
+browser.contextMenus.onClicked.addListener(async function(info, tab) {
+ console.log(info);
+ // Get URL
+ var url = info.linkUrl || info.srcUrl || info.pageUrl;
+ // Determine to Use BBDown / you-get / yt-dlp and Execute Commands
+ var command;
+ if (url.indexOf("music.163.com") !== -1 ||
+ url.indexOf("56.com") !== -1 ||
+ url.indexOf("acfun.cn") !== -1 ||
+ url.indexOf("baidu.com") !== -1 ||
+ url.indexOf("baomihua.com") !== -1 ||
+ url.indexOf("douyutv.com") !== -1 ||
+ url.indexOf("ifeng.com") !== -1 ||
+ url.indexOf("fun.tv") !== -1 ||
+ url.indexOf("iqiyi.com") !== -1 ||
+ url.indexOf("kou.cn") !== -1 ||
+ url.indexOf("ku6.com") !== -1 ||
+ url.indexOf("kugou.com") !== -1 ||
+ url.indexOf("kuwo.cn") !== -1 ||
+ url.indexOf("le.com") !== -1 ||
+ url.indexOf("lizhi.fm") !== -1 ||
+ url.indexOf("lrts.me") !== -1 ||
+ url.indexOf("miaopai.com") !== -1 ||
+ url.indexOf("miomio.tv") !== -1 ||
+ url.indexOf("missevan.com") !== -1 ||
+ url.indexOf("pixnet.net") !== -1 ||
+ url.indexOf("pptv.com") !== -1 ||
+ url.indexOf("iqilu.com") !== -1 ||
+ url.indexOf("qq.com") !== -1 ||
+ url.indexOf("sina.com.cn") !== -1 ||
+ url.indexOf("weibo.com") !== -1 ||
+ url.indexOf("sohu.com") !== -1 ||
+ url.indexOf("tudou.com") !== -1 ||
+ url.indexOf("isuntv.com") !== -1 ||
+ url.indexOf("youku.com") !== -1 ||
+ url.indexOf("zhanqi.tv") !== -1 ||
+ url.indexOf("cntv.cn") !== -1 ||
+ url.indexOf("mgtv.com") !== -1 ||
+ url.indexOf("huomao.com") !== -1 ||
+ url.indexOf("356yg.com") !== -1 ||
+ url.indexOf("ixigua.com") !== -1 ||
+ url.indexOf("xinpianchang.com") !== -1 ||
+ url.indexOf("kuaishou.com") !== -1 ||
+ url.indexOf("douyin.com") !== -1 ||
+ url.indexOf("zhibo.tv") !== -1 ||
+ url.indexOf("zhihu.com") !== -1) {
+ command =
+ 'start you-get.exe -o "%HOMEPATH%/Downloads/Video" ' + url;
+ } else if (url.indexOf("bilibili.com") !== -1) {
+ var cookies = await browser.cookies.getAll({ url: "https://www.bilibili.com" });
+ var cookie = cookies.map(cookie => `${cookie.name}=${cookie.value}`).join('; ');
+ if (cookies.length > 0) {
+ command =
+ `start bbdown.exe ${url} --work-dir "%HOMEPATH%/Downloads/Video" -tv --cookie "${cookie}"`;
+ } else {
+ command =
+ `start bbdown.exe ${url} --work-dir "%HOMEPATH%/Downloads/Video"`;
+ }
+ } else {
+ command =
+ 'start yt-dlp.exe --output "../../../Downloads/Video/%(title)s.%(ext)s" --merge-output-format mp4 ' +
+ url;
+ }
+ try {
+ await browser.runtime.sendNativeMessage("linkdown", { command });
+ console.log("LinkDown for Browsers: Command Sent: " + command);
+ } catch (error) {
+ console.error(error);
+ console.error("LinkDown for Browsers: Failed to Send Command: " + command);
+ }
+});
diff --git a/webext-firefox/browser-polyfill.js b/webext-firefox/browser-polyfill.js
new file mode 100644
index 0000000..8e732a2
--- /dev/null
+++ b/webext-firefox/browser-polyfill.js
@@ -0,0 +1,1269 @@
+(function (global, factory) {
+ if (typeof define === "function" && define.amd) {
+ define("webextension-polyfill", ["module"], factory);
+ } else if (typeof exports !== "undefined") {
+ factory(module);
+ } else {
+ var mod = {
+ exports: {}
+ };
+ factory(mod);
+ global.browser = mod.exports;
+ }
+})(typeof globalThis !== "undefined" ? globalThis : typeof self !== "undefined" ? self : this, function (module) {
+ /* webextension-polyfill - v0.10.0 - Fri Aug 12 2022 19:42:44 */
+
+ /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+
+ /* vim: set sts=2 sw=2 et tw=80: */
+
+ /* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ "use strict";
+
+ if (!globalThis.chrome?.runtime?.id) {
+ throw new Error("This script should only be loaded in a browser extension.");
+ }
+
+ if (typeof globalThis.browser === "undefined" || Object.getPrototypeOf(globalThis.browser) !== Object.prototype) {
+ const CHROME_SEND_MESSAGE_CALLBACK_NO_RESPONSE_MESSAGE = "The message port closed before a response was received."; // Wrapping the bulk of this polyfill in a one-time-use function is a minor
+ // optimization for Firefox. Since Spidermonkey does not fully parse the
+ // contents of a function until the first time it's called, and since it will
+ // never actually need to be called, this allows the polyfill to be included
+ // in Firefox nearly for free.
+
+ const wrapAPIs = extensionAPIs => {
+ // NOTE: apiMetadata is associated to the content of the api-metadata.json file
+ // at build time by replacing the following "include" with the content of the
+ // JSON file.
+ const apiMetadata = {
+ "alarms": {
+ "clear": {
+ "minArgs": 0,
+ "maxArgs": 1
+ },
+ "clearAll": {
+ "minArgs": 0,
+ "maxArgs": 0
+ },
+ "get": {
+ "minArgs": 0,
+ "maxArgs": 1
+ },
+ "getAll": {
+ "minArgs": 0,
+ "maxArgs": 0
+ }
+ },
+ "bookmarks": {
+ "create": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "get": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "getChildren": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "getRecent": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "getSubTree": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "getTree": {
+ "minArgs": 0,
+ "maxArgs": 0
+ },
+ "move": {
+ "minArgs": 2,
+ "maxArgs": 2
+ },
+ "remove": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "removeTree": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "search": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "update": {
+ "minArgs": 2,
+ "maxArgs": 2
+ }
+ },
+ "browserAction": {
+ "disable": {
+ "minArgs": 0,
+ "maxArgs": 1,
+ "fallbackToNoCallback": true
+ },
+ "enable": {
+ "minArgs": 0,
+ "maxArgs": 1,
+ "fallbackToNoCallback": true
+ },
+ "getBadgeBackgroundColor": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "getBadgeText": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "getPopup": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "getTitle": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "openPopup": {
+ "minArgs": 0,
+ "maxArgs": 0
+ },
+ "setBadgeBackgroundColor": {
+ "minArgs": 1,
+ "maxArgs": 1,
+ "fallbackToNoCallback": true
+ },
+ "setBadgeText": {
+ "minArgs": 1,
+ "maxArgs": 1,
+ "fallbackToNoCallback": true
+ },
+ "setIcon": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "setPopup": {
+ "minArgs": 1,
+ "maxArgs": 1,
+ "fallbackToNoCallback": true
+ },
+ "setTitle": {
+ "minArgs": 1,
+ "maxArgs": 1,
+ "fallbackToNoCallback": true
+ }
+ },
+ "browsingData": {
+ "remove": {
+ "minArgs": 2,
+ "maxArgs": 2
+ },
+ "removeCache": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "removeCookies": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "removeDownloads": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "removeFormData": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "removeHistory": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "removeLocalStorage": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "removePasswords": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "removePluginData": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "settings": {
+ "minArgs": 0,
+ "maxArgs": 0
+ }
+ },
+ "commands": {
+ "getAll": {
+ "minArgs": 0,
+ "maxArgs": 0
+ }
+ },
+ "contextMenus": {
+ "remove": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "removeAll": {
+ "minArgs": 0,
+ "maxArgs": 0
+ },
+ "update": {
+ "minArgs": 2,
+ "maxArgs": 2
+ }
+ },
+ "cookies": {
+ "get": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "getAll": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "getAllCookieStores": {
+ "minArgs": 0,
+ "maxArgs": 0
+ },
+ "remove": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "set": {
+ "minArgs": 1,
+ "maxArgs": 1
+ }
+ },
+ "devtools": {
+ "inspectedWindow": {
+ "eval": {
+ "minArgs": 1,
+ "maxArgs": 2,
+ "singleCallbackArg": false
+ }
+ },
+ "panels": {
+ "create": {
+ "minArgs": 3,
+ "maxArgs": 3,
+ "singleCallbackArg": true
+ },
+ "elements": {
+ "createSidebarPane": {
+ "minArgs": 1,
+ "maxArgs": 1
+ }
+ }
+ }
+ },
+ "downloads": {
+ "cancel": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "download": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "erase": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "getFileIcon": {
+ "minArgs": 1,
+ "maxArgs": 2
+ },
+ "open": {
+ "minArgs": 1,
+ "maxArgs": 1,
+ "fallbackToNoCallback": true
+ },
+ "pause": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "removeFile": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "resume": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "search": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "show": {
+ "minArgs": 1,
+ "maxArgs": 1,
+ "fallbackToNoCallback": true
+ }
+ },
+ "extension": {
+ "isAllowedFileSchemeAccess": {
+ "minArgs": 0,
+ "maxArgs": 0
+ },
+ "isAllowedIncognitoAccess": {
+ "minArgs": 0,
+ "maxArgs": 0
+ }
+ },
+ "history": {
+ "addUrl": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "deleteAll": {
+ "minArgs": 0,
+ "maxArgs": 0
+ },
+ "deleteRange": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "deleteUrl": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "getVisits": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "search": {
+ "minArgs": 1,
+ "maxArgs": 1
+ }
+ },
+ "i18n": {
+ "detectLanguage": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "getAcceptLanguages": {
+ "minArgs": 0,
+ "maxArgs": 0
+ }
+ },
+ "identity": {
+ "launchWebAuthFlow": {
+ "minArgs": 1,
+ "maxArgs": 1
+ }
+ },
+ "idle": {
+ "queryState": {
+ "minArgs": 1,
+ "maxArgs": 1
+ }
+ },
+ "management": {
+ "get": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "getAll": {
+ "minArgs": 0,
+ "maxArgs": 0
+ },
+ "getSelf": {
+ "minArgs": 0,
+ "maxArgs": 0
+ },
+ "setEnabled": {
+ "minArgs": 2,
+ "maxArgs": 2
+ },
+ "uninstallSelf": {
+ "minArgs": 0,
+ "maxArgs": 1
+ }
+ },
+ "notifications": {
+ "clear": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "create": {
+ "minArgs": 1,
+ "maxArgs": 2
+ },
+ "getAll": {
+ "minArgs": 0,
+ "maxArgs": 0
+ },
+ "getPermissionLevel": {
+ "minArgs": 0,
+ "maxArgs": 0
+ },
+ "update": {
+ "minArgs": 2,
+ "maxArgs": 2
+ }
+ },
+ "pageAction": {
+ "getPopup": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "getTitle": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "hide": {
+ "minArgs": 1,
+ "maxArgs": 1,
+ "fallbackToNoCallback": true
+ },
+ "setIcon": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "setPopup": {
+ "minArgs": 1,
+ "maxArgs": 1,
+ "fallbackToNoCallback": true
+ },
+ "setTitle": {
+ "minArgs": 1,
+ "maxArgs": 1,
+ "fallbackToNoCallback": true
+ },
+ "show": {
+ "minArgs": 1,
+ "maxArgs": 1,
+ "fallbackToNoCallback": true
+ }
+ },
+ "permissions": {
+ "contains": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "getAll": {
+ "minArgs": 0,
+ "maxArgs": 0
+ },
+ "remove": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "request": {
+ "minArgs": 1,
+ "maxArgs": 1
+ }
+ },
+ "runtime": {
+ "getBackgroundPage": {
+ "minArgs": 0,
+ "maxArgs": 0
+ },
+ "getPlatformInfo": {
+ "minArgs": 0,
+ "maxArgs": 0
+ },
+ "openOptionsPage": {
+ "minArgs": 0,
+ "maxArgs": 0
+ },
+ "requestUpdateCheck": {
+ "minArgs": 0,
+ "maxArgs": 0
+ },
+ "sendMessage": {
+ "minArgs": 1,
+ "maxArgs": 3
+ },
+ "sendNativeMessage": {
+ "minArgs": 2,
+ "maxArgs": 2
+ },
+ "setUninstallURL": {
+ "minArgs": 1,
+ "maxArgs": 1
+ }
+ },
+ "sessions": {
+ "getDevices": {
+ "minArgs": 0,
+ "maxArgs": 1
+ },
+ "getRecentlyClosed": {
+ "minArgs": 0,
+ "maxArgs": 1
+ },
+ "restore": {
+ "minArgs": 0,
+ "maxArgs": 1
+ }
+ },
+ "storage": {
+ "local": {
+ "clear": {
+ "minArgs": 0,
+ "maxArgs": 0
+ },
+ "get": {
+ "minArgs": 0,
+ "maxArgs": 1
+ },
+ "getBytesInUse": {
+ "minArgs": 0,
+ "maxArgs": 1
+ },
+ "remove": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "set": {
+ "minArgs": 1,
+ "maxArgs": 1
+ }
+ },
+ "managed": {
+ "get": {
+ "minArgs": 0,
+ "maxArgs": 1
+ },
+ "getBytesInUse": {
+ "minArgs": 0,
+ "maxArgs": 1
+ }
+ },
+ "sync": {
+ "clear": {
+ "minArgs": 0,
+ "maxArgs": 0
+ },
+ "get": {
+ "minArgs": 0,
+ "maxArgs": 1
+ },
+ "getBytesInUse": {
+ "minArgs": 0,
+ "maxArgs": 1
+ },
+ "remove": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "set": {
+ "minArgs": 1,
+ "maxArgs": 1
+ }
+ }
+ },
+ "tabs": {
+ "captureVisibleTab": {
+ "minArgs": 0,
+ "maxArgs": 2
+ },
+ "create": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "detectLanguage": {
+ "minArgs": 0,
+ "maxArgs": 1
+ },
+ "discard": {
+ "minArgs": 0,
+ "maxArgs": 1
+ },
+ "duplicate": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "executeScript": {
+ "minArgs": 1,
+ "maxArgs": 2
+ },
+ "get": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "getCurrent": {
+ "minArgs": 0,
+ "maxArgs": 0
+ },
+ "getZoom": {
+ "minArgs": 0,
+ "maxArgs": 1
+ },
+ "getZoomSettings": {
+ "minArgs": 0,
+ "maxArgs": 1
+ },
+ "goBack": {
+ "minArgs": 0,
+ "maxArgs": 1
+ },
+ "goForward": {
+ "minArgs": 0,
+ "maxArgs": 1
+ },
+ "highlight": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "insertCSS": {
+ "minArgs": 1,
+ "maxArgs": 2
+ },
+ "move": {
+ "minArgs": 2,
+ "maxArgs": 2
+ },
+ "query": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "reload": {
+ "minArgs": 0,
+ "maxArgs": 2
+ },
+ "remove": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "removeCSS": {
+ "minArgs": 1,
+ "maxArgs": 2
+ },
+ "sendMessage": {
+ "minArgs": 2,
+ "maxArgs": 3
+ },
+ "setZoom": {
+ "minArgs": 1,
+ "maxArgs": 2
+ },
+ "setZoomSettings": {
+ "minArgs": 1,
+ "maxArgs": 2
+ },
+ "update": {
+ "minArgs": 1,
+ "maxArgs": 2
+ }
+ },
+ "topSites": {
+ "get": {
+ "minArgs": 0,
+ "maxArgs": 0
+ }
+ },
+ "webNavigation": {
+ "getAllFrames": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "getFrame": {
+ "minArgs": 1,
+ "maxArgs": 1
+ }
+ },
+ "webRequest": {
+ "handlerBehaviorChanged": {
+ "minArgs": 0,
+ "maxArgs": 0
+ }
+ },
+ "windows": {
+ "create": {
+ "minArgs": 0,
+ "maxArgs": 1
+ },
+ "get": {
+ "minArgs": 1,
+ "maxArgs": 2
+ },
+ "getAll": {
+ "minArgs": 0,
+ "maxArgs": 1
+ },
+ "getCurrent": {
+ "minArgs": 0,
+ "maxArgs": 1
+ },
+ "getLastFocused": {
+ "minArgs": 0,
+ "maxArgs": 1
+ },
+ "remove": {
+ "minArgs": 1,
+ "maxArgs": 1
+ },
+ "update": {
+ "minArgs": 2,
+ "maxArgs": 2
+ }
+ }
+ };
+
+ if (Object.keys(apiMetadata).length === 0) {
+ throw new Error("api-metadata.json has not been included in browser-polyfill");
+ }
+ /**
+ * A WeakMap subclass which creates and stores a value for any key which does
+ * not exist when accessed, but behaves exactly as an ordinary WeakMap
+ * otherwise.
+ *
+ * @param {function} createItem
+ * A function which will be called in order to create the value for any
+ * key which does not exist, the first time it is accessed. The
+ * function receives, as its only argument, the key being created.
+ */
+
+
+ class DefaultWeakMap extends WeakMap {
+ constructor(createItem, items = undefined) {
+ super(items);
+ this.createItem = createItem;
+ }
+
+ get(key) {
+ if (!this.has(key)) {
+ this.set(key, this.createItem(key));
+ }
+
+ return super.get(key);
+ }
+
+ }
+ /**
+ * Returns true if the given object is an object with a `then` method, and can
+ * therefore be assumed to behave as a Promise.
+ *
+ * @param {*} value The value to test.
+ * @returns {boolean} True if the value is thenable.
+ */
+
+
+ const isThenable = value => {
+ return value && typeof value === "object" && typeof value.then === "function";
+ };
+ /**
+ * Creates and returns a function which, when called, will resolve or reject
+ * the given promise based on how it is called:
+ *
+ * - If, when called, `chrome.runtime.lastError` contains a non-null object,
+ * the promise is rejected with that value.
+ * - If the function is called with exactly one argument, the promise is
+ * resolved to that value.
+ * - Otherwise, the promise is resolved to an array containing all of the
+ * function's arguments.
+ *
+ * @param {object} promise
+ * An object containing the resolution and rejection functions of a
+ * promise.
+ * @param {function} promise.resolve
+ * The promise's resolution function.
+ * @param {function} promise.reject
+ * The promise's rejection function.
+ * @param {object} metadata
+ * Metadata about the wrapped method which has created the callback.
+ * @param {boolean} metadata.singleCallbackArg
+ * Whether or not the promise is resolved with only the first
+ * argument of the callback, alternatively an array of all the
+ * callback arguments is resolved. By default, if the callback
+ * function is invoked with only a single argument, that will be
+ * resolved to the promise, while all arguments will be resolved as
+ * an array if multiple are given.
+ *
+ * @returns {function}
+ * The generated callback function.
+ */
+
+
+ const makeCallback = (promise, metadata) => {
+ return (...callbackArgs) => {
+ if (extensionAPIs.runtime.lastError) {
+ promise.reject(new Error(extensionAPIs.runtime.lastError.message));
+ } else if (metadata.singleCallbackArg || callbackArgs.length <= 1 && metadata.singleCallbackArg !== false) {
+ promise.resolve(callbackArgs[0]);
+ } else {
+ promise.resolve(callbackArgs);
+ }
+ };
+ };
+
+ const pluralizeArguments = numArgs => numArgs == 1 ? "argument" : "arguments";
+ /**
+ * Creates a wrapper function for a method with the given name and metadata.
+ *
+ * @param {string} name
+ * The name of the method which is being wrapped.
+ * @param {object} metadata
+ * Metadata about the method being wrapped.
+ * @param {integer} metadata.minArgs
+ * The minimum number of arguments which must be passed to the
+ * function. If called with fewer than this number of arguments, the
+ * wrapper will raise an exception.
+ * @param {integer} metadata.maxArgs
+ * The maximum number of arguments which may be passed to the
+ * function. If called with more than this number of arguments, the
+ * wrapper will raise an exception.
+ * @param {boolean} metadata.singleCallbackArg
+ * Whether or not the promise is resolved with only the first
+ * argument of the callback, alternatively an array of all the
+ * callback arguments is resolved. By default, if the callback
+ * function is invoked with only a single argument, that will be
+ * resolved to the promise, while all arguments will be resolved as
+ * an array if multiple are given.
+ *
+ * @returns {function(object, ...*)}
+ * The generated wrapper function.
+ */
+
+
+ const wrapAsyncFunction = (name, metadata) => {
+ return function asyncFunctionWrapper(target, ...args) {
+ if (args.length < metadata.minArgs) {
+ throw new Error(`Expected at least ${metadata.minArgs} ${pluralizeArguments(metadata.minArgs)} for ${name}(), got ${args.length}`);
+ }
+
+ if (args.length > metadata.maxArgs) {
+ throw new Error(`Expected at most ${metadata.maxArgs} ${pluralizeArguments(metadata.maxArgs)} for ${name}(), got ${args.length}`);
+ }
+
+ return new Promise((resolve, reject) => {
+ if (metadata.fallbackToNoCallback) {
+ // This API method has currently no callback on Chrome, but it return a promise on Firefox,
+ // and so the polyfill will try to call it with a callback first, and it will fallback
+ // to not passing the callback if the first call fails.
+ try {
+ target[name](...args, makeCallback({
+ resolve,
+ reject
+ }, metadata));
+ } catch (cbError) {
+ console.warn(`${name} API method doesn't seem to support the callback parameter, ` + "falling back to call it without a callback: ", cbError);
+ target[name](...args); // Update the API method metadata, so that the next API calls will not try to
+ // use the unsupported callback anymore.
+
+ metadata.fallbackToNoCallback = false;
+ metadata.noCallback = true;
+ resolve();
+ }
+ } else if (metadata.noCallback) {
+ target[name](...args);
+ resolve();
+ } else {
+ target[name](...args, makeCallback({
+ resolve,
+ reject
+ }, metadata));
+ }
+ });
+ };
+ };
+ /**
+ * Wraps an existing method of the target object, so that calls to it are
+ * intercepted by the given wrapper function. The wrapper function receives,
+ * as its first argument, the original `target` object, followed by each of
+ * the arguments passed to the original method.
+ *
+ * @param {object} target
+ * The original target object that the wrapped method belongs to.
+ * @param {function} method
+ * The method being wrapped. This is used as the target of the Proxy
+ * object which is created to wrap the method.
+ * @param {function} wrapper
+ * The wrapper function which is called in place of a direct invocation
+ * of the wrapped method.
+ *
+ * @returns {Proxy}
+ * A Proxy object for the given method, which invokes the given wrapper
+ * method in its place.
+ */
+
+
+ const wrapMethod = (target, method, wrapper) => {
+ return new Proxy(method, {
+ apply(targetMethod, thisObj, args) {
+ return wrapper.call(thisObj, target, ...args);
+ }
+
+ });
+ };
+
+ let hasOwnProperty = Function.call.bind(Object.prototype.hasOwnProperty);
+ /**
+ * Wraps an object in a Proxy which intercepts and wraps certain methods
+ * based on the given `wrappers` and `metadata` objects.
+ *
+ * @param {object} target
+ * The target object to wrap.
+ *
+ * @param {object} [wrappers = {}]
+ * An object tree containing wrapper functions for special cases. Any
+ * function present in this object tree is called in place of the
+ * method in the same location in the `target` object tree. These
+ * wrapper methods are invoked as described in {@see wrapMethod}.
+ *
+ * @param {object} [metadata = {}]
+ * An object tree containing metadata used to automatically generate
+ * Promise-based wrapper functions for asynchronous. Any function in
+ * the `target` object tree which has a corresponding metadata object
+ * in the same location in the `metadata` tree is replaced with an
+ * automatically-generated wrapper function, as described in
+ * {@see wrapAsyncFunction}
+ *
+ * @returns {Proxy}
+ */
+
+ const wrapObject = (target, wrappers = {}, metadata = {}) => {
+ let cache = Object.create(null);
+ let handlers = {
+ has(proxyTarget, prop) {
+ return prop in target || prop in cache;
+ },
+
+ get(proxyTarget, prop, receiver) {
+ if (prop in cache) {
+ return cache[prop];
+ }
+
+ if (!(prop in target)) {
+ return undefined;
+ }
+
+ let value = target[prop];
+
+ if (typeof value === "function") {
+ // This is a method on the underlying object. Check if we need to do
+ // any wrapping.
+ if (typeof wrappers[prop] === "function") {
+ // We have a special-case wrapper for this method.
+ value = wrapMethod(target, target[prop], wrappers[prop]);
+ } else if (hasOwnProperty(metadata, prop)) {
+ // This is an async method that we have metadata for. Create a
+ // Promise wrapper for it.
+ let wrapper = wrapAsyncFunction(prop, metadata[prop]);
+ value = wrapMethod(target, target[prop], wrapper);
+ } else {
+ // This is a method that we don't know or care about. Return the
+ // original method, bound to the underlying object.
+ value = value.bind(target);
+ }
+ } else if (typeof value === "object" && value !== null && (hasOwnProperty(wrappers, prop) || hasOwnProperty(metadata, prop))) {
+ // This is an object that we need to do some wrapping for the children
+ // of. Create a sub-object wrapper for it with the appropriate child
+ // metadata.
+ value = wrapObject(value, wrappers[prop], metadata[prop]);
+ } else if (hasOwnProperty(metadata, "*")) {
+ // Wrap all properties in * namespace.
+ value = wrapObject(value, wrappers[prop], metadata["*"]);
+ } else {
+ // We don't need to do any wrapping for this property,
+ // so just forward all access to the underlying object.
+ Object.defineProperty(cache, prop, {
+ configurable: true,
+ enumerable: true,
+
+ get() {
+ return target[prop];
+ },
+
+ set(value) {
+ target[prop] = value;
+ }
+
+ });
+ return value;
+ }
+
+ cache[prop] = value;
+ return value;
+ },
+
+ set(proxyTarget, prop, value, receiver) {
+ if (prop in cache) {
+ cache[prop] = value;
+ } else {
+ target[prop] = value;
+ }
+
+ return true;
+ },
+
+ defineProperty(proxyTarget, prop, desc) {
+ return Reflect.defineProperty(cache, prop, desc);
+ },
+
+ deleteProperty(proxyTarget, prop) {
+ return Reflect.deleteProperty(cache, prop);
+ }
+
+ }; // Per contract of the Proxy API, the "get" proxy handler must return the
+ // original value of the target if that value is declared read-only and
+ // non-configurable. For this reason, we create an object with the
+ // prototype set to `target` instead of using `target` directly.
+ // Otherwise we cannot return a custom object for APIs that
+ // are declared read-only and non-configurable, such as `chrome.devtools`.
+ //
+ // The proxy handlers themselves will still use the original `target`
+ // instead of the `proxyTarget`, so that the methods and properties are
+ // dereferenced via the original targets.
+
+ let proxyTarget = Object.create(target);
+ return new Proxy(proxyTarget, handlers);
+ };
+ /**
+ * Creates a set of wrapper functions for an event object, which handles
+ * wrapping of listener functions that those messages are passed.
+ *
+ * A single wrapper is created for each listener function, and stored in a
+ * map. Subsequent calls to `addListener`, `hasListener`, or `removeListener`
+ * retrieve the original wrapper, so that attempts to remove a
+ * previously-added listener work as expected.
+ *
+ * @param {DefaultWeakMap} wrapperMap
+ * A DefaultWeakMap object which will create the appropriate wrapper
+ * for a given listener function when one does not exist, and retrieve
+ * an existing one when it does.
+ *
+ * @returns {object}
+ */
+
+
+ const wrapEvent = wrapperMap => ({
+ addListener(target, listener, ...args) {
+ target.addListener(wrapperMap.get(listener), ...args);
+ },
+
+ hasListener(target, listener) {
+ return target.hasListener(wrapperMap.get(listener));
+ },
+
+ removeListener(target, listener) {
+ target.removeListener(wrapperMap.get(listener));
+ }
+
+ });
+
+ const onRequestFinishedWrappers = new DefaultWeakMap(listener => {
+ if (typeof listener !== "function") {
+ return listener;
+ }
+ /**
+ * Wraps an onRequestFinished listener function so that it will return a
+ * `getContent()` property which returns a `Promise` rather than using a
+ * callback API.
+ *
+ * @param {object} req
+ * The HAR entry object representing the network request.
+ */
+
+
+ return function onRequestFinished(req) {
+ const wrappedReq = wrapObject(req, {}
+ /* wrappers */
+ , {
+ getContent: {
+ minArgs: 0,
+ maxArgs: 0
+ }
+ });
+ listener(wrappedReq);
+ };
+ });
+ const onMessageWrappers = new DefaultWeakMap(listener => {
+ if (typeof listener !== "function") {
+ return listener;
+ }
+ /**
+ * Wraps a message listener function so that it may send responses based on
+ * its return value, rather than by returning a sentinel value and calling a
+ * callback. If the listener function returns a Promise, the response is
+ * sent when the promise either resolves or rejects.
+ *
+ * @param {*} message
+ * The message sent by the other end of the channel.
+ * @param {object} sender
+ * Details about the sender of the message.
+ * @param {function(*)} sendResponse
+ * A callback which, when called with an arbitrary argument, sends
+ * that value as a response.
+ * @returns {boolean}
+ * True if the wrapped listener returned a Promise, which will later
+ * yield a response. False otherwise.
+ */
+
+
+ return function onMessage(message, sender, sendResponse) {
+ let didCallSendResponse = false;
+ let wrappedSendResponse;
+ let sendResponsePromise = new Promise(resolve => {
+ wrappedSendResponse = function (response) {
+ didCallSendResponse = true;
+ resolve(response);
+ };
+ });
+ let result;
+
+ try {
+ result = listener(message, sender, wrappedSendResponse);
+ } catch (err) {
+ result = Promise.reject(err);
+ }
+
+ const isResultThenable = result !== true && isThenable(result); // If the listener didn't returned true or a Promise, or called
+ // wrappedSendResponse synchronously, we can exit earlier
+ // because there will be no response sent from this listener.
+
+ if (result !== true && !isResultThenable && !didCallSendResponse) {
+ return false;
+ } // A small helper to send the message if the promise resolves
+ // and an error if the promise rejects (a wrapped sendMessage has
+ // to translate the message into a resolved promise or a rejected
+ // promise).
+
+
+ const sendPromisedResult = promise => {
+ promise.then(msg => {
+ // send the message value.
+ sendResponse(msg);
+ }, error => {
+ // Send a JSON representation of the error if the rejected value
+ // is an instance of error, or the object itself otherwise.
+ let message;
+
+ if (error && (error instanceof Error || typeof error.message === "string")) {
+ message = error.message;
+ } else {
+ message = "An unexpected error occurred";
+ }
+
+ sendResponse({
+ __mozWebExtensionPolyfillReject__: true,
+ message
+ });
+ }).catch(err => {
+ // Print an error on the console if unable to send the response.
+ console.error("Failed to send onMessage rejected reply", err);
+ });
+ }; // If the listener returned a Promise, send the resolved value as a
+ // result, otherwise wait the promise related to the wrappedSendResponse
+ // callback to resolve and send it as a response.
+
+
+ if (isResultThenable) {
+ sendPromisedResult(result);
+ } else {
+ sendPromisedResult(sendResponsePromise);
+ } // Let Chrome know that the listener is replying.
+
+
+ return true;
+ };
+ });
+
+ const wrappedSendMessageCallback = ({
+ reject,
+ resolve
+ }, reply) => {
+ if (extensionAPIs.runtime.lastError) {
+ // Detect when none of the listeners replied to the sendMessage call and resolve
+ // the promise to undefined as in Firefox.
+ // See https://github.com/mozilla/webextension-polyfill/issues/130
+ if (extensionAPIs.runtime.lastError.message === CHROME_SEND_MESSAGE_CALLBACK_NO_RESPONSE_MESSAGE) {
+ resolve();
+ } else {
+ reject(new Error(extensionAPIs.runtime.lastError.message));
+ }
+ } else if (reply && reply.__mozWebExtensionPolyfillReject__) {
+ // Convert back the JSON representation of the error into
+ // an Error instance.
+ reject(new Error(reply.message));
+ } else {
+ resolve(reply);
+ }
+ };
+
+ const wrappedSendMessage = (name, metadata, apiNamespaceObj, ...args) => {
+ if (args.length < metadata.minArgs) {
+ throw new Error(`Expected at least ${metadata.minArgs} ${pluralizeArguments(metadata.minArgs)} for ${name}(), got ${args.length}`);
+ }
+
+ if (args.length > metadata.maxArgs) {
+ throw new Error(`Expected at most ${metadata.maxArgs} ${pluralizeArguments(metadata.maxArgs)} for ${name}(), got ${args.length}`);
+ }
+
+ return new Promise((resolve, reject) => {
+ const wrappedCb = wrappedSendMessageCallback.bind(null, {
+ resolve,
+ reject
+ });
+ args.push(wrappedCb);
+ apiNamespaceObj.sendMessage(...args);
+ });
+ };
+
+ const staticWrappers = {
+ devtools: {
+ network: {
+ onRequestFinished: wrapEvent(onRequestFinishedWrappers)
+ }
+ },
+ runtime: {
+ onMessage: wrapEvent(onMessageWrappers),
+ onMessageExternal: wrapEvent(onMessageWrappers),
+ sendMessage: wrappedSendMessage.bind(null, "sendMessage", {
+ minArgs: 1,
+ maxArgs: 3
+ })
+ },
+ tabs: {
+ sendMessage: wrappedSendMessage.bind(null, "sendMessage", {
+ minArgs: 2,
+ maxArgs: 3
+ })
+ }
+ };
+ const settingMetadata = {
+ clear: {
+ minArgs: 1,
+ maxArgs: 1
+ },
+ get: {
+ minArgs: 1,
+ maxArgs: 1
+ },
+ set: {
+ minArgs: 1,
+ maxArgs: 1
+ }
+ };
+ apiMetadata.privacy = {
+ network: {
+ "*": settingMetadata
+ },
+ services: {
+ "*": settingMetadata
+ },
+ websites: {
+ "*": settingMetadata
+ }
+ };
+ return wrapObject(extensionAPIs, staticWrappers, apiMetadata);
+ }; // The build process adds a UMD wrapper around this file, which makes the
+ // `module` variable available.
+
+
+ module.exports = wrapAPIs(chrome);
+ } else {
+ module.exports = globalThis.browser;
+ }
+});
+//# sourceMappingURL=browser-polyfill.js.map
diff --git a/webext-firefox/browser-polyfill.js.map b/webext-firefox/browser-polyfill.js.map
new file mode 100644
index 0000000..d5bd275
--- /dev/null
+++ b/webext-firefox/browser-polyfill.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"browser-polyfill.js","names":["globalThis","chrome","runtime","id","Error","browser","Object","getPrototypeOf","prototype","CHROME_SEND_MESSAGE_CALLBACK_NO_RESPONSE_MESSAGE","wrapAPIs","extensionAPIs","apiMetadata","keys","length","DefaultWeakMap","WeakMap","constructor","createItem","items","undefined","get","key","has","set","isThenable","value","then","makeCallback","promise","metadata","callbackArgs","lastError","reject","message","singleCallbackArg","resolve","pluralizeArguments","numArgs","wrapAsyncFunction","name","asyncFunctionWrapper","target","args","minArgs","maxArgs","Promise","fallbackToNoCallback","cbError","console","warn","noCallback","wrapMethod","method","wrapper","Proxy","apply","targetMethod","thisObj","call","hasOwnProperty","Function","bind","wrapObject","wrappers","cache","create","handlers","proxyTarget","prop","receiver","defineProperty","configurable","enumerable","desc","Reflect","deleteProperty","wrapEvent","wrapperMap","addListener","listener","hasListener","removeListener","onRequestFinishedWrappers","onRequestFinished","req","wrappedReq","getContent","onMessageWrappers","onMessage","sender","sendResponse","didCallSendResponse","wrappedSendResponse","sendResponsePromise","response","result","err","isResultThenable","sendPromisedResult","msg","error","__mozWebExtensionPolyfillReject__","catch","wrappedSendMessageCallback","reply","wrappedSendMessage","apiNamespaceObj","wrappedCb","push","sendMessage","staticWrappers","devtools","network","onMessageExternal","tabs","settingMetadata","clear","privacy","services","websites","module","exports"],"sources":["browser-polyfill.js"],"sourcesContent":["/* webextension-polyfill - v0.10.0 - Fri Aug 12 2022 19:42:44 */\n/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */\n/* vim: set sts=2 sw=2 et tw=80: */\n/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at http://mozilla.org/MPL/2.0/. */\n\"use strict\";\n\nif (!globalThis.chrome?.runtime?.id) {\n throw new Error(\"This script should only be loaded in a browser extension.\");\n}\n\nif (typeof globalThis.browser === \"undefined\" || Object.getPrototypeOf(globalThis.browser) !== Object.prototype) {\n const CHROME_SEND_MESSAGE_CALLBACK_NO_RESPONSE_MESSAGE = \"The message port closed before a response was received.\";\n\n // Wrapping the bulk of this polyfill in a one-time-use function is a minor\n // optimization for Firefox. Since Spidermonkey does not fully parse the\n // contents of a function until the first time it's called, and since it will\n // never actually need to be called, this allows the polyfill to be included\n // in Firefox nearly for free.\n const wrapAPIs = extensionAPIs => {\n // NOTE: apiMetadata is associated to the content of the api-metadata.json file\n // at build time by replacing the following \"include\" with the content of the\n // JSON file.\n const apiMetadata = {\n \"alarms\": {\n \"clear\": {\n \"minArgs\": 0,\n \"maxArgs\": 1\n },\n \"clearAll\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n },\n \"get\": {\n \"minArgs\": 0,\n \"maxArgs\": 1\n },\n \"getAll\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n }\n },\n \"bookmarks\": {\n \"create\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"get\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"getChildren\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"getRecent\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"getSubTree\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"getTree\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n },\n \"move\": {\n \"minArgs\": 2,\n \"maxArgs\": 2\n },\n \"remove\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"removeTree\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"search\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"update\": {\n \"minArgs\": 2,\n \"maxArgs\": 2\n }\n },\n \"browserAction\": {\n \"disable\": {\n \"minArgs\": 0,\n \"maxArgs\": 1,\n \"fallbackToNoCallback\": true\n },\n \"enable\": {\n \"minArgs\": 0,\n \"maxArgs\": 1,\n \"fallbackToNoCallback\": true\n },\n \"getBadgeBackgroundColor\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"getBadgeText\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"getPopup\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"getTitle\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"openPopup\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n },\n \"setBadgeBackgroundColor\": {\n \"minArgs\": 1,\n \"maxArgs\": 1,\n \"fallbackToNoCallback\": true\n },\n \"setBadgeText\": {\n \"minArgs\": 1,\n \"maxArgs\": 1,\n \"fallbackToNoCallback\": true\n },\n \"setIcon\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"setPopup\": {\n \"minArgs\": 1,\n \"maxArgs\": 1,\n \"fallbackToNoCallback\": true\n },\n \"setTitle\": {\n \"minArgs\": 1,\n \"maxArgs\": 1,\n \"fallbackToNoCallback\": true\n }\n },\n \"browsingData\": {\n \"remove\": {\n \"minArgs\": 2,\n \"maxArgs\": 2\n },\n \"removeCache\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"removeCookies\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"removeDownloads\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"removeFormData\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"removeHistory\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"removeLocalStorage\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"removePasswords\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"removePluginData\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"settings\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n }\n },\n \"commands\": {\n \"getAll\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n }\n },\n \"contextMenus\": {\n \"remove\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"removeAll\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n },\n \"update\": {\n \"minArgs\": 2,\n \"maxArgs\": 2\n }\n },\n \"cookies\": {\n \"get\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"getAll\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"getAllCookieStores\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n },\n \"remove\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"set\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n }\n },\n \"devtools\": {\n \"inspectedWindow\": {\n \"eval\": {\n \"minArgs\": 1,\n \"maxArgs\": 2,\n \"singleCallbackArg\": false\n }\n },\n \"panels\": {\n \"create\": {\n \"minArgs\": 3,\n \"maxArgs\": 3,\n \"singleCallbackArg\": true\n },\n \"elements\": {\n \"createSidebarPane\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n }\n }\n }\n },\n \"downloads\": {\n \"cancel\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"download\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"erase\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"getFileIcon\": {\n \"minArgs\": 1,\n \"maxArgs\": 2\n },\n \"open\": {\n \"minArgs\": 1,\n \"maxArgs\": 1,\n \"fallbackToNoCallback\": true\n },\n \"pause\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"removeFile\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"resume\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"search\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"show\": {\n \"minArgs\": 1,\n \"maxArgs\": 1,\n \"fallbackToNoCallback\": true\n }\n },\n \"extension\": {\n \"isAllowedFileSchemeAccess\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n },\n \"isAllowedIncognitoAccess\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n }\n },\n \"history\": {\n \"addUrl\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"deleteAll\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n },\n \"deleteRange\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"deleteUrl\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"getVisits\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"search\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n }\n },\n \"i18n\": {\n \"detectLanguage\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"getAcceptLanguages\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n }\n },\n \"identity\": {\n \"launchWebAuthFlow\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n }\n },\n \"idle\": {\n \"queryState\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n }\n },\n \"management\": {\n \"get\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"getAll\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n },\n \"getSelf\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n },\n \"setEnabled\": {\n \"minArgs\": 2,\n \"maxArgs\": 2\n },\n \"uninstallSelf\": {\n \"minArgs\": 0,\n \"maxArgs\": 1\n }\n },\n \"notifications\": {\n \"clear\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"create\": {\n \"minArgs\": 1,\n \"maxArgs\": 2\n },\n \"getAll\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n },\n \"getPermissionLevel\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n },\n \"update\": {\n \"minArgs\": 2,\n \"maxArgs\": 2\n }\n },\n \"pageAction\": {\n \"getPopup\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"getTitle\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"hide\": {\n \"minArgs\": 1,\n \"maxArgs\": 1,\n \"fallbackToNoCallback\": true\n },\n \"setIcon\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"setPopup\": {\n \"minArgs\": 1,\n \"maxArgs\": 1,\n \"fallbackToNoCallback\": true\n },\n \"setTitle\": {\n \"minArgs\": 1,\n \"maxArgs\": 1,\n \"fallbackToNoCallback\": true\n },\n \"show\": {\n \"minArgs\": 1,\n \"maxArgs\": 1,\n \"fallbackToNoCallback\": true\n }\n },\n \"permissions\": {\n \"contains\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"getAll\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n },\n \"remove\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"request\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n }\n },\n \"runtime\": {\n \"getBackgroundPage\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n },\n \"getPlatformInfo\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n },\n \"openOptionsPage\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n },\n \"requestUpdateCheck\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n },\n \"sendMessage\": {\n \"minArgs\": 1,\n \"maxArgs\": 3\n },\n \"sendNativeMessage\": {\n \"minArgs\": 2,\n \"maxArgs\": 2\n },\n \"setUninstallURL\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n }\n },\n \"sessions\": {\n \"getDevices\": {\n \"minArgs\": 0,\n \"maxArgs\": 1\n },\n \"getRecentlyClosed\": {\n \"minArgs\": 0,\n \"maxArgs\": 1\n },\n \"restore\": {\n \"minArgs\": 0,\n \"maxArgs\": 1\n }\n },\n \"storage\": {\n \"local\": {\n \"clear\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n },\n \"get\": {\n \"minArgs\": 0,\n \"maxArgs\": 1\n },\n \"getBytesInUse\": {\n \"minArgs\": 0,\n \"maxArgs\": 1\n },\n \"remove\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"set\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n }\n },\n \"managed\": {\n \"get\": {\n \"minArgs\": 0,\n \"maxArgs\": 1\n },\n \"getBytesInUse\": {\n \"minArgs\": 0,\n \"maxArgs\": 1\n }\n },\n \"sync\": {\n \"clear\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n },\n \"get\": {\n \"minArgs\": 0,\n \"maxArgs\": 1\n },\n \"getBytesInUse\": {\n \"minArgs\": 0,\n \"maxArgs\": 1\n },\n \"remove\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"set\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n }\n }\n },\n \"tabs\": {\n \"captureVisibleTab\": {\n \"minArgs\": 0,\n \"maxArgs\": 2\n },\n \"create\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"detectLanguage\": {\n \"minArgs\": 0,\n \"maxArgs\": 1\n },\n \"discard\": {\n \"minArgs\": 0,\n \"maxArgs\": 1\n },\n \"duplicate\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"executeScript\": {\n \"minArgs\": 1,\n \"maxArgs\": 2\n },\n \"get\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"getCurrent\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n },\n \"getZoom\": {\n \"minArgs\": 0,\n \"maxArgs\": 1\n },\n \"getZoomSettings\": {\n \"minArgs\": 0,\n \"maxArgs\": 1\n },\n \"goBack\": {\n \"minArgs\": 0,\n \"maxArgs\": 1\n },\n \"goForward\": {\n \"minArgs\": 0,\n \"maxArgs\": 1\n },\n \"highlight\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"insertCSS\": {\n \"minArgs\": 1,\n \"maxArgs\": 2\n },\n \"move\": {\n \"minArgs\": 2,\n \"maxArgs\": 2\n },\n \"query\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"reload\": {\n \"minArgs\": 0,\n \"maxArgs\": 2\n },\n \"remove\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"removeCSS\": {\n \"minArgs\": 1,\n \"maxArgs\": 2\n },\n \"sendMessage\": {\n \"minArgs\": 2,\n \"maxArgs\": 3\n },\n \"setZoom\": {\n \"minArgs\": 1,\n \"maxArgs\": 2\n },\n \"setZoomSettings\": {\n \"minArgs\": 1,\n \"maxArgs\": 2\n },\n \"update\": {\n \"minArgs\": 1,\n \"maxArgs\": 2\n }\n },\n \"topSites\": {\n \"get\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n }\n },\n \"webNavigation\": {\n \"getAllFrames\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"getFrame\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n }\n },\n \"webRequest\": {\n \"handlerBehaviorChanged\": {\n \"minArgs\": 0,\n \"maxArgs\": 0\n }\n },\n \"windows\": {\n \"create\": {\n \"minArgs\": 0,\n \"maxArgs\": 1\n },\n \"get\": {\n \"minArgs\": 1,\n \"maxArgs\": 2\n },\n \"getAll\": {\n \"minArgs\": 0,\n \"maxArgs\": 1\n },\n \"getCurrent\": {\n \"minArgs\": 0,\n \"maxArgs\": 1\n },\n \"getLastFocused\": {\n \"minArgs\": 0,\n \"maxArgs\": 1\n },\n \"remove\": {\n \"minArgs\": 1,\n \"maxArgs\": 1\n },\n \"update\": {\n \"minArgs\": 2,\n \"maxArgs\": 2\n }\n }\n };\n\n if (Object.keys(apiMetadata).length === 0) {\n throw new Error(\"api-metadata.json has not been included in browser-polyfill\");\n }\n\n /**\n * A WeakMap subclass which creates and stores a value for any key which does\n * not exist when accessed, but behaves exactly as an ordinary WeakMap\n * otherwise.\n *\n * @param {function} createItem\n * A function which will be called in order to create the value for any\n * key which does not exist, the first time it is accessed. The\n * function receives, as its only argument, the key being created.\n */\n class DefaultWeakMap extends WeakMap {\n constructor(createItem, items = undefined) {\n super(items);\n this.createItem = createItem;\n }\n\n get(key) {\n if (!this.has(key)) {\n this.set(key, this.createItem(key));\n }\n\n return super.get(key);\n }\n }\n\n /**\n * Returns true if the given object is an object with a `then` method, and can\n * therefore be assumed to behave as a Promise.\n *\n * @param {*} value The value to test.\n * @returns {boolean} True if the value is thenable.\n */\n const isThenable = value => {\n return value && typeof value === \"object\" && typeof value.then === \"function\";\n };\n\n /**\n * Creates and returns a function which, when called, will resolve or reject\n * the given promise based on how it is called:\n *\n * - If, when called, `chrome.runtime.lastError` contains a non-null object,\n * the promise is rejected with that value.\n * - If the function is called with exactly one argument, the promise is\n * resolved to that value.\n * - Otherwise, the promise is resolved to an array containing all of the\n * function's arguments.\n *\n * @param {object} promise\n * An object containing the resolution and rejection functions of a\n * promise.\n * @param {function} promise.resolve\n * The promise's resolution function.\n * @param {function} promise.reject\n * The promise's rejection function.\n * @param {object} metadata\n * Metadata about the wrapped method which has created the callback.\n * @param {boolean} metadata.singleCallbackArg\n * Whether or not the promise is resolved with only the first\n * argument of the callback, alternatively an array of all the\n * callback arguments is resolved. By default, if the callback\n * function is invoked with only a single argument, that will be\n * resolved to the promise, while all arguments will be resolved as\n * an array if multiple are given.\n *\n * @returns {function}\n * The generated callback function.\n */\n const makeCallback = (promise, metadata) => {\n return (...callbackArgs) => {\n if (extensionAPIs.runtime.lastError) {\n promise.reject(new Error(extensionAPIs.runtime.lastError.message));\n } else if (metadata.singleCallbackArg ||\n (callbackArgs.length <= 1 && metadata.singleCallbackArg !== false)) {\n promise.resolve(callbackArgs[0]);\n } else {\n promise.resolve(callbackArgs);\n }\n };\n };\n\n const pluralizeArguments = (numArgs) => numArgs == 1 ? \"argument\" : \"arguments\";\n\n /**\n * Creates a wrapper function for a method with the given name and metadata.\n *\n * @param {string} name\n * The name of the method which is being wrapped.\n * @param {object} metadata\n * Metadata about the method being wrapped.\n * @param {integer} metadata.minArgs\n * The minimum number of arguments which must be passed to the\n * function. If called with fewer than this number of arguments, the\n * wrapper will raise an exception.\n * @param {integer} metadata.maxArgs\n * The maximum number of arguments which may be passed to the\n * function. If called with more than this number of arguments, the\n * wrapper will raise an exception.\n * @param {boolean} metadata.singleCallbackArg\n * Whether or not the promise is resolved with only the first\n * argument of the callback, alternatively an array of all the\n * callback arguments is resolved. By default, if the callback\n * function is invoked with only a single argument, that will be\n * resolved to the promise, while all arguments will be resolved as\n * an array if multiple are given.\n *\n * @returns {function(object, ...*)}\n * The generated wrapper function.\n */\n const wrapAsyncFunction = (name, metadata) => {\n return function asyncFunctionWrapper(target, ...args) {\n if (args.length < metadata.minArgs) {\n throw new Error(`Expected at least ${metadata.minArgs} ${pluralizeArguments(metadata.minArgs)} for ${name}(), got ${args.length}`);\n }\n\n if (args.length > metadata.maxArgs) {\n throw new Error(`Expected at most ${metadata.maxArgs} ${pluralizeArguments(metadata.maxArgs)} for ${name}(), got ${args.length}`);\n }\n\n return new Promise((resolve, reject) => {\n if (metadata.fallbackToNoCallback) {\n // This API method has currently no callback on Chrome, but it return a promise on Firefox,\n // and so the polyfill will try to call it with a callback first, and it will fallback\n // to not passing the callback if the first call fails.\n try {\n target[name](...args, makeCallback({resolve, reject}, metadata));\n } catch (cbError) {\n console.warn(`${name} API method doesn't seem to support the callback parameter, ` +\n \"falling back to call it without a callback: \", cbError);\n\n target[name](...args);\n\n // Update the API method metadata, so that the next API calls will not try to\n // use the unsupported callback anymore.\n metadata.fallbackToNoCallback = false;\n metadata.noCallback = true;\n\n resolve();\n }\n } else if (metadata.noCallback) {\n target[name](...args);\n resolve();\n } else {\n target[name](...args, makeCallback({resolve, reject}, metadata));\n }\n });\n };\n };\n\n /**\n * Wraps an existing method of the target object, so that calls to it are\n * intercepted by the given wrapper function. The wrapper function receives,\n * as its first argument, the original `target` object, followed by each of\n * the arguments passed to the original method.\n *\n * @param {object} target\n * The original target object that the wrapped method belongs to.\n * @param {function} method\n * The method being wrapped. This is used as the target of the Proxy\n * object which is created to wrap the method.\n * @param {function} wrapper\n * The wrapper function which is called in place of a direct invocation\n * of the wrapped method.\n *\n * @returns {Proxy}\n * A Proxy object for the given method, which invokes the given wrapper\n * method in its place.\n */\n const wrapMethod = (target, method, wrapper) => {\n return new Proxy(method, {\n apply(targetMethod, thisObj, args) {\n return wrapper.call(thisObj, target, ...args);\n },\n });\n };\n\n let hasOwnProperty = Function.call.bind(Object.prototype.hasOwnProperty);\n\n /**\n * Wraps an object in a Proxy which intercepts and wraps certain methods\n * based on the given `wrappers` and `metadata` objects.\n *\n * @param {object} target\n * The target object to wrap.\n *\n * @param {object} [wrappers = {}]\n * An object tree containing wrapper functions for special cases. Any\n * function present in this object tree is called in place of the\n * method in the same location in the `target` object tree. These\n * wrapper methods are invoked as described in {@see wrapMethod}.\n *\n * @param {object} [metadata = {}]\n * An object tree containing metadata used to automatically generate\n * Promise-based wrapper functions for asynchronous. Any function in\n * the `target` object tree which has a corresponding metadata object\n * in the same location in the `metadata` tree is replaced with an\n * automatically-generated wrapper function, as described in\n * {@see wrapAsyncFunction}\n *\n * @returns {Proxy}\n */\n const wrapObject = (target, wrappers = {}, metadata = {}) => {\n let cache = Object.create(null);\n let handlers = {\n has(proxyTarget, prop) {\n return prop in target || prop in cache;\n },\n\n get(proxyTarget, prop, receiver) {\n if (prop in cache) {\n return cache[prop];\n }\n\n if (!(prop in target)) {\n return undefined;\n }\n\n let value = target[prop];\n\n if (typeof value === \"function\") {\n // This is a method on the underlying object. Check if we need to do\n // any wrapping.\n\n if (typeof wrappers[prop] === \"function\") {\n // We have a special-case wrapper for this method.\n value = wrapMethod(target, target[prop], wrappers[prop]);\n } else if (hasOwnProperty(metadata, prop)) {\n // This is an async method that we have metadata for. Create a\n // Promise wrapper for it.\n let wrapper = wrapAsyncFunction(prop, metadata[prop]);\n value = wrapMethod(target, target[prop], wrapper);\n } else {\n // This is a method that we don't know or care about. Return the\n // original method, bound to the underlying object.\n value = value.bind(target);\n }\n } else if (typeof value === \"object\" && value !== null &&\n (hasOwnProperty(wrappers, prop) ||\n hasOwnProperty(metadata, prop))) {\n // This is an object that we need to do some wrapping for the children\n // of. Create a sub-object wrapper for it with the appropriate child\n // metadata.\n value = wrapObject(value, wrappers[prop], metadata[prop]);\n } else if (hasOwnProperty(metadata, \"*\")) {\n // Wrap all properties in * namespace.\n value = wrapObject(value, wrappers[prop], metadata[\"*\"]);\n } else {\n // We don't need to do any wrapping for this property,\n // so just forward all access to the underlying object.\n Object.defineProperty(cache, prop, {\n configurable: true,\n enumerable: true,\n get() {\n return target[prop];\n },\n set(value) {\n target[prop] = value;\n },\n });\n\n return value;\n }\n\n cache[prop] = value;\n return value;\n },\n\n set(proxyTarget, prop, value, receiver) {\n if (prop in cache) {\n cache[prop] = value;\n } else {\n target[prop] = value;\n }\n return true;\n },\n\n defineProperty(proxyTarget, prop, desc) {\n return Reflect.defineProperty(cache, prop, desc);\n },\n\n deleteProperty(proxyTarget, prop) {\n return Reflect.deleteProperty(cache, prop);\n },\n };\n\n // Per contract of the Proxy API, the \"get\" proxy handler must return the\n // original value of the target if that value is declared read-only and\n // non-configurable. For this reason, we create an object with the\n // prototype set to `target` instead of using `target` directly.\n // Otherwise we cannot return a custom object for APIs that\n // are declared read-only and non-configurable, such as `chrome.devtools`.\n //\n // The proxy handlers themselves will still use the original `target`\n // instead of the `proxyTarget`, so that the methods and properties are\n // dereferenced via the original targets.\n let proxyTarget = Object.create(target);\n return new Proxy(proxyTarget, handlers);\n };\n\n /**\n * Creates a set of wrapper functions for an event object, which handles\n * wrapping of listener functions that those messages are passed.\n *\n * A single wrapper is created for each listener function, and stored in a\n * map. Subsequent calls to `addListener`, `hasListener`, or `removeListener`\n * retrieve the original wrapper, so that attempts to remove a\n * previously-added listener work as expected.\n *\n * @param {DefaultWeakMap} wrapperMap\n * A DefaultWeakMap object which will create the appropriate wrapper\n * for a given listener function when one does not exist, and retrieve\n * an existing one when it does.\n *\n * @returns {object}\n */\n const wrapEvent = wrapperMap => ({\n addListener(target, listener, ...args) {\n target.addListener(wrapperMap.get(listener), ...args);\n },\n\n hasListener(target, listener) {\n return target.hasListener(wrapperMap.get(listener));\n },\n\n removeListener(target, listener) {\n target.removeListener(wrapperMap.get(listener));\n },\n });\n\n const onRequestFinishedWrappers = new DefaultWeakMap(listener => {\n if (typeof listener !== \"function\") {\n return listener;\n }\n\n /**\n * Wraps an onRequestFinished listener function so that it will return a\n * `getContent()` property which returns a `Promise` rather than using a\n * callback API.\n *\n * @param {object} req\n * The HAR entry object representing the network request.\n */\n return function onRequestFinished(req) {\n const wrappedReq = wrapObject(req, {} /* wrappers */, {\n getContent: {\n minArgs: 0,\n maxArgs: 0,\n },\n });\n listener(wrappedReq);\n };\n });\n\n const onMessageWrappers = new DefaultWeakMap(listener => {\n if (typeof listener !== \"function\") {\n return listener;\n }\n\n /**\n * Wraps a message listener function so that it may send responses based on\n * its return value, rather than by returning a sentinel value and calling a\n * callback. If the listener function returns a Promise, the response is\n * sent when the promise either resolves or rejects.\n *\n * @param {*} message\n * The message sent by the other end of the channel.\n * @param {object} sender\n * Details about the sender of the message.\n * @param {function(*)} sendResponse\n * A callback which, when called with an arbitrary argument, sends\n * that value as a response.\n * @returns {boolean}\n * True if the wrapped listener returned a Promise, which will later\n * yield a response. False otherwise.\n */\n return function onMessage(message, sender, sendResponse) {\n let didCallSendResponse = false;\n\n let wrappedSendResponse;\n let sendResponsePromise = new Promise(resolve => {\n wrappedSendResponse = function(response) {\n didCallSendResponse = true;\n resolve(response);\n };\n });\n\n let result;\n try {\n result = listener(message, sender, wrappedSendResponse);\n } catch (err) {\n result = Promise.reject(err);\n }\n\n const isResultThenable = result !== true && isThenable(result);\n\n // If the listener didn't returned true or a Promise, or called\n // wrappedSendResponse synchronously, we can exit earlier\n // because there will be no response sent from this listener.\n if (result !== true && !isResultThenable && !didCallSendResponse) {\n return false;\n }\n\n // A small helper to send the message if the promise resolves\n // and an error if the promise rejects (a wrapped sendMessage has\n // to translate the message into a resolved promise or a rejected\n // promise).\n const sendPromisedResult = (promise) => {\n promise.then(msg => {\n // send the message value.\n sendResponse(msg);\n }, error => {\n // Send a JSON representation of the error if the rejected value\n // is an instance of error, or the object itself otherwise.\n let message;\n if (error && (error instanceof Error ||\n typeof error.message === \"string\")) {\n message = error.message;\n } else {\n message = \"An unexpected error occurred\";\n }\n\n sendResponse({\n __mozWebExtensionPolyfillReject__: true,\n message,\n });\n }).catch(err => {\n // Print an error on the console if unable to send the response.\n console.error(\"Failed to send onMessage rejected reply\", err);\n });\n };\n\n // If the listener returned a Promise, send the resolved value as a\n // result, otherwise wait the promise related to the wrappedSendResponse\n // callback to resolve and send it as a response.\n if (isResultThenable) {\n sendPromisedResult(result);\n } else {\n sendPromisedResult(sendResponsePromise);\n }\n\n // Let Chrome know that the listener is replying.\n return true;\n };\n });\n\n const wrappedSendMessageCallback = ({reject, resolve}, reply) => {\n if (extensionAPIs.runtime.lastError) {\n // Detect when none of the listeners replied to the sendMessage call and resolve\n // the promise to undefined as in Firefox.\n // See https://github.com/mozilla/webextension-polyfill/issues/130\n if (extensionAPIs.runtime.lastError.message === CHROME_SEND_MESSAGE_CALLBACK_NO_RESPONSE_MESSAGE) {\n resolve();\n } else {\n reject(new Error(extensionAPIs.runtime.lastError.message));\n }\n } else if (reply && reply.__mozWebExtensionPolyfillReject__) {\n // Convert back the JSON representation of the error into\n // an Error instance.\n reject(new Error(reply.message));\n } else {\n resolve(reply);\n }\n };\n\n const wrappedSendMessage = (name, metadata, apiNamespaceObj, ...args) => {\n if (args.length < metadata.minArgs) {\n throw new Error(`Expected at least ${metadata.minArgs} ${pluralizeArguments(metadata.minArgs)} for ${name}(), got ${args.length}`);\n }\n\n if (args.length > metadata.maxArgs) {\n throw new Error(`Expected at most ${metadata.maxArgs} ${pluralizeArguments(metadata.maxArgs)} for ${name}(), got ${args.length}`);\n }\n\n return new Promise((resolve, reject) => {\n const wrappedCb = wrappedSendMessageCallback.bind(null, {resolve, reject});\n args.push(wrappedCb);\n apiNamespaceObj.sendMessage(...args);\n });\n };\n\n const staticWrappers = {\n devtools: {\n network: {\n onRequestFinished: wrapEvent(onRequestFinishedWrappers),\n },\n },\n runtime: {\n onMessage: wrapEvent(onMessageWrappers),\n onMessageExternal: wrapEvent(onMessageWrappers),\n sendMessage: wrappedSendMessage.bind(null, \"sendMessage\", {minArgs: 1, maxArgs: 3}),\n },\n tabs: {\n sendMessage: wrappedSendMessage.bind(null, \"sendMessage\", {minArgs: 2, maxArgs: 3}),\n },\n };\n const settingMetadata = {\n clear: {minArgs: 1, maxArgs: 1},\n get: {minArgs: 1, maxArgs: 1},\n set: {minArgs: 1, maxArgs: 1},\n };\n apiMetadata.privacy = {\n network: {\"*\": settingMetadata},\n services: {\"*\": settingMetadata},\n websites: {\"*\": settingMetadata},\n };\n\n return wrapObject(extensionAPIs, staticWrappers, apiMetadata);\n };\n\n // The build process adds a UMD wrapper around this file, which makes the\n // `module` variable available.\n module.exports = wrapAPIs(chrome);\n} else {\n module.exports = globalThis.browser;\n}\n"],"mappings":"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"}
\ No newline at end of file
diff --git a/webext-firefox/icons/icon48.png b/webext-firefox/icons/icon48.png
new file mode 100644
index 0000000..6808983
Binary files /dev/null and b/webext-firefox/icons/icon48.png differ
diff --git a/webext-firefox/icons/icon96.png b/webext-firefox/icons/icon96.png
new file mode 100644
index 0000000..a32d706
Binary files /dev/null and b/webext-firefox/icons/icon96.png differ
diff --git a/webext-firefox/manifest.json b/webext-firefox/manifest.json
new file mode 100644
index 0000000..af72b4f
--- /dev/null
+++ b/webext-firefox/manifest.json
@@ -0,0 +1,40 @@
+{
+ "manifest_version": 2,
+ "name": "LinkDown for Browsers",
+ "description": "LinkDown's Add-on for browsers, download the favourite videos you like within a click!",
+ "version": "0.2",
+ "icons": {
+ "48": "icons/icon48.png",
+ "96": "icons/icon96.png"
+ },
+ "default_locale": "en",
+ "permissions": ["", "contextMenus", "cookies", "nativeMessaging"],
+ "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'",
+ "background": {
+ "scripts": ["browser-polyfill.js", "background.js"]
+ },
+ "content_scripts": [
+ {
+ "matches": [""],
+ "js": ["browser-polyfill.js", "background.js"]
+ }
+ ],
+ "browser_action": {
+ "default_icon": "icons/icon48.png",
+ "default_title": "LinkDown for Browsers"
+ },
+ "applications": {
+ "gecko": {
+ "id": "linkdown_for_browsers@linkscape.app",
+ "strict_min_version": "57.0"
+ }
+ },
+ "browser_specific_settings": {
+ "gecko": {
+ "id": "linkdown_for_browsers@linkscape.app"
+ },
+ "chrome": {
+ "id": "lbgiocaippfiilgkgppmlonoaddljidk"
+ }
+ }
+}