You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Chrome扩展Gmail插件开发:自定义元素chrome.runtime.getURL报错解决

问题原因

你注入到页面主世界的gmail-compose-assistant-element.js无法访问chrome.runtime.getURL API——该API仅在Chrome扩展的隔离上下文(如content script、service worker)中可用,页面主世界(普通网页脚本)没有权限调用扩展API,因此出现报错。

解决方案

以下两种方案均可解决该问题:

方案一:内联HTML与CSS资源

直接将自定义元素的HTML结构和CSS样式写入脚本,避免依赖外部资源请求,彻底绕开chrome.runtime.getURL的使用。

修改custom-elements/gmail-compose-assistant-element.js:

const template = document.createElement('template');

// 内联原HTML文件内容
const inlineHTML = `
<div class="assistant-container">
  <button class="assistant-trigger">助手选项</button>
  <ul class="assistant-dropdown">
    <li class="dropdown-item">选项1</li>
    <li class="dropdown-item">选项2</li>
  </ul>
</div>
`;

// 内联原CSS文件内容
const inlineStyle = document.createElement('style');
inlineStyle.textContent = `
.assistant-container {
  position: relative;
  display: inline-block;
}
.assistant-trigger {
  padding: 6px 12px;
  border: 1px solid #dadce0;
  border-radius: 4px;
  background: white;
  cursor: pointer;
  margin-left: 8px;
}
.assistant-dropdown {
  position: absolute;
  top: 100%;
  right: 0;
  margin: 8px 0;
  padding: 0;
  list-style: none;
  background: white;
  border: 1px solid #dadce0;
  border-radius: 8px;
  box-shadow: 0 2px 10px rgba(0,0,0,0.1);
  display: none;
}
.dropdown-item {
  padding: 8px 16px;
  cursor: pointer;
}
.dropdown-item:hover {
  background: #f1f3f4;
}
`;

template.innerHTML = inlineHTML;
template.content.prepend(inlineStyle);

class CustomGmailComposeAssistant extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({ mode: 'open' });
    this.shadowRoot.appendChild(template.content.cloneNode(true));
    
    // 绑定下拉菜单逻辑
    const trigger = this.shadowRoot.querySelector('.assistant-trigger');
    const dropdown = this.shadowRoot.querySelector('.assistant-dropdown');
    trigger.addEventListener('click', () => {
      dropdown.style.display = dropdown.style.display === 'none' ? 'block' : 'none';
    });
    // 点击外部关闭下拉
    document.addEventListener('click', (e) => {
      if (!this.contains(e.target)) {
        dropdown.style.display = 'none';
      }
    });
  }
}

window.customElements.define('custom-gmail-compose-assistant', CustomGmailComposeAssistant);

方案二:在Content Script中预获取资源URL并传递给主世界

利用Content Script可访问扩展API的特性,提前获取HTML/CSS资源的可访问URL,再传递给注入到主世界的自定义元素脚本。

修改gmail-compose-assistant.js:

// 1. 在Content Script中获取资源URL
const htmlResourceUrl = chrome.runtime.getURL('/custom-elements/gmail-compose-assistant-element.html');
const cssResourceUrl = chrome.runtime.getURL('/custom-elements/gmail-compose-assistant-element.css');

// 2. 构造主世界脚本内容,注入预获取的URL
const scriptContent = `
const htmlUrl = "${htmlResourceUrl}";
const cssUrl = "${cssResourceUrl}";

const template = document.createElement('template');

fetch(htmlUrl).then((response) =>
  response.text().then((html) => {
    template.innerHTML = html;

    const styleSheetLink = document.createElement('link');
    styleSheetLink.rel = 'stylesheet';
    styleSheetLink.href = cssUrl;
    template.content.prepend(styleSheetLink);
  })
);

class CustomGmailComposeAssistant extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({ mode: 'open' });
    this.shadowRoot.appendChild(template.content.cloneNode(true));
  }
}

window.customElements.define('custom-gmail-compose-assistant', CustomGmailComposeAssistant);
`;

// 3. 注入脚本到页面主世界
const script = document.createElement('script');
script.type = 'module';
script.textContent = scriptContent;
document.body.appendChild(script);

// 原观察者逻辑保持不变
const observerConfig = {
  childList: true,
};

const observer = new MutationObserver(observeDocumentBody);
observer.observe(document.body, observerConfig);

function injectDummyText(element, index) {
  const newTabledataRef = document.querySelector('#newTableData' + index);
  const tableRow = element.querySelector('tr');

  if (!newTabledataRef) {
    const tableData = tableRow.insertCell(1);
    const customElement = document.createElement('custom-gmail-compose-assistant');
    tableData.appendChild(customElement);
    tableData.id = 'newTableData' + index;
  }
}

function observeDocumentBody() {
  const tableElemets = document.getElementsByClassName('IZ');
  if (tableElemets.length) {
    Object.values(tableElemets).forEach((element, index) => {
      injectDummyText(element, index);
    });
  }
}
额外注意事项
  • 确保manifest.json中的web_accessible_resources配置正确,已将自定义元素相关资源列为可访问项(你的现有配置符合要求)。
  • 主世界脚本无法直接调用任何Chrome扩展API,若后续需要与扩展通信,需通过window.postMessage与Content Script交互,再由Content Script调用扩展API。

内容的提问来源于stack exchange,提问作者vijay s

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 15:54:51