reactjs - Spustiť skript v novom paneli vytvorenom pomocou chrome.tabs.create()


Ako spustím skript pomocou chrome.tabs.executeScript() v karte, ktorú som vytvoril pomocou chrome.tabs.create()? Hľadal som po internete a žiadne z riešení nezaberajú.

Toto je môj aktuálny kód.

    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"});
        });
    }

Môj manifest je tiež správne nastavený.

UPRAVA: Pridal som celý kód, pretože si nie som istý, či niečo nezasahuje do funkcie 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;

RIEŠENIE Musíte pridať "active: false", pretože ak vytvoríte kartu, automaticky sa na ňu sústredí a zatvorí rozšírenie, ak to nie je špecifikované. Funkcia spätného volania sa preto nikdy nevyvolá.

        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'});
              }
            });
        });

Odpoveď č. 1

Ak predpokladáme, že permissions v súbore manifest.json obsahuje "https://google.com/" (alebo širší vzor), tu sú tri problémy.

1. Obchádzka chyby Chrome pri presmerovaní

crbug.com/820080: chrome.tabs.executeScript môže zlyhať, ak je odovzdaná karta aktívne pod navigáciou hlavného rámca. Špecifikujete https://google.com/, ale na tejto stránke nie je nič, presmeruje sa na https://www.google.com/, čo v novom Chrome nevidíte, pretože skrýva www. Buď povoľte zobrazovanie úplných URL, alebo použite location.href v konzole vývojárskych nástrojov na zobrazenie skutočnej URL.

Verdikt: keďže akákoľvek stránka sa môže rozhodnúť vykonať presmerovanie z akéhokoľvek dôvodu, spoľahlivým riešením je použiť chrome.tabs.onUpdated, ale urobiť to správne, pozri nižšie.

2. Použite chrome.tabs.onUpdated správne

  • Použite vytvorené ID karty v spätnom volaní na obmedzenie onUpdated len na túto kartu
  • Zrušte registráciu poslucháča, aby ste predišli hromadeniu poslucháčov, čo by spôsobilo chyby

3. Zabrániť automatickému zatváraniu popupu

Otvorenie aktívnej/zameranej karty zatvorí aktuálne zobrazené rozšírenie vyskakovacie okno, úplne ho ukončí, takže spätné volanie pre create() sa nezobrazí, pretože sa volá asynchrónne.

  • Obchádzka.

    Jednoduchým obchvatom je otvoriť kartu ako neaktívnu a potom sa na ňu zamerať:

    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
          });
        }
      });
    });
    
  • Riešenie.

    Spoľahlivé riešenie je urobiť to v skripte na pozadí, pretože vyššie uvedené obchádzanie môže zlyhať, ak iné rozšírenie mení predvolené správanie nových záložiek a násilne zameriava vašu záložku, čo automaticky zatvorí vyskakovacie okno a zničí obchádzanie.

    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});
            }
          });
        });
      }
    });
    

Odpoveď č. 2

Môžete použiť spätnú volaciu funkciu chrome.tabs.create.

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

}