Skip to content

Commit 0ab56ce

Browse files
committed
fix the ability to restore tokens from a JSON text file
previous methodology: ===================== * popup window: - dynamically creates an input file element - click event triggers the element to open the file chooser dialog - processes the list of selected files by: * reading the content of each file * parsing its JSON * passing the resulting object in a message to the background page * background page: - merges backup data with the tokens already saved in local storage - updates local storage with this new aggregate value - updates the popup window problem with previous methodology: ================================== * only works if the browser doesn't close the popup window before the data is passed to the background page * many browsers do close the popup window when the file input dialog is triggered for selection of input files new methodology: ================ * popup window: - sends a message to the background page * background page: - opens a new tab and loads a static html page * static html page: - dynamically creates an input file element - adds a click event handler to the element, which requires user interaction to trigger - the click event handler processes the list of selected files by: * reading the content of each file * parsing its JSON * passing the resulting object in a message to the background page - the click event handler also tracks the count of files pending * after the processing of all files is complete, sends a final message to the background page * background page: - merges backup data with the tokens already saved in local storage - updates local storage with this new aggregate value - closes the tab containing the static html page comparison between methodologies: ================================= * previous methodology: - pros: * simpler implementation * more elegant user experience - cons: * doesn't work in many browsers * new methodology: - pros: * works in all supported browsers - cons: * much more complicated implementation * less elegant user experience, which requires interaction with a standalone page in a new tab
1 parent 8283f46 commit 0ab56ce

File tree

9 files changed

+163
-59
lines changed

9 files changed

