Files
tubestation/waterfox/browser/components/sidebar/common/retrieve-url.js
2025-11-06 14:13:52 +00:00

198 lines
5.2 KiB
JavaScript

/*
# 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';
import {
log as internalLogger,
} from './common.js';
import * as Permissions from './permissions.js';
function log(...args) {
internalLogger('common/retrieve-url', ...args);
}
export const kTYPE_PLAIN_TEXT = 'text/plain';
export const kTYPE_X_MOZ_URL = 'text/x-moz-url';
export const kTYPE_URI_LIST = 'text/uri-list';
export const kTYPE_MOZ_TEXT_INTERNAL = 'text/x-moz-text-internal';
const kBOOKMARK_FOLDER = 'x-moz-place:';
const ACCEPTABLE_DATA_TYPES = [
kTYPE_URI_LIST,
kTYPE_X_MOZ_URL,
kTYPE_MOZ_TEXT_INTERNAL,
kTYPE_PLAIN_TEXT,
];
let mFileURLResolver = null;
export async function registerFileURLResolver(resolver) {
mFileURLResolver = resolver;
}
export async function fromDragEvent(event) {
log('fromDragEvent ', event);
const dt = event.dataTransfer;
const urls = [];
if (dt.files.length > 0) {
for (const file of dt.files) {
if (typeof mFileURLResolver == 'function') {
urls.push(await mFileURLResolver(file));
}
else {
// Created object URLs need to be revoked by tryRevokeObjectURL()
// in common/common.js, after they are loaded.
urls.push(URL.createObjectURL(file));
}
}
}
else {
for (const type of ACCEPTABLE_DATA_TYPES) {
const urlData = dt.getData(type);
if (urlData)
urls.push(...fromData(urlData, type));
if (urls.length)
break;
}
for (const type of dt.types) {
if (!/^application\/x-ws-drag-data;(.+)$/.test(type))
continue;
const params = RegExp.$1;
const providerId = /provider=([^;&]+)/.test(params) && RegExp.$1;
const dataId = /id=([^;&]+)/.test(params) && RegExp.$1;
try {
const dragData = await browser.runtime.sendMessage(providerId, {
type: 'get-drag-data',
id: dataId
});
if (!dragData || typeof dragData != 'object')
break;
for (const type of ACCEPTABLE_DATA_TYPES) {
const urlData = dragData[type];
if (urlData)
urls.push(...fromData(urlData, type));
if (urls.length)
break;
}
}
catch(_error) {
}
}
}
log(' => retrieved: ', urls);
return sanitizeURLs(urls);
}
let mSelectionClipboardProvider = null;
/* provider should have two methods:
isAvailable(): returns boolean which indicates the selection clipboard is available or not.
getTextData(): returns a string from the selection clipboard.
*/
export function registerSelectionClipboardProvider(provider) {
mSelectionClipboardProvider = provider;
}
export async function fromClipboard({ selection } = {}) {
const urls = [];
if (selection && mSelectionClipboardProvider) {
if (await mSelectionClipboardProvider.isAvailable()) {
const maybeUrlString = await mSelectionClipboardProvider.getTextData();
if (maybeUrlString)
urls.push(...fromData(maybeUrlString, kTYPE_PLAIN_TEXT));
return sanitizeURLs(urls);
}
}
if (!(await Permissions.isGranted(Permissions.CLIPBOARD_READ)) ||
typeof navigator.clipboard.read != 'function')
return urls;
const clipboardContents = await navigator.clipboard.read();
log('fromClipboard ', clipboardContents);
for (const item of clipboardContents) {
for (const type of item.types) {
const maybeUrlBlob = await item.getType(type);
const maybeUrlString = await maybeUrlBlob.text();
if (maybeUrlString)
urls.push(...fromData(maybeUrlString, type));
if (urls.length)
break;
}
}
log(' => retrieved: ', urls);
return sanitizeURLs(urls);
}
function sanitizeURLs(urls) {
const filteredUrls = urls.filter(url =>
url &&
url.length &&
url.indexOf(kBOOKMARK_FOLDER) == 0 ||
!/^\s*(javascript|data):/.test(url)
);
log('sanitizeURLs filtered: ', filteredUrls);
const fixedUrls = filteredUrls.map(fixupURIFromText);
log('sanitizeURLs fixed: ', fixedUrls);
return fixedUrls;
}
function fromData(data, type) {
log('fromData: ', type, data);
switch (type) {
case kTYPE_URI_LIST:
return data
.replace(/\r/g, '\n')
.replace(/\n\n+/g, '\n')
.split('\n')
.filter(line => {
return line.charAt(0) != '#';
});
case kTYPE_X_MOZ_URL:
return data
.trim()
.replace(/\r/g, '\n')
.replace(/\n\n+/g, '\n')
.split('\n')
.filter((_line, index) => {
return index % 2 == 0;
});
case kTYPE_MOZ_TEXT_INTERNAL:
return data
.replace(/\r/g, '\n')
.replace(/\n\n+/g, '\n')
.trim()
.split('\n');
case kTYPE_PLAIN_TEXT:
return data
.replace(/\r/g, '\n')
.replace(/\n\n+/g, '\n')
.trim()
.split('\n')
.map(line => {
return /^\w+:\/\/.+/.test(line) ? line : `ext+ws:search:${line}`;
});
}
return [];
}
function fixupURIFromText(maybeURI) {
if (/^(ext\+)?\w+:/.test(maybeURI))
return maybeURI;
if (/^([^\.\s]+\.)+[^\.\s]{2}/.test(maybeURI))
return `http://${maybeURI}`;
return maybeURI;
}