Skip to content

Commit

Permalink
Use content script to stat browsing time for mv3 (#71)
Browse files Browse the repository at this point in the history
  • Loading branch information
sheepzh authored and [email protected] committed Feb 23, 2023
1 parent 1366890 commit 2b9956c
Show file tree
Hide file tree
Showing 39 changed files with 262 additions and 441 deletions.
17 changes: 9 additions & 8 deletions global.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,10 +110,6 @@ declare namespace timer {
}

type StatisticsOption = {
/**
* Count when idle
*/
countWhenIdle: boolean
/**
* Whether to collect the site name
*
Expand Down Expand Up @@ -230,6 +226,12 @@ declare namespace timer {
| 'mn'

namespace stat {
type Event = {
start: number
end: number
url: string
ignoreTabCheck: boolean
}
/**
* The dimension to statistics
*/
Expand Down Expand Up @@ -530,6 +532,7 @@ declare namespace timer {
| "cs.getTodayInfo"
| "cs.moreMinutes"
| "cs.getLimitedRules"
| "cs.trackTime"
type ResCode = "success" | "fail" | "ignore"

/**
Expand All @@ -550,7 +553,7 @@ declare namespace timer {
/**
* @since 1.3.0
*/
type Handler<Req, Res> = (data: Req, sender: chrome.runtime.MessageSender) => Promise<Res>
type Handler<Req, Res> = (data: Req, sender?: chrome.runtime.MessageSender) => Promise<Res>
/**
* @since 0.8.4
*/
Expand All @@ -575,6 +578,4 @@ declare type ChromeAlarm = chrome.alarms.Alarm
// chrome.runtime
declare type ChromeOnInstalledReason = chrome.runtime.OnInstalledReason
declare type ChromeMessageSender = chrome.runtime.MessageSender
declare type ChromeMessageHandler<T = any, R = any> = (req: timer.mq.Request<T>, sender: ChromeMessageSender) => Promise<timer.mq.Response<R>>
// chrome.idle
declare type ChromeIdleState = chrome.idle.IdleState
declare type ChromeMessageHandler<T = any, R = any> = (req: timer.mq.Request<T>, sender: ChromeMessageSender) => Promise<timer.mq.Response<R>>
10 changes: 0 additions & 10 deletions script/crowdin/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -214,13 +214,3 @@ export async function checkMainBranch(client: CrowdinClient) {
}
return branch
}

// function main() {
// const file = fs.readFileSync(path.join(MSG_BASE, 'app', 'habit.ts'), { encoding: 'utf-8' })
// const result = /(const|let|var) _default(.*)=\s*\{\s*(\n?.*\n)+\}/.exec(file)
// const origin = result[0]
// console.log(origin)
// console.log(file.indexOf(origin))
// }

// main()
11 changes: 11 additions & 0 deletions src/api/chrome/context-menu.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
import { IS_MV3 } from "@util/constant/environment"
import { handleError } from "./common"

function onClick(id: string, handler: Function) {
chrome.contextMenus.onClicked.addListener(({ menuItemId }) => menuItemId === id && handler?.())
}

export function createContextMenu(props: ChromeContextMenuCreateProps): Promise<void> {
let clickHandler: Function = undefined
if (IS_MV3) {
clickHandler = props.onclick
delete props.onclick
}
return new Promise(resolve => chrome.contextMenus.create(props, () => {
handleError('createContextMenu')
clickHandler && onClick(props.id, clickHandler)
resolve()
}))
}
Expand Down
3 changes: 0 additions & 3 deletions src/api/chrome/idle.ts

This file was deleted.

49 changes: 13 additions & 36 deletions src/app/components/option/components/statistics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,12 @@ import { defaultStatistics } from "@util/constant/option"
import { defineComponent, h, reactive, unref } from "vue"
import { t } from "@app/locale"
import { renderOptionItem, tagText, tooltip } from "../common"
import { IS_SAFARI } from "@util/constant/environment"

function updateOptionVal(key: keyof timer.option.StatisticsOption, newVal: boolean, option: UnwrapRef<timer.option.StatisticsOption>) {
option[key] = newVal
optionService.setStatisticsOption(unref(option))
}

const countWhenIdle = (option: UnwrapRef<timer.option.StatisticsOption>) => h(ElSwitch, {
modelValue: option.countWhenIdle,
onChange: (newVal: boolean) => updateOptionVal('countWhenIdle', newVal, option)
})

const countLocalFiles = (option: UnwrapRef<timer.option.StatisticsOption>) => h(ElSwitch, {
modelValue: option.countLocalFiles,
Expand All @@ -36,40 +31,10 @@ const collectSiteName = (option: UnwrapRef<timer.option.StatisticsOption>) => h(
})

function copy(target: timer.option.StatisticsOption, source: timer.option.StatisticsOption) {
target.countWhenIdle = source.countWhenIdle
target.collectSiteName = source.collectSiteName
target.countLocalFiles = source.countLocalFiles
}

function renderOptionItems(option: timer.option.StatisticsOption) {
const result = []
if (!IS_SAFARI) {
// chrome.idle does not work in Safari, so not to display this option
result.push(
renderOptionItem({
input: countWhenIdle(option),
idleTime: tagText(msg => msg.option.statistics.idleTime),
info: tooltip(msg => msg.option.statistics.idleTimeInfo)
}, msg => msg.statistics.countWhenIdle, t(msg => msg.option.yes)),
h(ElDivider)
)
}
result.push(
renderOptionItem({
input: countLocalFiles(option),
localFileTime: tagText(msg => msg.option.statistics.localFileTime),
info: tooltip(msg => msg.option.statistics.localFilesInfo)
}, msg => msg.statistics.countLocalFiles, t(msg => msg.option.no)),
h(ElDivider),
renderOptionItem({
input: collectSiteName(option),
siteName: tagText(msg => msg.option.statistics.siteName),
siteNameUsage: tooltip(msg => msg.option.statistics.siteNameUsage)
}, msg => msg.statistics.collectSiteName, t(msg => msg.option.yes))
)
return result
}

const _default = defineComponent({
name: "StatisticsOptionContainer",
setup(_props, ctx) {
Expand All @@ -81,7 +46,19 @@ const _default = defineComponent({
await optionService.setStatisticsOption(unref(option))
}
})
return () => h('div', renderOptionItems(option))
return () => h('div', [
renderOptionItem({
input: countLocalFiles(option),
localFileTime: tagText(msg => msg.option.statistics.localFileTime),
info: tooltip(msg => msg.option.statistics.localFilesInfo)
}, msg => msg.statistics.countLocalFiles, t(msg => msg.option.no)),
h(ElDivider),
renderOptionItem({
input: collectSiteName(option),
siteName: tagText(msg => msg.option.statistics.siteName),
siteNameUsage: tooltip(msg => msg.option.statistics.siteNameUsage)
}, msg => msg.statistics.collectSiteName, t(msg => msg.option.yes))
])
}
})

Expand Down
2 changes: 1 addition & 1 deletion src/background/active-tab-listener.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export default class ActiveTabListener {
listen() {
onTabActivated(async tabId => {
const tab = await getTab(tabId)
this.processWithTabInfo(tab)
tab && this.processWithTabInfo(tab)
})
}
}
Expand Down
12 changes: 5 additions & 7 deletions src/background/badge-text-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import TimerDatabase from "@db/timer-database"
import whitelistHolder from "@service/components/whitelist-holder"
import optionService from "@service/option-service"
import { extractHostname, isBrowserUrl } from "@util/pattern"
import alarmManager from "./alarm-manager"

const storage = chrome.storage.local
const timerDb: TimerDatabase = new TimerDatabase(storage)
Expand All @@ -26,6 +25,7 @@ export type BadgeLocation = {
* The url of tab
*/
url: string
focus?: number
}

function mill2Str(milliseconds: number) {
Expand Down Expand Up @@ -67,7 +67,7 @@ async function updateFocus(badgeLocation?: BadgeLocation, lastLocation?: BadgeLo
if (!badgeLocation) {
return badgeLocation
}
const { url, tabId } = badgeLocation
const { url, tabId, focus } = badgeLocation
if (!url || isBrowserUrl(url)) {
return badgeLocation
}
Expand All @@ -76,7 +76,7 @@ async function updateFocus(badgeLocation?: BadgeLocation, lastLocation?: BadgeLo
setBadgeText('W', tabId)
return badgeLocation
}
const milliseconds = host ? (await timerDb.get(host, new Date())).focus : undefined
const milliseconds = focus || (host ? (await timerDb.get(host, new Date())).focus : undefined)
setBadgeTextOfMills(milliseconds, tabId)
return badgeLocation
}
Expand All @@ -90,14 +90,12 @@ class BadgeTextManager {
this.pauseOrResumeAccordingToOption(!!option.displayBadgeText)
optionService.addOptionChangeListener(({ displayBadgeText }) => this.pauseOrResumeAccordingToOption(displayBadgeText))
whitelistHolder.addPostHandler(updateFocus)

alarmManager.setInterval('badage-text-manager', 1000, () => !this.isPaused && updateFocus())
}

/**
* Hide the badge text
*/
async pause() {
private async pause() {
this.isPaused = true
const tab = await findActiveTab()
setBadgeText('', tab?.tabId)
Expand All @@ -106,7 +104,7 @@ class BadgeTextManager {
/**
* Show the badge text
*/
resume() {
private resume() {
this.isPaused = false
// Update badge text immediately
this.forceUpdate()
Expand Down
11 changes: 4 additions & 7 deletions src/background/browser-action-menu-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,15 @@
import { OPTION_ROUTE } from "../app/router/constants"
import { getAppPageUrl, getGuidePageUrl, SOURCE_CODE_PAGE, TU_CAO_PAGE } from "@util/constant/url"
import { t2Chrome } from "@i18n/chrome/t"
import { IS_MV3, IS_SAFARI } from "@util/constant/environment"
import { IS_SAFARI } from "@util/constant/environment"
import { createTab } from "@api/chrome/tab"
import { createContextMenu } from "@api/chrome/context-menu"
import { getRuntimeId } from "@api/chrome/runtime"
import { createContextMenu } from "@api/chrome/context-menu"

const APP_PAGE_URL = getAppPageUrl(true)

const baseProps: Partial<ChromeContextMenuCreateProps> = {
// Cast unknown to fix the error with manifestV2
// Because 'browser_action' will be replaced with 'action' in union type chrome.contextMenus.ContextType since V3
// But 'action' does not work in V2
contexts: [IS_MV3 ? 'action' : 'browser_action'],
const baseProps: Partial<chrome.contextMenus.CreateProperties> = {
contexts: ['action'],
visible: true
}

Expand Down
14 changes: 3 additions & 11 deletions src/background/content-script-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,13 @@ import MessageDispatcher from "./message-dispatcher"
export default function init(dispatcher: MessageDispatcher) {
dispatcher
// Increase the visit time
.register<string, void>('cs.incVisitCount', async host => {
timerService.addOneTime(host)
})
.register<string, void>('cs.incVisitCount', host => timerService.addOneTime(host))
// Judge is in whitelist
.register<string, boolean>('cs.isInWhitelist', host => whitelistService.include(host))
// Need to print the information of today
.register<void, boolean>('cs.printTodayInfo', async () => {
const option = await optionService.getAllOption()
return !!option.printInConsole
})
.register<void, boolean>('cs.printTodayInfo', async () => !!(await optionService.getAllOption())?.printInConsole)
// Get today info
.register<string, timer.stat.Result>('cs.getTodayInfo', host => {
const now = new Date()
return timerService.getResult(host, now)
})
.register<string, timer.stat.Result>('cs.getTodayInfo', host => timerService.getResult(host, new Date()))
// More minutes
.register<string, timer.limit.Item[]>('cs.moreMinutes', url => limitService.moreMinutes(url))
// cs.getLimitedRules
Expand Down
5 changes: 1 addition & 4 deletions src/background/icon-and-alias-collector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import HostAliasDatabase from "@db/host-alias-database"
import IconUrlDatabase from "@db/icon-url-database"
import OptionDatabase from "@db/option-database"
import { IS_CHROME, IS_SAFARI } from "@util/constant/environment"
import { iconUrlOfBrowser } from "@util/constant/url"
import { extractHostname, isBrowserUrl, isHomepage } from "@util/pattern"
import { defaultStatistics } from "@util/constant/option"
import { extractSiteName } from "@util/site"
Expand Down Expand Up @@ -46,13 +45,11 @@ async function processTabInfo(tab: ChromeTab): Promise<void> {
if (isBrowserUrl(url)) return
const hostInfo = extractHostname(url)
const host = hostInfo.host
const protocol = hostInfo.protocol
if (!host) return
let favIconUrl = tab.favIconUrl
// localhost hosts with Chrome use cache, so keep the favIcon url undefined
IS_CHROME && /^localhost(:.+)?/.test(host) && (favIconUrl = undefined)
const iconUrl = favIconUrl || iconUrlOfBrowser(protocol, host)
iconUrl && iconUrlDatabase.put(host, iconUrl)
favIconUrl && iconUrlDatabase.put(host, favIconUrl)
collectAliasEnabled && !isBrowserUrl(url) && isHomepage(url) && collectAlias(host, tab.title)
}

Expand Down
24 changes: 7 additions & 17 deletions src/background/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,18 @@ import { openLog } from "../common/logger"
import WhitelistMenuManager from "./whitelist-menu-manager"
import BrowserActionMenuManager from "./browser-action-menu-manager"
import IconAndAliasCollector from "./icon-and-alias-collector"
import Timer from "./timer"
import VersionManager from "./version-manager"
import ActiveTabListener from "./active-tab-listener"
import badgeTextManager from "./badge-text-manager"
import metaService from "@service/meta-service"
import UninstallListener from "./uninstall-listener"
import { getGuidePageUrl } from "@util/constant/url"
import MessageDispatcher from "./message-dispatcher"
import initLimitProcesser from "./limit-processor"
import initCsHandler from "./content-script-handler"
import { isBrowserUrl } from "@util/pattern"
import BackupScheduler from "./backup-scheduler"
import { createTab, listTabs } from "@api/chrome/tab"
import { listTabs } from "@api/chrome/tab"
import { isNoneWindowId, onNormalWindowFocusChanged } from "@api/chrome/window"
import { onInstalled } from "@api/chrome/runtime"
import initServer from "./timer/server"
import handleInstall from "./install-handler"

// Open the log of console
openLog()
Expand All @@ -37,7 +34,8 @@ initLimitProcesser(messageDispatcher)
initCsHandler(messageDispatcher)

// Start the timer
new Timer().start()
// new Timer().start()
initServer(messageDispatcher)

// Collect the icon url and title
new IconAndAliasCollector().listen()
Expand Down Expand Up @@ -70,15 +68,7 @@ onNormalWindowFocusChanged(async windowId => {
.forEach(({ url, id }) => badgeTextManager.forceUpdate({ url, tabId: id }))
})

// Collect the install time
onInstalled(async reason => {
if (reason === "install") {
createTab(getGuidePageUrl(true))
await metaService.updateInstallTime(new Date())
}
// Questionnaire for uninstall
new UninstallListener().listen()
})
handleInstall()

// Start message dispatcher
messageDispatcher.start()
messageDispatcher.start()
35 changes: 35 additions & 0 deletions src/background/install-handler/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { onInstalled } from "@api/chrome/runtime"
import { createTab, listTabs } from "@api/chrome/tab"
import metaService from "@service/meta-service"
import { getGuidePageUrl } from "@util/constant/url"
import { isBrowserUrl } from "@util/pattern"
import UninstallListener from './uninstall-listener'

async function onFirstInstall() {
createTab(getGuidePageUrl(true))
metaService.updateInstallTime(new Date())
}

function executeScript(tabId: number, files: string[]) {
chrome.scripting.executeScript({ target: { tabId }, files }).catch(err => console.log(err))
}

async function reloadContentScript() {
const files = chrome.runtime.getManifest().content_scripts?.[0]?.js
if (!files?.length) {
return
}
const tabs = await listTabs()
tabs.filter(({ url }) => url && !isBrowserUrl(url))
.forEach(tab => executeScript(tab.id, files))
}

export default function handleInstall() {
onInstalled(async reason => {
reason === "install" && await onFirstInstall()
// Questionnaire for uninstall
new UninstallListener().listen()
// Reload content-script
await reloadContentScript()
})
}
File renamed without changes.
Loading

0 comments on commit 2b9956c

Please sign in to comment.