Skip to content

Commit

Permalink
Make backup available in service worker using offscreen and alarms
Browse files Browse the repository at this point in the history
  • Loading branch information
sienori committed May 26, 2024
1 parent 8646f55 commit 6e5cba0
Show file tree
Hide file tree
Showing 6 changed files with 97 additions and 15 deletions.
4 changes: 3 additions & 1 deletion src/background/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@ const onStartupListener = async () => {
if (startupBehavior === "previousSession") openLastSession();
else if (startupBehavior === "startupSession") openStartupSessions();
setAutoSave();
setTimeout(backupSessions, 30000);
syncCloudAuto();
browser.alarms.create("backupSessions", { delayInMinutes: 0.5 });
};

const onMessageListener = async (request, sender, sendResponse) => {
Expand Down Expand Up @@ -194,6 +194,8 @@ const onAlarmListener = async (alarmInfo) => {
switch (alarmInfo.name) {
case "autoSaveRegular":
return autoSaveRegular();
case "backupSessions":
return backupSessions();
}
}

Expand Down
60 changes: 48 additions & 12 deletions src/background/export.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import moment from "moment";
import log from "loglevel";
import getSessions from "./getSessions";
import { getSettings } from "../settings/settings";
import { init } from "./background";

const logDir = "background/export";

Expand All @@ -12,12 +13,7 @@ export default async function exportSessions(id = null, folderName = "", isBacku
if (sessions == undefined) return;
if (!Array.isArray(sessions)) sessions = [sessions];

const downloadUrl = URL.createObjectURL(
new Blob([JSON.stringify(sessions, null, " ")], {
type: "application/json"
})
);

const downloadUrl = await createObjectURL(sessions);
const replacedFolderName = replaceFolderName(folderName);
const fileName = generateFileName(sessions, isBackup);

Expand All @@ -30,7 +26,7 @@ export default async function exportSessions(id = null, folderName = "", isBacku
})
.catch(e => {
log.warn(logDir, "exportSessions()", e);
URL.revokeObjectURL(downloadUrl);
revokeObjectURL(downloadUrl);
});

if (downloadId) recordDownloadUrl(downloadId, downloadUrl, isBackup);
Expand Down Expand Up @@ -76,27 +72,67 @@ function replaceFolderName(folderName) {
return folderName;
}

// バックアップ開始からダウンロード完了までの間にServiceWorkerは停止しない想定
let downloadRecords = {};

export const handleDownloadsChanged = async (status) => {
await init();
const downloadUrl = downloadRecords[status.id]?.downloadUrl;
if (!downloadUrl) return;

if (status.state?.current === "complete") revokeDownloadUrl(status.id);
if (status.state?.current === "complete") await revokeDownloadUrl(status.id);
if (status?.error?.current === "FILE_FAILED") {
log.error(logDir, "handleDownloadsChanged()", "failed", status, downloadRecords[status.id]);
revokeDownloadUrl(status.id);
await revokeDownloadUrl(status.id);
}
};

const recordDownloadUrl = (downloadId, downloadUrl, isBackup) => {
downloadRecords[downloadId] = { downloadUrl, isBackup };
};

const revokeDownloadUrl = (downloadId) => {
const revokeDownloadUrl = async (downloadId) => {
const { downloadUrl, isBackup } = downloadRecords[downloadId];
if (isBackup) browser.downloads.erase({ id: downloadId });
URL.revokeObjectURL(downloadUrl);
revokeObjectURL(downloadUrl);
delete downloadRecords[downloadId];
};
};

const revokeObjectURL = downloadUrl => {
if (URL?.revokeObjectURL) {
URL.revokeObjectURL(downloadUrl);
} else {
browser.runtime.sendMessage({
message: "offscreen_revokeObjectUrl",
downloadUrl: downloadUrl
});
}
}

const createObjectURL = async (sessions) => {
if (URL?.createObjectURL) {
return URL.createObjectURL(
new Blob([JSON.stringify(sessions, null, " ")], {
type: "application/json"
})
);
} else {
// ChromeのServiceWorkerではURL.createObjectURLが利用できないため、offscreen経由で生成する
const existingContexts = await browser.runtime.getContexts({
contextTypes: ['OFFSCREEN_DOCUMENT'],
documentUrls: [browser.runtime.getURL("offscreen/index.html")]
});
if (existingContexts == 0) {
await browser.offscreen.createDocument({
url: "offscreen/index.html",
reasons: ["BLOBS"],
justification: 'Use URL.createObjectURL',
});
}

return await browser.runtime.sendMessage({
message: "offscreen_createObjectUrl",
sessions: sessions
});
}
}
3 changes: 2 additions & 1 deletion src/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
"tabs",
"downloads",
"identity",
"alarms"
"alarms",
"offscreen"
],
"optional_host_permissions": [
"https://www.googleapis.com/*"
Expand Down
4 changes: 4 additions & 0 deletions src/offscreen/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<!doctype html>
<html>

</html>
32 changes: 32 additions & 0 deletions src/offscreen/offscreen.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import browser from "webextension-polyfill";

const createObjectURL = sessions => {
return URL.createObjectURL(
new Blob([JSON.stringify(sessions, null, " ")], {
type: "application/json"
})
);
}

const revokeObjectURL = downloadUrl => {
URL.revokeObjectURL(downloadUrl);
}

const onMessageListener = async (request, sender, sendResponse) => {
switch (request.message) {
case "offscreen_createObjectUrl":
return createObjectURL(request.sessions);
case "offscreen_revokeObjectUrl":
return revokeObjectURL(request.downloadUrl);
}

// backgroundやpopup宛のメッセージとの混信を防ぐ
const empty = new Promise(resolve => {
setTimeout(() => {
return resolve("");
}, 1000);
});
return empty;
}

browser.runtime.onMessage.addListener(onMessageListener);
9 changes: 8 additions & 1 deletion webpack.utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ const getHTMLPlugins = (browserDir, outputDir = "dev", sourceDir = "src") => [
filename: path.resolve(__dirname, `${outputDir}/${browserDir}/replaced/replaced.html`),
template: `${sourceDir}/replaced/index.html`,
chunks: ["replaced"]
}),
new HtmlWebpackPlugin({
title: "Offscreen",
filename: path.resolve(__dirname, `${outputDir}/${browserDir}/offscreen/index.html`),
template: `${sourceDir}/offscreen/index.html`,
chunks: ["offscreen"]
})
];

Expand All @@ -49,7 +55,8 @@ const getEntry = (sourceDir = "src") => {
popup: path.resolve(__dirname, `${sourceDir}/popup/index.js`),
options: path.resolve(__dirname, `${sourceDir}/options/index.js`),
replaced: path.resolve(__dirname, `${sourceDir}/replaced/replaced.js`),
background: path.resolve(__dirname, `${sourceDir}/background/background.js`)
background: path.resolve(__dirname, `${sourceDir}/background/background.js`),
offscreen: path.resolve(__dirname, `${sourceDir}/offscreen/offscreen.js`)
};
};

Expand Down

0 comments on commit 6e5cba0

Please sign in to comment.