mirror of
https://github.com/SolninjaA/Chhoto-URL-Extension.git
synced 2025-02-27 18:29:33 +10:00
Convert extension to manifest v3. Still under testing
This commit is contained in:
parent
9aef574167
commit
35120f752a
4
lib/browser-polyfill.min.js
vendored
4
lib/browser-polyfill.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"manifest_version": 2,
|
"manifest_version": 3,
|
||||||
"name": "Chhoto URL",
|
"name": "Chhoto URL",
|
||||||
"version": "1.3.1",
|
"version": "1.3.1",
|
||||||
"description": "An unofficial extension for shortening URLs using the Chhoto URL API. Requires a Chhoto URL instance.",
|
"description": "An unofficial extension for shortening URLs using the Chhoto URL API. Requires a Chhoto URL instance.",
|
||||||
@ -16,13 +16,16 @@
|
|||||||
"notifications",
|
"notifications",
|
||||||
"clipboardWrite",
|
"clipboardWrite",
|
||||||
"storage",
|
"storage",
|
||||||
"https://*/*",
|
"contextMenus",
|
||||||
"contextMenus"
|
"offscreen"
|
||||||
|
],
|
||||||
|
"host_permissions": [
|
||||||
|
"https://*/*"
|
||||||
],
|
],
|
||||||
"options_ui": {
|
"options_ui": {
|
||||||
"page": "options/options.html"
|
"page": "options/options.html"
|
||||||
},
|
},
|
||||||
"browser_action": {
|
"action": {
|
||||||
"default_icon": {
|
"default_icon": {
|
||||||
"16": "icons/chhoto-url-16.png",
|
"16": "icons/chhoto-url-16.png",
|
||||||
"48": "icons/chhoto-url-48.png",
|
"48": "icons/chhoto-url-48.png",
|
||||||
@ -34,9 +37,13 @@
|
|||||||
"default_title": "Generate a Chhoto URL!"
|
"default_title": "Generate a Chhoto URL!"
|
||||||
},
|
},
|
||||||
"background": {
|
"background": {
|
||||||
"scripts": [
|
"service_worker": "service_worker.js",
|
||||||
"lib/browser-polyfill.min.js",
|
"type": "module",
|
||||||
"background/background.js"
|
"scripts": ["lib/browser-polyfill.min.js", "service_worker.js"]
|
||||||
]
|
},
|
||||||
|
"browser_specific_settings": {
|
||||||
|
"gecko": {
|
||||||
|
"id": "{49edc8ea-f91d-4510-8710-e0e807ad5842}"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
3
offscreen/offscreen.html
Normal file
3
offscreen/offscreen.html
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<textarea id="text"></textarea>
|
||||||
|
<script src="offscreen.js"></script>
|
87
offscreen/offscreen.js
Normal file
87
offscreen/offscreen.js
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
/**
|
||||||
|
* COPYRIGHT NOTICE
|
||||||
|
* THIS FILE WAS INSPIRED FROM: https://github.com/GoogleChrome/chrome-extensions-samples/tree/main/functional-samples/cookbook.offscreen-clipboard-write
|
||||||
|
* THE BASE CODE IS LICENSED UNDER THE APACHE 2 LICENSE
|
||||||
|
* (https://www.apache.org/licenses/LICENSE-2.0)
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Copyright 2023 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* COPYRIGHT NOTICE
|
||||||
|
* ALL PERSONAL **MODIFICATIONS** ARE LICENSED UNDER THE GNU GENEARL PUBLIC LICENSE VERSION 3
|
||||||
|
* YOU MAY FIND THIS LICENSE IN THE "LICENSE.md" FILE.
|
||||||
|
* IF NOT, SEE https://www.gnu.org/licenses
|
||||||
|
* */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* An unofficial extension for easy link shortening using the Chhoto URL API.
|
||||||
|
* Copyright (C) 2025 Solomon Tech
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Get element
|
||||||
|
const textEle = document.querySelector("#text");
|
||||||
|
|
||||||
|
// Function which actually copies the link
|
||||||
|
async function copy(link) {
|
||||||
|
try {
|
||||||
|
// If the link is not a string
|
||||||
|
if (typeof link !== 'string') {
|
||||||
|
// Return an error
|
||||||
|
throw new TypeError(`The shortened URL's type must be a 'string', not "${typeof link}"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the value of the text element
|
||||||
|
textEle.value = link;
|
||||||
|
|
||||||
|
// Select the element
|
||||||
|
textEle.select();
|
||||||
|
|
||||||
|
// Copy to the clipboard
|
||||||
|
document.execCommand('copy');
|
||||||
|
} finally {
|
||||||
|
// Close the offscreen document
|
||||||
|
window.close();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Ensures the message was meant for this message handler
|
||||||
|
async function handleMessages(message) {
|
||||||
|
// Only activate if the received message was intended for the offscreen handler
|
||||||
|
if (message.type === 'clipboard') {
|
||||||
|
// Call the copy function and pass the link
|
||||||
|
copy(message.link);
|
||||||
|
} else {
|
||||||
|
// Return, if the message wasn't intended for the offscreen handler
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Listen for runtime messages
|
||||||
|
chrome.runtime.onMessage.addListener(handleMessages);
|
@ -52,7 +52,9 @@ const requestParams = new URLSearchParams(window.location.search);
|
|||||||
const requestValue = requestParams.get('url');
|
const requestValue = requestParams.get('url');
|
||||||
|
|
||||||
// Get the background page, and call the sendRequest function (which is in this script)
|
// Get the background page, and call the sendRequest function (which is in this script)
|
||||||
|
if (typeof chrome === undefined) {
|
||||||
const backgroundFunc = browser.runtime.getBackgroundPage();
|
const backgroundFunc = browser.runtime.getBackgroundPage();
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Functions
|
* Functions
|
||||||
@ -136,7 +138,12 @@ function deleteLink(host, link) {
|
|||||||
deleteErrorEle.classList.remove("warning");
|
deleteErrorEle.classList.remove("warning");
|
||||||
|
|
||||||
// Send request to function which handles requests to the background page
|
// Send request to function which handles requests to the background page
|
||||||
|
if (typeof chrome !== undefined) {
|
||||||
|
browser.runtime.sendMessage({type: "generate-via-popup", longurl: longurl, shorturl: shorturl});
|
||||||
|
} else {
|
||||||
backgroundFunc.then(sendRequest, onError);
|
backgroundFunc.then(sendRequest, onError);
|
||||||
|
};
|
||||||
|
|
||||||
} else if (result.json.error) {
|
} else if (result.json.error) {
|
||||||
// Set error message
|
// Set error message
|
||||||
deleteErrorMessageEle.innerText = `Error (${result.status}): ${result.json.reason}`;
|
deleteErrorMessageEle.innerText = `Error (${result.status}): ${result.json.reason}`;
|
||||||
@ -224,7 +231,12 @@ generateEle.addEventListener("submit", (event) => {
|
|||||||
message3Ele.classList.remove("warning");
|
message3Ele.classList.remove("warning");
|
||||||
|
|
||||||
// Send request to function which handles requests to the background page
|
// Send request to function which handles requests to the background page
|
||||||
|
if (typeof chrome !== undefined) {
|
||||||
|
browser.runtime.sendMessage({type: "generate-via-popup", longurl: longurl, shorturl: shorturl});
|
||||||
|
} else {
|
||||||
backgroundFunc.then(sendRequest, onError);
|
backgroundFunc.then(sendRequest, onError);
|
||||||
|
};
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// Add the warning class
|
// Add the warning class
|
||||||
message3Ele.classList.add("warning");
|
message3Ele.classList.add("warning");
|
||||||
|
@ -28,6 +28,25 @@
|
|||||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode
|
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
// Import polyfill
|
||||||
|
import "/lib/browser-polyfill.min.js";
|
||||||
|
|
||||||
|
// Create a context menu
|
||||||
|
browser.runtime.onInstalled.addListener(() => {
|
||||||
|
browser.contextMenus.create({
|
||||||
|
title: "Manually generate a Chhoto URL",
|
||||||
|
contexts: ["all"],
|
||||||
|
id: "generate-chhoto-url"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// Run code when the context menu is clicked
|
||||||
|
browser.contextMenus.onClicked.addListener( (info) => {
|
||||||
|
browser.windows.create({url: `/popup/popup.html?url=${info.pageUrl}`, type: "popup"});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Type Definitions
|
* Type Definitions
|
||||||
*/
|
*/
|
||||||
@ -81,6 +100,12 @@
|
|||||||
* @returns {!Promise<[URL, string, string], Error>}
|
* @returns {!Promise<[URL, string, string], Error>}
|
||||||
*/
|
*/
|
||||||
function validateURL(url, title, type) {
|
function validateURL(url, title, type) {
|
||||||
|
// Define a new URL object, in case the url was collected from a runtime message
|
||||||
|
// In which case, the URL object wouldn't be preserved
|
||||||
|
if (type === "popup") {
|
||||||
|
url = new URL(url);
|
||||||
|
}
|
||||||
|
|
||||||
return browser.storage.local.get("allowedProtocols").then(({ allowedProtocols }) => {
|
return browser.storage.local.get("allowedProtocols").then(({ allowedProtocols }) => {
|
||||||
// Initialize a list of protocols that are allowed if unset.
|
// Initialize a list of protocols that are allowed if unset.
|
||||||
// This needs to be synced with the initialization code in options.js.
|
// This needs to be synced with the initialization code in options.js.
|
||||||
@ -269,23 +294,25 @@ function validateChhotoResponse(httpResp) {
|
|||||||
* @returns {!Promise<ChhotoJSON, Error>} `ChhotoJSON`, unmodified, on
|
* @returns {!Promise<ChhotoJSON, Error>} `ChhotoJSON`, unmodified, on
|
||||||
* success, or an error indicating that we failed to copy to the clipboard.
|
* success, or an error indicating that we failed to copy to the clipboard.
|
||||||
*/
|
*/
|
||||||
function copyLinkToClipboard(chhotoResp) {
|
async function copyLinkToClipboard(chhotoResp) {
|
||||||
// Send finished message
|
// Send finished message
|
||||||
browser.runtime.sendMessage({message: "finished"});
|
browser.runtime.sendMessage({message: "finished"});
|
||||||
|
|
||||||
// Chrome requires this hacky workaround :(
|
// Chrome requires this hacky workaround :(
|
||||||
if (typeof chrome !== "undefined") {
|
// Currently the "getBrowserInfo" function is only available on Firefox browsers
|
||||||
const prevSelected = document.activeElement;
|
// So, we can partially detect the type of the browser
|
||||||
const tempEle = document.createElement("input");
|
// This check may change in the future
|
||||||
document.body.appendChild(tempEle);
|
if (typeof browser.runtime.getBrowserInfo !== "function") {
|
||||||
tempEle.value = chhotoResp.shorturl;
|
// Create an offscreen document
|
||||||
tempEle.select();
|
await browser.offscreen.createDocument({
|
||||||
document.execCommand('copy');
|
url: "/offscreen/offscreen.html",
|
||||||
document.body.removeChild(tempEle);
|
reasons: [chrome.offscreen.Reason.CLIPBOARD],
|
||||||
// Depending on what was previously selected, we might not be able to select the text.
|
justification: "Copy the shortened URL to the clipboard"
|
||||||
if (prevSelected?.select) {
|
});
|
||||||
prevSelected.select();
|
|
||||||
}
|
// Send a runtime message to the offscreen page
|
||||||
|
browser.runtime.sendMessage({type: "clipboard", link: chhotoResp.shorturl});
|
||||||
|
|
||||||
return Promise.resolve(chhotoResp);
|
return Promise.resolve(chhotoResp);
|
||||||
} else {
|
} else {
|
||||||
return navigator.clipboard
|
return navigator.clipboard
|
||||||
@ -360,17 +387,27 @@ function popupGenerateChhoto(url, title) {
|
|||||||
.catch(notifyError);
|
.catch(notifyError);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function to handle messages
|
||||||
|
* */
|
||||||
|
async function handleMessages(message) {
|
||||||
|
// Only activate if the messge was intended for the background page
|
||||||
|
if (message.type === "generate-via-popup") {
|
||||||
|
if (typeof message.longurl !== 'string' && typeof message.shorturl !== 'string') {
|
||||||
|
throw new TypeError(`The long URL and short URL's type must be a 'string', got "${typeof message.longurl}" and "${typeof message.shorturl} respectively."`)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Generate
|
||||||
|
console.log(message.longurl);
|
||||||
|
popupGenerateChhoto(message.longurl, message.shorturl);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// When the extension icon is clicked, call the function above
|
// When the extension icon is clicked, call the function above
|
||||||
browser.browserAction.onClicked.addListener(generateChhoto);
|
browser.action.onClicked.addListener(generateChhoto);
|
||||||
|
|
||||||
// Create a context menu
|
// When a message is sent
|
||||||
browser.contextMenus.removeAll();
|
browser.runtime.onMessage.addListener(handleMessages);
|
||||||
browser.contextMenus.create({
|
|
||||||
title: "Manually generate a Chhoto URL",
|
|
||||||
contexts: ["all"]
|
|
||||||
});
|
|
||||||
|
|
||||||
// Run code when the context menu is clicked
|
|
||||||
browser.contextMenus.onClicked.addListener( (info) => {
|
|
||||||
browser.windows.create({url: `/popup/popup.html?url=${info.pageUrl}`, type: "popup"});
|
|
||||||
});
|
|
Loading…
Reference in New Issue
Block a user