@extension.dev/mcp 6.3.0 → 6.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +57 -18
- package/README.md +12 -2
- package/bin/extension-mcp.js +1 -0
- package/dist/module.js +3524 -2054
- package/dist/src/lib/carrier.d.ts +0 -57
- package/dist/src/lib/credentials.d.ts +0 -9
- package/dist/src/lib/device-flow.d.ts +0 -6
- package/dist/src/lib/github-device.d.ts +0 -9
- package/dist/src/lib/guest-load-oracle.d.ts +0 -10
- package/dist/src/lib/login-flow.d.ts +0 -36
- package/dist/src/lib/preview-upload.d.ts +33 -0
- package/dist/src/lib/project-ignore.d.ts +10 -0
- package/dist/src/lib/publish.d.ts +0 -10
- package/dist/src/lib/registry-access.d.ts +3 -19
- package/dist/src/lib/registry.d.ts +0 -65
- package/dist/src/lib/share-record.d.ts +26 -0
- package/dist/src/lib/vendor/chrome-theme/chrome-theme-color-math.d.ts +84 -0
- package/dist/src/lib/vendor/chrome-theme/chrome-theme-reference.d.ts +270 -0
- package/dist/src/lib/vendor/chrome-theme/chrome-theme-resolve.d.ts +52 -0
- package/dist/src/tools/open.d.ts +1 -0
- package/dist/src/tools/preview-web.d.ts +76 -0
- package/dist/src/tools/stop.d.ts +0 -1
- package/dist/src/tools/store-status.d.ts +0 -9
- package/dist/src/tools/theme-verify.d.ts +22 -0
- package/extensions/live-preview/chromium/action/index.js +9 -1
- package/extensions/live-preview/chromium/background/service_worker.js +10 -2
- package/extensions/live-preview/chromium/manifest.json +1 -0
- package/package.json +2 -2
- package/server.json +3 -3
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
(()=>{"use strict";JSON.stringify({alarms:["alarms"],bookmarks:["bookmarks"],contextMenus:["contextMenus"],cookies:["cookies"],downloads:["downloads"],history:["history"],notifications:["notifications"],offscreen:["offscreen"],contentSettings:["contentSettings"],debugger:["debugger"],declarativeNetRequest:["declarativeNetRequest","declarativeNetRequestWithHostAccess"],fontSettings:["fontSettings"],identity:["identity"],idle:["idle"],instanceID:["gcm"],management:["management"],mdns:["mdns"],power:["power"],printerProvider:["printerProvider"],printing:["printing"],privacy:["privacy"],proxy:["proxy"],sidePanel:["sidePanel"],tabCapture:["tabCapture"],tabGroups:["tabGroups"],topSites:["topSites"],tts:["tts"],userScripts:["userScripts"],webNavigation:["webNavigation"],webRequest:["webRequest"]}),JSON.stringify(["getSelf","getPermissionWarningsByManifest","uninstallSelf","installReplacementWebApp"]),JSON.stringify({"system.cpu":["system.cpu"],"system.memory":["system.memory"],"system.storage":["system.storage"],"system.display":["system.display"]}),JSON.stringify(["extension","i18n","runtime","scripting","storage"]);let e={storage:["storage"],notifications:["notifications"],contextMenus:["contextMenus"],downloads:["downloads"],cookies:["cookies"],bookmarks:["bookmarks"],history:["history"],alarms:["alarms"],offscreen:["offscreen"],scripting:["scripting","activeTab"]};function t(t,r){let n=t.split(".")[0]??"",o=e[n];return!o||null===r||o.some(e=>r.has(e))?null:`chrome.${n} needs the "${o.join('" or "')}" permission, which this session did not declare. Declare it in the manifest (and re-register the session) to use this API.`}function r(e,t,r){let n=t.active??t.id===e.activeTabId;return{id:t.id,index:t.index??r,windowId:e.id,url:t.url,title:t.title,active:n,highlighted:n,selected:n,pinned:t.pinned??!1,discarded:t.discarded??!1,groupId:t.tabGroupId??-1,status:t.status??(t.discarded?"unloaded":"complete"),incognito:t.incognito??!1}}let n=new Set(["storage.setAccessLevel","storage.get","storage.set","storage.remove","storage.clear","tabs.create","tabs.update","tabs.remove","tabs.move","tabs.discard","tabs.goBack","tabs.goForward","tabs.group","tabs.ungroup","tabGroups.query","tabGroups.get","tabGroups.update","tabGroups.move","tabs.get"]);async function o(e){let{data:t,windows:n,getStorage:o,setStorage:s,removeStorageKeys:a,clearStorage:i,onStorageChanged:l,onCreateTab:c,onUpdateTab:d,onRemoveTab:u,onMoveTab:g,onDiscardTab:m,onGroupTabs:p,onUngroupTabs:f,onQueryTabGroups:h,onGetTabGroup:w,onUpdateTabGroup:b,onMoveTabGroup:y,onTabGoBack:x,onTabGoForward:v,onSetStorageAccessLevel:k,respond:I}=e;switch(t.api){case"storage.setAccessLevel":{let[e,r]=t.args,n=e?.accessLevel;if("TRUSTED_CONTEXTS"!==n&&"TRUSTED_AND_UNTRUSTED_CONTEXTS"!==n)return void I({error:"Invalid access level."});await k?.(t.extensionId,r??"session",n),I({result:void 0});return}case"storage.get":{let[e,r]=t.args,n=o(t.extensionId,r??"local");if(!e)return void I({result:n});if(Array.isArray(e)){let t={};e.forEach(e=>{e in n&&(t[e]=n[e])}),I({result:t});return}if("string"==typeof e)return void I({result:e in n?{[e]:n[e]}:{}});if("object"==typeof e){let t={};Object.keys(e).forEach(r=>{t[r]=r in n?n[r]:e[r]}),I({result:t});return}I({result:n});return}case"storage.set":{let[e,r]=t.args,n=r??"local";if(e&&"object"==typeof e){let r=o(t.extensionId,n),a={};for(let t of Object.keys(e)){let n=r[t],o=e[t];if(JSON.stringify(n)===JSON.stringify(o))continue;let s={newValue:o};t in r&&(s.oldValue=n),a[t]=s}await s(t.extensionId,e,n),l&&Object.keys(a).length>0&&l(t.extensionId,a,n)}I({result:!0});return}case"storage.remove":{let[e,r]=t.args,n=r??"local",s=(Array.isArray(e)?e:[e]).filter(Boolean).map(String),i=o(t.extensionId,n),c={};for(let e of s)e in i&&(c[e]={oldValue:i[e]});await a(t.extensionId,s,n),l&&Object.keys(c).length>0&&l(t.extensionId,c,n),I({result:!0});return}case"storage.clear":{let[e]=t.args,r=e??"local",n=o(t.extensionId,r),s={};for(let e of Object.keys(n))s[e]={oldValue:n[e]};await i(t.extensionId,r),l&&Object.keys(s).length>0&&l(t.extensionId,s,r),I({result:!0});return}case"tabs.create":{let[e]=t.args;I({result:await c(e?.url??"chrome://newtab",e)??!0});return}case"tabs.update":{let[e,r]=t.args;null!==e&&"object"==typeof e&&(r=e,e=void 0),I({result:await d(e,r??{})??!0});return}case"tabs.remove":{let[e]=t.args;await u(e),I({result:void 0});return}case"tabs.move":{let[e,r]=t.args;I({result:await g?.(t.extensionId,e,r??{})});return}case"tabs.discard":{let[e]=t.args;I({result:await m?.(t.extensionId,e)});return}case"tabs.goBack":{let[e]=t.args;await x?.(t.extensionId,e),I({result:void 0});return}case"tabs.goForward":{let[e]=t.args;await v?.(t.extensionId,e),I({result:void 0});return}case"tabs.group":{let[e]=t.args;I({result:await p?.(t.extensionId,e??{})});return}case"tabs.ungroup":{let[e]=t.args;await f?.(t.extensionId,e),I({result:void 0});return}case"tabGroups.query":{let[e]=t.args;I({result:await h?.(t.extensionId,e)??[]});return}case"tabGroups.get":{let[e]=t.args;I({result:await w?.(t.extensionId,e)});return}case"tabGroups.update":{let[e,r]=t.args;I({result:await b?.(t.extensionId,e,r??{})});return}case"tabGroups.move":{let[e,r]=t.args;I({result:await y?.(t.extensionId,e,r??{})});return}case"tabs.get":{let[e]=t.args;for(let t of n)for(let n=0;n<t.tabs.length;n+=1){let o=t.tabs[n];if(o&&o.id===e)return void I({result:r(t,o,n)})}I({error:`No tab with id: ${String(e)}.`})}}}function s(e,t,r,n){let o;return"function"!=typeof e&&(r({error:(o=n?` on the ${n} lane`:"",`No backend implements ${t}${o}, so the call did not run. This is a preview limitation, not an error in your extension: the API is on the bridge's wire protocol but nothing is wired behind it here.`),refused:!0}),!0)}let a=new Set(["tabs.query","sidePanel.open","action.openPopup","runtime.openOptionsPage","devtools.panels.create","action.setPopup","action.getBadgeText","action.getBadgeBackgroundColor","action.getTitle","action.setBadgeText","action.setBadgeBackgroundColor","action.setTitle","action.setIcon"]);async function i(e){let{data:t,windows:n,onOpenSidePanel:o,onOpenActionPopup:a,onOpenOptionsPage:i,onDevtoolsPanelCreate:l,onSetActionPopup:c,onGetActionState:d,onSetBadgeText:u,onSetBadgeBackground:g,onSetActionTitle:m,onSetActionIcon:p,respond:f}=e;switch(t.api){case"tabs.query":{let[e]=t.args,o=n.find(e=>e.focused)??n[0],s=n;e?.windowId!==void 0?s=-2===e.windowId?o?[o]:[]:n.filter(t=>t.id===e.windowId):e?.currentWindow===!0||e?.lastFocusedWindow===!0?s=o?[o]:[]:e?.currentWindow===!1&&(s=n.filter(e=>e!==o));let a=e?.url?Array.isArray(e.url)?e.url:[e.url]:null;f({result:s.flatMap(t=>t.tabs.map((e,n)=>r(t,e,n)).filter(t=>e?.active===void 0||t.active===e.active).filter(t=>e?.pinned===void 0||t.pinned===e.pinned).filter(t=>e?.status===void 0||t.status===e.status).filter(t=>e?.discarded===void 0||t.discarded===e.discarded).filter(t=>e?.groupId===void 0||(t.groupId??-1)===e.groupId).filter(e=>!a||a.some(t=>(function(e,t){if(!e)return!1;if("<all_urls>"===t)return!0;let r=t.replace(/[.+?^${}()|[\]\\]/g,"\\$&");return RegExp(`^${r.replace(/\*/g,".*")}$`).test(e)})(e.url,t))))});return}case"sidePanel.open":await o(t.extensionId),f({result:void 0});return;case"action.openPopup":if(s(a,t.api,f))return;a?.(t.extensionId),f({result:void 0});return;case"runtime.openOptionsPage":if(s(i,t.api,f))return;i?.(t.extensionId),f({result:void 0});return;case"devtools.panels.create":{if(s(l,t.api,f))return;let[e]=t.args;l?.(t.extensionId,e?.page??""),f({result:!0});return}case"action.setPopup":{if(s(c,t.api,f))return;let[e]=t.args;c?.(t.extensionId,e?.popup??""),f({result:void 0});return}case"action.getBadgeText":{let[e]=t.args,r=await d?.(t.extensionId,e?.tabId);f({result:r?.badgeText??""});return}case"action.getBadgeBackgroundColor":{let[e]=t.args,r=await d?.(t.extensionId,e?.tabId);f({result:r?.badgeColor??null});return}case"action.getTitle":{let[e]=t.args,r=await d?.(t.extensionId,e?.tabId);f({result:r?.title??""});return}case"action.setBadgeText":{let[e]=t.args;await u(t.extensionId,e?.text,e?.tabId),f({result:!0});return}case"action.setBadgeBackgroundColor":{let[e]=t.args;await g(t.extensionId,e?.color,e?.tabId),f({result:!0});return}case"action.setTitle":{let[e]=t.args;await m(t.extensionId,e?.title,e?.tabId),f({result:!0});return}case"action.setIcon":{let[e]=t.args,r="string"==typeof e?.path?e.path:"object"==typeof e?.path?Object.values(e.path)[0]:void 0;await p(t.extensionId,r),f({result:!0})}}}let l=new Set(["contextMenus.create","contextMenus.update","contextMenus.remove","contextMenus.removeAll","notifications.create","notifications.clear","notifications.update","notifications.getAll","downloads.search","downloads.cancel","downloads.pause","downloads.resume","downloads.erase","downloads.download"]);async function c(e){let{data:t,onCreateContextMenu:r,onUpdateContextMenu:n,onRemoveContextMenu:o,onClearContextMenus:a,onCreateNotification:i,onClearNotification:l,onUpdateNotification:c,onGetNotifications:d,onDownload:u,onSearchDownloads:g,onCancelDownload:m,onPauseDownload:p,onResumeDownload:f,onEraseDownloads:h,respond:w}=e;switch(t.api){case"contextMenus.create":{let[e]=t.args;w({result:await r(t.extensionId,e??{})});return}case"contextMenus.update":{let[e,r]=t.args;w({result:await n(t.extensionId,String(e??""),r??{})});return}case"contextMenus.remove":{let[e]=t.args;w({result:await o(t.extensionId,String(e??""))});return}case"contextMenus.removeAll":return void w({result:await a(t.extensionId)});case"notifications.create":{let[e,r]=t.args,n="string"==typeof e?e:void 0,o="string"==typeof e?r:e;w({result:await i(t.extensionId,n,o??{})});return}case"notifications.clear":{let[e]=t.args;w({result:await l(t.extensionId,String(e??""))});return}case"notifications.update":{let[e,r]=t.args;w({result:await c?.(t.extensionId,String(e??""),r??{})??!1});return}case"notifications.getAll":return void w({result:await d(t.extensionId)});case"downloads.search":{let[e]=t.args;w({result:await g?.(t.extensionId,e)??[]});return}case"downloads.cancel":{if(s(m,t.api,w))return;let[e]=t.args;await m?.(t.extensionId,e),w({result:void 0});return}case"downloads.pause":{if(s(p,t.api,w))return;let[e]=t.args;await p?.(t.extensionId,e),w({result:void 0});return}case"downloads.resume":{if(s(f,t.api,w))return;let[e]=t.args;await f?.(t.extensionId,e),w({result:void 0});return}case"downloads.erase":{if(s(h,t.api,w))return;let[e]=t.args;w({result:await h?.(t.extensionId,e)??[]});return}case"downloads.download":{let[e]=t.args;w({result:await u(t.extensionId,e??{})})}}}let d=new Set(["bookmarks.getTree","bookmarks.get","bookmarks.getChildren","bookmarks.search","bookmarks.create","bookmarks.update","bookmarks.remove","bookmarks.removeTree","bookmarks.move","history.search","history.getVisits","history.addUrl","history.deleteUrl","history.deleteRange","history.deleteAll","windows.get","windows.getAll","windows.getCurrent","windows.create","windows.update","windows.remove"]);async function u(e){let{data:t,windows:r,onGetWindow:n,onGetAllWindows:o,onCreateWindow:s,onUpdateWindow:a,onRemoveWindow:i,onGetBookmarkTree:l,onGetBookmarks:c,onGetBookmarkChildren:d,onSearchBookmarks:u,onCreateBookmark:g,onUpdateBookmark:m,onRemoveBookmark:p,onMoveBookmark:f,onSearchHistory:h,onGetHistoryVisits:w,onAddHistoryUrl:b,onDeleteHistoryUrl:y,onDeleteHistoryRange:x,onDeleteAllHistory:v,respond:k}=e;switch(t.api){case"bookmarks.getTree":return void k({result:await l(t.extensionId)});case"bookmarks.get":{let[e]=t.args,r=Array.isArray(e)?e:[e];k({result:await c(t.extensionId,r.filter(e=>null!=e).map(String))});return}case"bookmarks.getChildren":{let[e]=t.args;k({result:await d(t.extensionId,null==e?void 0:String(e))});return}case"bookmarks.search":{let[e]=t.args;k({result:await u(t.extensionId,e)});return}case"bookmarks.create":{let[e]=t.args;k({result:await g(t.extensionId,e??{})});return}case"bookmarks.update":{let[e,r]=t.args;k({result:await m(t.extensionId,String(e??""),r??{})});return}case"bookmarks.remove":{let[e]=t.args;k({result:await p(t.extensionId,String(e??""),!1)});return}case"bookmarks.removeTree":{let[e]=t.args;k({result:await p(t.extensionId,String(e??""),!0)});return}case"bookmarks.move":{let[e,r]=t.args;k({result:await f?.(t.extensionId,String(e??""),r??{})});return}case"history.search":{let[e]=t.args;k({result:await h(t.extensionId,e??{})});return}case"history.getVisits":{let[e]=t.args;k({result:await w(t.extensionId,e??{})});return}case"history.addUrl":{let[e]=t.args;await b(t.extensionId,e??{}),k({result:!0});return}case"history.deleteUrl":{let[e]=t.args;await y(t.extensionId,e??{}),k({result:!0});return}case"history.deleteRange":{let[e]=t.args;await x(t.extensionId,e??{}),k({result:!0});return}case"history.deleteAll":await v(t.extensionId),k({result:!0});return;case"windows.get":{let[e,r]=t.args;k({result:await n(t.extensionId,null==e?void 0:String(e),r)});return}case"windows.getAll":{let[e]=t.args;k({result:await o(t.extensionId,e)});return}case"windows.getCurrent":{let e=r.find(e=>e.focused)??r[0];k({result:e?{id:e.id,focused:!0,tabs:e.tabs}:null});return}case"windows.create":{let[e]=t.args;k({result:await s(t.extensionId,e?.url)});return}case"windows.update":{let[e,r]=t.args;k({result:await a(t.extensionId,null==e?void 0:String(e),r??{})});return}case"windows.remove":{let[e]=t.args;k({result:await i(t.extensionId,null==e?void 0:String(e))})}}}let g=new Set(["runtime.sendMessage","tabs.sendMessage","runtime.getContexts","permissions.contains","permissions.request","permissions.remove","permissions.getAll","offscreen.createDocument","offscreen.closeDocument","offscreen.hasDocument","cookies.get","cookies.getAll","cookies.set","cookies.remove","scripting.executeScript","scripting.insertCSS"]);async function m(e){let{data:t,onRuntimeMessage:r,onTabMessage:n,onGetRuntimeContexts:o,onContainsPermissions:s,onRequestPermissions:a,onRemovePermissions:i,onGetAllPermissions:l,onCreateOffscreenDocument:c,onCloseOffscreenDocument:d,onHasOffscreenDocument:u,onGetCookie:g,onGetAllCookies:m,onSetCookie:p,onRemoveCookie:f,onExecuteScript:h,onInsertCss:w,respond:b,source:y}=e;switch(t.api){case"runtime.sendMessage":{let[e]=t.args;b({result:await r(t.extensionId,e,y)});return}case"tabs.sendMessage":{let[e,r]=t.args;if(!n)return void b({error:"Could not establish connection. Receiving end does not exist."});b({result:await n(t.extensionId,e,r)});return}case"runtime.getContexts":return void b({result:await o(t.extensionId)});case"permissions.contains":{let[e]=t.args;b({result:await s(t.extensionId,e??{})});return}case"permissions.request":{let[e]=t.args;b({result:await a(t.extensionId,e??{})});return}case"permissions.remove":{let[e]=t.args;b({result:await i(t.extensionId,e??{})});return}case"permissions.getAll":return void b({result:await l(t.extensionId)});case"offscreen.createDocument":{let[e]=t.args,r=await c(t.extensionId,e??{});b({result:"boolean"!=typeof r||r});return}case"offscreen.closeDocument":{let e=await d(t.extensionId);b({result:"boolean"!=typeof e||e});return}case"offscreen.hasDocument":return void b({result:await u(t.extensionId)});case"cookies.get":{let[e]=t.args;b({result:await g(t.extensionId,e??{})});return}case"cookies.getAll":{let[e]=t.args;b({result:await m(t.extensionId,e??{})});return}case"cookies.set":{let[e]=t.args;b({result:await p(t.extensionId,e??{})});return}case"cookies.remove":{let[e]=t.args;b({result:await f(t.extensionId,e??{})});return}case"scripting.executeScript":{let[e]=t.args;b({result:await h(t.extensionId,e??{})});return}case"scripting.insertCSS":{let[e]=t.args;await w(t.extensionId,e??{}),b({result:!0})}}}let p=["local","sync","session","managed"];function f(e,t){let r=Math.min(e.length,t.length),n=0;for(;n<r&&e[n]===t[n];)n+=1;return n}function h(e){let t=function(e){let t=[...n,...a,...l,...d,...g].sort();if(t.includes(e))return null;let r=e.split(".");if(3===r.length&&"storage"===r[0]&&p.includes(r[1]??"")){let e=`storage.${r[2]}`;if(t.includes(e))return e}let o=r[r.length-1]??"",s=t.filter(e=>e.endsWith(`.${o}`));if(s.length)return s.sort((t,r)=>f(r,e)-f(t,e))[0];let i=t.filter(e=>e.startsWith(`${r[0]}.`));return i.length?i[0]??null:null}(e),r=`Unsupported API: ${e}.`;if(!t)return`${r} No wire name resembles it, so this API is not on the bridge at all. It may still work natively inside your extension's own contexts: the bridge is what an observer page can DRIVE, not the whole runtime.`;let o=t.startsWith("storage.")&&e.startsWith("storage.")?` The bridge takes the storage area as an ARGUMENT, not as part of the name: ${t}(keys, "local").`:"";return`${r} The closest wire name the bridge routes is ${t}.${o}`}let w=e=>"string"==typeof e&&e.length>0;function b(e,t){return`Error in invocation of ${e}: ${t}`}function y(e,t){return b(e,`missing required property '${t}'`)}function x(e,t,r,n){return b(e,`'${t}' expects a ${r}, got ${typeof n}`)}async function v(e){let{data:r,respond:s,getDeclaredPermissions:p,getSessionStorageAccessLevel:f}=e;try{var b;if(b=r.api,!(n.has(b)||a.has(b)||l.has(b)||d.has(b)||g.has(b)))return void s({error:h(r.api),refused:!0});let v=function(e,t){let r=t[0];switch(e){case"downloads.download":if(!r||!("url"in r))return y(e,"url");if(!w(r.url))return x(e,"url","non-empty string",r.url);if(void 0!==r.filename&&"string"!=typeof r.filename)return x(e,"filename","string",r.filename);return null;case"offscreen.createDocument":if(!r||!w(r.url))return y(e,"url");if(!Array.isArray(r.reasons)||0===r.reasons.length)return y(e,"reasons");if(!w(r.justification))return y(e,"justification");return null;case"contextMenus.create":if(!r)return y(e,"title");if(void 0!==r.id&&"string"!=typeof r.id)return x(e,"id","string",r.id);if("separator"!==r.type&&!w(r.title))return y(e,"title");return null;case"notifications.create":{let r="string"==typeof t[0]?t[1]:t[0];if(!r)return y(e,"options");for(let t of["type","iconUrl","title","message"])if(!w(r[t]))return y(e,t);return null}case"action.setTitle":if(r&&void 0!==r.title&&"string"!=typeof r.title)return x(e,"title","string",r.title);return null;case"action.setBadgeText":if(r&&void 0!==r.text&&"string"!=typeof r.text)return x(e,"text","string",r.text);return null;case"action.setBadgeBackgroundColor":if(!r||void 0===r.color)return y(e,"color");if("string"!=typeof r.color&&!Array.isArray(r.color))return x(e,"color","string or array",r.color);return null;case"tabs.create":if(r?.url!==void 0&&"string"!=typeof r.url)return x(e,"url","string",r.url);return null;default:return null}}(r.api,r.args);if(v)return void s({error:v});if(p){let e=t(r.api,p(r.extensionId));if(e)return void s({error:e,refused:!0})}if(r.untrustedContext&&r.api.startsWith("storage.")){let e="storage.clear"===r.api?r.args[0]:r.args[1],t=f?.(r.extensionId)??"TRUSTED_CONTEXTS";if("session"===e&&"TRUSTED_AND_UNTRUSTED_CONTEXTS"!==t)return void s({error:"Access to storage is not allowed from this context.",refused:!0})}if(n.has(r.api))return void await o(e);if(a.has(r.api))return void await i(e);if(l.has(r.api))return void await c(e);if(d.has(r.api))return void await u(e);if(g.has(r.api))return void await m(e);s({error:h(r.api),refused:!0})}catch(e){s({error:e instanceof Error?e.message:"Unknown error"})}}function k(e,t){throw Error(`${e} did not run on the real lane: ${t}`)}let I=e=>{let t=Number(e);return Number.isFinite(t)?t:void 0},S=e=>{let t=Number(e);return Number.isFinite(t)?t:void 0},C=new Set(["system.cpu.getInfo","system.memory.getInfo","system.storage.getInfo","system.storage.ejectDevice","system.display.getInfo","system.display.getDisplayLayout","system.display.setDisplayProperties","system.display.setDisplayLayout"]),T=["topSites","fontSettings","printing","power","tts","userScripts","debugger","system.network","enterprise.deviceAttributes","instanceID","identity","webNavigation","proxy.settings","contentSettings","tabCapture"].sort((e,t)=>t.length-e.length);function A(e){if(C.has(e))return"refused";for(let t of T)if(e===t||e.startsWith(`${t}.`))return"mocked";return"emulated"}let E=new Set(["storage","action","offscreen","runtime","tabs.sendMessage","scripting","downloads","notifications","contextMenus"]),$=new Set(["local","sync","session","managed"]);function D(e,t){let r=e.split(".");if("storage"!==r[0])return e;if(3===r.length&&$.has(r[1]??""))return`storage.${r[2]}@${r[1]}`;if(2===r.length){let e="clear"===r[1]?t?.[0]:t?.[1];return`storage.${r[1]}@${"string"==typeof e?e:"local"}`}return e}function M(e){return e.refused||N(e.error)?"refused":"real"===e.lane?"real":A(e.api)}function N(e){return!!e&&(e.startsWith("Unsupported API: ")||e.startsWith("No backend implements ")||/needs the ".+" permission, which this (session did not declare|extension's manifest does not declare)/.test(e))}let B=new TextEncoder;function R(e,t){return{path:e,bytes:B.encode(t),text:t}}let P={manifest_version:3,name:"Hello Emulator",version:"0.1.0",description:"Trace fixture exercising emulated, mocked, and refused chrome.* tiers.",permissions:["storage","tabs","notifications","system.cpu","bookmarks","history","cookies","alarms","topSites","fontSettings","power","tts","identity","webNavigation"],action:{default_title:"Hello Emulator",default_popup:"popup.html"},background:{service_worker:"background.js"}},O=`<!doctype html>
|
|
1
|
+
// ███╗ ███╗ ██████╗██████╗
|
|
2
|
+
// ████╗ ████║██╔════╝██╔══██╗
|
|
3
|
+
// ██╔████╔██║██║ ██████╔╝
|
|
4
|
+
// ██║╚██╔╝██║██║ ██╔═══╝
|
|
5
|
+
// ██║ ╚═╝ ██║╚██████╗██║
|
|
6
|
+
// ╚═╝ ╚═╝ ╚═════╝╚═╝
|
|
7
|
+
// Apache License 2.0 (c) 2026 Cezar Augusto and the extension.dev collaborators
|
|
8
|
+
|
|
9
|
+
(()=>{"use strict";JSON.stringify({alarms:["alarms"],bookmarks:["bookmarks"],contextMenus:["contextMenus"],cookies:["cookies"],downloads:["downloads"],history:["history"],notifications:["notifications"],offscreen:["offscreen"],contentSettings:["contentSettings"],debugger:["debugger"],declarativeNetRequest:["declarativeNetRequest","declarativeNetRequestWithHostAccess"],fontSettings:["fontSettings"],identity:["identity"],idle:["idle"],instanceID:["gcm"],management:["management"],mdns:["mdns"],power:["power"],printerProvider:["printerProvider"],printing:["printing"],privacy:["privacy"],proxy:["proxy"],sidePanel:["sidePanel"],tabCapture:["tabCapture"],tabGroups:["tabGroups"],topSites:["topSites"],tts:["tts"],userScripts:["userScripts"],webNavigation:["webNavigation"],webRequest:["webRequest"]}),JSON.stringify(["getSelf","getPermissionWarningsByManifest","uninstallSelf","installReplacementWebApp"]),JSON.stringify({"system.cpu":["system.cpu"],"system.memory":["system.memory"],"system.storage":["system.storage"],"system.display":["system.display"]}),JSON.stringify(["extension","i18n","runtime","scripting","storage"]);let e={storage:["storage"],notifications:["notifications"],contextMenus:["contextMenus"],downloads:["downloads"],cookies:["cookies"],bookmarks:["bookmarks"],history:["history"],alarms:["alarms"],offscreen:["offscreen"],scripting:["scripting","activeTab"]};function t(t,r){let n=t.split(".")[0]??"",o=e[n];return!o||null===r||o.some(e=>r.has(e))?null:`chrome.${n} needs the "${o.join('" or "')}" permission, which this session did not declare. Declare it in the manifest (and re-register the session) to use this API.`}function r(e,t,r){let n=t.active??t.id===e.activeTabId;return{id:t.id,index:t.index??r,windowId:e.id,url:t.url,title:t.title,active:n,highlighted:n,selected:n,pinned:t.pinned??!1,discarded:t.discarded??!1,groupId:t.tabGroupId??-1,status:t.status??(t.discarded?"unloaded":"complete"),incognito:t.incognito??!1}}let n=new Set(["storage.setAccessLevel","storage.get","storage.set","storage.remove","storage.clear","tabs.create","tabs.update","tabs.remove","tabs.move","tabs.discard","tabs.goBack","tabs.goForward","tabs.group","tabs.ungroup","tabGroups.query","tabGroups.get","tabGroups.update","tabGroups.move","tabs.get"]);async function o(e){let{data:t,windows:n,getStorage:o,setStorage:s,removeStorageKeys:a,clearStorage:i,onStorageChanged:l,onCreateTab:c,onUpdateTab:d,onRemoveTab:u,onMoveTab:g,onDiscardTab:m,onGroupTabs:p,onUngroupTabs:f,onQueryTabGroups:h,onGetTabGroup:w,onUpdateTabGroup:b,onMoveTabGroup:y,onTabGoBack:x,onTabGoForward:v,onSetStorageAccessLevel:k,respond:I}=e;switch(t.api){case"storage.setAccessLevel":{let[e,r]=t.args,n=e?.accessLevel;if("TRUSTED_CONTEXTS"!==n&&"TRUSTED_AND_UNTRUSTED_CONTEXTS"!==n)return void I({error:"Invalid access level."});await k?.(t.extensionId,r??"session",n),I({result:void 0});return}case"storage.get":{let[e,r]=t.args,n=o(t.extensionId,r??"local");if(!e)return void I({result:n});if(Array.isArray(e)){let t={};e.forEach(e=>{e in n&&(t[e]=n[e])}),I({result:t});return}if("string"==typeof e)return void I({result:e in n?{[e]:n[e]}:{}});if("object"==typeof e){let t={};Object.keys(e).forEach(r=>{t[r]=r in n?n[r]:e[r]}),I({result:t});return}I({result:n});return}case"storage.set":{let[e,r]=t.args,n=r??"local";if(e&&"object"==typeof e){let r=o(t.extensionId,n),a={};for(let t of Object.keys(e)){let n=r[t],o=e[t];if(JSON.stringify(n)===JSON.stringify(o))continue;let s={newValue:o};t in r&&(s.oldValue=n),a[t]=s}await s(t.extensionId,e,n),l&&Object.keys(a).length>0&&l(t.extensionId,a,n)}I({result:!0});return}case"storage.remove":{let[e,r]=t.args,n=r??"local",s=(Array.isArray(e)?e:[e]).filter(Boolean).map(String),i=o(t.extensionId,n),c={};for(let e of s)e in i&&(c[e]={oldValue:i[e]});await a(t.extensionId,s,n),l&&Object.keys(c).length>0&&l(t.extensionId,c,n),I({result:!0});return}case"storage.clear":{let[e]=t.args,r=e??"local",n=o(t.extensionId,r),s={};for(let e of Object.keys(n))s[e]={oldValue:n[e]};await i(t.extensionId,r),l&&Object.keys(s).length>0&&l(t.extensionId,s,r),I({result:!0});return}case"tabs.create":{let[e]=t.args;I({result:await c(e?.url??"chrome://newtab",e)??!0});return}case"tabs.update":{let[e,r]=t.args;null!==e&&"object"==typeof e&&(r=e,e=void 0),I({result:await d(e,r??{})??!0});return}case"tabs.remove":{let[e]=t.args;await u(e),I({result:void 0});return}case"tabs.move":{let[e,r]=t.args;I({result:await g?.(t.extensionId,e,r??{})});return}case"tabs.discard":{let[e]=t.args;I({result:await m?.(t.extensionId,e)});return}case"tabs.goBack":{let[e]=t.args;await x?.(t.extensionId,e),I({result:void 0});return}case"tabs.goForward":{let[e]=t.args;await v?.(t.extensionId,e),I({result:void 0});return}case"tabs.group":{let[e]=t.args;I({result:await p?.(t.extensionId,e??{})});return}case"tabs.ungroup":{let[e]=t.args;await f?.(t.extensionId,e),I({result:void 0});return}case"tabGroups.query":{let[e]=t.args;I({result:await h?.(t.extensionId,e)??[]});return}case"tabGroups.get":{let[e]=t.args;I({result:await w?.(t.extensionId,e)});return}case"tabGroups.update":{let[e,r]=t.args;I({result:await b?.(t.extensionId,e,r??{})});return}case"tabGroups.move":{let[e,r]=t.args;I({result:await y?.(t.extensionId,e,r??{})});return}case"tabs.get":{let[e]=t.args;for(let t of n)for(let n=0;n<t.tabs.length;n+=1){let o=t.tabs[n];if(o&&o.id===e)return void I({result:r(t,o,n)})}I({error:`No tab with id: ${String(e)}.`})}}}function s(e,t,r,n){let o;return"function"!=typeof e&&(r({error:(o=n?` on the ${n} lane`:"",`No backend implements ${t}${o}, so the call did not run. This is a preview limitation, not an error in your extension: the API is on the bridge's wire protocol but nothing is wired behind it here.`),refused:!0}),!0)}let a=new Set(["tabs.query","sidePanel.open","action.openPopup","runtime.openOptionsPage","devtools.panels.create","action.setPopup","action.getBadgeText","action.getBadgeBackgroundColor","action.getTitle","action.setBadgeText","action.setBadgeBackgroundColor","action.setTitle","action.setIcon"]);async function i(e){let{data:t,windows:n,onOpenSidePanel:o,onOpenActionPopup:a,onOpenOptionsPage:i,onDevtoolsPanelCreate:l,onSetActionPopup:c,onGetActionState:d,onSetBadgeText:u,onSetBadgeBackground:g,onSetActionTitle:m,onSetActionIcon:p,respond:f}=e;switch(t.api){case"tabs.query":{let[e]=t.args,o=n.find(e=>e.focused)??n[0],s=n;e?.windowId!==void 0?s=-2===e.windowId?o?[o]:[]:n.filter(t=>t.id===e.windowId):e?.currentWindow===!0||e?.lastFocusedWindow===!0?s=o?[o]:[]:e?.currentWindow===!1&&(s=n.filter(e=>e!==o));let a=e?.url?Array.isArray(e.url)?e.url:[e.url]:null;f({result:s.flatMap(t=>t.tabs.map((e,n)=>r(t,e,n)).filter(t=>e?.active===void 0||t.active===e.active).filter(t=>e?.pinned===void 0||t.pinned===e.pinned).filter(t=>e?.status===void 0||t.status===e.status).filter(t=>e?.discarded===void 0||t.discarded===e.discarded).filter(t=>e?.groupId===void 0||(t.groupId??-1)===e.groupId).filter(e=>!a||a.some(t=>(function(e,t){if(!e)return!1;if("<all_urls>"===t)return!0;let r=t.replace(/[.+?^${}()|[\]\\]/g,"\\$&");return RegExp(`^${r.replace(/\*/g,".*")}$`).test(e)})(e.url,t))))});return}case"sidePanel.open":await o(t.extensionId),f({result:void 0});return;case"action.openPopup":if(s(a,t.api,f))return;a?.(t.extensionId),f({result:void 0});return;case"runtime.openOptionsPage":if(s(i,t.api,f))return;i?.(t.extensionId),f({result:void 0});return;case"devtools.panels.create":{if(s(l,t.api,f))return;let[e]=t.args;l?.(t.extensionId,e?.page??""),f({result:!0});return}case"action.setPopup":{if(s(c,t.api,f))return;let[e]=t.args;c?.(t.extensionId,e?.popup??""),f({result:void 0});return}case"action.getBadgeText":{let[e]=t.args,r=await d?.(t.extensionId,e?.tabId);f({result:r?.badgeText??""});return}case"action.getBadgeBackgroundColor":{let[e]=t.args,r=await d?.(t.extensionId,e?.tabId);f({result:r?.badgeColor??null});return}case"action.getTitle":{let[e]=t.args,r=await d?.(t.extensionId,e?.tabId);f({result:r?.title??""});return}case"action.setBadgeText":{let[e]=t.args;await u(t.extensionId,e?.text,e?.tabId),f({result:!0});return}case"action.setBadgeBackgroundColor":{let[e]=t.args;await g(t.extensionId,e?.color,e?.tabId),f({result:!0});return}case"action.setTitle":{let[e]=t.args;await m(t.extensionId,e?.title,e?.tabId),f({result:!0});return}case"action.setIcon":{let[e]=t.args,r="string"==typeof e?.path?e.path:"object"==typeof e?.path?Object.values(e.path)[0]:void 0;await p(t.extensionId,r),f({result:!0})}}}let l=new Set(["contextMenus.create","contextMenus.update","contextMenus.remove","contextMenus.removeAll","notifications.create","notifications.clear","notifications.update","notifications.getAll","downloads.search","downloads.cancel","downloads.pause","downloads.resume","downloads.erase","downloads.download"]);async function c(e){let{data:t,onCreateContextMenu:r,onUpdateContextMenu:n,onRemoveContextMenu:o,onClearContextMenus:a,onCreateNotification:i,onClearNotification:l,onUpdateNotification:c,onGetNotifications:d,onDownload:u,onSearchDownloads:g,onCancelDownload:m,onPauseDownload:p,onResumeDownload:f,onEraseDownloads:h,respond:w}=e;switch(t.api){case"contextMenus.create":{let[e]=t.args;w({result:await r(t.extensionId,e??{})});return}case"contextMenus.update":{let[e,r]=t.args;w({result:await n(t.extensionId,String(e??""),r??{})});return}case"contextMenus.remove":{let[e]=t.args;w({result:await o(t.extensionId,String(e??""))});return}case"contextMenus.removeAll":return void w({result:await a(t.extensionId)});case"notifications.create":{let[e,r]=t.args,n="string"==typeof e?e:void 0,o="string"==typeof e?r:e;w({result:await i(t.extensionId,n,o??{})});return}case"notifications.clear":{let[e]=t.args;w({result:await l(t.extensionId,String(e??""))});return}case"notifications.update":{let[e,r]=t.args;w({result:await c?.(t.extensionId,String(e??""),r??{})??!1});return}case"notifications.getAll":return void w({result:await d(t.extensionId)});case"downloads.search":{let[e]=t.args;w({result:await g?.(t.extensionId,e)??[]});return}case"downloads.cancel":{if(s(m,t.api,w))return;let[e]=t.args;await m?.(t.extensionId,e),w({result:void 0});return}case"downloads.pause":{if(s(p,t.api,w))return;let[e]=t.args;await p?.(t.extensionId,e),w({result:void 0});return}case"downloads.resume":{if(s(f,t.api,w))return;let[e]=t.args;await f?.(t.extensionId,e),w({result:void 0});return}case"downloads.erase":{if(s(h,t.api,w))return;let[e]=t.args;w({result:await h?.(t.extensionId,e)??[]});return}case"downloads.download":{let[e]=t.args;w({result:await u(t.extensionId,e??{})})}}}let d=new Set(["bookmarks.getTree","bookmarks.get","bookmarks.getChildren","bookmarks.search","bookmarks.create","bookmarks.update","bookmarks.remove","bookmarks.removeTree","bookmarks.move","history.search","history.getVisits","history.addUrl","history.deleteUrl","history.deleteRange","history.deleteAll","windows.get","windows.getAll","windows.getCurrent","windows.create","windows.update","windows.remove"]);async function u(e){let{data:t,windows:r,onGetWindow:n,onGetAllWindows:o,onCreateWindow:s,onUpdateWindow:a,onRemoveWindow:i,onGetBookmarkTree:l,onGetBookmarks:c,onGetBookmarkChildren:d,onSearchBookmarks:u,onCreateBookmark:g,onUpdateBookmark:m,onRemoveBookmark:p,onMoveBookmark:f,onSearchHistory:h,onGetHistoryVisits:w,onAddHistoryUrl:b,onDeleteHistoryUrl:y,onDeleteHistoryRange:x,onDeleteAllHistory:v,respond:k}=e;switch(t.api){case"bookmarks.getTree":return void k({result:await l(t.extensionId)});case"bookmarks.get":{let[e]=t.args,r=Array.isArray(e)?e:[e];k({result:await c(t.extensionId,r.filter(e=>null!=e).map(String))});return}case"bookmarks.getChildren":{let[e]=t.args;k({result:await d(t.extensionId,null==e?void 0:String(e))});return}case"bookmarks.search":{let[e]=t.args;k({result:await u(t.extensionId,e)});return}case"bookmarks.create":{let[e]=t.args;k({result:await g(t.extensionId,e??{})});return}case"bookmarks.update":{let[e,r]=t.args;k({result:await m(t.extensionId,String(e??""),r??{})});return}case"bookmarks.remove":{let[e]=t.args;k({result:await p(t.extensionId,String(e??""),!1)});return}case"bookmarks.removeTree":{let[e]=t.args;k({result:await p(t.extensionId,String(e??""),!0)});return}case"bookmarks.move":{let[e,r]=t.args;k({result:await f?.(t.extensionId,String(e??""),r??{})});return}case"history.search":{let[e]=t.args;k({result:await h(t.extensionId,e??{})});return}case"history.getVisits":{let[e]=t.args;k({result:await w(t.extensionId,e??{})});return}case"history.addUrl":{let[e]=t.args;await b(t.extensionId,e??{}),k({result:!0});return}case"history.deleteUrl":{let[e]=t.args;await y(t.extensionId,e??{}),k({result:!0});return}case"history.deleteRange":{let[e]=t.args;await x(t.extensionId,e??{}),k({result:!0});return}case"history.deleteAll":await v(t.extensionId),k({result:!0});return;case"windows.get":{let[e,r]=t.args;k({result:await n(t.extensionId,null==e?void 0:String(e),r)});return}case"windows.getAll":{let[e]=t.args;k({result:await o(t.extensionId,e)});return}case"windows.getCurrent":{let e=r.find(e=>e.focused)??r[0];k({result:e?{id:e.id,focused:!0,tabs:e.tabs}:null});return}case"windows.create":{let[e]=t.args;k({result:await s(t.extensionId,e?.url)});return}case"windows.update":{let[e,r]=t.args;k({result:await a(t.extensionId,null==e?void 0:String(e),r??{})});return}case"windows.remove":{let[e]=t.args;k({result:await i(t.extensionId,null==e?void 0:String(e))})}}}let g=new Set(["runtime.sendMessage","tabs.sendMessage","runtime.getContexts","permissions.contains","permissions.request","permissions.remove","permissions.getAll","offscreen.createDocument","offscreen.closeDocument","offscreen.hasDocument","cookies.get","cookies.getAll","cookies.set","cookies.remove","scripting.executeScript","scripting.insertCSS"]);async function m(e){let{data:t,onRuntimeMessage:r,onTabMessage:n,onGetRuntimeContexts:o,onContainsPermissions:s,onRequestPermissions:a,onRemovePermissions:i,onGetAllPermissions:l,onCreateOffscreenDocument:c,onCloseOffscreenDocument:d,onHasOffscreenDocument:u,onGetCookie:g,onGetAllCookies:m,onSetCookie:p,onRemoveCookie:f,onExecuteScript:h,onInsertCss:w,respond:b,source:y}=e;switch(t.api){case"runtime.sendMessage":{let[e]=t.args;b({result:await r(t.extensionId,e,y)});return}case"tabs.sendMessage":{let[e,r]=t.args;if(!n)return void b({error:"Could not establish connection. Receiving end does not exist."});b({result:await n(t.extensionId,e,r)});return}case"runtime.getContexts":return void b({result:await o(t.extensionId)});case"permissions.contains":{let[e]=t.args;b({result:await s(t.extensionId,e??{})});return}case"permissions.request":{let[e]=t.args;b({result:await a(t.extensionId,e??{})});return}case"permissions.remove":{let[e]=t.args;b({result:await i(t.extensionId,e??{})});return}case"permissions.getAll":return void b({result:await l(t.extensionId)});case"offscreen.createDocument":{let[e]=t.args,r=await c(t.extensionId,e??{});b({result:"boolean"!=typeof r||r});return}case"offscreen.closeDocument":{let e=await d(t.extensionId);b({result:"boolean"!=typeof e||e});return}case"offscreen.hasDocument":return void b({result:await u(t.extensionId)});case"cookies.get":{let[e]=t.args;b({result:await g(t.extensionId,e??{})});return}case"cookies.getAll":{let[e]=t.args;b({result:await m(t.extensionId,e??{})});return}case"cookies.set":{let[e]=t.args;b({result:await p(t.extensionId,e??{})});return}case"cookies.remove":{let[e]=t.args;b({result:await f(t.extensionId,e??{})});return}case"scripting.executeScript":{let[e]=t.args;b({result:await h(t.extensionId,e??{})});return}case"scripting.insertCSS":{let[e]=t.args;await w(t.extensionId,e??{}),b({result:!0})}}}let p=["local","sync","session","managed"];function f(e,t){let r=Math.min(e.length,t.length),n=0;for(;n<r&&e[n]===t[n];)n+=1;return n}function h(e){let t=function(e){let t=[...n,...a,...l,...d,...g].sort();if(t.includes(e))return null;let r=e.split(".");if(3===r.length&&"storage"===r[0]&&p.includes(r[1]??"")){let e=`storage.${r[2]}`;if(t.includes(e))return e}let o=r[r.length-1]??"",s=t.filter(e=>e.endsWith(`.${o}`));if(s.length)return s.sort((t,r)=>f(r,e)-f(t,e))[0];let i=t.filter(e=>e.startsWith(`${r[0]}.`));return i.length?i[0]??null:null}(e),r=`Unsupported API: ${e}.`;if(!t)return`${r} No wire name resembles it, so this API is not on the bridge at all. It may still work natively inside your extension's own contexts: the bridge is what an observer page can DRIVE, not the whole runtime.`;let o=t.startsWith("storage.")&&e.startsWith("storage.")?` The bridge takes the storage area as an ARGUMENT, not as part of the name: ${t}(keys, "local").`:"";return`${r} The closest wire name the bridge routes is ${t}.${o}`}let w=e=>"string"==typeof e&&e.length>0;function b(e,t){return`Error in invocation of ${e}: ${t}`}function y(e,t){return b(e,`missing required property '${t}'`)}function x(e,t,r,n){return b(e,`'${t}' expects a ${r}, got ${typeof n}`)}async function v(e){let{data:r,respond:s,getDeclaredPermissions:p,getSessionStorageAccessLevel:f}=e;try{var b;if(b=r.api,!(n.has(b)||a.has(b)||l.has(b)||d.has(b)||g.has(b)))return void s({error:h(r.api),refused:!0});let v=function(e,t){let r=t[0];switch(e){case"downloads.download":if(!r||!("url"in r))return y(e,"url");if(!w(r.url))return x(e,"url","non-empty string",r.url);if(void 0!==r.filename&&"string"!=typeof r.filename)return x(e,"filename","string",r.filename);return null;case"offscreen.createDocument":if(!r||!w(r.url))return y(e,"url");if(!Array.isArray(r.reasons)||0===r.reasons.length)return y(e,"reasons");if(!w(r.justification))return y(e,"justification");return null;case"contextMenus.create":if(!r)return y(e,"title");if(void 0!==r.id&&"string"!=typeof r.id)return x(e,"id","string",r.id);if("separator"!==r.type&&!w(r.title))return y(e,"title");return null;case"notifications.create":{let r="string"==typeof t[0]?t[1]:t[0];if(!r)return y(e,"options");for(let t of["type","iconUrl","title","message"])if(!w(r[t]))return y(e,t);return null}case"action.setTitle":if(r&&void 0!==r.title&&"string"!=typeof r.title)return x(e,"title","string",r.title);return null;case"action.setBadgeText":if(r&&void 0!==r.text&&"string"!=typeof r.text)return x(e,"text","string",r.text);return null;case"action.setBadgeBackgroundColor":if(!r||void 0===r.color)return y(e,"color");if("string"!=typeof r.color&&!Array.isArray(r.color))return x(e,"color","string or array",r.color);return null;case"tabs.create":if(r?.url!==void 0&&"string"!=typeof r.url)return x(e,"url","string",r.url);return null;default:return null}}(r.api,r.args);if(v)return void s({error:v});if(p){let e=t(r.api,p(r.extensionId));if(e)return void s({error:e,refused:!0})}if(r.untrustedContext&&r.api.startsWith("storage.")){let e="storage.clear"===r.api?r.args[0]:r.args[1],t=f?.(r.extensionId)??"TRUSTED_CONTEXTS";if("session"===e&&"TRUSTED_AND_UNTRUSTED_CONTEXTS"!==t)return void s({error:"Access to storage is not allowed from this context.",refused:!0})}if(n.has(r.api))return void await o(e);if(a.has(r.api))return void await i(e);if(l.has(r.api))return void await c(e);if(d.has(r.api))return void await u(e);if(g.has(r.api))return void await m(e);s({error:h(r.api),refused:!0})}catch(e){s({error:e instanceof Error?e.message:"Unknown error"})}}function k(e,t){throw Error(`${e} did not run on the real lane: ${t}`)}let I=e=>{let t=Number(e);return Number.isFinite(t)?t:void 0},S=e=>{let t=Number(e);return Number.isFinite(t)?t:void 0},C=new Set(["system.cpu.getInfo","system.memory.getInfo","system.storage.getInfo","system.storage.ejectDevice","system.display.getInfo","system.display.getDisplayLayout","system.display.setDisplayProperties","system.display.setDisplayLayout"]),T=["topSites","fontSettings","printing","power","tts","userScripts","debugger","system.network","enterprise.deviceAttributes","instanceID","identity","webNavigation","proxy.settings","contentSettings","tabCapture"].sort((e,t)=>t.length-e.length);function A(e){if(C.has(e))return"refused";for(let t of T)if(e===t||e.startsWith(`${t}.`))return"mocked";return"emulated"}let E=new Set(["storage","action","offscreen","runtime","tabs.sendMessage","scripting","downloads","notifications","contextMenus"]);function $(e){return!!e&&(e.startsWith("Unsupported API: ")||e.startsWith("No backend implements ")||/needs the ".+" permission, which this (session did not declare|extension's manifest does not declare)/.test(e))}function D(e){return e.refused||$(e.error)?"refused":"real"===e.lane?"real":A(e.api)}let M=new Set(["local","sync","session","managed"]);function N(e,t){let r=e.split(".");if("storage"!==r[0])return e;if(3===r.length&&M.has(r[1]??""))return`storage.${r[2]}@${r[1]}`;if(2===r.length){let e="clear"===r[1]?t?.[0]:t?.[1];return`storage.${r[1]}@${"string"==typeof e?e:"local"}`}return e}let B=new TextEncoder;function R(e,t){return{path:e,bytes:B.encode(t),text:t}}let P={manifest_version:3,name:"Hello Emulator",version:"0.1.0",description:"Trace fixture exercising emulated, mocked, and refused chrome.* tiers.",permissions:["storage","tabs","notifications","system.cpu","bookmarks","history","cookies","alarms","topSites","fontSettings","power","tts","identity","webNavigation"],action:{default_title:"Hello Emulator",default_popup:"popup.html"},background:{service_worker:"background.js"}},O=`<!doctype html>
|
|
2
10
|
<html>
|
|
3
11
|
<head>
|
|
4
12
|
<meta charset="utf-8" />
|
|
@@ -166,4 +174,4 @@ chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
|
|
|
166
174
|
}
|
|
167
175
|
return true;
|
|
168
176
|
});
|
|
169
|
-
`;R("manifest.json",JSON.stringify(P,null,2)),R("popup.html",O),R("popup.js",q),R("background.js",U),P.name;let{PING:j,PONG:G,BRIDGE:L,SESSION:W,INJECT:V,EVENT_PORT:_,EVENT:H,TRACE:F}={PING:"extensiondev:ping",PONG:"extensiondev:pong",BRIDGE:"extensiondev:bridge",SESSION:"extensiondev:session",INJECT:"extensiondev:inject",EVENT_PORT:"extensiondev:events",EVENT:"extensiondev:event",TRACE:"extensiondev:trace"},J=function(e={}){let t=e.engine??"chromium-emulated",r=e.limit??500,n=e.now??(()=>new Date),o=n().toISOString(),s=[],a=new Set,i=0,l=e=>{for(let t of(s.push(e),s.length>r&&s.splice(0,s.length-r),a))t(e);return e};return{recordCall:(e,r,o,s)=>l({seq:i+=1,api:e,args:o??[],extensionId:r,at:n().toISOString(),tier:A(e),engine:t,policy:s}),recordOutcome(e){let r=D(e.api,e.args),o=s.find(t=>t.extensionId===e.extensionId&&D(t.api,t.args)===r&&void 0===t.status&&void 0===t.requestId),c={requestId:e.requestId,lane:e.lane,status:void 0===e.error?"ok":"error",error:e.error,result:e.result,durationMs:e.durationMs,carrierScoped:!e.refused&&!N(e.error)&&function(e,t){if("real"!==t)return!1;if(E.has(e))return!0;let r=e.split(".")[0]??"";return E.has(r)}(e.api,e.lane)||void 0};if(o){Object.assign(o,c),o.tier=M(e);for(let e of a)e(o);return o}return l({seq:i+=1,api:e.api,args:e.args,extensionId:e.extensionId,at:n().toISOString(),tier:M(e),engine:t,...c})},events:()=>[...s],snapshot(){let e={real:0,emulated:0,mocked:0,refused:0},r=0;for(let t of s)e[t.tier??A(t.api)]+=1,"error"===t.status&&(r+=1);return{engine:t,startedAt:o,events:[...s],counts:{total:s.length,byTier:e,errors:r}}},subscribe:e=>(a.add(e),()=>a.delete(e)),clear(){s.length=0}}}({engine:"chromium-real"}),z=new Set;J.subscribe(e=>{for(let t of z)try{t.postMessage({type:F,event:e})}catch{}});let X=function(){let e={local:{},sync:{},session:{},managed:{}},t=e=>chrome.storage[e??"local"]??chrome.storage.local,r=t=>e[t??"local"]??e.local;for(let e of["local","sync","session","managed"])t(e).get(null).then(t=>{Object.assign(r(e),t)}).catch(()=>void 0);chrome.storage.onChanged.addListener((t,n)=>{if(!(n in e))return;let o=r(n);for(let[e,r]of Object.entries(t))void 0===r.newValue?delete o[e]:o[e]=r.newValue});let n=new Map;return{getStorage:(e,t)=>({...r(t)}),setStorage:(e,n,o)=>(Object.assign(r(o),n),t(o).set(n)),removeStorageKeys:(e,n,o)=>{let s=r(o);for(let e of n)delete s[e];return t(o).remove(n)},clearStorage:(e,n)=>{let o=r(n);for(let e of Object.keys(o))delete o[e];return t(n).clear()},onSetStorageAccessLevel:async(e,t,r)=>{if("session"!==t)return;n.set(e,r);let o=chrome.storage.session;await o.setAccessLevel?.({accessLevel:r})},getSessionStorageAccessLevel:e=>n.get(e),onCreateTab:(e,t)=>chrome.tabs.create({url:e,...t?.active!==void 0?{active:t.active}:{},...t?.pinned!==void 0?{pinned:t.pinned}:{},...t?.index!==void 0?{index:t.index}:{},...t?.windowId!==void 0?{windowId:t.windowId}:{},...t?.openerTabId!==void 0?{openerTabId:t.openerTabId}:{}}),onUpdateTab:(e,t)=>{let r=I(e);return null!=r?chrome.tabs.update(r,t):chrome.tabs.update(t)},onRemoveTab:e=>{let t=I(e);return null==t&&k("tabs.remove",`"${String(e)}" is not a real tab id`),chrome.tabs.remove(t)},onGetWindow:(e,t,r)=>{let n=S(t);return null==n?chrome.windows.getCurrent(r??{}):chrome.windows.get(n,r??{})},onGetAllWindows:(e,t)=>chrome.windows.getAll(t??{}),onCreateWindow:(e,t)=>chrome.windows.create({url:t}),onUpdateWindow:(e,t,r)=>{let n=S(t);return null==n?null:chrome.windows.update(n,r)},onRemoveWindow:async(e,t)=>{let r=S(t);return null!=r&&(await chrome.windows.remove(r),!0)},onOpenSidePanel:async()=>{let e=await chrome.windows.getCurrent();null==e.id&&k("sidePanel.open","there is no focused window to open it in"),await chrome.sidePanel.open({windowId:e.id})},onSetBadgeText:(e,t,r)=>{let n=I(r);return chrome.action.setBadgeText(null!=n?{text:t??"",tabId:n}:{text:t??""})},onSetBadgeBackground:(e,t,r)=>{t||k("action.setBadgeBackgroundColor","no color was given");let n=I(r);return chrome.action.setBadgeBackgroundColor(null!=n?{color:t,tabId:n}:{color:t})},onSetActionTitle:(e,t,r)=>{let n=I(r);return chrome.action.setTitle(null!=n?{title:t??"",tabId:n}:{title:t??""})},onGetActionState:async(e,t)=>{let r=I(t),n=null!=r?{tabId:r}:{},[o,s,a]=await Promise.all([chrome.action.getBadgeText(n),chrome.action.getBadgeBackgroundColor(n),chrome.action.getTitle(n)]);return{badgeText:o,badgeColor:Array.isArray(s)?`rgba(${s[0]}, ${s[1]}, ${s[2]}, ${(s[3]??255)/255})`:s??void 0,title:a}},onSetActionIcon:(e,t)=>(t||k("action.setIcon","no icon path was given"),chrome.action.setIcon({path:t})),onCreateContextMenu:(e,t)=>chrome.contextMenus.create(t),onUpdateContextMenu:async(e,t,r)=>(await chrome.contextMenus.update(t,r),!0),onRemoveContextMenu:async(e,t)=>(await chrome.contextMenus.remove(t),!0),onClearContextMenus:async()=>(await chrome.contextMenus.removeAll(),!0),onCreateNotification:(e,t,r)=>chrome.notifications.create(t??"",r),onClearNotification:(e,t)=>chrome.notifications.clear(t),onGetNotifications:()=>chrome.notifications.getAll(),onDownload:(e,t)=>chrome.downloads.download(t),onSearchDownloads:(e,t)=>chrome.downloads.search(t??{}),onCancelDownload:async(e,t)=>{await chrome.downloads.cancel(t).catch(()=>void 0)},onPauseDownload:(e,t)=>chrome.downloads.pause(t),onResumeDownload:(e,t)=>chrome.downloads.resume(t),onEraseDownloads:(e,t)=>chrome.downloads.erase(t??{}),onGetBookmarkTree:()=>chrome.bookmarks.getTree(),onGetBookmarks:(e,t)=>chrome.bookmarks.get(t),onGetBookmarkChildren:(e,t)=>t?chrome.bookmarks.getChildren(t):chrome.bookmarks.getTree(),onSearchBookmarks:(e,t)=>chrome.bookmarks.search(t??""),onCreateBookmark:(e,t)=>chrome.bookmarks.create(t),onUpdateBookmark:(e,t,r)=>chrome.bookmarks.update(t,r),onRemoveBookmark:async(e,t,r)=>(r?await chrome.bookmarks.removeTree(t):await chrome.bookmarks.remove(t),!0),onSearchHistory:(e,t)=>chrome.history.search({text:t?.text??"",...t}),onGetHistoryVisits:(e,t)=>chrome.history.getVisits({url:t?.url??""}),onAddHistoryUrl:async(e,t)=>{t?.url&&await chrome.history.addUrl({url:t.url})},onDeleteHistoryUrl:async(e,t)=>{t?.url&&await chrome.history.deleteUrl({url:t.url})},onDeleteHistoryRange:async(e,t)=>{await chrome.history.deleteRange({startTime:t?.startTime??0,endTime:t?.endTime??Date.now()})},onDeleteAllHistory:async()=>{await chrome.history.deleteAll()},onRuntimeMessage:(e,t)=>chrome.runtime.sendMessage(t),onGetRuntimeContexts:()=>chrome.runtime.getContexts({}),onContainsPermissions:(e,t)=>chrome.permissions.contains(t),onRequestPermissions:(e,t)=>chrome.permissions.request(t),onRemovePermissions:(e,t)=>chrome.permissions.remove(t),onGetAllPermissions:async()=>{let e=await chrome.permissions.getAll();return{permissions:e.permissions??[],origins:e.origins??[]}},onCreateOffscreenDocument:async(e,t)=>(await chrome.offscreen.createDocument(t),!0),onCloseOffscreenDocument:async()=>(await chrome.offscreen.closeDocument(),await chrome.offscreen.hasDocument().catch(()=>!1)&&k("offscreen.closeDocument","the offscreen document was still open after closeDocument resolved"),!0),onHasOffscreenDocument:()=>chrome.offscreen.hasDocument(),onGetCookie:(e,t)=>chrome.cookies.get(t),onGetAllCookies:(e,t)=>chrome.cookies.getAll(t),onSetCookie:(e,t)=>chrome.cookies.set(t),onRemoveCookie:(e,t)=>chrome.cookies.remove(t),onExecuteScript:(e,t)=>{let r=I(t.target?.tabId);return null==r&&k("scripting.executeScript",`target.tabId "${String(t.target?.tabId)}" is not a real tab id`),t.files?.length||k("scripting.executeScript","only the `files` form is supported here; a func/code body cannot be serialized across the bridge into a real page. Ship the code as a file in your extension and pass files: [...]"),chrome.scripting.executeScript({target:{tabId:r},files:t.files})},onInsertCss:async(e,t)=>{let r=I(t.target?.tabId);(null==r&&k("scripting.insertCSS",`target.tabId "${String(t.target?.tabId)}" is not a real tab id`),t.css)?await chrome.scripting.insertCSS({target:{tabId:r},css:t.css}):t.files?.length?await chrome.scripting.insertCSS({target:{tabId:r},files:t.files}):k("scripting.insertCSS","neither css nor files was given")}}}(),K=new Map;async function Q(){try{return(await chrome.windows.getAll({populate:!0})).map(e=>{let t=e.tabs??[],r=t.find(e=>e.active);return{id:e.id??void 0,focused:e.focused,activeTabId:r?.id??"",tabs:t.map(e=>({id:e.id??"",url:e.url??e.pendingUrl??"",title:e.title??"",active:e.active,index:e.index,status:e.status,pinned:e.pinned,discarded:e.discarded,incognito:e.incognito,tabGroupId:e.groupId}))}})}catch{return[]}}async function Y(e){let r,n=Date.now(),o=t=>(J.recordOutcome({requestId:e.requestId,extensionId:e.extensionId,api:e.api,args:e.args,lane:"real",result:t.result,error:t.error,durationMs:Date.now()-n,source:"frame",...t.refused?{refused:!0}:{}}),t),s=(r=t(e.api,K.get(e.extensionId)??new Set))?{error:r,refused:!0}:null;if(s)return o(s);let a="tabs.query"===e.api||"tabs.get"===e.api||"windows.getCurrent"===e.api?await Q():[];return new Promise(t=>{let r=!1,n=e=>{r||(r=!0,t(o(e)))};v({data:e,windows:a,...X,respond:n,source:"frame"}).catch(e=>n({error:e instanceof Error?e.message:"Bridge error"}))})}async function Z(e){let t,r,{extensionId:n,url:o,code:s,css:a,matches:i=[]}=e,l=K.get(n);if(!(l&&(l.has("scripting")||l.has("activeTab"))))return{ok:!1,error:'This extension does not declare the "scripting" (or "activeTab") permission.'};if(!i.length||!i.some(e=>{let t=function(e){let t;if("<all_urls>"===e)return/^https?:\/\/.*/;let r=/^(\*|https?|file|ftp):\/\/([^/]*)(\/.*)$/.exec(e);if(!r)return null;let[,n="",o="",s=""]=r,a="*"===n?"https?":n.replace(/[.+?^${}()|[\]\\]/g,"\\$&");t="*"===o?"[^/]+":o.startsWith("*.")?`(?:[^/]+\\.)?${o.slice(2).replace(/[.+?^${}()|[\]\\]/g,"\\$&").replace(/\*/g,"[^/]*")}`:o.replace(/[.+?^${}()|[\]\\]/g,"\\$&").replace(/\*/g,"[^/]*");let i=s.replace(/[.+?^${}()|[\]\\]/g,"\\$&").replace(/\*/g,".*");return RegExp(`^${a}://${t}${i}$`)}(e);return!!t&&t.test(o)}))return{ok:!1,error:`This extension's content scripts / host permissions do not match ${o}.`};try{t=await chrome.tabs.create({url:o,active:!0})}catch(e){return{ok:!1,error:`Could not open the tab: ${String(e)}`}}let c=t.id;if(null==c)return{ok:!1,error:"No tab id."};if(await new Promise(e=>{let t=setTimeout(()=>e(),8e3),r=(n,o)=>{n===c&&"complete"===o.status&&(clearTimeout(t),chrome.tabs.onUpdated.removeListener(r),e())};chrome.tabs.onUpdated.addListener(r)}),a)try{await chrome.scripting.insertCSS({target:{tabId:c},css:a})}catch(e){r=String(e)}if(!s)return{ok:!0,injected:!1,...r?{cssError:r}:{}};try{let[e]=await chrome.scripting.executeScript({target:{tabId:c},world:"MAIN",func:e=>{try{return(0,eval)(e),{ran:!0}}catch(e){return{ran:!1,error:String(e)}}},args:[s]}),t=e?.result;if(t&&!1===t.ran)return{ok:!0,injected:!1,blocked:!0,reason:t.error,...r?{cssError:r}:{}};return{ok:!0,injected:!0,...r?{cssError:r}:{}}}catch(e){return{ok:!1,error:`Injection failed: ${String(e)}`}}}chrome.runtime.onInstalled.addListener(()=>{}),chrome.runtime.onConnectExternal.addListener(e=>{if(e.name!==_)return;for(let t of(z.add(e),J.events()))try{e.postMessage({type:F,event:t,backlog:!0})}catch{break}let t=[],r=(r,n)=>{let o=chrome[r],s=o?.[n];if(!s||"function"!=typeof s.addListener)return;let a=(...t)=>((t,r,n)=>{try{e.postMessage({type:H,namespace:t,event:r,args:n})}catch{}})(r,n,t);s.addListener(a),t.push(()=>s.removeListener(a))};r("storage","onChanged"),r("alarms","onAlarm"),r("tabs","onCreated"),r("tabs","onUpdated"),r("tabs","onRemoved"),r("tabs","onActivated"),r("windows","onCreated"),r("windows","onRemoved"),r("windows","onFocusChanged"),r("bookmarks","onCreated"),r("bookmarks","onChanged"),r("bookmarks","onMoved"),r("bookmarks","onRemoved"),r("history","onVisited"),r("history","onVisitRemoved"),r("downloads","onCreated"),r("downloads","onChanged"),r("downloads","onErased"),r("cookies","onChanged"),r("notifications","onClicked"),r("notifications","onClosed"),r("notifications","onButtonClicked"),r("contextMenus","onClicked"),e.onDisconnect.addListener(()=>{for(let r of(z.delete(e),t))r()})}),chrome.runtime.onMessageExternal.addListener((e,t,r)=>e?.type===j?(r({type:G,version:chrome.runtime.getManifest().version,ready:!0}),!1):e?.type===W&&"string"==typeof e.extensionId?(K.set(e.extensionId,new Set(Array.isArray(e.permissions)?e.permissions:[])),r({ok:!0}),!1):e?.type===L&&e.request?(Y(e.request).then(r),!0):e?.type===V&&"string"==typeof e.url&&(Z(e).then(r),!0))})();
|
|
177
|
+
`;R("manifest.json",JSON.stringify(P,null,2)),R("popup.html",O),R("popup.js",q),R("background.js",U),P.name;let{PING:j,PONG:G,BRIDGE:L,SESSION:W,INJECT:V,EVENT_PORT:_,EVENT:H,TRACE:F}={PING:"extensiondev:ping",PONG:"extensiondev:pong",BRIDGE:"extensiondev:bridge",SESSION:"extensiondev:session",INJECT:"extensiondev:inject",EVENT_PORT:"extensiondev:events",EVENT:"extensiondev:event",TRACE:"extensiondev:trace"},J=function(e={}){let t=e.engine??"chromium-emulated",r=e.limit??500,n=e.now??(()=>new Date),o=n().toISOString(),s=[],a=new Set,i=0,l=e=>{for(let t of(s.push(e),s.length>r&&s.splice(0,s.length-r),a))t(e);return e};return{recordCall:(e,r,o,s)=>l({seq:i+=1,api:e,args:o??[],extensionId:r,at:n().toISOString(),tier:A(e),engine:t,policy:s}),recordOutcome(e){let r=N(e.api,e.args),o=s.find(t=>t.extensionId===e.extensionId&&N(t.api,t.args)===r&&void 0===t.status&&void 0===t.requestId),c={requestId:e.requestId,lane:e.lane,status:void 0===e.error?"ok":"error",error:e.error,result:e.result,durationMs:e.durationMs,carrierScoped:!e.refused&&!$(e.error)&&function(e,t){if("real"!==t)return!1;if(E.has(e))return!0;let r=e.split(".")[0]??"";return E.has(r)}(e.api,e.lane)||void 0};if(o){Object.assign(o,c),o.tier=D(e);for(let e of a)e(o);return o}return l({seq:i+=1,api:e.api,args:e.args,extensionId:e.extensionId,at:n().toISOString(),tier:D(e),engine:t,...c})},events:()=>[...s],snapshot(){let e={real:0,emulated:0,mocked:0,refused:0},r=0;for(let t of s)e[t.tier??A(t.api)]+=1,"error"===t.status&&(r+=1);return{engine:t,startedAt:o,events:[...s],counts:{total:s.length,byTier:e,errors:r}}},subscribe:e=>(a.add(e),()=>a.delete(e)),clear(){s.length=0}}}({engine:"chromium-real"}),z=new Set;J.subscribe(e=>{for(let t of z)try{t.postMessage({type:F,event:e})}catch{}});let X=function(){let e={local:{},sync:{},session:{},managed:{}},t=e=>chrome.storage[e??"local"]??chrome.storage.local,r=t=>e[t??"local"]??e.local;for(let e of["local","sync","session","managed"])t(e).get(null).then(t=>{Object.assign(r(e),t)}).catch(()=>void 0);chrome.storage.onChanged.addListener((t,n)=>{if(!(n in e))return;let o=r(n);for(let[e,r]of Object.entries(t))void 0===r.newValue?delete o[e]:o[e]=r.newValue});let n=new Map;return{getStorage:(e,t)=>({...r(t)}),setStorage:(e,n,o)=>(Object.assign(r(o),n),t(o).set(n)),removeStorageKeys:(e,n,o)=>{let s=r(o);for(let e of n)delete s[e];return t(o).remove(n)},clearStorage:(e,n)=>{let o=r(n);for(let e of Object.keys(o))delete o[e];return t(n).clear()},onSetStorageAccessLevel:async(e,t,r)=>{if("session"!==t)return;n.set(e,r);let o=chrome.storage.session;await o.setAccessLevel?.({accessLevel:r})},getSessionStorageAccessLevel:e=>n.get(e),onCreateTab:(e,t)=>chrome.tabs.create({url:e,...t?.active!==void 0?{active:t.active}:{},...t?.pinned!==void 0?{pinned:t.pinned}:{},...t?.index!==void 0?{index:t.index}:{},...t?.windowId!==void 0?{windowId:t.windowId}:{},...t?.openerTabId!==void 0?{openerTabId:t.openerTabId}:{}}),onUpdateTab:(e,t)=>{let r=I(e);return null!=r?chrome.tabs.update(r,t):chrome.tabs.update(t)},onRemoveTab:e=>{let t=I(e);return null==t&&k("tabs.remove",`"${String(e)}" is not a real tab id`),chrome.tabs.remove(t)},onGetWindow:(e,t,r)=>{let n=S(t);return null==n?chrome.windows.getCurrent(r??{}):chrome.windows.get(n,r??{})},onGetAllWindows:(e,t)=>chrome.windows.getAll(t??{}),onCreateWindow:(e,t)=>chrome.windows.create({url:t}),onUpdateWindow:(e,t,r)=>{let n=S(t);return null==n?null:chrome.windows.update(n,r)},onRemoveWindow:async(e,t)=>{let r=S(t);return null!=r&&(await chrome.windows.remove(r),!0)},onOpenSidePanel:async()=>{let e=await chrome.windows.getCurrent();null==e.id&&k("sidePanel.open","there is no focused window to open it in"),await chrome.sidePanel.open({windowId:e.id})},onSetBadgeText:(e,t,r)=>{let n=I(r);return chrome.action.setBadgeText(null!=n?{text:t??"",tabId:n}:{text:t??""})},onSetBadgeBackground:(e,t,r)=>{t||k("action.setBadgeBackgroundColor","no color was given");let n=I(r);return chrome.action.setBadgeBackgroundColor(null!=n?{color:t,tabId:n}:{color:t})},onSetActionTitle:(e,t,r)=>{let n=I(r);return chrome.action.setTitle(null!=n?{title:t??"",tabId:n}:{title:t??""})},onGetActionState:async(e,t)=>{let r=I(t),n=null!=r?{tabId:r}:{},[o,s,a]=await Promise.all([chrome.action.getBadgeText(n),chrome.action.getBadgeBackgroundColor(n),chrome.action.getTitle(n)]);return{badgeText:o,badgeColor:Array.isArray(s)?`rgba(${s[0]}, ${s[1]}, ${s[2]}, ${(s[3]??255)/255})`:s??void 0,title:a}},onSetActionIcon:(e,t)=>(t||k("action.setIcon","no icon path was given"),chrome.action.setIcon({path:t})),onCreateContextMenu:(e,t)=>chrome.contextMenus.create(t),onUpdateContextMenu:async(e,t,r)=>(await chrome.contextMenus.update(t,r),!0),onRemoveContextMenu:async(e,t)=>(await chrome.contextMenus.remove(t),!0),onClearContextMenus:async()=>(await chrome.contextMenus.removeAll(),!0),onCreateNotification:(e,t,r)=>chrome.notifications.create(t??"",r),onClearNotification:(e,t)=>chrome.notifications.clear(t),onGetNotifications:()=>chrome.notifications.getAll(),onDownload:(e,t)=>chrome.downloads.download(t),onSearchDownloads:(e,t)=>chrome.downloads.search(t??{}),onCancelDownload:async(e,t)=>{await chrome.downloads.cancel(t).catch(()=>void 0)},onPauseDownload:(e,t)=>chrome.downloads.pause(t),onResumeDownload:(e,t)=>chrome.downloads.resume(t),onEraseDownloads:(e,t)=>chrome.downloads.erase(t??{}),onGetBookmarkTree:()=>chrome.bookmarks.getTree(),onGetBookmarks:(e,t)=>chrome.bookmarks.get(t),onGetBookmarkChildren:(e,t)=>t?chrome.bookmarks.getChildren(t):chrome.bookmarks.getTree(),onSearchBookmarks:(e,t)=>chrome.bookmarks.search(t??""),onCreateBookmark:(e,t)=>chrome.bookmarks.create(t),onUpdateBookmark:(e,t,r)=>chrome.bookmarks.update(t,r),onRemoveBookmark:async(e,t,r)=>(r?await chrome.bookmarks.removeTree(t):await chrome.bookmarks.remove(t),!0),onSearchHistory:(e,t)=>chrome.history.search({text:t?.text??"",...t}),onGetHistoryVisits:(e,t)=>chrome.history.getVisits({url:t?.url??""}),onAddHistoryUrl:async(e,t)=>{t?.url&&await chrome.history.addUrl({url:t.url})},onDeleteHistoryUrl:async(e,t)=>{t?.url&&await chrome.history.deleteUrl({url:t.url})},onDeleteHistoryRange:async(e,t)=>{await chrome.history.deleteRange({startTime:t?.startTime??0,endTime:t?.endTime??Date.now()})},onDeleteAllHistory:async()=>{await chrome.history.deleteAll()},onRuntimeMessage:(e,t)=>chrome.runtime.sendMessage(t),onGetRuntimeContexts:()=>chrome.runtime.getContexts({}),onContainsPermissions:(e,t)=>chrome.permissions.contains(t),onRequestPermissions:(e,t)=>chrome.permissions.request(t),onRemovePermissions:(e,t)=>chrome.permissions.remove(t),onGetAllPermissions:async()=>{let e=await chrome.permissions.getAll();return{permissions:e.permissions??[],origins:e.origins??[]}},onCreateOffscreenDocument:async(e,t)=>(await chrome.offscreen.createDocument(t),!0),onCloseOffscreenDocument:async()=>(await chrome.offscreen.closeDocument(),await chrome.offscreen.hasDocument().catch(()=>!1)&&k("offscreen.closeDocument","the offscreen document was still open after closeDocument resolved"),!0),onHasOffscreenDocument:()=>chrome.offscreen.hasDocument(),onGetCookie:(e,t)=>chrome.cookies.get(t),onGetAllCookies:(e,t)=>chrome.cookies.getAll(t),onSetCookie:(e,t)=>chrome.cookies.set(t),onRemoveCookie:(e,t)=>chrome.cookies.remove(t),onExecuteScript:(e,t)=>{let r=I(t.target?.tabId);return null==r&&k("scripting.executeScript",`target.tabId "${String(t.target?.tabId)}" is not a real tab id`),t.files?.length||k("scripting.executeScript","only the `files` form is supported here; a func/code body cannot be serialized across the bridge into a real page. Ship the code as a file in your extension and pass files: [...]"),chrome.scripting.executeScript({target:{tabId:r},files:t.files})},onInsertCss:async(e,t)=>{let r=I(t.target?.tabId);(null==r&&k("scripting.insertCSS",`target.tabId "${String(t.target?.tabId)}" is not a real tab id`),t.css)?await chrome.scripting.insertCSS({target:{tabId:r},css:t.css}):t.files?.length?await chrome.scripting.insertCSS({target:{tabId:r},files:t.files}):k("scripting.insertCSS","neither css nor files was given")}}}(),K=new Map;async function Q(){try{return(await chrome.windows.getAll({populate:!0})).map(e=>{let t=e.tabs??[],r=t.find(e=>e.active);return{id:e.id??void 0,focused:e.focused,activeTabId:r?.id??"",tabs:t.map(e=>({id:e.id??"",url:e.url??e.pendingUrl??"",title:e.title??"",active:e.active,index:e.index,status:e.status,pinned:e.pinned,discarded:e.discarded,incognito:e.incognito,tabGroupId:e.groupId}))}})}catch{return[]}}async function Y(e){let r,n=Date.now(),o=t=>(J.recordOutcome({requestId:e.requestId,extensionId:e.extensionId,api:e.api,args:e.args,lane:"real",result:t.result,error:t.error,durationMs:Date.now()-n,source:"frame",...t.refused?{refused:!0}:{}}),t),s=(r=t(e.api,K.get(e.extensionId)??new Set))?{error:r,refused:!0}:null;if(s)return o(s);let a="tabs.query"===e.api||"tabs.get"===e.api||"windows.getCurrent"===e.api?await Q():[];return new Promise(t=>{let r=!1,n=e=>{r||(r=!0,t(o(e)))};v({data:e,windows:a,...X,respond:n,source:"frame"}).catch(e=>n({error:e instanceof Error?e.message:"Bridge error"}))})}async function Z(e){let t,r,{extensionId:n,url:o,code:s,css:a,matches:i=[]}=e,l=K.get(n);if(!(l&&(l.has("scripting")||l.has("activeTab"))))return{ok:!1,error:'This extension does not declare the "scripting" (or "activeTab") permission.'};if(!i.length||!i.some(e=>{let t=function(e){let t;if("<all_urls>"===e)return/^https?:\/\/.*/;let r=/^(\*|https?|file|ftp):\/\/([^/]*)(\/.*)$/.exec(e);if(!r)return null;let[,n="",o="",s=""]=r,a="*"===n?"https?":n.replace(/[.+?^${}()|[\]\\]/g,"\\$&");t="*"===o?"[^/]+":o.startsWith("*.")?`(?:[^/]+\\.)?${o.slice(2).replace(/[.+?^${}()|[\]\\]/g,"\\$&").replace(/\*/g,"[^/]*")}`:o.replace(/[.+?^${}()|[\]\\]/g,"\\$&").replace(/\*/g,"[^/]*");let i=s.replace(/[.+?^${}()|[\]\\]/g,"\\$&").replace(/\*/g,".*");return RegExp(`^${a}://${t}${i}$`)}(e);return!!t&&t.test(o)}))return{ok:!1,error:`This extension's content scripts / host permissions do not match ${o}.`};try{t=await chrome.tabs.create({url:o,active:!0})}catch(e){return{ok:!1,error:`Could not open the tab: ${String(e)}`}}let c=t.id;if(null==c)return{ok:!1,error:"No tab id."};if(await new Promise(e=>{let t=setTimeout(()=>e(),8e3),r=(n,o)=>{n===c&&"complete"===o.status&&(clearTimeout(t),chrome.tabs.onUpdated.removeListener(r),e())};chrome.tabs.onUpdated.addListener(r)}),a)try{await chrome.scripting.insertCSS({target:{tabId:c},css:a})}catch(e){r=String(e)}if(!s)return{ok:!0,injected:!1,...r?{cssError:r}:{}};try{let[e]=await chrome.scripting.executeScript({target:{tabId:c},world:"MAIN",func:e=>{try{return(0,eval)(e),{ran:!0}}catch(e){return{ran:!1,error:String(e)}}},args:[s]}),t=e?.result;if(t&&!1===t.ran)return{ok:!0,injected:!1,blocked:!0,reason:t.error,...r?{cssError:r}:{}};return{ok:!0,injected:!0,...r?{cssError:r}:{}}}catch(e){return{ok:!1,error:`Injection failed: ${String(e)}`}}}chrome.runtime.onInstalled.addListener(()=>{}),chrome.runtime.onConnectExternal.addListener(e=>{if(e.name!==_)return;for(let t of(z.add(e),J.events()))try{e.postMessage({type:F,event:t,backlog:!0})}catch{break}let t=[],r=(r,n)=>{let o=chrome[r],s=o?.[n];if(!s||"function"!=typeof s.addListener)return;let a=(...t)=>((t,r,n)=>{try{e.postMessage({type:H,namespace:t,event:r,args:n})}catch{}})(r,n,t);s.addListener(a),t.push(()=>s.removeListener(a))};r("storage","onChanged"),r("alarms","onAlarm"),r("tabs","onCreated"),r("tabs","onUpdated"),r("tabs","onRemoved"),r("tabs","onActivated"),r("windows","onCreated"),r("windows","onRemoved"),r("windows","onFocusChanged"),r("bookmarks","onCreated"),r("bookmarks","onChanged"),r("bookmarks","onMoved"),r("bookmarks","onRemoved"),r("history","onVisited"),r("history","onVisitRemoved"),r("downloads","onCreated"),r("downloads","onChanged"),r("downloads","onErased"),r("cookies","onChanged"),r("notifications","onClicked"),r("notifications","onClosed"),r("notifications","onButtonClicked"),r("contextMenus","onClicked"),e.onDisconnect.addListener(()=>{for(let r of(z.delete(e),t))r()})}),chrome.runtime.onMessageExternal.addListener((e,t,r)=>e?.type===j?(r({type:G,version:chrome.runtime.getManifest().version,ready:!0}),!1):e?.type===W&&"string"==typeof e.extensionId?(K.set(e.extensionId,new Set(Array.isArray(e.permissions)?e.permissions:[])),r({ok:!0}),!1):e?.type===L&&e.request?(Y(e.request).then(r),!0):e?.type===V&&"string"==typeof e.url&&(Z(e).then(r),!0))})();
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@extension.dev/mcp",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "6.
|
|
5
|
-
"description": "MCP server that lets AI agents (Claude Code, Claude Desktop, Cursor, Copilot, Codex) build, run, inspect, and publish browser extensions.
|
|
4
|
+
"version": "6.4.0",
|
|
5
|
+
"description": "MCP server that lets AI agents (Claude Code, Claude Desktop, Cursor, Copilot, Codex) build, run, inspect, and publish browser extensions. 35 tools for scaffolding, live DOM inspection, log streaming, and store-ready builds across Chrome, Edge, Firefox, Safari, and every Chromium- or Gecko-based browser (Brave, Opera, Vivaldi, Yandex, Waterfox, LibreWolf). Powered by extension.dev and Extension.js.",
|
|
6
6
|
"mcpName": "io.github.extensiondev/mcp",
|
|
7
7
|
"license": "Apache-2.0",
|
|
8
8
|
"author": {
|
package/server.json
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json",
|
|
3
3
|
"name": "io.github.extensiondev/mcp",
|
|
4
|
-
"description": "Build, run, inspect, and publish browser extensions from any MCP client.
|
|
4
|
+
"description": "Build, run, inspect, and publish browser extensions from any MCP client. 35 tools, 11 browsers.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"url": "https://github.com/extensiondev/mcp",
|
|
7
7
|
"source": "github"
|
|
8
8
|
},
|
|
9
9
|
"websiteUrl": "https://extension.dev",
|
|
10
|
-
"version": "6.
|
|
10
|
+
"version": "6.4.0",
|
|
11
11
|
"packages": [
|
|
12
12
|
{
|
|
13
13
|
"registryType": "npm",
|
|
14
14
|
"registryBaseUrl": "https://registry.npmjs.org",
|
|
15
15
|
"identifier": "@extension.dev/mcp",
|
|
16
|
-
"version": "6.
|
|
16
|
+
"version": "6.4.0",
|
|
17
17
|
"runtimeHint": "npx",
|
|
18
18
|
"transport": {
|
|
19
19
|
"type": "stdio"
|