Migration tabs sample (#964)

* Add pin sample

* Add README.md

* Add screenshot sample

* Add README.md

* Add zoom sample

* Add README.md

* Add inspector sample

* Add README.md

* Fix typo

* Remove wrapper

* Remove inline style

* Update api-samples/tabs/pin/README.md

Co-authored-by: Joe Medley <jmedley@google.com>

* Update api-samples/tabs/screenshot/README.md

Co-authored-by: Joe Medley <jmedley@google.com>

* Update api-samples/tabs/zoom/README.md

Co-authored-by: Joe Medley <jmedley@google.com>

* Update api-samples/tabs/zoom/README.md

Co-authored-by: Joe Medley <jmedley@google.com>

* Update api-samples/tabs/zoom/README.md

Co-authored-by: Joe Medley <jmedley@google.com>

* Update api-samples/tabs/zoom/README.md

Co-authored-by: Joe Medley <jmedley@google.com>

* Update api-samples/tabs/inspector/README.md

Co-authored-by: Joe Medley <jmedley@google.com>

* Update api-samples/tabs/zoom/README.md

Co-authored-by: Joe Medley <jmedley@google.com>

* Update README

* Update for loop

* Remove children select

* Change selected to active

* Fix event listener

* Set checkboxs to disabled

* Fix layout

* Fix wrong input data in create tab function

* Update api-samples/tabs/zoom/README.md

Co-authored-by: Joe Medley <jmedley@google.com>

* Update description

* Update description

* Update description

* Fix typo

* Update description

* Rename

* Fix file name

* Update active check box

* Update code style

* Update api-samples/tabs/zoom/README.md

Co-authored-by: Joe Medley <jmedley@google.com>

* Update api-samples/tabs/pin/README.md

Co-authored-by: Joe Medley <jmedley@google.com>

* Update api-samples/tabs/pin/README.md

Co-authored-by: Joe Medley <jmedley@google.com>

* Apply suggestions from code review

Co-authored-by: Joe Medley <jmedley@google.com>

* Update api-samples/tabs/inspector/manifest.json

Co-authored-by: Joe Medley <jmedley@google.com>

* Fix case issues

* Update api-samples/tabs/inspector/manifest.json

---------

Co-authored-by: Joe Medley <jmedley@google.com>
Co-authored-by: Oliver Dunk <oliver@oliverdunk.com>
This commit is contained in:
Xuezhou Dai
2023-11-13 21:35:41 +08:00
committed by GitHub
parent 38f60eb2d4
commit bbcd025851
24 changed files with 1081 additions and 0 deletions

View File

@@ -0,0 +1,13 @@
# chrome.tabs - Tab Inspector
A sample that demonstrates how to use the [`chrome.tabs`](https://developer.chrome.com/docs/extensions/reference/tabs/) API.
## Overview
In the sample, a simple tab inspector manipulates the tabs and windows.
## Running this extension
1. Clone this repository.
2. Load this directory in Chrome as an [unpacked extension](https://developer.chrome.com/docs/extensions/mv3/getstarted/development-basics/#load-unpacked).
3. Click the extension's icon to open the tab inspector.

View File

@@ -0,0 +1,13 @@
{
"name": "Tab Inspector",
"description": "Demonstrates the chrome.tabs API and the chrome.windows API by providing a user interface to manage tabs and windows.",
"version": "0.3",
"permissions": ["tabs"],
"background": {
"service_worker": "service-worker.js"
},
"action": {
"default_title": "Show tab inspector"
},
"manifest_version": 3
}

View File

@@ -0,0 +1,5 @@
chrome.action.onClicked.addListener(function () {
chrome.tabs.create({
url: chrome.runtime.getURL('window_and_tabs_manager.html')
});
});

View File

@@ -0,0 +1,110 @@
.window-item {
background-color: #aaeeee;
margin: 4px;
padding: 8px;
margin: 20px;
}
.window-id-wrapper {
font-style: italic;
width: 80px;
display: inline-block;
}
.window_left,
.window_right,
.window_width,
.window_height {
display: inline-block;
}
.window-status-input {
width: 60px;
}
.tab-item {
background-color: #eeeeee;
margin: 8px;
padding: 4px;
}
.wrapper {
margin: 8px;
}
.tab_id {
font-style: italic;
width: 80px;
display: inline-block;
}
.tab-actions-wrapper {
display: inline-block;
}
.tab_index {
width: 20px;
}
.tab_window_id {
width: 80px;
}
#log {
background-color: #eeaaee;
margin: 20px;
padding: 8px;
}
.input-group {
display: flex;
justify-content: space-between;
}
.label {
display: inline-block;
}
.tab_title,
.tab_url,
#new_window_url,
#window_id_new,
#url_new {
width: 90%;
}
.new-window-wrapper {
background-color: #eeeebb;
margin: 20px;
padding: 8px;
}
.new-tab-wrapper {
background-color: #eeeeaa;
margin: 20px;
padding: 8px;
}
h3 {
text-align: center;
margin: 8px;
}
.window_left,
.window_top,
.window_width,
.window_height,
#new_window_left,
#new_window_top,
#new_window_width,
#new_window_height {
width: 50px;
}
.new-window-status-wrapper {
display: inline-block;
}
.actions-wrapper {
margin: 20px;
}

