在使用 chrome.tabs.create() 创建的新选项卡中执行脚本

Execute script within new tab created with chrome.tabs.create()

如何在使用 chrome.tabs.create() 创建的选项卡中使用 chrome.tabs.executeScript() 执行脚本。我四处搜索,none 的解决方案似乎有效。

这是我目前的代码。

    runContentScript(){
        chrome.tabs.create({url: "https://google.com/", active: true});

        chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab){
            chrome.tabs.executeScript(tab.id!, {file: "tracking.js"});
        });
    }

我的清单也已正确设置。

编辑: 添加了完整代码,因为我不确定是否有什么东西干扰了 runContentScript 函数。

import React, { Component } from 'react';
import 'bootstrap/dist/css/bootstrap.min.css';
import { ListGroup } from 'react-bootstrap';
import './Cart.css';
import logo from './greenlake_logo.png';
import { url } from 'inspector';

class ProductList extends Component<{}, {products: any[][]}> {
    constructor(props: any){
        super(props);
        this.addProduct.bind(this);
        this.runContentScript.bind(this);
        this.checkIfPathsExists.bind(this)
        this.loadProductPage.bind(this);
        this.removeLocalStorage.bind(this);
        this.state = {products: []};
    }

    setLocalStorage(values: any[]){
        chrome.storage.sync.set({'productList': values});
    }

    async appendLocalStorage(product: any[]){
        var promisedStorageValues = new Promise(function(resolve, reject){
            chrome.storage.sync.get('productList', function(result){
                resolve(result.productList);
            })
        });

        const values: any = await promisedStorageValues;
        const newValues: any[] = [];
        for(let i=0; i < values.length; i++){
            newValues.push(values[i]);
        }
        newValues.push(product);
        this.setLocalStorage(newValues);
        this.readLocalStorage();
    }

    async removeLocalStorage(product: any[]){
        var promisedStorageValues = new Promise(function(resolve, reject){
            chrome.storage.sync.get('productList', function(result){
                resolve(result.productList);
            })
        });

        const values: any = await promisedStorageValues;
        values.splice(values.indexOf(product, 0), 1);
        this.setLocalStorage(values);
        this.readLocalStorage();
    }

    async readLocalStorage(){
        var promisedStorageValues = new Promise(function(resolve, reject){
            chrome.storage.sync.get('productList', function(result){
                resolve(result.productList);
            })
        });

        const values: any = await promisedStorageValues;
        this.setState({products: values});
    }

    async addProduct(){
        var promisedURL = new Promise(function(resolve, reject){
            chrome.tabs.query({active: true, currentWindow: true}, function(tabs){
                resolve([tabs[0].url, tabs[0].favIconUrl, tabs[0].title]);
            })
        });
        const urlInformation: any = await promisedURL;
        const urlFavicon: any = urlInformation[1];
        const urlProduct: any = urlInformation[0]
        var workaroundHost: any = new URL(urlInformation[0]);
        const urlHost: any = workaroundHost.host;
        const urlTitle: any = urlInformation[2];
        const urlPrice: any = 'placeholder';

        this.appendLocalStorage([urlProduct, urlFavicon, urlTitle, urlPrice, urlHost]);
    }

    loadProductPage(url: string){
        chrome.tabs.create({url: url, active: true});
    }

    runContentScript(urlProduct: string){
        chrome.tabs.create({url: urlProduct}, createdTab => {
            chrome.tabs.onUpdated.addListener(function _(tabId, info, tab) {
              if (tabId === createdTab.id && info.url) {
                chrome.tabs.onUpdated.removeListener(_);
                chrome.tabs.executeScript(tabId, {file: 'tracking.js'});
              }
            });
        });
    }

    loadPath(url: string){
        fetch("http://127.0.0.1:5000/getpath/" + url)
        .then(response => response.json())
        .then(response => {
            console.log('loadPath: ' + response)
        })
    }

    checkIfPathsExists(){
        for(let i=0; i < this.state.products.length; i++){
            const urlHost: string = this.state.products[i][4];
            let formattedHost = urlHost.split('.')[1];
            const status = fetch("http://127.0.0.1:5000/checkpath/" + formattedHost)
            .then(response => response.json())
            .then(response => {if(response == true){this.loadPath(formattedHost)}else{this.runContentScript(this.state.products[i][0])}});
        }
    }

    componentDidMount(){
        this.readLocalStorage();
    }

