reactjs - Pokreni skriptu unutar nove kartice kreirane s chrome.tabs.create()


Kako izvršiti skriptu koristeći chrome.tabs.executeScript() u kartici koju sam stvorio koristeći chrome.tabs.create(). Pretražio sam i čini se da nijedno od rješenja ne radi.

Ovo je moj trenutni kod.

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

Moj manifest je također ispravno postavljen.

UREDI: Dodan je cijeli kod jer nisam siguran da li nešto ometa funkciju 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;

RIJEŠENJE Trebate dodati "active: false" jer se prilikom stvaranja kartice automatski fokusira na nju i zatvara ekstenziju ako nije drugačije navedeno. Povratna funkcija stoga se nikada ne poziva.

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

Odgovor #1

Pretpostavljajući da permissions u manifest.json uključuje "https://google.com/" (ili širi obrazac), ovdje postoje tri problema.

1. Zaobilazno rješenje za Chrome bug pri preusmjeravanju

crbug.com/820080: chrome.tabs.executeScript može ne uspjeti ako proslijeđeni tab prolazi kroz navigaciju glavnog okvira. Specifikujete https://google.com/, ali tamo nema stranice, ona preusmjerava na https://www.google.com/ što ne vidite u novom Chromeu jer skriva www. Ili omogućite prikaz pune URL adrese ili koristite location.href u konzoli za razvojne alate da vidite stvarni URL.

Presuda: budući da bilo koja stranica može odlučiti izvršiti preusmjeravanje iz bilo kojeg razloga, pouzdano rješenje je koristiti chrome.tabs.onUpdated, ali to učinite ispravno, vidi dolje.

2. Ispravno koristite chrome.tabs.onUpdated

  • Koristite ID stvorenog taba u povratnom pozivu kako biste ograničili onUpdated samo na taj tab
  • Odjavite slušatelja kako biste spriječili nakupljanje slušatelja što bi uzrokovalo pogreške

3. Spriječite automatsko zatvaranje skočnog prozora

Otvaranje aktivne/fokusirane kartice zatvara trenutno prikazani dodatak, potpuno ga prekida, tako da se povratna funkcija za create() neće izvršiti jer se poziva asinhrono.

  • Zaobilazno rješenje.

    Jednostavno rješenje je otvoriti karticu kao neaktivnu, a zatim se usredotočiti na nju:

    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
          });
        }
      });
    });
    
  • Rješenje.

    Pouzdano rješenje je napraviti to u pozadinskom skriptu jer gore navedeno zaobilazno rješenje može ne uspjeti ako neki drugi dodatak mijenja zadano ponašanje novih kartica i prisilno fokusira vašu karticu, što automatski zatvara skočni prozor i uništava zaobilazno rješenje.

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

Odgovor #2

Možete koristiti chrome.tabs.create funkciju povratnog poziva.

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

}