View File

@@ -0,0 +1,107 @@
<!DOCTYPE html>
<html>
<head>
<title>Tabs Inspector</title>
<link rel="stylesheet" href="./window_and_tabs_manager.css" />
</head>
<body>
<div id="windowList">
<!-- WindowItems Here -->
</div>
<template id="windowItem">
<div class="window-item">
<div class="window-id-wrapper">
Window: <span class="window_id"></span>
</div>
<div class="window-status-wrapper">
left:
<input class="window_left" type="text" /> top:
<input class="window_top" type="text" /> width:
<input class="window_width" type="text" /> height:
<input class="window_height" type="text" />
<input class="window_focused" type="checkbox" disabled /> Focused
<input class="window_current" type="checkbox" disabled /> Current
<button class="window_refresh">Refresh</button>
</div>
<div id="tabList">
<!-- TabItems Here -->
</div>
<button class="update_window_button">Update Window</button>
<button class="remove_window_button">Close Window</button>
<button class="refresh_active_tab_button">Refresh Active Tab</button>
</div>
</template>
<template id="tabItem">
<div class="tab-item">
<div class="wrapper">
<div class="tab_id"></div>
<div class="tab-actions-wrapper">
index:
<input type="text" class="tab_index" />
windowId:
<input type="text" class="tab_window_id" />
<button class="move_tab_button">Move</button>
<button class="refresh_tab_button">Refresh</button>
</div>
</div>
<div class="wrapper">
<div class="input-group">
<div class="label">title:</div>
<input type="text" class="tab_title" />
</div>
<div class="input-group">
<div class="label">url:</div>
<input type="text" class="tab_url" />
</div>
<div><input type="checkbox" class="tab_active" /> Active</div>
</div>
<button class="update_tab_button">Update Tab</button>
<button class="remove_tab_button">Close Tab</button>
</div>
</template>
<div class="new-window-wrapper">
<h3>Create Window</h3>
<div class="wrapper">
<div class="new-window-status-wrapper">
left:
<input type="text" id="new_window_left" /> top:
<input type="text" id="new_window_top" /> width:
<input type="text" id="new_window_width" /> height:
<input type="text" id="new_window_height" />
</div>
</div>
<div class="wrapper">
<div class="input-group">
<div class="label">url:</div>
<input type="text" id="new_window_url" />
</div>
</div>
<button id="create_window_button">Create</button>
</div>
<div class="new-tab-wrapper">
<h3>Create Tab</h3>
<div class="wrapper">
<div class="input-group">
<div class="label">windowId:</div>
<input type="text" id="window_id_new" />
</div>
<div class="input-group">
<div class="label">url:</div>
<input type="text" id="url_new" />
</div>
<div><input type="checkbox" id="active_new" /> active</div>
</div>
<button id="create_tab_button">Create</button>
</div>
<div class="actions-wrapper">
<button id="load_window_list_button">Refresh</button>
<button id="update_all_button">Update All</button>
<button id="move_all_button">Move All</button>
<button id="clear_log_button">Clear Log</button>
<button id="new_window_button">New Window</button>
</div>
<div id="log"></div>
<script src="window_and_tabs_manager.js"></script>
</body>
</html>

