aboutsummaryrefslogtreecommitdiff
path: root/packages/taler-wallet-webextension/src/platform/chrome.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/taler-wallet-webextension/src/platform/chrome.ts')
-rw-r--r--packages/taler-wallet-webextension/src/platform/chrome.ts268
1 files changed, 187 insertions, 81 deletions
diff --git a/packages/taler-wallet-webextension/src/platform/chrome.ts b/packages/taler-wallet-webextension/src/platform/chrome.ts
index b0934f107..3151bd6ab 100644
--- a/packages/taler-wallet-webextension/src/platform/chrome.ts
+++ b/packages/taler-wallet-webextension/src/platform/chrome.ts
@@ -59,6 +59,8 @@ const api: BackgroundPlatformAPI & ForegroundPlatformAPI = {
useServiceWorkerAsBackgroundProcess,
keepAlive,
listenNetworkConnectionState,
+ registerTalerHeaderListener,
+ containsTalerHeaderListener,
};
export default api;
@@ -95,10 +97,6 @@ function isFirefox(): boolean {
return false;
}
-// const hostPermissions = {
-// permissions: ["webRequest"],
-// origins: ["http://*/*", "https://*/*"],
-// };
export function containsClipboardPermissions(): Promise<boolean> {
return new Promise((res, rej) => {
@@ -113,18 +111,6 @@ export function containsClipboardPermissions(): Promise<boolean> {
});
}
-// export function containsHostPermissions(): Promise<boolean> {
-// return new Promise((res, rej) => {
-// chrome.permissions.contains(hostPermissions, (resp) => {
-// const le = chrome.runtime.lastError?.message;
-// if (le) {
-// rej(le);
-// }
-// res(resp);
-// });
-// });
-// }
-
export async function requestClipboardPermissions(): Promise<boolean> {
return new Promise((res, rej) => {
res(false);
@@ -138,67 +124,7 @@ export async function requestClipboardPermissions(): Promise<boolean> {
});
}
-// export async function requestHostPermissions(): Promise<boolean> {
-// return new Promise((res, rej) => {
-// chrome.permissions.request(hostPermissions, (resp) => {
-// const le = chrome.runtime.lastError?.message;
-// if (le) {
-// rej(le);
-// }
-// res(resp);
-// });
-// });
-// }
-
-// type HeaderListenerFunc = (
-// details: chrome.webRequest.WebResponseHeadersDetails,
-// ) => void;
-// let currentHeaderListener: HeaderListenerFunc | undefined = undefined;
-
-// type TabListenerFunc = (tabId: number, info: chrome.tabs.TabChangeInfo) => void;
-// let currentTabListener: TabListenerFunc | undefined = undefined;
-
-// export async function removeHostPermissions(): Promise<boolean> {
-// //if there is a handler already, remove it
-// if (
-// currentHeaderListener &&
-// chrome?.webRequest?.onHeadersReceived?.hasListener(currentHeaderListener)
-// ) {
-// chrome.webRequest.onHeadersReceived.removeListener(currentHeaderListener);
-// }
-// if (
-// currentTabListener &&
-// chrome?.tabs?.onUpdated?.hasListener(currentTabListener)
-// ) {
-// chrome.tabs.onUpdated.removeListener(currentTabListener);
-// }
-
-// currentHeaderListener = undefined;
-// currentTabListener = undefined;
-
-// //notify the browser about this change, this operation is expensive
-// if ("webRequest" in chrome) {
-// chrome.webRequest.handlerBehaviorChanged(() => {
-// if (chrome.runtime.lastError) {
-// logger.error(JSON.stringify(chrome.runtime.lastError));
-// }
-// });
-// }
-
-// if (extensionIsManifestV3()) {
-// // Trying to remove host permissions with manifest >= v3 throws an error
-// return true;
-// }
-// return new Promise((res, rej) => {
-// chrome.permissions.remove(hostPermissions, (resp) => {
-// const le = chrome.runtime.lastError?.message;
-// if (le) {
-// rej(le);
-// }
-// res(resp);
-// });
-// });
-// }
+
export function removeClipboardPermissions(): Promise<boolean> {
return new Promise((res, rej) => {
@@ -225,9 +151,9 @@ function addPermissionsListener(
function getPermissionsApi(): CrossBrowserPermissionsApi {
return {
addPermissionsListener,
- // containsHostPermissions,
- // requestHostPermissions,
- // removeHostPermissions,
+ containsHostPermissions,
+ requestHostPermissions,
+ removeHostPermissions,
requestClipboardPermissions,
removeClipboardPermissions,
containsClipboardPermissions,
@@ -365,7 +291,7 @@ async function sendMessageToBackground<
const timerId = setTimeout(() => {
timedout = true;
reject(TalerError.fromDetail(TalerErrorCode.GENERIC_TIMEOUT, {}) );
- }, 20 * 1000); //five seconds
+ }, 20 * 1000);
chrome.runtime.sendMessage(messageWithId, (backgroundResponse) => {
if (timedout) {
return false; //already rejected
@@ -782,3 +708,183 @@ function listenNetworkConnectionState(
window.removeEventListener("online", notifyOnline);
};
}
+
+type HeaderListenerFunc = (
+ details: chrome.webRequest.WebResponseHeadersDetails,
+) => void;
+let currentHeaderListener: HeaderListenerFunc | undefined = undefined;
+
+type TabListenerFunc = (tabId: number, info: chrome.tabs.TabChangeInfo) => void;
+let currentTabListener: TabListenerFunc | undefined = undefined;
+
+
+function containsTalerHeaderListener(): boolean {
+ return (
+ currentHeaderListener !== undefined || currentTabListener !== undefined
+ );
+}
+function registerTalerHeaderListener(
+ callback: (tabId: number, url: string) => void,
+): void {
+ logger.info("setting up header listener");
+
+ function headerListener(
+ details: chrome.webRequest.WebResponseHeadersDetails,
+ ): void {
+ if (chrome.runtime.lastError) {
+ logger.error(JSON.stringify(chrome.runtime.lastError));
+ return;
+ }
+ if (
+ details.statusCode === 402 ||
+ details.statusCode === 202 ||
+ details.statusCode === 200
+ ) {
+ const values = (details.responseHeaders || [])
+ .filter((h) => h.name.toLowerCase() === "taler")
+ .map((h) => h.value)
+ .filter((value): value is string => !!value);
+ if (values.length > 0) {
+ logger.info(
+ `Found a Taler URI in a response header for the request ${details.url} from tab ${details.tabId}`,
+ );
+ callback(details.tabId, values[0]);
+ }
+ }
+ return;
+ }
+
+ async function tabListener(
+ tabId: number,
+ info: chrome.tabs.TabChangeInfo,
+ ): Promise<void> {
+ if (tabId < 0) return;
+ const tabLocationHasBeenUpdated = info.status === "complete";
+ const tabTitleHasBeenUpdated = info.title !== undefined;
+ if (tabLocationHasBeenUpdated || tabTitleHasBeenUpdated) {
+ const uri = await findTalerUriInTab(tabId);
+ if (!uri) return;
+ logger.info(`Found a Taler URI in the tab ${tabId}`);
+ callback(tabId, uri);
+ }
+ }
+
+ const prevHeaderListener = currentHeaderListener;
+ const prevTabListener = currentTabListener;
+
+ getPermissionsApi()
+ .containsHostPermissions()
+ .then((result) => {
+ //if there is a handler already, remove it
+ if (
+ prevHeaderListener &&
+ chrome?.webRequest?.onHeadersReceived?.hasListener(prevHeaderListener)
+ ) {
+ chrome.webRequest.onHeadersReceived.removeListener(prevHeaderListener);
+ }
+ if (
+ prevTabListener &&
+ chrome?.tabs?.onUpdated?.hasListener(prevTabListener)
+ ) {
+ chrome.tabs.onUpdated.removeListener(prevTabListener);
+ }
+
+ //if the result was positive, add the headerListener
+ if (result) {
+ const headersEvent:
+ | chrome.webRequest.WebResponseHeadersEvent
+ | undefined = chrome?.webRequest?.onHeadersReceived;
+ if (headersEvent) {
+ headersEvent.addListener(headerListener, { urls: ["<all_urls>"] }, [
+ "responseHeaders",
+ ]);
+ currentHeaderListener = headerListener;
+ }
+
+ const tabsEvent: chrome.tabs.TabUpdatedEvent | undefined =
+ chrome?.tabs?.onUpdated;
+ if (tabsEvent) {
+ tabsEvent.addListener(tabListener);
+ currentTabListener = tabListener;
+ }
+ }
+
+ //notify the browser about this change, this operation is expensive
+ chrome?.webRequest?.handlerBehaviorChanged(() => {
+ if (chrome.runtime.lastError) {
+ logger.error(JSON.stringify(chrome.runtime.lastError));
+ }
+ });
+ });
+}
+
+const hostPermissions = {
+ permissions: ["webRequest"],
+ origins: ["http://*/*", "https://*/*"],
+};
+
+export function containsHostPermissions(): Promise<boolean> {
+ return new Promise((res, rej) => {
+ chrome.permissions.contains(hostPermissions, (resp) => {
+ const le = chrome.runtime.lastError?.message;
+ if (le) {
+ rej(le);
+ }
+ res(resp);
+ });
+ });
+}
+
+export async function requestHostPermissions(): Promise<boolean> {
+ return new Promise((res, rej) => {
+ chrome.permissions.request(hostPermissions, (resp) => {
+ const le = chrome.runtime.lastError?.message;
+ if (le) {
+ rej(le);
+ }
+ res(resp);
+ });
+ });
+}
+
+export async function removeHostPermissions(): Promise<boolean> {
+ //if there is a handler already, remove it
+ if (
+ currentHeaderListener &&
+ chrome?.webRequest?.onHeadersReceived?.hasListener(currentHeaderListener)
+ ) {
+ chrome.webRequest.onHeadersReceived.removeListener(currentHeaderListener);
+ }
+ if (
+ currentTabListener &&
+ chrome?.tabs?.onUpdated?.hasListener(currentTabListener)
+ ) {
+ chrome.tabs.onUpdated.removeListener(currentTabListener);
+ }
+
+ currentHeaderListener = undefined;
+ currentTabListener = undefined;
+
+ //notify the browser about this change, this operation is expensive
+ if ("webRequest" in chrome) {
+ chrome.webRequest.handlerBehaviorChanged(() => {
+ if (chrome.runtime.lastError) {
+ logger.error(JSON.stringify(chrome.runtime.lastError));
+ }
+ });
+ }
+
+ if (extensionIsManifestV3()) {
+ // Trying to remove host permissions with manifest >= v3 throws an error
+ return true;
+ }
+ return new Promise((res, rej) => {
+ chrome.permissions.remove(hostPermissions, (resp) => {
+ const le = chrome.runtime.lastError?.message;
+ if (le) {
+ rej(le);
+ }
+ res(resp);
+ });
+ });
+} \ No newline at end of file