    render(){
        return (
            <div>
                <div className="logoDiv">
                    <img src={logo} width="128" height="64" />
                </div>
                <div className="centeredDiv">
                    {this.state.products.map(product => {return (
                        <div>
                            <ListGroup horizontal id="Test">
                                <ListGroup.Item className="ProductListLogo" onClick={() => this.loadProductPage(product[0])}><img src={product[1]} width="32" height="32"></img></ListGroup.Item>
                                <ListGroup.Item className="ProductListProduct">{product[2]}</ListGroup.Item>
                                <ListGroup.Item className="ProductListPrice">{product[3]}</ListGroup.Item>
                                <ListGroup.Item className="ProductListAction" onClick={() => this.removeLocalStorage(product)}>X</ListGroup.Item>
                            </ListGroup>
                            <div className="spacerLine"/>
                        </div>
                    )})}
                </div>
                <div className="actionDiv">
                        <ListGroup horizontal>
                            <ListGroup.Item className="ActionButtons" onClick={() => this.addProduct()}>Add To Cart</ListGroup.Item>
                            <ListGroup.Item className="ActionButtons" onClick={() => this.checkIfPathsExists()}>Checkout</ListGroup.Item>
                        </ListGroup>
                </div>
            </div>
        );
    }
}

class Cart extends Component {
    render(){
        return (
            <div className="containerDiv">
                <div>
                    <ProductList />
                </div>
            </div>
        )
    }
}

export default Cart;

解决方案 您需要添加“active:false”,就好像您创建了一个选项卡一样,它会自动关注它并在未指定时关闭扩展名。因此永远不会调用回调函数。

        chrome.tabs.create({url: urlProduct, active: false}, createdTab => {
            chrome.tabs.onUpdated.addListener(function _(tabId, info, tab) {
              if (tabId === createdTab.id && info.url) {
                chrome.tabs.onUpdated.removeListener(_);
                chrome.tabs.executeScript(tabId, {file: 'tracking.js'});
              }
            });
        });

您可以使用chrome.tabs.create回调函数。

runContentScript(){
    chrome.tabs.create({url: 'https://google.com/', active: true}, tab => {
        chrome.tabs.executeScript(tab.id, {file: 'tracking.js'}); 
    });

}

假设manifest.json的permissions包括"https://google.com/"(或更广泛的模式),这里存在三个问题。

1。 Chrome 重定向错误的解决方法

crbug.com/820080: chrome.tabs.executeScript may fail if the passed tab is undergoing a main frame navigation. You're specifying https://google.com/ but there's no site there, it redirects to https://www.google.com/ which you don't see in the new Chrome as it hides www. Either enable showing full URLs或者在devtools控制台使用location.href查看真实的URL.

结论:由于任何站点都可以出于任何原因决定执行重定向,因此可靠的解决方案是使用 chrome.tabs.onUpdated 但要正确执行,请参见下文。

2。正确使用chrome.tabs.onUpdated

  • 在回调中使用创建的选项卡 ID 将 onUpdated 限制为仅该选项卡
  • 注销侦听器以防止侦听器堆积导致错误

3。阻止来自 auto-closing

的弹出窗口

打开 active/focused 选项卡会关闭当前显示的扩展弹出窗口,它会完全终止,因此 create() 的回调不会 运行 因为它是异步调用的。

  • 解决方法。

    一个简单的解决方法是将选项卡打开为非活动状态,然后将其聚焦:

    chrome.tabs.create({url: 'https://google.com/', active: false}, createdTab => {
      chrome.tabs.onUpdated.addListener(function _(tabId, info, tab) {
        if (tabId === createdTab.id && info.url) {
          chrome.tabs.onUpdated.removeListener(_);
          chrome.tabs.executeScript(tabId, {file: 'tracking.js'}, () => {
            chrome.tabs.update(tabId, {active: true});
            // the above will close the popup if it doesn't have its own devtools open
          });
        }
      });
    });
    
  • 解.

    一个可靠的解决方案是在 background script 中执行此操作,因为如果另一个扩展正在修改新选项卡的默认行为并强制聚焦您的选项卡,则上述解决方法可能会失败,auto-closes 弹出窗口并打破了解决方法。

    popup.js:

    chrome.runtime.sendMessage({
      cmd: 'openTab',
      url: 'https://google.com/',
      script: 'tracking.js',
    });
    

    background.js:

    chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
      if (msg.cmd === 'openTab') {
        chrome.tabs.create({url: msg.url}, createdTab => {
          chrome.tabs.onUpdated.addListener(function _(tabId, info, tab) {
            if (tabId === createdTab.id && info.url) {
              chrome.tabs.onUpdated.removeListener(_);
              chrome.tabs.executeScript(tabId, {file: msg.script});
            }
          });
        });
      }
    });