View File

@@ -0,0 +1,437 @@
let tabs = {};
let tabIds = [];
let focusedWindowId = undefined;
let currentWindowId = undefined;
async function bootstrap() {
const currentWindow = await chrome.windows.getCurrent();
currentWindowId = currentWindow.id;
const focusedWindow = await chrome.windows.getLastFocused();
focusedWindowId = focusedWindow.id;
loadWindowList();
}
function isInt(i) {
return typeof i == 'number' && !(i % 1) && !isNaN(i);
}
const windowTemplate = document.getElementById('windowItem').content;
const tabTemplate = document.getElementById('tabItem').content;
async function loadWindowList() {
const windowList = await chrome.windows.getAll({ populate: true });
tabs = {};
tabIds = [];
for (let window of windowList) {
for (let tab of window.tabs) {
tabIds.push(tab.id);
tabs[tab.id] = tab;
}
}
const output = document.getElementById('windowList');
output.innerHTML = '';
for (let window of windowList) {
const windowItem = document.importNode(windowTemplate, true).children[0];
renderWindow(window, windowItem);
registerWindowEvents(window, windowItem);
output.appendChild(windowItem);
}
}
function renderWindow(window, windowItem) {
windowItem.id = `window_${window.id}`;
windowItem.querySelector('.window_left').id = `left_${window.id}`;
windowItem.querySelector('.window_top').id = `top_${window.id}`;
windowItem.querySelector('.window_width').id = `width_${window.id}`;
windowItem.querySelector('.window_height').id = `height_${window.id}`;
windowItem.querySelector('.window_focused').id = `focused_${window.id}`;
windowItem.querySelector('.window_current').id = `current_${window.id}`;
windowItem.querySelector('.window_id').innerText = window.id;
windowItem.querySelector('.window_left').value = window.left;
windowItem.querySelector('.window_top').value = window.top;
windowItem.querySelector('.window_width').value = window.width;
windowItem.querySelector('.window_height').value = window.height;
windowItem.querySelector('.window_focused').checked =
window.id == focusedWindowId;
windowItem.querySelector('.window_current').checked =
window.id == currentWindowId;
windowItem.querySelector('#tabList').innerHTML = '';
for (let tab of window.tabs) {
const tabItem = document.importNode(tabTemplate, true).children[0];
renderTab(tab, tabItem);
registerTabEvents(tab, tabItem);
windowItem.querySelector('#tabList').appendChild(tabItem);
}
}
function registerWindowEvents(window, windowItem) {
windowItem
.querySelector('.window_refresh')
.addEventListener('click', function () {
refreshWindow(window.id);
});
windowItem
.querySelector('.update_window_button')
.addEventListener('click', function () {
updateWindow(window.id);
});
windowItem
.querySelector('.remove_window_button')
.addEventListener('click', function () {
removeWindow(window.id);
});
windowItem
.querySelector('.refresh_active_tab_button')
.addEventListener('click', function () {
refreshActiveTab(window.id);
});
}
function renderTab(tab, tabItem) {
tabItem.id = `tab_${tab.id}`;
tabItem.querySelector('.tab_index').id = `index_${tab.id}`;
tabItem.querySelector('.tab_window_id').id = `windowId_${tab.id}`;
tabItem.querySelector('.tab_title').id = `title_${tab.id}`;
tabItem.querySelector('.tab_url').id = `url_${tab.id}`;
tabItem.querySelector('.tab_active').id = `active_${tab.id}`;
tabItem.querySelector('.tab_id').innerText = `TabId: ${tab.id}`;
tabItem.querySelector('.tab_index').value = tab.index;
tabItem.querySelector('.tab_window_id').value = tab.windowId;
tabItem.querySelector('.tab_title').value = tab.title;
tabItem.querySelector('.tab_url').value = tab.url;
tabItem.querySelector('.tab_active').checked = tab.active;
}
function registerTabEvents(tab, tabItem) {
tabItem
.querySelector('.move_tab_button')
.addEventListener('click', function () {
moveTab(tab.id);
});
tabItem
.querySelector('.refresh_tab_button')
.addEventListener('click', function () {
refreshTab(tab.id);
});
tabItem
.querySelector('.update_tab_button')
.addEventListener('click', function () {
updateTab(tab.id);
});
tabItem
.querySelector('.remove_tab_button')
.addEventListener('click', function () {
removeTab(tab.id);
});
tabItem
.querySelector('.tab_active')
.addEventListener('change', function (event) {
const active = event.target.checked;
const tabId = parseInt(event.target.id.split('_')[1]);
chrome.tabs.update(tabId, { active });
});
}
function updateTabData(id) {
const retval = {
url: document.getElementById('url_' + id).value,
active: document.getElementById('active_' + id).value ? true : false
};
return retval;
}
async function updateTab(id) {
try {
await chrome.tabs.update(id, updateTabData(id));
} catch (e) {
alert(e);
}
}
function moveTabData(id) {
return {
index: parseInt(document.getElementById('index_' + id).value),
windowId: parseInt(document.getElementById('windowId_' + id).value)
};
}
function moveTab(id) {
chrome.tabs.move(id, moveTabData(id)).catch(alert);
}
function createTabData() {
return {
windowId: parseInt(document.getElementById('window_id_new').value),
url: document.getElementById('url_new').value,
active: document.getElementById('active_new').checked
};
}
function createTab() {
const args = createTabData();
if (!isInt(args.windowId)) delete args.windowId;
if (!args.url) delete args.url;
chrome.tabs.create(args).catch(alert);
}
document
.getElementById('create_tab_button')
.addEventListener('click', createTab);
async function updateAll() {
try {
for (let i = 0; i < tabIds.length; i++) {
await chrome.tabs.update(tabIds[i], updateTabData(tabIds[i]));
}
} catch (e) {
alert(e);
}
}
async function moveAll() {
appendToLog('moving all');
try {
for (let i = 0; i < tabIds.length; i++) {
await chrome.tabs.move(tabIds[i], moveTabData(tabIds[i]));
}
} catch (e) {
alert(e);
}
}
function removeTab(tabId) {
chrome.tabs
.remove(tabId)
.then(function () {
appendToLog('tab: ' + tabId + ' removed.');
})
.catch(alert);
}
function appendToLog(logLine) {
document
.getElementById('log')
.appendChild(document.createElement('div')).innerText = '> ' + logLine;
}
function clearLog() {
document.getElementById('log').innerText = '';
}
chrome.windows.onCreated.addListener(function (createInfo) {
appendToLog('windows.onCreated -- window: ' + createInfo.id);
loadWindowList();
});
chrome.windows.onBoundsChanged.addListener(function (window) {
appendToLog('windows.onBoundsChanged -- window: ' + window.id);
refreshWindow(window.id);
});
chrome.windows.onFocusChanged.addListener(function (windowId) {
focusedWindowId = windowId;
appendToLog('windows.onFocusChanged -- window: ' + windowId);
loadWindowList();
});
chrome.windows.onRemoved.addListener(function (windowId) {
appendToLog('windows.onRemoved -- window: ' + windowId);
loadWindowList();
});
chrome.tabs.onCreated.addListener(function (tab) {
appendToLog(
'tabs.onCreated -- window: ' +
tab.windowId +
' tab: ' +
tab.id +
' title: ' +
tab.title +
' index ' +
tab.index +
' url ' +
tab.url
);
loadWindowList();
});
chrome.tabs.onAttached.addListener(function (tabId, props) {
appendToLog(
'tabs.onAttached -- window: ' +
props.newWindowId +
' tab: ' +
tabId +
' index ' +
props.newPosition
);
loadWindowList();
});
chrome.tabs.onMoved.addListener(function (tabId, props) {
appendToLog(
'tabs.onMoved -- window: ' +
props.windowId +
' tab: ' +
tabId +
' from ' +
props.fromIndex +
' to ' +
props.toIndex
);
loadWindowList();
});
async function refreshTab(tabId) {
const tab = await chrome.tabs.get(tabId);
const output = document.getElementById('tab_' + tab.id);
if (!output) return;
renderTab(tab, output);
appendToLog('tab refreshed -- tabId: ' + tab.id + ' url: ' + tab.url);
}
chrome.tabs.onUpdated.addListener(function (tabId, props) {
appendToLog(
'tabs.onUpdated -- tab: ' +
tabId +
' status ' +
props.status +
' url ' +
props.url
);
refreshTab(tabId);
});
chrome.tabs.onDetached.addListener(function (tabId, props) {
appendToLog(
'tabs.onDetached -- window: ' +
props.oldWindowId +
' tab: ' +
tabId +
' index ' +
props.oldPosition
);
loadWindowList();
});
chrome.tabs.onActivated.addListener(function (props) {
appendToLog(
'tabs.onActivated -- window: ' + props.windowId + ' tab: ' + props.tabId
);
loadWindowList();
});
chrome.tabs.onRemoved.addListener(function (tabId) {
appendToLog('tabs.onRemoved -- tab: ' + tabId);
loadWindowList();
});
async function createWindow() {
const args = {
left: parseInt(document.getElementById('new_window_left').value),
top: parseInt(document.getElementById('new_window_top').value),
width: parseInt(document.getElementById('new_window_width').value),
height: parseInt(document.getElementById('new_window_height').value),
url: document.getElementById('new_window_url').value
};
if (!isInt(args.left)) delete args.left;
if (!isInt(args.top)) delete args.top;
if (!isInt(args.width)) delete args.width;
if (!isInt(args.height)) delete args.height;
if (!args.url) delete args.url;
chrome.windows.create(args).catch(alert);
}
document
.getElementById('create_window_button')
.addEventListener('click', createWindow);
async function refreshWindow(windowId) {
const window = await chrome.windows.get(windowId);
const tabList = await chrome.tabs.query({ windowId });
window.tabs = tabList;
const output = document.getElementById('window_' + window.id);
if (!output) return;
renderWindow(window, output);
}
function updateWindowData(id) {
const retval = {
left: parseInt(document.getElementById('left_' + id).value),
top: parseInt(document.getElementById('top_' + id).value),
width: parseInt(document.getElementById('width_' + id).value),
height: parseInt(document.getElementById('height_' + id).value)
};
if (!isInt(retval.left)) delete retval.left;
if (!isInt(retval.top)) delete retval.top;
if (!isInt(retval.width)) delete retval.width;
if (!isInt(retval.height)) delete retval.height;
return retval;
}
function updateWindow(id) {
chrome.windows.update(id, updateWindowData(id)).catch(alert);
}
function removeWindow(windowId) {
chrome.windows
.remove(windowId)
.then(function () {
appendToLog('window: ' + windowId + ' removed.');
})
.catch(alert);
}
async function refreshActiveTab(windowId) {
const tabs = await chrome.tabs.query({ active: true, windowId });
const output = document.getElementById('tab_' + tabs[0].id);
if (!output) return;
renderTab(tabs[0], output);
appendToLog(
'Active tab refreshed -- tabId: ' + tabs[0].id + ' url:' + tabs[0].url
);
}
document.addEventListener('DOMContentLoaded', function () {
bootstrap();
});
document
.getElementById('load_window_list_button')
.addEventListener('click', function () {
loadWindowList();
});
document
.getElementById('update_all_button')
.addEventListener('click', function () {
updateAll();
});
document
.getElementById('move_all_button')
.addEventListener('click', function () {
moveAll();
});
document
.getElementById('clear_log_button')
.addEventListener('click', function () {
clearLog();
});
document
.getElementById('new_window_button')
.addEventListener('click', function () {
chrome.windows.create();
});

