diff --git a/api-samples/contextMenus/global_context_search/README.md b/api-samples/contextMenus/global_context_search/README.md index 54ef8c73d4..63a3d50dcc 100644 --- a/api-samples/contextMenus/global_context_search/README.md +++ b/api-samples/contextMenus/global_context_search/README.md @@ -1,15 +1,30 @@ # chrome.contextMenus -This sample demonstrates the `chrome.contextMenus` API by letting a user switch between searching different countries' versions of Google via a `contextMenu`. +This sample demonstrates the `chrome.contextMenus` API by letting a user search Google with region-specific parameters via a context menu. ## Overview -The extension uses `chrome.contextMenus.create()` to populate the context menu with locale options based on an options menu in the popup. A `chrome.contextMenus.onClicked.addListener()` event will open a specific locale's Google homepage when one of the extension's context menu options are clicked. +Modern Google Search uses IP geolocation and query parameters for localization instead of domain-based routing (e.g., google.com.br). This sample reflects that reality. + +The extension uses: +- `chrome.contextMenus.create()` to populate context menu with region options +- Query parameters: `cr` (country restriction) and `lr` (language restriction) +- A popup UI to enable/disable regions +- `chrome.contextMenus.onClicked.addListener()` to open searches in new tabs + +## Key Implementation Details + +- **Base URL**: All searches use `https://www.google.com/search` +- **Country Parameter (cr)**: Restricts results to a specific country (e.g., `countryCA` for Canada) +- **Language Parameter (lr)**: Restricts results to a specific language (e.g., `lang_en` for English) + +While these parameters don't guarantee region-specific results (as IP geolocation is the primary factor), they're the modern, programmatic way to express regional preference to Google Search. ## 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. Pin the extension to the taskbar to access the action button. -4. Open the extension popup by clicking the action button and interact with the UI. -5. Select the text you want to search and right-click within the selection to view and interact with the context menu. +4. Open the extension popup by clicking the action button to enable/disable regions. +5. Select text on any webpage and right-click to access the context menu with region options. +6. Click a region to search for your selected text with that region's parameters. diff --git a/api-samples/contextMenus/global_context_search/background.js b/api-samples/contextMenus/global_context_search/background.js index f504b6ad43..25f75c8b30 100644 --- a/api-samples/contextMenus/global_context_search/background.js +++ b/api-samples/contextMenus/global_context_search/background.js @@ -3,54 +3,71 @@ // found in the LICENSE file. // When you specify "type": "module" in the manifest background, -// you can include the service worker as an ES Module, -import { tldLocales } from './locales.js'; +// you can include the service worker as an ES Module. +import { regions } from './locales.js'; -// Add a listener to create the initial context menu items, -// context menu items only need to be created at runtime.onInstalled +/** + * Creates context menu items when the extension is installed. + * Each region gets its own context menu option for searching. + */ chrome.runtime.onInstalled.addListener(async () => { - for (const [tld, locale] of Object.entries(tldLocales)) { + for (const [regionId, regionConfig] of Object.entries(regions)) { chrome.contextMenus.create({ - id: tld, - title: locale, + id: regionId, + title: regionConfig.display, type: 'normal', contexts: ['selection'] }); } }); -// Open a new search tab when the user clicks a context menu +/** + * Performs a Google Search with region-specific parameters when menu item is clicked. + * + * Uses the base URL https://www.google.com/search with query parameters: + * - q: the search query (user selection) + * - cr: country restriction parameter + * - lr: language restriction parameter + */ chrome.contextMenus.onClicked.addListener((item, tab) => { - const tld = item.menuItemId; - const url = new URL(`https://google.${tld}/search`); + const regionId = item.menuItemId; + const regionConfig = regions[regionId]; + + // Build the search URL with region parameters + const url = new URL('https://www.google.com/search'); url.searchParams.set('q', item.selectionText); + url.searchParams.set('cr', regionConfig.country); + url.searchParams.set('lr', regionConfig.language); + chrome.tabs.create({ url: url.href, index: tab.index + 1 }); }); -// Add or removes the locale from context menu -// when the user checks or unchecks the locale in the popup -chrome.storage.onChanged.addListener(({ enabledTlds }) => { - if (typeof enabledTlds === 'undefined') return; - - const allTlds = Object.keys(tldLocales); - const currentTlds = new Set(enabledTlds.newValue); - const oldTlds = new Set(enabledTlds.oldValue ?? allTlds); - const changes = allTlds.map((tld) => ({ - tld, - added: currentTlds.has(tld) && !oldTlds.has(tld), - removed: !currentTlds.has(tld) && oldTlds.has(tld) - })); - - for (const { tld, added, removed } of changes) { - if (added) { +/** + * Updates the context menu when the user toggles regions in the popup. + * Adds or removes menu items based on user preferences. + */ +chrome.storage.onChanged.addListener(({ enabledRegions }) => { + if (typeof enabledRegions === 'undefined') return; + + const allRegionIds = Object.keys(regions); + const currentRegions = new Set(enabledRegions.newValue); + const oldRegions = new Set(enabledRegions.oldValue ?? allRegionIds); + + for (const regionId of allRegionIds) { + const wasEnabled = oldRegions.has(regionId); + const isEnabled = currentRegions.has(regionId); + + if (isEnabled && !wasEnabled) { + // Region was enabled, add menu item chrome.contextMenus.create({ - id: tld, - title: tldLocales[tld], + id: regionId, + title: regions[regionId].display, type: 'normal', contexts: ['selection'] }); - } else if (removed) { - chrome.contextMenus.remove(tld); + } else if (!isEnabled && wasEnabled) { + // Region was disabled, remove menu item + chrome.contextMenus.remove(regionId); } } }); diff --git a/api-samples/contextMenus/global_context_search/locales.js b/api-samples/contextMenus/global_context_search/locales.js index 1573ef6285..225a65bcb6 100644 --- a/api-samples/contextMenus/global_context_search/locales.js +++ b/api-samples/contextMenus/global_context_search/locales.js @@ -2,18 +2,80 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// TLD: top level domain; the "com" in "google.com" -export const tldLocales = { - 'com.au': 'Australia', - 'com.br': 'Brazil', - ca: 'Canada', - cn: 'China', - fr: 'France', - it: 'Italy', - 'co.in': 'India', - 'co.jp': 'Japan', - 'com.ms': 'Mexico', - ru: 'Russia', - 'co.za': 'South Africa', - 'co.uk': 'United Kingdom' +/** + * Region configurations for Google Search. + * + * Historically, Google Search results were localized by domain (e.g., google.com.br). + * However, modern Google Search uses IP geolocation for most localization. + * + * To simulate region-specific searches, we now use query parameters: + * - cr (country restriction): Restricts results to a specific country + * - lr (language restriction): Restricts results to a specific language + * + * This demonstrates how to customize Google Search programmatically while + * reflecting modern Google behavior. + * + * @type {Object.} + */ +export const regions = { + 'au': { + country: 'countryAU', + language: 'lang_en', + display: 'Australia' + }, + 'br': { + country: 'countryBR', + language: 'lang_pt', + display: 'Brazil' + }, + 'ca': { + country: 'countryCA', + language: 'lang_en', + display: 'Canada' + }, + 'cn': { + country: 'countryCN', + language: 'lang_zh-CN', + display: 'China' + }, + 'fr': { + country: 'countryFR', + language: 'lang_fr', + display: 'France' + }, + 'it': { + country: 'countryIT', + language: 'lang_it', + display: 'Italy' + }, + 'in': { + country: 'countryIN', + language: 'lang_en', + display: 'India' + }, + 'jp': { + country: 'countryJP', + language: 'lang_ja', + display: 'Japan' + }, + 'mx': { + country: 'countryMX', + language: 'lang_es', + display: 'Mexico' + }, + 'ru': { + country: 'countryRU', + language: 'lang_ru', + display: 'Russia' + }, + 'za': { + country: 'countryZA', + language: 'lang_en', + display: 'South Africa' + }, + 'uk': { + country: 'countryGB', + language: 'lang_en', + display: 'United Kingdom' + } }; diff --git a/api-samples/contextMenus/global_context_search/manifest.json b/api-samples/contextMenus/global_context_search/manifest.json index 20be806b2f..9c61951b4a 100644 --- a/api-samples/contextMenus/global_context_search/manifest.json +++ b/api-samples/contextMenus/global_context_search/manifest.json @@ -1,7 +1,7 @@ { "name": "Global Google Search", - "description": "Uses the context menu to search a different country's Google", - "version": "1.1", + "description": "Search Google with region-specific parameters using the context menu", + "version": "2.0", "manifest_version": 3, "permissions": ["contextMenus", "storage"], "background": { diff --git a/api-samples/contextMenus/global_context_search/popup.js b/api-samples/contextMenus/global_context_search/popup.js index 5ca1aa84f5..0e7b030ffd 100644 --- a/api-samples/contextMenus/global_context_search/popup.js +++ b/api-samples/contextMenus/global_context_search/popup.js @@ -2,28 +2,31 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// TLD: top level domain; the "com" in "google.com" -import { tldLocales } from './locales.js'; +import { regions } from './locales.js'; createForm().catch(console.error); +/** + * Creates the popup form with region checkboxes. + * Users can enable or disable regions to customize context menu options. + */ async function createForm() { - const { enabledTlds = Object.keys(tldLocales) } = - await chrome.storage.sync.get('enabledTlds'); - const checked = new Set(enabledTlds); + const { enabledRegions = Object.keys(regions) } = + await chrome.storage.sync.get('enabledRegions'); + const checked = new Set(enabledRegions); const form = document.getElementById('form'); - for (const [tld, locale] of Object.entries(tldLocales)) { + for (const [regionId, regionConfig] of Object.entries(regions)) { const checkbox = document.createElement('input'); checkbox.type = 'checkbox'; - checkbox.checked = checked.has(tld); - checkbox.name = tld; + checkbox.checked = checked.has(regionId); + checkbox.name = regionId; checkbox.addEventListener('click', (event) => { handleCheckboxClick(event).catch(console.error); }); const span = document.createElement('span'); - span.textContent = locale; + span.textContent = regionConfig.display; const div = document.createElement('div'); div.appendChild(checkbox); @@ -33,17 +36,23 @@ async function createForm() { } } +/** + * Handles checkbox state changes and updates storage. + */ async function handleCheckboxClick(event) { const checkbox = event.target; - const tld = checkbox.name; + const regionId = checkbox.name; const enabled = checkbox.checked; - const { enabledTlds = Object.keys(tldLocales) } = - await chrome.storage.sync.get('enabledTlds'); - const tldSet = new Set(enabledTlds); + const { enabledRegions = Object.keys(regions) } = + await chrome.storage.sync.get('enabledRegions'); + const regionSet = new Set(enabledRegions); - if (enabled) tldSet.add(tld); - else tldSet.delete(tld); + if (enabled) { + regionSet.add(regionId); + } else { + regionSet.delete(regionId); + } - await chrome.storage.sync.set({ enabledTlds: [...tldSet] }); + await chrome.storage.sync.set({ enabledRegions: [...regionSet] }); }