Tabs Decorators
The Tabs decorators allow you to easily respond to browser tab events in your extension services. These decorators provide a clean way to handle tab creation, activation, updates, and removal.
Method Decorators
onTabActivated
This decorator handles events that fire when a tab becomes active.
import { onTabActivated, InjectableService } from 'deco-ext';
@InjectableService()
class TabMonitor {
@onTabActivated()
handleTabActivation(activeInfo: browser.Tabs.OnActivatedActiveInfoType) {
console.log(`Tab ${activeInfo.tabId} became active in window ${activeInfo.windowId}`);
// Perform actions when a tab is activated
this.trackActiveTab(activeInfo.tabId);
}
private trackActiveTab(tabId: number) {
// Track or respond to the newly active tab
}
}
With parameter decorator:
import { onTabActivated, activatedTabDetails, InjectableService } from 'deco-ext';
@InjectableService()
class TabMonitor {
@onTabActivated()
trackTabFocus(
@activatedTabDetails('tabId') tabId: number,
@activatedTabDetails('windowId') windowId: number
) {
console.log(`Tab ${tabId} became active in window ${windowId}`);
// Now you can work directly with the extracted properties
this.updateActiveTabIndicator(tabId);
}
private updateActiveTabIndicator(tabId: number) {
// Update UI or state based on active tab
}
}
onTabCreated
This decorator handles events that fire when a new tab is created.
import { onTabCreated, InjectableService } from 'deco-ext';
@InjectableService()
class TabTracker {
@onTabCreated()
handleNewTab(tab: browser.Tabs.Tab) {
console.log(`New tab created with ID: ${tab.id}`);
console.log(`Tab URL: ${tab.url}`);
// Perform actions when a new tab is created
if (tab.url?.startsWith('https://example.com')) {
this.processExampleTab(tab);
}
}
private processExampleTab(tab: browser.Tabs.Tab) {
// Process tabs from a specific domain
}
}
With parameter decorator:
import { onTabCreated, createdTabDetails, InjectableService } from 'deco-ext';
@InjectableService()
class TabTracker {
@onTabCreated()
logNewTab(
@createdTabDetails('id') tabId: number,
@createdTabDetails('url') url: string,
@createdTabDetails('index') position: number
) {
console.log(`New tab created with ID ${tabId} at position ${position}`);
if (url) {
console.log(`Initial URL: ${url}`);
this.categorizeTabByDomain(url);
}
}
private categorizeTabByDomain(url: string) {
// Categorize or track tabs based on domain
}
}
onTabRemoved
This decorator handles events that fire when a tab is closed.
import { onTabRemoved, InjectableService } from 'deco-ext';
@InjectableService()
class TabCleanupService {
@onTabRemoved()
handleTabClosure(info: browser.Tabs.OnRemovedRemoveInfoType & { tabId: number }) {
console.log(`Tab ${info.tabId} was closed`);
if (info.isWindowClosing) {
console.log(`Tab was closed because window ${info.windowId} is closing`);
} else {
console.log(`Tab was closed individually`);
}
// Clean up resources associated with the closed tab
this.cleanupTabResources(info.tabId);
}
private cleanupTabResources(tabId: number) {
// Remove cached data for the closed tab
}
}
With parameter decorator:
import { onTabRemoved, removedTabDetails, InjectableService } from 'deco-ext';
@InjectableService()
class TabCleanupService {
@onTabRemoved()
handleClosedTab(
@removedTabDetails('tabId') tabId: number,
@removedTabDetails('windowId') windowId: number,
@removedTabDetails('isWindowClosing') isWindowClosing: boolean
) {
if (isWindowClosing) {
console.log(`Tab ${tabId} closed due to window ${windowId} closing`);
this.handleWindowClosure(windowId);
} else {
console.log(`Tab ${tabId} closed individually in window ${windowId}`);
this.removeTabFromTracking(tabId);
}
}
private removeTabFromTracking(tabId: number) {
// Remove tab from internal tracking
}
private handleWindowClosure(windowId: number) {
// Handle window closure event
}
}
onTabUpdated
This decorator handles events that fire when a tab is updated.
import { onTabUpdated, InjectableService } from 'deco-ext';
@InjectableService()
class PageLoadMonitor {
@onTabUpdated()
handleTabUpdate(arg: {
details: browser.Tabs.OnUpdatedChangeInfoType,
tabId: number,
tab: browser.Tabs.Tab
}) {
console.log(`Tab ${arg.tabId} was updated`);
// Check if the page finished loading
if (arg.details.status === 'complete') {
console.log(`Page fully loaded: ${arg.tab.url}`);
this.analyzeLoadedPage(arg.tab);
}
// Check if the title changed
if (arg.details.title) {
console.log(`Title changed to: ${arg.details.title}`);
}
}
private analyzeLoadedPage(tab: browser.Tabs.Tab) {
// Process the fully loaded page
}
}
With parameter decorators:
import { onTabUpdated, tabUpdatedDetails, tabUpdatedTab, InjectableService } from 'deco-ext';
@InjectableService()
class PageMonitor {
@onTabUpdated()
trackPageChanges(
@tabUpdatedDetails('status') status: string,
@tabUpdatedDetails('title') title: string,
@tabUpdatedTab('id') tabId: number,
@tabUpdatedTab('url') url: string
) {
// Track loading state changes
if (status) {
console.log(`Tab ${tabId} status: ${status}`);
if (status === 'complete') {
console.log(`Page loaded: ${title || 'Untitled'} (${url || 'about:blank'})`);
this.indexPageContent(tabId, url, title);
}
}
// Track title changes
if (title) {
console.log(`Tab ${tabId} title changed to: ${title}`);
this.updateTabTitle(tabId, title);
}
}
private indexPageContent(tabId: number, url: string, title: string) {
// Index or process the content of the loaded page
}
private updateTabTitle(tabId: number, title: string) {
// Update internal records of tab titles
}
}
onTabZoomChange
This decorator handles events that fire when a tab's zoom factor changes.
import { onTabZoomChange, InjectableService } from 'deco-ext';
@InjectableService()
class ZoomMonitor {
@onTabZoomChange()
handleZoomChange(zoomChangeInfo: {
tabId: number;
oldZoomFactor: number;
newZoomFactor: number;
zoomSettings: browser.Tabs.ZoomSettings;
}) {
console.log(`Zoom changed for tab ${zoomChangeInfo.tabId}`);
console.log(`Zoom factor changed from ${zoomChangeInfo.oldZoomFactor} to ${zoomChangeInfo.newZoomFactor}`);
// Respond to zoom changes
this.recordZoomPreference(zoomChangeInfo.tabId, zoomChangeInfo.newZoomFactor);
}
private recordZoomPreference(tabId: number, zoomFactor: number) {
// Store user's zoom preferences
}
}
With parameter decorators:
import { onTabZoomChange, zoomChangeInfo, InjectableService } from 'deco-ext';
@InjectableService()
class ZoomTracker {
@onTabZoomChange()
logZoomChanges(
@zoomChangeInfo('tabId') tabId: number,
@zoomChangeInfo('oldZoomFactor') oldZoom: number,
@zoomChangeInfo('newZoomFactor') newZoom: number,
@zoomChangeInfo('zoomSettings') settings: browser.Tabs.ZoomSettings
) {
const changePercentage = Math.round((newZoom / oldZoom - 1) * 100);
console.log(`Tab ${tabId} zoom ${changePercentage > 0 ? 'increased' : 'decreased'} by ${Math.abs(changePercentage)}%`);
console.log(`Zoom mode: ${settings.mode}, Scope: ${settings.scope}`);
// Save user's zoom preferences for this domain
if (settings.scope === 'per-origin') {
this.saveZoomPreference(tabId, newZoom);
}
}
private saveZoomPreference(tabId: number, zoom: number) {
// Save zoom preferences for the current site
}
}
Parameter Decorators
activatedTabDetails
Used with onTabActivated
to extract specific properties from the activated tab details:
import { onTabActivated, activatedTabDetails, InjectableService } from 'deco-ext';
@InjectableService()
class TabMonitor {
@onTabActivated()
trackTabFocus(
@activatedTabDetails('tabId') tabId: number,
@activatedTabDetails('windowId') windowId: number
) {
console.log(`Tab ${tabId} became active in window ${windowId}`);
}
}
createdTabDetails
Used with onTabCreated
to extract specific properties from the created tab:
import { onTabCreated, createdTabDetails, InjectableService } from 'deco-ext';
@InjectableService()
class TabTracker {
@onTabCreated()
logNewTab(
@createdTabDetails('id') tabId: number,
@createdTabDetails('url') url: string,
@createdTabDetails('index') position: number,
@createdTabDetails('pinned') isPinned: boolean
) {
console.log(`New tab created with ID ${tabId}`);
console.log(`Position in window: ${position}`);
console.log(`URL: ${url || 'about:blank'}`);
console.log(`Pinned: ${isPinned ? 'Yes' : 'No'}`);
}
}
removedTabDetails
Used with onTabRemoved
to extract specific properties from the removed tab details:
import { onTabRemoved, removedTabDetails, InjectableService } from 'deco-ext';
@InjectableService()
class TabCleanupService {
@onTabRemoved()
handleClosedTab(
@removedTabDetails('tabId') tabId: number,
@removedTabDetails('windowId') windowId: number,
@removedTabDetails('isWindowClosing') isWindowClosing: boolean
) {
if (isWindowClosing) {
console.log(`Tab ${tabId} closed due to window ${windowId} closing`);
} else {
console.log(`Tab ${tabId} closed individually in window ${windowId}`);
}
}
}
tabUpdatedDetails
Used with onTabUpdated
to extract specific properties from the change info object:
import { onTabUpdated, tabUpdatedDetails, InjectableService } from 'deco-ext';
@InjectableService()
class PageStateMonitor {
@onTabUpdated()
trackPageState(
@tabUpdatedDetails('status') status: string,
@tabUpdatedDetails('title') title: string,
@tabUpdatedDetails('favIconUrl') favicon: string
) {
if (status) {
console.log(`Page status changed to: ${status}`);
if (status === 'complete') {
console.log('Page has finished loading');
} else if (status === 'loading') {
console.log('Page is currently loading');
}
}
if (title) {
console.log(`Page title updated: ${title}`);
}
if (favicon) {
console.log(`Favicon changed: ${favicon}`);
}
}
}
tabUpdatedTab
Used with onTabUpdated
to extract specific properties from the tab object:
import { onTabUpdated, tabUpdatedTab, InjectableService } from 'deco-ext';
@InjectableService()
class TabPropertiesMonitor {
@onTabUpdated()
trackTabProperties(
@tabUpdatedTab('id') tabId: number,
@tabUpdatedTab('url') url: string,
@tabUpdatedTab('active') isActive: boolean,
@tabUpdatedTab('pinned') isPinned: boolean
) {
console.log(`Tab ${tabId} properties updated`);
if (url) {
console.log(`Current URL: ${url}`);
this.categorizeTab(url);
}
console.log(`Active: ${isActive ? 'Yes' : 'No'}`);
console.log(`Pinned: ${isPinned ? 'Yes' : 'No'}`);
}
private categorizeTab(url: string) {
// Categorize the tab based on the URL
}
}
zoomChangeInfo
Used with onTabZoomChange
to extract properties from the zoom change info object:
import { onTabZoomChange, zoomChangeInfo, InjectableService } from 'deco-ext';
@InjectableService()
class ZoomTracker {
@onTabZoomChange()
logZoomChanges(
@zoomChangeInfo('tabId') tabId: number,
@zoomChangeInfo('oldZoomFactor') oldZoom: number,
@zoomChangeInfo('newZoomFactor') newZoom: number,
@zoomChangeInfo('zoomSettings') settings: browser.Tabs.ZoomSettings
) {
const changePercentage = Math.round((newZoom / oldZoom - 1) * 100);
console.log(`Tab ${tabId} zoom ${changePercentage > 0 ? 'increased' : 'decreased'} by ${Math.abs(changePercentage)}%`);
console.log(`Zoom settings: ${settings.mode} (${settings.scope})`);
}
}
Implementation Details
These decorators use a singleton pattern to ensure only one event listener is registered per event type, and then route events to all decorated methods. When a tab event occurs:
- The event is received by the single registered browser API listener
- The event data is passed to all registered method handlers
- For each handler:
- The class instance is resolved from the dependency injection container
- If the class has an
init
method, it's called before handling the event - If parameter decorators are used, the event data is transformed accordingly
- The method is called with the appropriate parameters
The decorators can only be used on methods within classes that have been decorated with the InjectableService
decorator from deco-ext.