mirror of
https://github.com/GoogleChrome/chrome-extensions-samples.git
synced 2026-03-26 13:19:49 +07:00
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:
13
api-samples/tabs/inspector/README.md
Normal file
13
api-samples/tabs/inspector/README.md
Normal 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.
|
||||
13
api-samples/tabs/inspector/manifest.json
Normal file
13
api-samples/tabs/inspector/manifest.json
Normal 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
|
||||
}
|
||||
5
api-samples/tabs/inspector/service-worker.js
Normal file
5
api-samples/tabs/inspector/service-worker.js
Normal file
@@ -0,0 +1,5 @@
|
||||
chrome.action.onClicked.addListener(function () {
|
||||
chrome.tabs.create({
|
||||
url: chrome.runtime.getURL('window_and_tabs_manager.html')
|
||||
});
|
||||
});
|
||||
110
api-samples/tabs/inspector/window_and_tabs_manager.css
Normal file
110
api-samples/tabs/inspector/window_and_tabs_manager.css
Normal 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;
|
||||
}
|
||||
107
api-samples/tabs/inspector/window_and_tabs_manager.html
Normal file
107
api-samples/tabs/inspector/window_and_tabs_manager.html
Normal 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>
|
||||
437
api-samples/tabs/inspector/window_and_tabs_manager.js
Normal file
437
api-samples/tabs/inspector/window_and_tabs_manager.js
Normal 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();
|
||||
});
|
||||
19
api-samples/tabs/pin/README.md
Normal file
19
api-samples/tabs/pin/README.md
Normal 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.
|
||||
17
api-samples/tabs/pin/manifest.json
Normal file
17
api-samples/tabs/pin/manifest.json
Normal 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
|
||||
}
|
||||
9
api-samples/tabs/pin/service-worker.js
Normal file
9
api-samples/tabs/pin/service-worker.js
Normal 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 });
|
||||
}
|
||||
});
|
||||
17
api-samples/tabs/screenshot/README.md
Normal file
17
api-samples/tabs/screenshot/README.md
Normal 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.
|
||||
BIN
api-samples/tabs/screenshot/camera.png
Normal file
BIN
api-samples/tabs/screenshot/camera.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
14
api-samples/tabs/screenshot/manifest.json
Normal file
14
api-samples/tabs/screenshot/manifest.json
Normal 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
|
||||
}
|
||||
20
api-samples/tabs/screenshot/screenshot.html
Normal file
20
api-samples/tabs/screenshot/screenshot.html
Normal 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>
|
||||
9
api-samples/tabs/screenshot/screenshot.js
Normal file
9
api-samples/tabs/screenshot/screenshot.js
Normal 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);
|
||||
}
|
||||
});
|
||||
24
api-samples/tabs/screenshot/service-worker.js
Normal file
24
api-samples/tabs/screenshot/service-worker.js
Normal 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;
|
||||
});
|
||||
BIN
api-samples/tabs/screenshot/white.png
Normal file
BIN
api-samples/tabs/screenshot/white.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 132 B |
21
api-samples/tabs/zoom/README.md
Normal file
21
api-samples/tabs/zoom/README.md
Normal 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.
|
||||
18
api-samples/tabs/zoom/manifest.json
Normal file
18
api-samples/tabs/zoom/manifest.json
Normal 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"
|
||||
}
|
||||
}
|
||||
78
api-samples/tabs/zoom/popup.html
Normal file
78
api-samples/tabs/zoom/popup.html
Normal 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>
|
||||
124
api-samples/tabs/zoom/popup.js
Normal file
124
api-samples/tabs/zoom/popup.js
Normal 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();
|
||||
}
|
||||
26
api-samples/tabs/zoom/service-worker.js
Normal file
26
api-samples/tabs/zoom/service-worker.js
Normal 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);
|
||||
BIN
api-samples/tabs/zoom/zoom16.png
Normal file
BIN
api-samples/tabs/zoom/zoom16.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 315 B |
BIN
api-samples/tabs/zoom/zoom19.png
Normal file
BIN
api-samples/tabs/zoom/zoom19.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 421 B |
BIN
api-samples/tabs/zoom/zoom48.png
Normal file
BIN
api-samples/tabs/zoom/zoom48.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
Reference in New Issue
Block a user