├── source-code ├── HOW-TO-USE.txt ├── assets │ └── icons │ │ ├── 16.png │ │ ├── 32.png │ │ ├── 48.png │ │ ├── 64.png │ │ ├── 128.png │ │ ├── Original.png │ │ ├── disabled │ │ ├── 128.png │ │ ├── 16.png │ │ ├── 32.png │ │ ├── 48.png │ │ ├── 64.png │ │ └── Original.png │ │ └── enabled │ │ ├── 128.png │ │ ├── 16.png │ │ ├── 32.png │ │ ├── 48.png │ │ ├── 64.png │ │ └── Original.png ├── background.js ├── core │ ├── inject-to.js │ ├── extension.js │ ├── injection-types.js │ ├── injection-process.js │ └── functions.js ├── inject-to │ ├── 192.168.1.1.js │ ├── twitter.com.js │ ├── www.ea.com.js │ ├── www.youtube.com.js │ ├── linktr.ee.js │ ├── 127.0.0.1.js │ ├── www.facebook.com.js │ ├── www.wikipedia.org.js │ ├── chat.openai.com.js │ ├── @Examples.js │ └── example.com.js └── manifest.json ├── images ├── background.js.png ├── How-To-Debug-Extension.gif ├── Chrome-Storage-Sync-vs-Local.png ├── Project-Structure-Explanation.png └── InjectorPlus-Chrome-Extension-Manifest-V3.png ├── LICENSE └── README.md /source-code/HOW-TO-USE.txt: -------------------------------------------------------------------------------- 1 | Visit: https://www.youtube.com/@gilgeekify | https://youtu.be/13yCU0tQ4cY 2 | -------------------------------------------------------------------------------- /images/background.js.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saeedkohansal/InjectorPlus-Chrome-Extension-Manifest-V3/HEAD/images/background.js.png -------------------------------------------------------------------------------- /source-code/assets/icons/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saeedkohansal/InjectorPlus-Chrome-Extension-Manifest-V3/HEAD/source-code/assets/icons/16.png -------------------------------------------------------------------------------- /source-code/assets/icons/32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saeedkohansal/InjectorPlus-Chrome-Extension-Manifest-V3/HEAD/source-code/assets/icons/32.png -------------------------------------------------------------------------------- /source-code/assets/icons/48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saeedkohansal/InjectorPlus-Chrome-Extension-Manifest-V3/HEAD/source-code/assets/icons/48.png -------------------------------------------------------------------------------- /source-code/assets/icons/64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saeedkohansal/InjectorPlus-Chrome-Extension-Manifest-V3/HEAD/source-code/assets/icons/64.png -------------------------------------------------------------------------------- /images/How-To-Debug-Extension.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saeedkohansal/InjectorPlus-Chrome-Extension-Manifest-V3/HEAD/images/How-To-Debug-Extension.gif -------------------------------------------------------------------------------- /source-code/assets/icons/128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saeedkohansal/InjectorPlus-Chrome-Extension-Manifest-V3/HEAD/source-code/assets/icons/128.png -------------------------------------------------------------------------------- /source-code/assets/icons/Original.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saeedkohansal/InjectorPlus-Chrome-Extension-Manifest-V3/HEAD/source-code/assets/icons/Original.png -------------------------------------------------------------------------------- /images/Chrome-Storage-Sync-vs-Local.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saeedkohansal/InjectorPlus-Chrome-Extension-Manifest-V3/HEAD/images/Chrome-Storage-Sync-vs-Local.png -------------------------------------------------------------------------------- /images/Project-Structure-Explanation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saeedkohansal/InjectorPlus-Chrome-Extension-Manifest-V3/HEAD/images/Project-Structure-Explanation.png -------------------------------------------------------------------------------- /source-code/assets/icons/disabled/128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saeedkohansal/InjectorPlus-Chrome-Extension-Manifest-V3/HEAD/source-code/assets/icons/disabled/128.png -------------------------------------------------------------------------------- /source-code/assets/icons/disabled/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saeedkohansal/InjectorPlus-Chrome-Extension-Manifest-V3/HEAD/source-code/assets/icons/disabled/16.png -------------------------------------------------------------------------------- /source-code/assets/icons/disabled/32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saeedkohansal/InjectorPlus-Chrome-Extension-Manifest-V3/HEAD/source-code/assets/icons/disabled/32.png -------------------------------------------------------------------------------- /source-code/assets/icons/disabled/48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saeedkohansal/InjectorPlus-Chrome-Extension-Manifest-V3/HEAD/source-code/assets/icons/disabled/48.png -------------------------------------------------------------------------------- /source-code/assets/icons/disabled/64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saeedkohansal/InjectorPlus-Chrome-Extension-Manifest-V3/HEAD/source-code/assets/icons/disabled/64.png -------------------------------------------------------------------------------- /source-code/assets/icons/enabled/128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saeedkohansal/InjectorPlus-Chrome-Extension-Manifest-V3/HEAD/source-code/assets/icons/enabled/128.png -------------------------------------------------------------------------------- /source-code/assets/icons/enabled/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saeedkohansal/InjectorPlus-Chrome-Extension-Manifest-V3/HEAD/source-code/assets/icons/enabled/16.png -------------------------------------------------------------------------------- /source-code/assets/icons/enabled/32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saeedkohansal/InjectorPlus-Chrome-Extension-Manifest-V3/HEAD/source-code/assets/icons/enabled/32.png -------------------------------------------------------------------------------- /source-code/assets/icons/enabled/48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saeedkohansal/InjectorPlus-Chrome-Extension-Manifest-V3/HEAD/source-code/assets/icons/enabled/48.png -------------------------------------------------------------------------------- /source-code/assets/icons/enabled/64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saeedkohansal/InjectorPlus-Chrome-Extension-Manifest-V3/HEAD/source-code/assets/icons/enabled/64.png -------------------------------------------------------------------------------- /source-code/assets/icons/disabled/Original.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saeedkohansal/InjectorPlus-Chrome-Extension-Manifest-V3/HEAD/source-code/assets/icons/disabled/Original.png -------------------------------------------------------------------------------- /source-code/assets/icons/enabled/Original.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saeedkohansal/InjectorPlus-Chrome-Extension-Manifest-V3/HEAD/source-code/assets/icons/enabled/Original.png -------------------------------------------------------------------------------- /images/InjectorPlus-Chrome-Extension-Manifest-V3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saeedkohansal/InjectorPlus-Chrome-Extension-Manifest-V3/HEAD/images/InjectorPlus-Chrome-Extension-Manifest-V3.png -------------------------------------------------------------------------------- /source-code/background.js: -------------------------------------------------------------------------------- 1 | // The list of target websites 2 | importScripts('core/inject-to.js'); 3 | 4 | // The extension functions 5 | importScripts('core/functions.js'); 6 | 7 | // The extension actions controller 8 | importScripts('core/extension.js'); 9 | 10 | // The injection process 11 | importScripts('core/injection-process.js'); 12 | -------------------------------------------------------------------------------- /source-code/core/inject-to.js: -------------------------------------------------------------------------------- 1 | /* ••••••••••••••••••••••••••••••••••••••••••••••••••••• */ 2 | /* Put your target websites inside the `injectTo` array, */ 3 | /* they should be in the 'hostname' format. •••••••••••• */ 4 | /* ••••••••••••••••••••••••••••••••••••••••••••••••••••• */ 5 | const injectTo = [ 6 | 'linktr.ee', 7 | 'example.com', 8 | 'twitter.com', 9 | 'chat.openai.com', 10 | 'www.ea.com', 11 | 'www.youtube.com', 12 | 'www.facebook.com', 13 | 'www.wikipedia.org', 14 | '127.0.0.1', 15 | '192.168.1.1' 16 | ]; 17 | -------------------------------------------------------------------------------- /source-code/inject-to/192.168.1.1.js: -------------------------------------------------------------------------------- 1 | /* •••••••••••••••••••••••••••••••••••••••••••••••••••••••••••• */ 2 | /* Right here, you have access to the `injector` global object. */ 3 | /* So you can inject unlimited codes using: ••••••••••••••••••• */ 4 | /* injector.injectInlineJs(); ••••••••••••••••••••••••••••••••• */ 5 | /* injector.injectExternalJs(); ••••••••••••••••••••••••••••••• */ 6 | /* injector.injectInternalCss(); •••••••••••••••••••••••••••••• */ 7 | /* injector.injectExternalCss(); •••••••••••••••••••••••••••••• */ 8 | /* Anyway it's OPTIONAL, you can also define your logic. •••••• */ 9 | /* •••••••••••••••••••••••••••••••••••••••••••••••••••••••••••• */ 10 | injector.injectInternalCss(` 11 | html { 12 | filter: blur(3px); 13 | } 14 | `); 15 | -------------------------------------------------------------------------------- /source-code/inject-to/twitter.com.js: -------------------------------------------------------------------------------- 1 | /* •••••••••••••••••••••••••••••••••••••••••••••••••••••••••••• */ 2 | /* Right here, you have access to the `injector` global object. */ 3 | /* So you can inject unlimited codes using: ••••••••••••••••••• */ 4 | /* injector.injectInlineJs(); ••••••••••••••••••••••••••••••••• */ 5 | /* injector.injectExternalJs(); ••••••••••••••••••••••••••••••• */ 6 | /* injector.injectInternalCss(); •••••••••••••••••••••••••••••• */ 7 | /* injector.injectExternalCss(); •••••••••••••••••••••••••••••• */ 8 | /* Anyway it's OPTIONAL, you can also define your logic. •••••• */ 9 | /* •••••••••••••••••••••••••••••••••••••••••••••••••••••••••••• */ 10 | injector.injectInternalCss(` 11 | html { 12 | filter: invert(1); 13 | } 14 | `); 15 | -------------------------------------------------------------------------------- /source-code/inject-to/www.ea.com.js: -------------------------------------------------------------------------------- 1 | /* •••••••••••••••••••••••••••••••••••••••••••••••••••••••••••• */ 2 | /* Right here, you have access to the `injector` global object. */ 3 | /* So you can inject unlimited codes using: ••••••••••••••••••• */ 4 | /* injector.injectInlineJs(); ••••••••••••••••••••••••••••••••• */ 5 | /* injector.injectExternalJs(); ••••••••••••••••••••••••••••••• */ 6 | /* injector.injectInternalCss(); •••••••••••••••••••••••••••••• */ 7 | /* injector.injectExternalCss(); •••••••••••••••••••••••••••••• */ 8 | /* Anyway it's OPTIONAL, you can also define your logic. •••••• */ 9 | /* •••••••••••••••••••••••••••••••••••••••••••••••••••••••••••• */ 10 | injector.injectInternalCss(` 11 | html { 12 | filter: invert(1) saturate(4.5); 13 | } 14 | `); 15 | -------------------------------------------------------------------------------- /source-code/inject-to/www.youtube.com.js: -------------------------------------------------------------------------------- 1 | /* •••••••••••••••••••••••••••••••••••••••••••••••••••••••••••• */ 2 | /* Right here, you have access to the `injector` global object. */ 3 | /* So you can inject unlimited codes using: ••••••••••••••••••• */ 4 | /* injector.injectInlineJs(); ••••••••••••••••••••••••••••••••• */ 5 | /* injector.injectExternalJs(); ••••••••••••••••••••••••••••••• */ 6 | /* injector.injectInternalCss(); •••••••••••••••••••••••••••••• */ 7 | /* injector.injectExternalCss(); •••••••••••••••••••••••••••••• */ 8 | /* Anyway it's OPTIONAL, you can also define your logic. •••••• */ 9 | /* •••••••••••••••••••••••••••••••••••••••••••••••••••••••••••• */ 10 | injector.injectExternalCss('https://fonts.googleapis.com/css?family=Oswald'); 11 | 12 | injector.injectInternalCss(` 13 | * { 14 | font-family: "Oswald" !important; 15 | } 16 | 17 | html { 18 | filter: invert(1); 19 | } 20 | `); 21 | -------------------------------------------------------------------------------- /source-code/inject-to/linktr.ee.js: -------------------------------------------------------------------------------- 1 | /* •••••••••••••••••••••••••••••••••••••••••••••••••••••••••••• */ 2 | /* Right here, you have access to the `injector` global object. */ 3 | /* So you can inject unlimited codes using: ••••••••••••••••••• */ 4 | /* injector.injectInlineJs(); ••••••••••••••••••••••••••••••••• */ 5 | /* injector.injectExternalJs(); ••••••••••••••••••••••••••••••• */ 6 | /* injector.injectInternalCss(); •••••••••••••••••••••••••••••• */ 7 | /* injector.injectExternalCss(); •••••••••••••••••••••••••••••• */ 8 | /* Anyway it's OPTIONAL, you can also define your logic. •••••• */ 9 | /* •••••••••••••••••••••••••••••••••••••••••••••••••••••••••••• */ 10 | injector.injectExternalCss('https://fonts.googleapis.com/css?family=Oswald'); 11 | 12 | injector.injectInternalCss(` 13 | * { 14 | font-family: "Oswald" !important; 15 | } 16 | 17 | body { 18 | filter: hue-rotate(122deg); 19 | } 20 | `); 21 | -------------------------------------------------------------------------------- /source-code/inject-to/127.0.0.1.js: -------------------------------------------------------------------------------- 1 | /* •••••••••••••••••••••••••••••••••••••••••••••••••••••••••••• */ 2 | /* Right here, you have access to the `injector` global object. */ 3 | /* So you can inject unlimited codes using: ••••••••••••••••••• */ 4 | /* injector.injectInlineJs(); ••••••••••••••••••••••••••••••••• */ 5 | /* injector.injectExternalJs(); ••••••••••••••••••••••••••••••• */ 6 | /* injector.injectInternalCss(); •••••••••••••••••••••••••••••• */ 7 | /* injector.injectExternalCss(); •••••••••••••••••••••••••••••• */ 8 | /* Anyway it's OPTIONAL, you can also define your logic. •••••• */ 9 | /* •••••••••••••••••••••••••••••••••••••••••••••••••••••••••••• */ 10 | injector.injectExternalCss('https://fonts.googleapis.com/css?family=Courgette'); 11 | 12 | injector.injectInternalCss(` 13 | * { 14 | font-family: "Courgette" !important; 15 | } 16 | 17 | html { 18 | filter: invert(1) saturate(2.5); 19 | } 20 | `); 21 | -------------------------------------------------------------------------------- /source-code/inject-to/www.facebook.com.js: -------------------------------------------------------------------------------- 1 | /* •••••••••••••••••••••••••••••••••••••••••••••••••••••••••••• */ 2 | /* Right here, you have access to the `injector` global object. */ 3 | /* So you can inject unlimited codes using: ••••••••••••••••••• */ 4 | /* injector.injectInlineJs(); ••••••••••••••••••••••••••••••••• */ 5 | /* injector.injectExternalJs(); ••••••••••••••••••••••••••••••• */ 6 | /* injector.injectInternalCss(); •••••••••••••••••••••••••••••• */ 7 | /* injector.injectExternalCss(); •••••••••••••••••••••••••••••• */ 8 | /* Anyway it's OPTIONAL, you can also define your logic. •••••• */ 9 | /* •••••••••••••••••••••••••••••••••••••••••••••••••••••••••••• */ 10 | injector.injectExternalCss('https://fonts.googleapis.com/css?family=Courgette'); 11 | 12 | injector.injectInternalCss(` 13 | * { 14 | font-family: "Courgette" !important; 15 | } 16 | 17 | html { 18 | filter: invert(1) hue-rotate(94deg); 19 | } 20 | `); 21 | -------------------------------------------------------------------------------- /source-code/inject-to/www.wikipedia.org.js: -------------------------------------------------------------------------------- 1 | /* •••••••••••••••••••••••••••••••••••••••••••••••••••••••••••• */ 2 | /* Right here, you have access to the `injector` global object. */ 3 | /* So you can inject unlimited codes using: ••••••••••••••••••• */ 4 | /* injector.injectInlineJs(); ••••••••••••••••••••••••••••••••• */ 5 | /* injector.injectExternalJs(); ••••••••••••••••••••••••••••••• */ 6 | /* injector.injectInternalCss(); •••••••••••••••••••••••••••••• */ 7 | /* injector.injectExternalCss(); •••••••••••••••••••••••••••••• */ 8 | /* Anyway it's OPTIONAL, you can also define your logic. •••••• */ 9 | /* •••••••••••••••••••••••••••••••••••••••••••••••••••••••••••• */ 10 | alert('I don\'t want to use `injector` global object! ⛔'); 11 | 12 | alert(`😎😎😎😎😎😎😎😎😎😎 13 | 😎😎This is just an alert!😎😎 14 | 😎😎😎😎😎😎😎😎😎😎`); 15 | 16 | injector.injectInlineJs(` 17 | alert('Now I want to use \`injector\` global object! ✅'); 18 | 19 | alert(\`🥳🥳🥳🥳🥳🥳🥳🥳🥳🥳 20 | 🥳🥳This is just an alert!🥳🥳 21 | 🥳🥳🥳🥳🥳🥳🥳🥳🥳🥳\`); 22 | `); 23 | -------------------------------------------------------------------------------- /source-code/inject-to/chat.openai.com.js: -------------------------------------------------------------------------------- 1 | /* •••••••••••••••••••••••••••••••••••••••••••••••••••••••••••• */ 2 | /* Right here, you have access to the `injector` global object. */ 3 | /* So you can inject unlimited codes using: ••••••••••••••••••• */ 4 | /* injector.injectInlineJs(); ••••••••••••••••••••••••••••••••• */ 5 | /* injector.injectExternalJs(); ••••••••••••••••••••••••••••••• */ 6 | /* injector.injectInternalCss(); •••••••••••••••••••••••••••••• */ 7 | /* injector.injectExternalCss(); •••••••••••••••••••••••••••••• */ 8 | /* Anyway it's OPTIONAL, you can also define your logic. •••••• */ 9 | /* •••••••••••••••••••••••••••••••••••••••••••••••••••••••••••• */ 10 | injector.injectExternalCss('https://fonts.googleapis.com/css?family=Nova+Mono'); 11 | 12 | injector.injectInternalCss(` 13 | * { 14 | font-family: "Nova Mono" !important; 15 | } 16 | 17 | html { 18 | filter: saturate(5) brightness(1.2); 19 | } 20 | 21 | .dark.flex-shrink-0.overflow-x-hidden.bg-gray-900 { 22 | filter: saturate(5); 23 | } 24 | `); 25 | -------------------------------------------------------------------------------- /source-code/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Injector+", 3 | "description": "Experience an unlimited injection tool that supports: injecting inline & external JavaScript, internal & external CSS into any website.", 4 | "manifest_version": 3, 5 | "version": "0.0.0.1", 6 | "permissions": [ 7 | "scripting", 8 | "storage" 9 | ], 10 | "host_permissions": [ 11 | "" 12 | ], 13 | "background": { 14 | "service_worker": "background.js" 15 | }, 16 | "action": { 17 | "default_title": "Toggle", 18 | "default_icon": { 19 | "16": "assets/icons/enabled/16.png", 20 | "32": "assets/icons/enabled/32.png", 21 | "48": "assets/icons/enabled/48.png", 22 | "64": "assets/icons/enabled/64.png", 23 | "128": "assets/icons/enabled/128.png" 24 | } 25 | }, 26 | "icons": { 27 | "16": "assets/icons/16.png", 28 | "32": "assets/icons/32.png", 29 | "48": "assets/icons/48.png", 30 | "64": "assets/icons/64.png", 31 | "128": "assets/icons/128.png" 32 | } 33 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Saeed Kohansal 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /source-code/inject-to/@Examples.js: -------------------------------------------------------------------------------- 1 | /* •••••••••••••••••••••••••••••••• */ 2 | /* Injecting Inline JavaScript Code */ 3 | /* •••••••••••••••••••••••••••••••• */ 4 | 5 | // Example inline JavaScript code to be injected 6 | const inlineJsCode = ` 7 | function greet() { 8 | alert('Hello, world!'); 9 | } 10 | greet(); 11 | `; 12 | 13 | // Inject the inline JavaScript code 14 | injector.injectInlineJs(inlineJsCode); 15 | 16 | 17 | 18 | 19 | /* •••••••••••••••••••••••••••••••••• */ 20 | /* Injecting External JavaScript File */ 21 | /* •••••••••••••••••••••••••••••••••• */ 22 | 23 | // URL of the external JavaScript file to be injected 24 | const externalJsFile = 'https://example.com/scripts/script.js'; 25 | 26 | // Inject the external JavaScript file 27 | injector.injectExternalJs(externalJsFile); 28 | 29 | 30 | 31 | 32 | /* ••••••••••••••••••••••••••• */ 33 | /* Injecting Internal CSS Code */ 34 | /* ••••••••••••••••••••••••••• */ 35 | 36 | // Example internal CSS styles to be injected 37 | const internalCssStyles = ` 38 | body { 39 | background-color: lightblue; 40 | } 41 | h1 { 42 | color: darkblue; 43 | } 44 | `; 45 | 46 | // Inject the internal CSS styles 47 | injector.injectInternalCss(internalCssStyles); 48 | 49 | 50 | 51 | 52 | /* ••••••••••••••••••••••••••• */ 53 | /* Injecting External CSS File */ 54 | /* ••••••••••••••••••••••••••• */ 55 | 56 | // URL of the external CSS file to be injected 57 | const externalCssFile = 'https://example.com/styles/styles.css'; 58 | 59 | // Inject the external CSS file 60 | injector.injectExternalCss(externalCssFile); 61 | -------------------------------------------------------------------------------- /source-code/core/extension.js: -------------------------------------------------------------------------------- 1 | /* •••••••••••••••••••••••••••••••••••••••••••••••••••••••• */ 2 | /* Perform actions for the `first install` of the extension */ 3 | /* 'install' = Extension was just installed ••••••••••••••• */ 4 | /* 'update' = Extension was updated or reloaded ••••••••••• */ 5 | /* •••••••••••••••••••••••••••••••••••••••••••••••••••••••• */ 6 | chrome.runtime.onInstalled.addListener(details => { 7 | if (details.reason === 'install') { 8 | // Set extension initial state 9 | chrome.storage.local.set({ state: true }); 10 | 11 | // Call the function to update the extension icon 12 | updateExtensionIcon(); 13 | } else if (details.reason === 'update') { 14 | // Call the function to update the extension icon 15 | updateExtensionIcon(); 16 | } 17 | }); 18 | 19 | 20 | /* •••••••••••••••••••••••••••••••••••••••••••••••••••••••••••• */ 21 | /* Perform actions for when the Google Chrome starts (launched) */ 22 | /* •••••••••••••••••••••••••••••••••••••••••••••••••••••••••••• */ 23 | chrome.runtime.onStartup.addListener(() => { 24 | // Call the function to update the extension icon 25 | updateExtensionIcon(); 26 | }); 27 | 28 | 29 | /* •••••••••••••••••••••••••••••••••••••••••••••••••••••••••••• */ 30 | /* Perform actions for when user clicks on the extension button */ 31 | /* •••••••••••••••••••••••••••••••••••••••••••••••••••••••••••• */ 32 | chrome.action.onClicked.addListener(() => { 33 | // Call the function to change the extension icon 34 | changeExtensionIcon(); 35 | 36 | // Reload page 37 | chrome.tabs.reload(); 38 | }); 39 | 40 | 41 | /* ••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••• */ 42 | /* Perform actions for when the extension turned off and turned on by user */ 43 | /* ••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••• */ 44 | 45 | // Call the function to update the extension icon 46 | updateExtensionIcon(); 47 | -------------------------------------------------------------------------------- /source-code/core/injection-types.js: -------------------------------------------------------------------------------- 1 | /* •••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••• */ 2 | /* This `injector` object provides functions to ••••••••••••••••••••• */ 3 | /* dynamically inject inline and external JavaScript code, •••••••••• */ 4 | /* as well as internal and external CSS styles into a web page's DOM. */ 5 | /* •••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••• */ 6 | 7 | // Injection types global object 8 | var injector = { 9 | // Function to inject inline JavaScript code 10 | injectInlineJs: (code) => { 11 | const scriptElement = document.createElement('script'); 12 | scriptElement.setAttribute('type', 'text/javascript'); 13 | scriptElement.textContent = code; 14 | // Inject before closing HTML tag `` 15 | document.documentElement.appendChild(scriptElement); 16 | }, 17 | 18 | // Function to inject external JavaScript file 19 | injectExternalJs: (src) => { 20 | const scriptElement = document.createElement('script'); 21 | scriptElement.setAttribute('type', 'text/javascript'); 22 | scriptElement.setAttribute('src', src); 23 | // Inject before closing HTML tag `` 24 | document.documentElement.appendChild(scriptElement); 25 | }, 26 | 27 | // Function to inject internal CSS code 28 | injectInternalCss: (styles) => { 29 | const styleElement = document.createElement('style'); 30 | styleElement.textContent = styles; 31 | // Inject before closing HTML tag `` 32 | document.documentElement.appendChild(styleElement); 33 | }, 34 | 35 | // Function to inject external CSS file 36 | injectExternalCss: (href) => { 37 | const linkElement = document.createElement('link'); 38 | linkElement.setAttribute('rel', 'stylesheet'); 39 | linkElement.setAttribute('type', 'text/css'); 40 | linkElement.setAttribute('href', href); 41 | // Inject before closing HTML tag `` 42 | document.documentElement.appendChild(linkElement); 43 | } 44 | }; 45 | -------------------------------------------------------------------------------- /source-code/core/injection-process.js: -------------------------------------------------------------------------------- 1 | /* ••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••• */ 2 | /* This code listens for tab updates in the whole browser and, ••••••• */ 3 | /* upon page load completion, checks whether the tab's URL matches any */ 4 | /* predefined websites in the `injectTo` array. •••••••••••••••••••••• */ 5 | /* If there's a match and the 'state' value is truthy, ••••••••••••••• */ 6 | /* it injects specified content scripts into all frames of the tab. •• */ 7 | /* ••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••• */ 8 | chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => { 9 | // When a webpage loaded completely 10 | if (changeInfo.status === 'complete') { 11 | // Get the 'state' data from Chrome local storage 12 | chrome.storage.local.get('state', (result) => { 13 | // Get the current state 14 | const currentState = result.state; 15 | 16 | // Continue only if the extension's 'state' is true 17 | if (currentState) { 18 | // Check if 'tab.url' is defined and not null 19 | if (typeof tab.url !== 'undefined' && tab.url !== null) { 20 | // Loop through each website in the `injectTo` array 21 | injectTo.forEach(website => { 22 | // Check if 'tab.url' includes the current 'website', based on 'hostname' 23 | if (tab.url.includes(website)) { 24 | // Execute scripts in the specified tab and its all frames 25 | chrome.scripting.executeScript({ 26 | target: { tabId: tabId, allFrames: true }, 27 | // Content script runs alongside page's main JavaScript 28 | world: 'MAIN', 29 | files: [ 30 | // Include the core injection types 31 | 'core/injection-types.js', 32 | // Include the specific content script for the website 33 | `inject-to/${website}.js` // Equivalent: 'inject-to/' + website + '.js' 34 | ] 35 | }); 36 | } 37 | }); 38 | } 39 | } 40 | }); 41 | } 42 | }); 43 | -------------------------------------------------------------------------------- /source-code/core/functions.js: -------------------------------------------------------------------------------- 1 | /* ••••••••••••••••••••••••••••••••••• */ 2 | /* The extension icon updater function */ 3 | /* ••••••••••••••••••••••••••••••••••• */ 4 | function updateExtensionIcon() { 5 | // Get the 'state' data from Chrome local storage 6 | chrome.storage.local.get('state', (result) => { 7 | // Get the current state 8 | const currentState = result.state; 9 | 10 | // Set icon images relative path 11 | const enabledIconPath = 'assets/icons/enabled/16.png'; 12 | const disabledIconPath = 'assets/icons/disabled/16.png'; 13 | 14 | // Specify the correct icon image (enabled or disabled) 15 | const iconPath = currentState ? enabledIconPath : disabledIconPath; 16 | 17 | // Set the new icon based on the state 18 | chrome.action.setIcon({ path: iconPath }); 19 | }); 20 | } 21 | 22 | 23 | /* ••••••••••••••••••••••••••••••••••• */ 24 | /* The extension icon changer function */ 25 | /* ••••••••••••••••••••••••••••••••••• */ 26 | function changeExtensionIcon() { 27 | // Get the 'state' data from Chrome local storage 28 | chrome.storage.local.get('state', (result) => { 29 | // Get the current state 30 | const currentState = result.state; 31 | 32 | // Toggle the state value 33 | const newState = !currentState; 34 | 35 | // Update the new 'state' data in Chrome local storage 36 | chrome.storage.local.set({ state: newState }, () => { 37 | // Set icon images relative path 38 | const enabledIconPath = 'assets/icons/enabled/16.png'; 39 | const disabledIconPath = 'assets/icons/disabled/16.png'; 40 | 41 | // Specify the correct icon image (enabled or disabled) 42 | const iconPath = newState ? enabledIconPath : disabledIconPath; 43 | 44 | // Set the new icon based on the state 45 | chrome.action.setIcon({ path: iconPath }); 46 | }); 47 | }); 48 | } 49 | 50 | 51 | /* •••••••••••••••••••••••••••••••••••• */ 52 | /* The extension status report function */ 53 | /* •••••••••••••••••••••••••••••••••••• */ 54 | function extensionStatusReport() { 55 | // Clear the console to keep it clean 56 | console.clear(); 57 | 58 | // Display a table of the target websites in the console 59 | console.table(injectTo); 60 | 61 | // Output the number of target websites in the `injectTo` array 62 | console.log('Target Websites: ' + injectTo.length); 63 | 64 | // Retrieve all data from Chrome local storage 65 | chrome.storage.local.get(null, result => { 66 | // Display all data retrieved from Chrome local storage 67 | console.log('All Chrome local storage data:', result); 68 | 69 | // Output whether the extension is `enabled` or `disabled` based on the 'state' value 70 | result.state ? console.log('The extension is ENABLED!') : console.log('The extension is DISABLED!'); 71 | }); 72 | } 73 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Injector+ Chrome Extension Manifest V3 2 | 3 | _Chrome Extension Development Tutorial: How To Inject JavaScript And CSS Into Any Website Manifest V3_ 4 | 5 | ![YouTube Thumbnail](https://raw.githubusercontent.com/saeedkohansal/InjectorPlus-Chrome-Extension-Manifest-V3/main/images/InjectorPlus-Chrome-Extension-Manifest-V3.png "Injector+ Chrome Extension Manifest V3") 6 | 7 | A Chrome extension is a small software program that can be added to the Google Chrome web browser to add new features or modify the browser's functionality. We are going to learn how to build a Chrome extension using manifest v3 from scratch. You will learn how to control a Chrome extension's behavior, how to set and change Chrome extension icons based on conditions, what the Chrome Storage API and Chrome Scripting API are, how to use them, and much more... My extension is compatible with all Chromium-based browsers. I've tested it on Google Chrome, Microsoft Edge, Brave, and Opera. I chose the name of this Chrome extension Injector Plus (Injector+). We can call it an unlimited lightweight injection tool that supports injecting inline JavaScript code, external JavaScript files, internal CSS code, and external CSS files. JavaScript and CSS injection are powerful techniques in web development that enable developers to create interactive, dynamic, and visually appealing web applications. However, they should be used responsibly to avoid security and performance issues. For testing purposes, I will inject custom JavaScript and CSS code into multiple websites, including ChatGPT, YouTube, Facebook, Wikipedia, example.com, and more... 8 | 9 | ## Video Tutorial [ How To Build Chrome Extension From Scratch ] 10 | [https://youtu.be/13yCU0tQ4cY](https://youtu.be/13yCU0tQ4cY) 11 | 12 |
13 | 14 | ## The Project Structure Explanation 15 | ![Project Setup Files Explanation](https://raw.githubusercontent.com/saeedkohansal/InjectorPlus-Chrome-Extension-Manifest-V3/main/images/Project-Structure-Explanation.png "Project Structure Explanation") 16 | 17 | ## The Extension Icon In Different Conditions 18 | | ![Extension Original Icon](https://raw.githubusercontent.com/saeedkohansal/InjectorPlus-Chrome-Extension-Manifest-V3/main/source-code/assets/icons/Original.png "Extension Original Icon") | ![Extension Enabled Icon](https://raw.githubusercontent.com/saeedkohansal/InjectorPlus-Chrome-Extension-Manifest-V3/main/source-code/assets/icons/enabled/Original.png "Extension Enabled Icon") | ![Extension Disabled Icon](https://raw.githubusercontent.com/saeedkohansal/InjectorPlus-Chrome-Extension-Manifest-V3/main/source-code/assets/icons/disabled/Original.png "Extension Disabled Icon") | 19 | | :---: | :---: | :---: | 20 | | Original Icon | Enabled Icon | Disabled Icon | 21 | 22 | ### How To Debug Extension Using `extensionStatusReport();` Function 23 | ![How To Debug Extension](https://raw.githubusercontent.com/saeedkohansal/InjectorPlus-Chrome-Extension-Manifest-V3/main/images/How-To-Debug-Extension.gif "How To Debug Extension") 24 | 25 | ## `chrome.storage.local` vs `chrome.storage.sync` 26 | ![Chrome Storage Sync vs Local](https://raw.githubusercontent.com/saeedkohansal/InjectorPlus-Chrome-Extension-Manifest-V3/main/images/Chrome-Storage-Sync-vs-Local.png "Chrome Storage Sync vs Local") 27 | 28 | ## The `background.js` Script Preview 29 | ![background.js](https://raw.githubusercontent.com/saeedkohansal/InjectorPlus-Chrome-Extension-Manifest-V3/main/images/background.js.png "background.js") 30 | 31 | ## If You Enjoy My Content, Please Support Me 😍🙏 32 | 33 | 💙 PAYPAL DONATION 34 | 35 | https://paypal.me/gilgeekify 36 | 37 | ❤️ PATREON 38 | 39 | https://www.patreon.com/gilgeekify 40 | 41 | 💛 BUY ME A COFFEE 42 | 43 | https://www.buymeacoffee.com/gilgeekify 44 | 45 | 🪙 My Public Address To Receive BTC • Bitcoin 46 | 47 | bc1qerc5ev074cqknu9nz589w4vjf5ecmhuc2df83h 48 | 49 | 🥈 My Public Address To Receive ETH • Ethereum 50 | 51 | 0x566A47B9731209A5144336D274D44224bfb9C0ea 52 | -------------------------------------------------------------------------------- /source-code/inject-to/example.com.js: -------------------------------------------------------------------------------- 1 | /* •••••••••••••••••••••••••••••••••••••••••••••••••••••••••••• */ 2 | /* Right here, you have access to the `injector` global object. */ 3 | /* So you can inject unlimited codes using: ••••••••••••••••••• */ 4 | /* injector.injectInlineJs(); ••••••••••••••••••••••••••••••••• */ 5 | /* injector.injectExternalJs(); ••••••••••••••••••••••••••••••• */ 6 | /* injector.injectInternalCss(); •••••••••••••••••••••••••••••• */ 7 | /* injector.injectExternalCss(); •••••••••••••••••••••••••••••• */ 8 | /* Anyway it's OPTIONAL, you can also define your logic. •••••• */ 9 | /* •••••••••••••••••••••••••••••••••••••••••••••••••••••••••••• */ 10 | injector.injectExternalCss('https://fonts.googleapis.com/css?family=Oswald'); 11 | 12 | injector.injectInternalCss(` 13 | * { 14 | font-family: "Oswald" !important; 15 | } 16 | 17 | html { 18 | margin-bottom: 700px; 19 | } 20 | 21 | body { 22 | color: #fff; 23 | background-color: #0fb; 24 | } 25 | 26 | div { 27 | color: #0fb; 28 | background-color: #000; 29 | border-radius: 60px 12px; 30 | transition: .5s; 31 | } 32 | 33 | div:hover { 34 | transform: scale(1.2); 35 | border-radius: 12px 60px; 36 | } 37 | 38 | a:link, a:visited { 39 | color: #ff0; 40 | } 41 | 42 | #gilgeekify { 43 | width: 300px; 44 | margin: 50px auto -30px; 45 | display: block; 46 | border: 1px #000 solid; 47 | border-radius: 50%; 48 | user-select: none; 49 | transition: 4s linear; 50 | } 51 | 52 | #gilgeekify:hover { 53 | filter: hue-rotate(360deg) saturate(4); 54 | } 55 | 56 | #canvas { 57 | position: absolute; 58 | top: 110%; 59 | left: 50%; 60 | transform: translate(-50%, -50%); 61 | } 62 | 63 | svg { 64 | font-size: 110px 65 | } 66 | `); 67 | 68 | injector.injectInlineJs(` 69 | function injectBase64ImageWithId(base64Data, id) { 70 | var img = document.createElement('img'); 71 | img.id = id; 72 | img.src = base64Data; 73 | 74 | var body = document.querySelector('body'); 75 | body.insertBefore(img, body.firstChild); 76 | } 77 | 78 | // Your complete base64 image data here 79 | var base64ImageData = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA4MjAuNjcgODMwIj48ZGVmcz48c3R5bGU+LmNscy0xe2ZpbGw6IzBmYjt9LmNscy0ye2ZvbnQtc2l6ZTo4NXB4O2ZvbnQtZmFtaWx5OkFyaWFsLUJvbGRNVCwgQXJpYWw7Zm9udC13ZWlnaHQ6NzAwO308L3N0eWxlPjwvZGVmcz48ZyBpZD0iTGF5ZXJfMiIgZGF0YS1uYW1lPSJMYXllciAyIj48ZyBpZD0iTGF5ZXJfMS0yIiBkYXRhLW5hbWU9IkxheWVyIDEiPjxlbGxpcHNlIGNsYXNzPSJjbHMtMSIgY3g9IjQxMC4zMyIgY3k9IjQxNSIgcng9IjQxMC4zMyIgcnk9IjQxNSIvPjxwYXRoIGQ9Ik00MTYsODMwYTQwNS4xMSw0MDUuMTEsMCwwLDAsMjIyLjI3LTY5LjhMNjM3LDc1Niw1OTYuODgsNjIzLjQ1YTE0Ni45MywxNDYuOTMsMCwwLDEtMTg2LjI5LDI0bC0uMjYtLjE3aDBsLS4yNS4xN2ExNDYuOTMsMTQ2LjkzLDAsMCwxLTE4Ni4yOS0yNEwxODQuNTksNzUzbC0yLjE1LDcuMTVBNDA1LjE2LDQwNS4xNiwwLDAsMCw0MDQuNzEsODMwYzEuODcsMCwzLjczLDAsNS42MSwwaDBDNDEyLjIzLDgzMCw0MTQuMDksODMwLDQxNiw4MzBaIi8+PHBvbHlnb24gcG9pbnRzPSI0NDkuMTMgNjAzLjk3IDQyOS43MyA2MjEuMTUgNDEwLjMzIDYzOC4zNCAzOTAuOTQgNjIxLjE1IDM3MS41NCA2MDMuOTcgNDQ5LjEzIDYwMy45NyIvPjxwYXRoIGQ9Ik0yMDMuNTQsNTAyLjI4QTM2NC43NiwzNjQuNzYsMCwwLDEsMzAwLjQzLDQ1MGEzMjMuMjEsMzIzLjIxLDAsMCwxLDIxOS44MSwwLDM2NC4yMywzNjQuMjMsMCwwLDEsOTYuODksNTIuMjlsLTE2LjY1LDI1Yy0yOS41My0xNy4xNi02MC4yNC0zMS41My05Mi4xNy00MS4zOGEzMjUsMzI1LDAsMCwwLTE5NS45NSwwYy0zMS45Myw5Ljg0LTYyLjY1LDI0LjIyLTkyLjE4LDQxLjM4WiIvPjxwYXRoIGQ9Ik01OTYuODgsMTA0LjQyYTExNiwxMTYsMCwxLDEtMTE2LDExNiwxMTYsMTE2LDAsMCwxLDExNi0xMTZtMC0xNWExMzEsMTMxLDAsMSwwLDkyLjYzLDM4LjM3LDEzMC4xMSwxMzAuMTEsMCwwLDAtOTIuNjMtMzguMzdaIi8+PHBhdGggZD0iTTIyMy43OSwxMDQuNDJhMTE2LDExNiwwLDEsMS0xMTYsMTE2LDExNiwxMTYsMCwwLDEsMTE2LTExNm0wLTE1YTEzMSwxMzEsMCwxLDAsOTIuNjMsMzguMzcsMTMwLjExLDEzMC4xMSwwLDAsMC05Mi42My0zOC4zN1oiLz48dGV4dCBjbGFzcz0iY2xzLTIiIHRyYW5zZm9ybT0ibWF0cml4KC0wLjgsIDAsIDAsIDEsIDY4Mi44NywgMjEwLjQ1KSI+Jmd0O188L3RleHQ+PHRleHQgY2xhc3M9ImNscy0yIiB0cmFuc2Zvcm09Im1hdHJpeCgtMC44LCAwLCAwLCAxLCAzMDkuNzgsIDIxMC40NSkiPiZndDtfPC90ZXh0PjwvZz48L2c+PC9zdmc+'; 80 | var imageId = 'gilgeekify'; 81 | injectBase64ImageWithId(base64ImageData, imageId); 82 | `); 83 | 84 | injector.injectInlineJs(` 85 | document.querySelector('body > div > h1').innerHTML += \`, gilgeekify programming injected! 86 | 💉😎👌🤠🥳🔥🤡💚🦾🤖🍏🥸👽\`; 87 | `); 88 | 89 | injector.injectInlineJs(` 90 | async function fetchData() { 91 | try { 92 | const response = await fetch('https://get.geojs.io/v1/ip/country.json'); 93 | const data = await response.json(); 94 | console.table(data); 95 | } catch (error) { 96 | console.error('Error fetching data:', error); 97 | } 98 | } 99 | 100 | // Call the async function to fetch and display data 101 | fetchData(); 102 | `); 103 | 104 | injector.injectExternalJs('https://cdn.jsdelivr.net/npm/chart.js'); 105 | 106 | injector.injectInlineJs(` 107 | // Create a canvas inside the body 108 | const canvas = document.createElement('canvas'); 109 | canvas.id = 'canvas'; 110 | document.body.appendChild(canvas); 111 | 112 | // Wait for 1 second 113 | setTimeout(() => { 114 | // Get the canvas element 115 | const canvas = document.getElementById('canvas'); 116 | 117 | // Get the 2D context 118 | const ctx = canvas.getContext('2d'); 119 | 120 | // Create data for the pie chart 121 | const data = { 122 | labels: ['Python', 'Java', 'JavaScript', 'C++', 'Ruby', 'PHP', 'Swift', 'Go', 'Rust', 'Kotlin'], 123 | datasets: [{ 124 | data: [30, 25, 20, 15, 10, 40, 28, 30, 20, 19], 125 | backgroundColor: [ 126 | '#1a3f8b', 127 | '#ff5733', 128 | '#ff0059', 129 | '#d34100', 130 | '#8b4513', 131 | '#4c1b1b', 132 | '#009688', 133 | '#800080', 134 | '#228b22', 135 | '#ffd700' 136 | ] 137 | }] 138 | }; 139 | 140 | // Create a pie chart using Chart.js 141 | new Chart(ctx, { 142 | type: 'pie', 143 | data: data, 144 | }); 145 | }, 100); // Wait for 100 milliseconds before drawing the pie chart 146 | `); 147 | 148 | // injector.injectExternalJs('https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/js/all.min.js'); 149 | 150 | // injector.injectInlineJs(` 151 | // // Create a new element and set its innerHTML 152 | // const content = document.createElement('div'); 153 | // content.innerHTML = ''; 154 | // content.innerHTML += ''; 155 | // content.innerHTML += ''; 156 | // content.innerHTML += ''; 157 | // content.innerHTML += ''; 158 | 159 | // // Insert the content as the first child of the body 160 | // document.body.insertBefore(content, document.body.firstChild); 161 | 162 | 163 | // // Select the div element 164 | // const divElement = document.querySelector('body > div'); 165 | 166 | // // Create an h1 element 167 | // const h1Element = document.createElement('h1'); 168 | // h1Element.textContent = '👇 Icons From Font Awesome Library 👇'; 169 | 170 | // // Apply text-align center style to the h1 element 171 | // h1Element.style.textAlign = 'center'; 172 | 173 | // // Get the first child of the div (if any) 174 | // const firstChild = divElement.firstChild; 175 | 176 | // // Insert the h1 element before the first child 177 | // divElement.insertBefore(h1Element, firstChild); 178 | // `); 179 | --------------------------------------------------------------------------------