+163
-59
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "privacy-pass",
3-
"version": "3.6.5",
3+
"version": "3.6.6",
44
"private": true,
55
"contributors": [
66
"Suphanat Chunhapanya <[email protected]>",

public/manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "__MSG_appName__",
33
"description": "__MSG_appDescription__",
4-
"version": "3.6.5",
4+
"version": "3.6.6",
55
"manifest_version": 2,
66
"default_locale": "en",
77
"icons": {

public/restore.html

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta charset="utf-8">
5+
</head>
6+
<body>
7+
<div id="root"></div>
8+
</body>
9+
</html>

src/background/listeners/messageListener.ts

Lines changed: 40 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,21 @@ function addPasses(providerID: string, newTokensArray: string[]): boolean {
5555
}
5656
}
5757

58-
export function handleReceivedMessage(request: any, _sender: chrome.runtime.MessageSender, sendResponse: Function): void {
58+
function restorePasses(backup: {[key: string]: string[]} | void): boolean {
59+
let did_restore: boolean = false;
60+
61+
if (backup !== undefined) {
62+
for (const providerID in backup) {
63+
if (addPasses(providerID, backup[providerID]) && !did_restore) {
64+
did_restore = true;
65+
}
66+
}
67+
}
68+
69+
return did_restore;
70+
}
71+
72+
export function handleReceivedMessage(request: any, sender: chrome.runtime.MessageSender, sendResponse: Function): void {
5973

6074
// -------------------------------------------------------------------------
6175
if (request.tokensCount === true) {
@@ -80,23 +94,37 @@ export function handleReceivedMessage(request: any, _sender: chrome.runtime.Mess
8094

8195
// -------------------------------------------------------------------------
8296
if (request.restore === true) {
83-
const backup: {[key: string]: string[]} | void = request.backup;
84-
let did_restore: boolean = false;
97+
if (request.tab !== undefined) {
98+
if (request.tab.open === true) {
99+
chrome.tabs.create({ url: '/restore.html', active: true });
85100

86-
if (backup !== undefined) {
87-
for (const providerID in backup) {
88-
if (addPasses(providerID, backup[providerID]) && !did_restore) {
89-
did_restore = true;
101+
sendResponse();
102+
return;
103+
}
104+
105+
if (request.tab.close === true) {
106+
if ((sender.tab !== undefined) && (sender.tab.id !== undefined) && (sender.tab.id !== chrome.tabs.TAB_ID_NONE)) {
107+
chrome.tabs.remove(sender.tab.id);
90108
}
109+
110+
sendResponse();
111+
return;
91112
}
92113
}
93114

94-
if (did_restore) {
95-
// Update the browser action icon after restoring tokens.
96-
forceUpdateIcon();
115+
if (request.backup !== undefined) {
116+
const did_restore: boolean = restorePasses(request.backup);
117+
118+
if (did_restore) {
119+
// Update the browser action icon after restoring tokens.
120+
forceUpdateIcon();
121+
}
122+
123+
sendResponse(did_restore);
124+
return;
97125
}
98126

99-
sendResponse(did_restore);
127+
sendResponse();
100128
return;
101129
}
102130

@@ -107,6 +135,7 @@ export function handleReceivedMessage(request: any, _sender: chrome.runtime.Mess
107135
// Update the browser action icon after clearing the tokens.
108136
forceUpdateIcon();
109137

138+
sendResponse();
110139
return;
111140
}
112141

src/popup/store.ts

Lines changed: 1 addition & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -71,51 +71,7 @@ const reducer = (state: any | undefined, action: Action) => {
7171
});
7272
return state;
7373
case 'RESTORE_TOKENS':
74-
(async (): Promise<void> => {
75-
76-
const readFile = function (event: Event) {
77-
event.stopPropagation();
78-
event.stopImmediatePropagation();
79-
80-
const input: HTMLInputElement = <HTMLInputElement>event.target;
81-
const files: FileList | null = input.files;
82-
if (files === null) return;
83-
84-
for (let file_index=0; file_index < files.length; file_index++) {
85-
const reader = new FileReader();
86-
87-
reader.onload = function(){
88-
try {
89-
if ((typeof reader.result === 'string') && (reader.result.length > 0)) {
90-
const backupJSON: string = reader.result;
91-
const backup: {[key: string]: string[]} = JSON.parse(backupJSON);
92-
93-
chrome.runtime.sendMessage({ restore: true, backup }, (response: any) => {
94-
if (response === true) {
95-
store.dispatch({ type: 'OBTAIN_STATE' });
96-
}
97-
});
98-
}
99-
}
100-
catch(e) {}
101-
};
102-
103-
reader.readAsText(
104-
files[file_index]
105-
);
106-
}
107-
};
108-
109-
try {
110-
const input = window.document.createElement('input');
111-
input.setAttribute('type', 'file');
112-
input.setAttribute('accept', 'text/plain, application/json, .txt, .json');
113-
input.addEventListener('change', readFile);
114-
input.click();
115-
}
116-
catch(e) {}
117-
})();
118-
74+
chrome.runtime.sendMessage({ restore: true, tab: { open: true } });
11975
return state;
12076
case 'CLEAR_TOKENS':
12177
chrome.runtime.sendMessage({ clear: true });

src/restore/index.ts

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
function handleBackupFileImport(event: Event): void {
2+
event.stopPropagation();
3+
event.stopImmediatePropagation();
4+
5+
const input: HTMLInputElement = <HTMLInputElement>event.target;
6+
const files: FileList | null = input.files;
7+
if (files === null) return;
8+
9+
let remaining_files = files.length;
10+
11+
const onFileReadComplete = () => {
12+
remaining_files--;
13+
14+
if (remaining_files <= 0) {
15+
chrome.runtime.sendMessage({ restore: true, tab: { close: true } });
16+
}
17+
}
18+
19+
for (let file_index=0; file_index < files.length; file_index++) {
20+
const reader = new FileReader();
21+
22+
reader.onload = function(){
23+
try {
24+
if ((typeof reader.result === 'string') && (reader.result.length > 0)) {
25+
const backupJSON: string = reader.result;
26+
const backup: {[key: string]: string[]} = JSON.parse(backupJSON);
27+
28+
chrome.runtime.sendMessage({ restore: true, backup });
29+
}
30+
}
31+
catch(e) {}
32+
onFileReadComplete();
33+
};
34+
35+
reader.onerror = onFileReadComplete;
36+
reader.onabort = onFileReadComplete;
37+
38+
reader.readAsText(
39+
files[file_index]
40+
);
41+
}
42+
}
43+
44+
45+
window.addEventListener('DOMContentLoaded', (event) => {
46+
event.stopPropagation();
47+
event.stopImmediatePropagation();
48+
49+
const appName = chrome.i18n.getMessage('appName');
50+
const ctaRestorePasses = chrome.i18n.getMessage('ctaRestorePasses');
51+
52+
window.document.title = appName + ': ' + ctaRestorePasses;
53+
54+
const input = window.document.createElement('input');
55+
input.setAttribute('type', 'file');
56+
input.setAttribute('accept', 'text/plain, application/json, .txt, .json');
57+
input.setAttribute('multiple', '');
58+
input.addEventListener('change', handleBackupFileImport);
59+
60+
const root = window.document.getElementById('root');
61+
if (root !== null) {
62+
const heading = window.document.createElement('h2');
63+
heading.appendChild(
64+
window.document.createTextNode(appName)
65+
);
66+
67+
const subheading = window.document.createElement('h3');
68+
subheading.appendChild(
69+
window.document.createTextNode(ctaRestorePasses)
70+
);
71+
72+
root.appendChild(heading);
73+
root.appendChild(subheading);
74+
root.appendChild(input);
75+
}
76+
77+
input.click();
78+
});

src/restore/tsconfig.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"compilerOptions": {
3+
"isolatedModules": false
4+
},
5+
"extends": "../../tsconfig.json",
6+
"include": [
7+
"."
8+
]
9+
}

tsconfig.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@
3636
},
3737
{
3838
"path": "./src/popup"
39+
},
40+
{
41+
"path": "./src/restore"
3942
}
4043
]
4144
}

webpack.config.js

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,5 +90,25 @@ const popup = {
9090
],
9191
};
9292

93+
const restore = {
94+
...common,
95+
entry: {
96+
restore: path.resolve('src/restore/index.ts'),
97+
},
98+
module: {
99+
rules: [tsloader],
100+
},
101+
resolve: {
102+
extensions: ['.ts', '.js'],
103+
},
104+
plugins: [
105+
new HtmlWebpackPlugin({
106+
chunks: ['restore'],
107+
filename: 'restore.html',
108+
template: 'public/restore.html',
109+
}),
110+
],
111+
};
112+
93113
// Mutiple targets for webpack: https://webpack.js.org/concepts/targets/#multiple-targets
94-
export default [background, popup];
114+
export default [background, popup, restore];

0 commit comments

Comments
 (0)