View File

@@ -0,0 +1,19 @@
# chrome.tabs - Keyboard Pin
A sample that demonstrates how to use the [`chrome.tabs`](https://developer.chrome.com/docs/extensions/reference/tabs/) API to toggle the pinned state of the current tab.
## Overview
In this sample, a new keyboard shortcut (Alt + Shift + P) is enabled to pin the current tab.
## Implementation Notes
Chrome fires [`chrome.commands.onCommand`](https://developer.chrome.com/docs/extensions/reference/commands/#event-onCommand) when the user presses a registered keyboard shortcut.
Calls [`chrome.tabs.update()`](https://developer.chrome.com/docs/extensions/reference/tabs/#method-update) to pin or unpin the current tab.
## Running this extension
1. Clone this repository.
2. Load this directory in Chrome as an [unpacked extension](https://developer.chrome.com/docs/extensions/mv3/getstarted/development-basics/#load-unpacked).
3. Press Alt + Shift + P to pin the current tab.

View File

@@ -0,0 +1,17 @@
{
"name": "Keyboard Pin",
"version": "0.3",
"description": "Uses the chrome.tabs API to toggle the pinned state of the current tab.",
"background": {
"service_worker": "service-worker.js"
},
"commands": {
"toggle-pin": {
"suggested_key": {
"default": "Alt+Shift+P"
},
"description": "Toggle tab pin"
}
},
"manifest_version": 3
}

View File

@@ -0,0 +1,9 @@
chrome.commands.onCommand.addListener(async function (command) {
if (command == 'toggle-pin') {
// Get the currently selected tab
const tabs = await chrome.tabs.query({ active: true, currentWindow: true });
// Toggle the pinned status
const current = tabs[0];
chrome.tabs.update(current.id, { pinned: !current.pinned });
}
});

View File

@@ -0,0 +1,17 @@
# chrome.tabs - Tab Screenshot
A sample that demonstrates how to use the [`chrome.tabs`](https://developer.chrome.com/docs/extensions/reference/tabs/) API to take a screenshot of the current tab.
## Overview
When the user clicks the action icon, the extension takes a screenshot of the current tab and displays it in a new tab.
## Implementation Notes
Calls [`chrome.tabs.captureVisibleTab()`](https://developer.chrome.com/docs/extensions/reference/tabs/#method-captureVisibleTab) to capture the visible area of the current tab.
## Running this extension
1. Clone this repository.
2. Load this directory in Chrome as an [unpacked extension](https://developer.chrome.com/docs/extensions/mv3/getstarted/development-basics/#load-unpacked).
3. Click the extension's icon to take a screenshot of the current tab.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,14 @@
{
"name": "Test Screenshot Extension",
"version": "1.3",
"description": "Uses the chrome.tabs API to take a screenshot of the active tab.",
"background": {
"service_worker": "service-worker.js"
},
"action": {
"default_icon": "camera.png",
"default_title": "Take a screen shot!"
},
"permissions": ["activeTab"],
"manifest_version": 3
}

View File

@@ -0,0 +1,20 @@
<!DOCTYPE html>
<html>
<head>
<title>Tab Screenshot</title>
</head>
<body>
Image here:
<p>
<img
id="target"
src="white.png"
height="480"
style="border: black solid 1px"
/>
</p>
<script src="screenshot.js"></script>
</body>
</html>

View File

@@ -0,0 +1,9 @@
function setScreenshotUrl(url) {
document.getElementById('target').src = url;
}
chrome.runtime.onMessage.addListener(function (request) {
if (request.msg === 'screenshot') {
setScreenshotUrl(request.data);
}
});

View File

@@ -0,0 +1,24 @@
// Listen for a click on the camera icon. On that click, take a screenshot.
chrome.action.onClicked.addListener(async function () {
const screenshotUrl = await chrome.tabs.captureVisibleTab();
const viewTabUrl = chrome.runtime.getURL('screenshot.html');
let targetId = null;
chrome.tabs.onUpdated.addListener(function listener(tabId, changedProps) {
// We are waiting for the tab we opened to finish loading.
// Check that the tab's id matches the tab we opened,
// and that the tab is done loading.
if (tabId != targetId || changedProps.status != 'complete') return;
// Passing the above test means this is the event we were waiting for.
// There is nothing we need to do for future onUpdated events, so we
// use removeListner to stop getting called when onUpdated events fire.
chrome.tabs.onUpdated.removeListener(listener);
// Send screenshotUrl to the tab.
chrome.tabs.sendMessage(tabId, { msg: 'screenshot', data: screenshotUrl });
});
const tab = await chrome.tabs.create({ url: viewTabUrl });
targetId = tab.id;
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 B

View File

@@ -0,0 +1,21 @@
# chrome.tabs - Tab zoom
A sample that demonstrates how to use the zoom features of the [`chrome.tabs`](https://developer.chrome.com/docs/extensions/reference/tabs/) API.
## Overview
In this sample, the zoom related [`chrome.tabs`](https://developer.chrome.com/docs/extensions/reference/tabs/) APIs are used to change the magnification and zoom mode of the active tab.
## Implementation Notes
- [`chrome.tabs.getZoom()`](https://developer.chrome.com/docs/extensions/reference/tabs/#method-getZoom) returns the current zoom level of the tab.
- [`chrome.tabs.setZoom()`](https://developer.chrome.com/docs/extensions/reference/tabs/#method-setZoom) changes the zoom level of the tab.
- [`chrome.tabs.setZoomSettings()`](https://developer.chrome.com/docs/extensions/reference/tabs/#method-setZoomSettings) sets the zoom settings of the tab.
- [`chrome.tabs.getZoomSettings()`](https://developer.chrome.com/docs/extensions/reference/tabs/#method-getZoomSettings) returns the zoom settings of the tab.
- [`chrome.tabs.onZoomChange()`](https://developer.chrome.com/docs/extensions/reference/tabs/#event-onZoomChange) listens for zoom changes in the tab.
## Running this extension
1. Clone this repository.
2. Load this directory in Chrome as an [unpacked extension](https://developer.chrome.com/docs/extensions/mv3/getstarted/development-basics/#load-unpacked).
3. Open any website, such as `https://google.com` in a new tab, and then click the extension icon to open the popup.

View File

@@ -0,0 +1,18 @@
{
"manifest_version": 3,
"name": "Tabs zoom API Demo",
"description": "Uses the tabs.zoom API to manipulate the zoom level of the current tab.",
"version": "0.1",
"icons": {
"16": "zoom16.png",
"48": "zoom48.png"
},
"background": {
"service_worker": "service-worker.js"
},
"action": {
"default_icon": "zoom19.png",
"default_title": "Zoom Extension Demo",
"default_popup": "popup.html"
}
}

View File

@@ -0,0 +1,78 @@
<!DOCTYPE html>
<html>
<head>
<title>Tab Zoom Extension</title>
<style>
body {
width: 150px;
overflow-x: hidden;
color: #ffff00;
background-color: #186464;
}
img {
margin: 5px;
border: 2px solid black;
vertical-align: middle;
width: 19px;
height: 19px;
}
</style>
</head>
<body>
<div style="text-align: center">
<table style="margin: 0px auto">
<tr>
<td><button type="button" id="decreaseButton">-</button></td>
<td>
<div
style="width: 50px; border-style: solid; border-width: 1px"
id="displayDiv"
>
100%
</div>
</td>
<td><button type="button" id="increaseButton">+</button></td>
</tr>
</table>
<button type="button" id="defaultButton">Reset to Default</button>
<div id="defaultLabel"></div>
</div>
<p></p>
<div
style="
border-width: 2px;
border-style: solid;
border-color: #7f0000;
padding: 2px;
"
>
<form
style="border-width: 2px; border-style: solid; border-color: #7f0000"
>
<b>Mode:</b><br />
<input type="radio" name="modeRadio" value="automatic" />automatic<br />
<input type="radio" name="modeRadio" value="manual" />manual<br />
<input type="radio" name="modeRadio" value="disabled" />disabled
</form>
<br />
<form
style="border-width: 2px; border-style: solid; border-color: #7f0000"
>
<b>Scope:</b><br />
<input
type="radio"
name="scopeRadio"
value="per-origin"
/>per-origin<br />
<input type="radio" name="scopeRadio" value="per-tab" />per-tab
</form>
<button type="button" id="setModeButton">Set Zoom Settings</button>
</div>
<p>
<button type="button" id="closeButton">Close</button>
</p>
<script src="popup.js"></script>
</body>
</html>

View File

@@ -0,0 +1,124 @@
/**
* @fileoverview This code supports the popup behaviour of the extension, and
* demonstrates how to:
*
* 1) Set the zoom for a tab using tabs.setZoom()
* 2) Read the current zoom of a tab using tabs.getZoom()
* 3) Set the zoom mode of a tab using tabs.setZoomSettings()
* 4) Read the current zoom mode of a tab using
* tabs.getZoomSettings()
*
* It also demonstrates using a zoom change listener to update the
* contents of a control.
*/
const zoomStep = 1.1;
let tabId = -1;
function displayZoomLevel(level) {
const percentZoom = parseFloat(level) * 100;
const zoom_percent_str = percentZoom.toFixed(1) + '%';
document.getElementById('displayDiv').textContent = zoom_percent_str;
}
document.addEventListener('DOMContentLoaded', async function () {
// Find the tabId of the current (active) tab. We could just omit the tabId
// parameter in the function calls below, and they would act on the current
// tab by default, but for the purposes of this demo we will always use the
// API with an explicit tabId to demonstrate its use.
const tabs = await chrome.tabs.query({ active: true });
if (tabs.length > 1)
console.log(
'[ZoomDemoExtension] Query unexpectedly returned more than 1 tab.'
);
tabId = tabs[0].id;
const zoomSettings = await chrome.tabs.getZoomSettings(tabId);
const modeRadios = document.getElementsByName('modeRadio');
for (let i = 0; i < modeRadios.length; i++) {
if (modeRadios[i].value == zoomSettings.mode) modeRadios[i].checked = true;
}
const scopeRadios = document.getElementsByName('scopeRadio');
for (let i = 0; i < scopeRadios.length; i++) {
if (scopeRadios[i].value == zoomSettings.scope)
scopeRadios[i].checked = true;
}
const percentDefaultZoom = parseFloat(zoomSettings.defaultZoomFactor) * 100;
document.getElementById('defaultLabel').textContent =
'Default: ' + percentDefaultZoom.toFixed(1) + '%';
const zoomFactor = await chrome.tabs.getZoom(tabId);
displayZoomLevel(zoomFactor);
document.getElementById('increaseButton').onclick = doZoomIn;
document.getElementById('decreaseButton').onclick = doZoomOut;
document.getElementById('defaultButton').onclick = doZoomDefault;
document.getElementById('setModeButton').onclick = doSetMode;
document.getElementById('closeButton').onclick = doClose;
});
function zoomChangeListener(zoomChangeInfo) {
displayZoomLevel(zoomChangeInfo.newZoomFactor);
}
chrome.tabs.onZoomChange.addListener(zoomChangeListener);
function errorHandler(error) {
console.log('[ZoomDemoExtension] ' + error.message);
}
async function changeZoomByFactorDelta(factorDelta) {
if (tabId == -1) return;
const zoomFactor = await chrome.tabs.getZoom(tabId);
const newZoomFactor = factorDelta * zoomFactor;
chrome.tabs.setZoom(tabId, newZoomFactor).catch(errorHandler);
}
function doZoomIn() {
changeZoomByFactorDelta(zoomStep);
}
function doZoomOut() {
changeZoomByFactorDelta(1.0 / zoomStep);
}
function doZoomDefault() {
if (tabId == -1) return;
chrome.tabs.setZoom(tabId, 0).catch(errorHandler);
}
function doSetMode() {
if (tabId == -1) return;
let modeVal;
const modeRadios = document.getElementsByName('modeRadio');
for (let i = 0; i < modeRadios.length; i++) {
if (modeRadios[i].checked) modeVal = modeRadios[i].value;
}
let scopeVal;
const scopeRadios = document.getElementsByName('scopeRadio');
for (let i = 0; i < scopeRadios.length; i++) {
if (scopeRadios[i].checked) scopeVal = scopeRadios[i].value;
}
if (!modeVal || !scopeVal) {
console.log(
'[ZoomDemoExtension] Must specify values for both mode & scope.'
);
return;
}
chrome.tabs
.setZoomSettings(tabId, { mode: modeVal, scope: scopeVal })
.catch(errorHandler);
}
function doClose() {
self.close();
}

View File

@@ -0,0 +1,26 @@
/**
* @fileoverview In this extension, the background script demonstrates how to
* listen for zoom change events.
*/
function zoomChangeListener(zoomChangeInfo) {
const settings_str =
'mode:' +
zoomChangeInfo.zoomSettings.mode +
', scope:' +
zoomChangeInfo.zoomSettings.scope;
console.log(
'[ZoomDemoExtension] zoomChangeListener(tab=' +
zoomChangeInfo.tabId +
', new=' +
zoomChangeInfo.newZoomFactor +
', old=' +
zoomChangeInfo.oldZoomFactor +
', ' +
settings_str +
')'
);
}
chrome.tabs.onZoomChange.addListener(zoomChangeListener);

Binary file not shown.

After

Width:  |  Height:  |  Size: 315 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 421 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB