{"id":343,"date":"2022-11-07T13:03:00","date_gmt":"2022-11-07T12:03:00","guid":{"rendered":"https:\/\/www.stefanvd.net\/blog\/?p=343"},"modified":"2024-05-01T19:15:15","modified_gmt":"2024-05-01T18:15:15","slug":"10-important-code-notes-you-should-know-when-migrating-to-manifest-v3","status":"publish","type":"post","link":"https:\/\/www.stefanvd.net\/blog\/2022\/11\/07\/10-important-code-notes-you-should-know-when-migrating-to-manifest-v3\/","title":{"rendered":"10 important Code Notes You Should Know When Migrating to Manifest V3"},"content":{"rendered":"\n<div class=\"wp-block-rank-math-toc-block\" id=\"rank-math-toc\"><p>Table of Contents<\/p><nav><ul><li><a href=\"#migrating-to-manifest-v-3-useful-tips-for-beginner-developers\">Migrating to Manifest V3 useful tips for beginner developers<\/a><ul><li><a href=\"#1-do-not-use-active-tab-and-tabs-permission-together\">1. Do not use &#8220;activeTab&#8221; and &#8220;tabs&#8221; permission together<\/a><\/li><li><a href=\"#2-do-not-use-window-in-the-service-worker\">2. Do not use &#8220;window&#8221; in the service worker<\/a><ul><li><a href=\"#dom\">DOM<\/a><\/li><li><a href=\"#interval-and-timeout\">Interval and timeout<\/a><\/li><li><a href=\"#screen-size\">Screen size<\/a><\/li><li><a href=\"#local-storage\">localStorage<\/a><\/li><\/ul><\/li><li><a href=\"#3-use-of-constant-value-in-the-background-service-worker\">3. Use of Constant value in the background service worker<\/a><\/li><li><a href=\"#4-injecting-a-script-needs-permission-now\">4. Injecting a script needs permission now<\/a><\/li><li><a href=\"#5-use-not-inline-java-script-code-but-files-or-functions\">5. Use not inline JavaScript code but files or functions<\/a><ul><li><a href=\"#no-remote-java-script\">No remote JavaScript<\/a><\/li><li><a href=\"#a-new-way-of-injecting-scripts\">A new way of injecting scripts<\/a><\/li><\/ul><\/li><li><a href=\"#6-change-is-important-but-changelog-too\">6. Change is important but Changelog too<\/a><ul><li><a href=\"#clean-up-your-not-necessary-files\">Clean up your not necessary files<\/a><\/li><\/ul><\/li><li><a href=\"#7-context-menu-detection\">7. Context menu detection<\/a><\/li><li><a href=\"#8-use-chrome-runtime-and-not-chrome-extension\">8. Use &#8220;chrome.runtime&#8221; and not &#8220;chrome.extension&#8221;<\/a><\/li><li><a href=\"#9-non-documented-chrome-extension-changed-too\">9. Non-documented Chrome extension changed too<\/a><\/li><li><a href=\"#10-check-api-is-on-the-stable-release\">10. Check API is on the stable release<\/a><\/li><\/ul><\/li><li><a href=\"#addition-resource\">Addition resource<\/a><\/li><li><a href=\"#final-remarks\">Final remarks<\/a><\/li><\/ul><\/nav><\/div>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" width=\"1024\" height=\"536\" src=\"https:\/\/www.stefanvd.net\/blog\/wp-content\/uploads\/2022\/11\/code-migrating-to-manifest-v3-1024x536.png\" alt=\"Important code migrating to Manifest V3 you need to know\" class=\"wp-image-402\" srcset=\"https:\/\/www.stefanvd.net\/blog\/wp-content\/uploads\/2022\/11\/code-migrating-to-manifest-v3-1024x536.png 1024w, https:\/\/www.stefanvd.net\/blog\/wp-content\/uploads\/2022\/11\/code-migrating-to-manifest-v3-300x157.png 300w, https:\/\/www.stefanvd.net\/blog\/wp-content\/uploads\/2022\/11\/code-migrating-to-manifest-v3-768x402.png 768w, https:\/\/www.stefanvd.net\/blog\/wp-content\/uploads\/2022\/11\/code-migrating-to-manifest-v3.png 1200w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">Important code migrating to Manifest V3 you need to know<\/figcaption><\/figure>\n\n\n\n<p>It is time that your Chrome extensions are migrating to Manifest V3. As Chrome <a href=\"https:\/\/developer.chrome.com\/blog\/mv2-transition\/\" target=\"_blank\" rel=\"noreferrer noopener\">Manifest V3 starts on 17 January 2023<\/a>.<\/p>\n\n\n\n<p>The best way to learn to write Chrome extension code (that is Manifest V3 ready) is by reading other developers&#8217; code. So you as a beginner developer know how to solve problems. And be a problem solver in Manifest V3 development. Here is the overview of what I learned about getting my Chrome extensions migrated to Manifest V3.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"migrating-to-manifest-v-3-useful-tips-for-beginner-developers\">Migrating to Manifest V3 useful tips for beginner developers<\/h2>\n\n\n\n<p>This article is for general Chrome extension developers who do not use the <a href=\"https:\/\/developer.chrome.com\/docs\/extensions\/reference\/webRequest\/\" target=\"_blank\" rel=\"noreferrer noopener\">webRequest<\/a> blocking (AdBlock &#8211; adblocker extension) in their Chrome extension code. So this is for the creative developers in the world that create great visual web experiences in the current tab, for the users. This article will give you an overview of Manifest V2 to the new Manifest V3 changes I have made to my Chrome extensions.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"1-do-not-use-active-tab-and-tabs-permission-together\">1. Do not use &#8220;activeTab&#8221; and &#8220;tabs&#8221; permission together<\/h3>\n\n\n\n<p>I see that many developers are still using both permissions in the manifest.json file. While ignoring the proper use of this permission. Because <strong>activeTab<\/strong> only works when your invoice the action when you click on the browser button (and context menu, shortcut key, and the Omnibox suggestion type) on the currently open tab. And with the use of <strong>tabs<\/strong> permission, you just increase the to read and access all tabs in your web browser. That includes the currently open tab, but also the tabs that are in the background on your Google Chrome web browser.<\/p>\n\n\n\n<pre class=\"wp-block-code has-background\" style=\"background-color:#d300381a\"><code>\"permissions\": &#91;\"activeTab\", \"storage\", \"tabs\"]<\/code><\/pre>\n\n\n\n<p>When migrating to Manifest V3, there is no reason to get this <strong>activeTab<\/strong> together with the <strong>tabs<\/strong> permission in your Chrome extension.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" width=\"1024\" height=\"444\" src=\"https:\/\/www.stefanvd.net\/blog\/wp-content\/uploads\/2022\/11\/activeTab-and-tabs-chrome-permission-in-manifest-v3-1024x444.png\" alt=\"Still see developers using together the &quot;activeTab&quot; and &quot;tabs&quot; permission\" class=\"wp-image-398\" srcset=\"https:\/\/www.stefanvd.net\/blog\/wp-content\/uploads\/2022\/11\/activeTab-and-tabs-chrome-permission-in-manifest-v3-1024x444.png 1024w, https:\/\/www.stefanvd.net\/blog\/wp-content\/uploads\/2022\/11\/activeTab-and-tabs-chrome-permission-in-manifest-v3-300x130.png 300w, https:\/\/www.stefanvd.net\/blog\/wp-content\/uploads\/2022\/11\/activeTab-and-tabs-chrome-permission-in-manifest-v3-768x333.png 768w, https:\/\/www.stefanvd.net\/blog\/wp-content\/uploads\/2022\/11\/activeTab-and-tabs-chrome-permission-in-manifest-v3.png 1186w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">Still see developers using together the &#8220;activeTab&#8221; and &#8220;tabs&#8221; permission<\/figcaption><\/figure>\n\n\n\n<p>The best way for your users is to make it work in the currently active tab. That can be done by just using the <a href=\"https:\/\/developer.chrome.com\/docs\/extensions\/mv3\/manifest\/activeTab\/\" target=\"_blank\" rel=\"noreferrer noopener\">Chrome activeTabs<\/a> permission.<\/p>\n\n\n\n<pre class=\"wp-block-code has-background\" style=\"background-color:#0079361a\"><code>\"permissions\": &#91;\"activeTab\", \"storage\", \"scripting\"]<\/code><\/pre>\n\n\n\n<p>If you need access to all tabs to read sensitive properties such <em>url<\/em>, <em>pendingUrl<\/em>, <em>title<\/em>, and <em>favIconUrl<\/em>. Then you have to use the <strong>tabs<\/strong> permission. And with host permissions that give access to one or more hosts. Here is &#8220;&lt;all_urls&gt;&#8221; for the <em>http<\/em>, <em>https<\/em>, <em>ws<\/em>, <em>wss<\/em>, <em>ftp<\/em>, <em>data<\/em>, <em>file<\/em>, and <em>chrome-extension<\/em> on your device.<\/p>\n\n\n\n<pre class=\"wp-block-code has-background\" style=\"background-color:#0079361a\"><code>\"permissions\": &#91;\"tabs\", \"storage\", \"scripting\"],\n\"host_permissions\": &#91;\"&lt;all_urls&gt;\"]<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"2-do-not-use-window-in-the-service-worker\">2. Do not use &#8220;window&#8221; in the service worker<\/h3>\n\n\n\n<p>When migrating to Manifest V3, your Chrome extension will need to use a service worker because the events background page has ended. But that creates a number of limitations for this new worker.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"dom\">DOM<\/h4>\n\n\n\n<p>The important change to a service worker is that you cannot access the DOM (<em>Document Object Model<\/em>&nbsp;) such as the body, and head. Also, you cannot use the <em>document.getElementById(&#8220;player&#8221;)<\/em> to set or change the value. Or place an HTML <em>&lt;video&gt;<\/em> or <em>&lt;audio&gt;<\/em> in the background script.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" width=\"1024\" height=\"710\" src=\"https:\/\/www.stefanvd.net\/blog\/wp-content\/uploads\/2024\/05\/manifest-v2-background-page-event-page-1024x710-1.webp\" alt=\"Background page in Manifest V2 Chrome extension\" class=\"wp-image-1749\" srcset=\"https:\/\/www.stefanvd.net\/blog\/wp-content\/uploads\/2024\/05\/manifest-v2-background-page-event-page-1024x710-1.webp 1024w, https:\/\/www.stefanvd.net\/blog\/wp-content\/uploads\/2024\/05\/manifest-v2-background-page-event-page-1024x710-1-300x208.webp 300w, https:\/\/www.stefanvd.net\/blog\/wp-content\/uploads\/2024\/05\/manifest-v2-background-page-event-page-1024x710-1-768x533.webp 768w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">Background page in Manifest V2 Chrome extension<\/figcaption><\/figure>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"interval-and-timeout\">Interval and timeout<\/h4>\n\n\n\n<p>In addition, service workers are terminated when not in use and restarted when necessary. So using a timer function will not be reliable.<\/p>\n\n\n\n<p>Before in the Manifest V2 <em>background.js<\/em> event page<em>,<\/em> you can use the <em>window.setInterval<\/em> and <em>window.setTimeout<\/em>. This include also for the <em>window.clearInterval<\/em>, and <em>window.clearTimeout<\/em>.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ interval\nfunction refreshTime() {\nconsole.log(\"hello\");\n}\n\nwindow.setInterval(refreshTime, 1000);\n\n\/\/ time out\nfunction WelcomeThere(){\nconsole.log(\"welcome\");\n}\n\nconst myTimeout = window.setTimeout(WelcomeThere, 5000);\n\nfunction myStopFunction() {\n  window.clearTimeout(myTimeout);\n}<\/code><\/pre>\n\n\n\n<p>Now in a Service worker, it will become just <em>setInterval<\/em> and <em>setTimeout<\/em>.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ interval\nfunction refreshTime() {\nconsole.log(\"hello\");\n}\n\nsetInterval(refreshTime, 1000);\n\n\/\/ time out\nfunction WelcomeThere(){\nconsole.log(\"welcome\");\n}\n\nconst myTimeout = setTimeout(WelcomeThere, 5000);\n\nfunction myStopFunction() {\n   clearTimeout(myTimeout);\n}<\/code><\/pre>\n\n\n\n<p>However, there is no guarantee that the <em>setInterval<\/em> will continue to work in the service worker code. So use the <a href=\"https:\/\/developer.chrome.com\/docs\/extensions\/reference\/alarms\/\" target=\"_blank\" rel=\"noreferrer noopener\">Chrome alarm<\/a> for a reliable timekeeping function. And that needs extra permission in your manifest.json:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\"permissions\": &#91;\"activeTab\", \"storage\", \"alarms\"]<\/code><\/pre>\n\n\n\n<p>The use of the alarm in the service worker:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>chrome.alarms.create(\"startTime\", {when:Date.now() + 60*1000});\t\t\n\nchrome.alarms.onAlarm.addListener(function(alarm){\n     startTime();\n});\n\nfunction startTime(){\n     \/\/ draw clock\n}<\/code><\/pre>\n\n\n\n<p>An important note is that in the unpacked version (developer mode) there is no limit to how often the alarm can fire. But when you upload it to the Chrome Web Store, the alarm can <strong>not be set to less than 1 minute<\/strong>.<\/p>\n\n\n\n<p>This is used in my <a href=\"https:\/\/chrome.google.com\/webstore\/detail\/date-today-for-google-chr\/mhgknbehalhkedjgfhiaindklahhkccc\" target=\"_blank\" rel=\"noreferrer noopener\">Date Today Chrome extension<\/a>.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"screen-size\">Screen size<\/h4>\n\n\n\n<p>Also to get the current screen size is a part of the <em>window.screen<\/em> object. So you need to use the Chrome extension API&nbsp;<a href=\"https:\/\/developer.chrome.com\/docs\/extensions\/reference\/system_display\/#method-getInfo\" target=\"_blank\" rel=\"noreferrer noopener\">chrome.system.display.getInfo<\/a>&nbsp;to get the current screen work area size. And when you use this Chrome extension API, new permission needs to be added in your manifest.json to capture the screen size.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>permissions\": &#91;\"tabs\", \"storage\", \"system.display\"]<\/code><\/code><\/pre>\n\n\n\n<p>Note: Currently, there is a bug that does not provide the correct screen size. See this <a href=\"https:\/\/bugs.chromium.org\/p\/chromium\/issues\/detail?id=1303129&amp;q=&amp;can=4\" target=\"_blank\" rel=\"noreferrer noopener\">Chromium bug report 1303129<\/a>. <\/p>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"local-storage\">localStorage<\/h4>\n\n\n\n<p>There is no access to the local storage <em>window.localstorage<\/em> in the service worker, for that you need the <em><a href=\"https:\/\/developer.chrome.com\/docs\/extensions\/reference\/storage\/\" target=\"_blank\" rel=\"noreferrer noopener\">chrome.storage.local<\/a><\/em> or <em>chrome.storage.sync<\/em> API.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"3-use-of-constant-value-in-the-background-service-worker\">3. Use of Constant value in the background service worker<\/h3>\n\n\n\n<p>Before using the event background page, you can add many JavaScript files in the header as in one HTML document. Now with the service worker that is not possible. However, you can now import the script into a single background server worker.<\/p>\n\n\n\n<p>Here is an example of how to do it:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ Importing the constants\nimportScripts(\"constants.js\");<\/code><\/pre>\n\n\n\n<p>This is used in my <a href=\"https:\/\/chrome.google.com\/webstore\/detail\/turn-off-the-lights\/bfbmjmiodbnnpllbbbfblcplfjjepjdn\" target=\"_blank\" rel=\"noreferrer noopener\">Turn Off the Lights<\/a><a href=\"https:\/\/chrome.google.com\/webstore\/detail\/zoom-for-google-chrome\/lajondecmobodlejlcjllhojikagldgd\" target=\"_blank\" rel=\"noreferrer noopener\"> Chrome extension<\/a>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"4-injecting-a-script-needs-permission-now\">4. Injecting a script needs permission now<\/h3>\n\n\n\n<p>When migrating to Manifest V3, additional permission is required. Now to run a script, you need to add this &#8220;scripting&#8221; permission. Since most developers should add this permission &#8220;scripting&#8221; to their Chrome extension. You can use the <em>chrome.scripting<\/em> API to inject JavaScript and CSS into websites. Before you can run the code with it without additional permission.<\/p>\n\n\n\n<p>Manifest V2:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>chrome.tabs.executeScript(tabs.id, { file: \"js\/print.js\" }<\/code><\/pre>\n\n\n\n<p>Manifest V3:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>chrome.scripting.executeScript({ target: {tabId: tab.id}, files: &#91;\"js\/print.js\"] });<\/code><\/pre>\n\n\n\n<p>This is used in my <a href=\"https:\/\/chrome.google.com\/webstore\/detail\/print-for-google-chrome\/idfnpgjblkahngbondojabhffkkdekbd\" target=\"_blank\" rel=\"noreferrer noopener\">Print Chrome extension<\/a>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"5-use-not-inline-java-script-code-but-files-or-functions\">5. Use not inline JavaScript code but files or functions<\/h3>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"no-remote-java-script\">No remote JavaScript<\/h4>\n\n\n\n<p>You cannot use external JavaScript files when migrating to Manifest V3. All JavaScript files must be contained in the extension folder.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"a-new-way-of-injecting-scripts\">A new way of injecting scripts<\/h4>\n\n\n\n<p>There is a new and different way of injecting script into the current web page. Injecting the script and its parameters can be done from a file or function.<\/p>\n\n\n\n<p>To load your custom script file:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>chrome.scripting.executeScript({\n   target: {tabId: tabId},\n   files: &#91;\"js\/zoom.js\"],\n   injectImmediately: true\n});<\/code><\/pre>\n\n\n\n<p>To load custom code from this same JavaScript document:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>chrome.scripting.executeScript({\n   target: {tabId: tab.id},\n   func: codebodyzoom,\n   args: &#91;b]\n});\n\nfunction codebodyzoom(b){\n   document.body.style.zoom = b;\n}<\/code><\/pre>\n\n\n\n<p>This is used in my <a href=\"https:\/\/chrome.google.com\/webstore\/detail\/zoom-for-google-chrome\/lajondecmobodlejlcjllhojikagldgd\" target=\"_blank\" rel=\"noreferrer noopener\">Zoom Chrome extension<\/a>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"6-change-is-important-but-changelog-too\">6. Change is important but Changelog too<\/h3>\n\n\n\n<p>It is also important to document what you have changed in your code, that to be open and transparent for you and the users that are using your Chrome extension. If you use GitHub, you can write for each release the information on what is changed. If your code is not Open-Source, then provide a link to a web page with that information that includes the version number, date, and what was updated.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"clean-up-your-not-necessary-files\">Clean up your not necessary files<\/h4>\n\n\n\n<p>So developers use GitHub but try to add also the &#8220;.gitignore&#8221; file so that no other necessary files will be not there for the final distribution. You can use my free and Open-Source Chrome extension template (available on GitHub) that does all the job for you.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"7-context-menu-detection\">7. Context menu detection<\/h3>\n\n\n\n<p>Use function detection to check if the Chrome extension API is available for that platform. Because some platforms that support Chrome extensions will not support a context menu on that device. For example the mobile Safari web browser on iOS and iPadOS (and maybe Android soon).<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>if(chrome.contextMenus){\n   var contextspage = &#91;\"page\"];\n   menupage = chrome.contextMenus.create({\"title\": pagetitle, \"type\":\"normal\", \"id\": \"fspage\", \"contexts\":contextspage});\n}else{\n   \/\/ web browser support this not\n}<\/code><\/pre>\n\n\n\n<p>This is used in my <a href=\"https:\/\/chrome.google.com\/webstore\/detail\/full-screen-for-google-ch\/gmimocjjppdelmhpcmpkhekmpoddgima\" target=\"_blank\" rel=\"noreferrer noopener\">Full Screen Chrome extension<\/a>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"8-use-chrome-runtime-and-not-chrome-extension\">8. Use &#8220;chrome.runtime&#8221; and not &#8220;chrome.extension&#8221;<\/h3>\n\n\n\n<p>If you want to retrieve an item, do not this code anymore <em>chrome.extension.getURL(url)<\/em> but <em>chrome.runtime.getURL(url)<\/em>. And to protect privacy, enable sure the dynamic URL attribute. That prevents others from tracking you that you have installed that Chome extension on your device.<\/p>\n\n\n\n<p>Manifest V3 manifest.json file:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\"web_accessible_resources\": &#91;{\n    \"resources\": &#91; \"images\/slice1.png\",\n    \"images\/slice2.png\",\n    \"images\/slice3.png\",\n    \"images\/slice4.png\",\n    \"images\/slice5.png\",\n    \"images\/slice6.png\",\n    \"css\/body.css\"],\n    \"matches\": &#91;\"&lt;all_urls&gt;\"],\n    \"use_dynamic_url\": true\n  }]<\/code><\/pre>\n\n\n\n<p>Content script:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>document.getElementById(\"backgroundimagesource\").value = chrome.<em>runtime<\/em>.getURL('\/images\/slice1.png');<\/code><\/pre>\n\n\n\n<p>This is used in my <a href=\"https:\/\/chrome.google.com\/webstore\/detail\/proper-menubar-for-google\/egclcjdpndeoioimlbbbmdhcaopnedkp\" target=\"_blank\" rel=\"noreferrer noopener\">Proper Menubar Chrome extension<\/a>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"9-non-documented-chrome-extension-changed-too\">9. Non-documented Chrome extension changed too<\/h3>\n\n\n\n<p>When converting to this new Manifest (migrating to Manifest V3), there is a new Chrome extension API name for Manifest V3. Here is an overview of which name has been changed:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>chrome.extension.sendRequest() =&gt; chrome.runtime.sendMessage()\nchrome.extension.onRequest =&gt; chrome.runtime.onMessage\nchrome.extension.onRequestExternal =&gt; chrome.runtime.onMessageExternal\nchrome.extension.lastError =&gt; chrome.runtime.lastError\nchrome.extension.getURL() =&gt; chrome.runtime.getURL()\nchrome.extension.getExtensionTabs() =&gt; chrome.extension.getViews()\nchrome.tabs.Tab.selected =&gt; chrome.tabs.query({active: true})\nchrome.tabs.sendRequest() =&gt; chrome.runtime.runtime.sendMessage()\nchrome.tabs.getSelected() =&gt; chrome.tabs.query({active: true})\nchrome.tabs.getAllInWindow() =&gt; chrome.tabs.query({currentWindow: true})\nchrome.tabs.onSelectionChanged =&gt; chrome.tabs.onActivated()\nchrome.tabs.onActiveChanged =&gt; chrome.tabs.onActivated()\nchrome.tabs.onHighlightChanged =&gt; chrome.tabs.onHighlighted<\/code><\/pre>\n\n\n\n<p>As well as the undocumented:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>chrome.extension.sendMessage() =&gt; chrome.runtime.sendMessage()<br>chrome.extension.connect() =&gt; chrome.runtime.connect()<br>chrome.extension.onConnect =&gt; chrome.runtime.onConnect<br>chrome.extension.onMessage =&gt; chrome.runtime.onMessage<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"10-check-api-is-on-the-stable-release\">10. Check API is on the stable release<\/h3>\n\n\n\n<p>Always perform a function check in your Chrome. Because Manifest V3 is stable as Google said, but it is possible that not all features are available in the stable Chrome version that most everyday web browsers use these days.<\/p>\n\n\n\n<p>For example, <em>chrome.action.openpopup()<\/em> API. It works only in the Chrome beta and the dev channel. And not in the Chrome stable version, see <a href=\"https:\/\/bugs.chromium.org\/p\/chromium\/issues\/detail?id=1245093\" target=\"_blank\" rel=\"noreferrer noopener\">Chromium bug report 1245093<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"addition-resource\">Addition resource <\/h2>\n\n\n\n<p>To learn more about Chrome extension APIs and continue to create a creative experience for the users, see these resources:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/github.com\/stefanvd\/browser-extension-starter-template\" target=\"_blank\" rel=\"noreferrer noopener\">The Chrome extension template<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/developer.chrome.com\/docs\/extensions\" target=\"_blank\" rel=\"noreferrer noopener\">Chrome Developer Documentation<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Mozilla\/Add-ons\/WebExtensions\" target=\"_blank\" rel=\"noreferrer noopener\">MDN Browser Extensions<\/a><\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"final-remarks\">Final remarks<\/h2>\n\n\n\n<p>My <a href=\"https:\/\/www.stefanvd.net\/blog\/2022\/10\/05\/chrome-extension-development-template-manifest-v3-for-beginner-developer\/\" target=\"_blank\" rel=\"noreferrer noopener\">previous post for a good development environment for building a Chrome extension Manifest V3<\/a> has a good start with removing the unnecessary &#8220;.DS_Store&#8221; files and &#8220;node_modules&#8221; folder. And with the plugin to write your seamless and concisely JSON, HTML, JavaScript, and CSS code. That with automatically pretty your CSS code. And one command to distribute your Chrome extension to a zip file.<\/p>\n\n\n\n<p>In this article, you have learned about your Chrome extension <strong>migrating to Manifest V3<\/strong>, by using the correct permission in your Chrome extension and the use of a service worker. And the use of the new Chrome extension API names. Now you can develop and create amazing experiences in your web browser. I hope you enjoyed reading this post. If you would like to support me, consider <a href=\"https:\/\/www.stefanvd.net\/donate\/\" target=\"_blank\" rel=\"noreferrer noopener\">a small donation<\/a> to my web community work.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>It is time that your Chrome extensions are migrating to Manifest V3. As Chrome Manifest V3 starts on 17 January 2023. The best way to learn to write Chrome extension code (that is Manifest V3 ready) is by reading other developers&#8217; code. So you as a beginner developer know how to solve problems. And be [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":402,"comment_status":"closed","ping_status":"open","sticky":false,"template":"single-full-width.php","format":"standard","meta":{"footnotes":""},"categories":[4,9],"tags":[],"class_list":["post-343","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-browser","category-programming"],"_links":{"self":[{"href":"https:\/\/www.stefanvd.net\/blog\/wp-json\/wp\/v2\/posts\/343","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.stefanvd.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.stefanvd.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.stefanvd.net\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.stefanvd.net\/blog\/wp-json\/wp\/v2\/comments?post=343"}],"version-history":[{"count":67,"href":"https:\/\/www.stefanvd.net\/blog\/wp-json\/wp\/v2\/posts\/343\/revisions"}],"predecessor-version":[{"id":1750,"href":"https:\/\/www.stefanvd.net\/blog\/wp-json\/wp\/v2\/posts\/343\/revisions\/1750"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.stefanvd.net\/blog\/wp-json\/wp\/v2\/media\/402"}],"wp:attachment":[{"href":"https:\/\/www.stefanvd.net\/blog\/wp-json\/wp\/v2\/media?parent=343"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.stefanvd.net\/blog\/wp-json\/wp\/v2\/categories?post=343"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.stefanvd.net\/blog\/wp-json\/wp\/v2\/tags?post=343"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}