Running a series of scripts across multiple open tabs

I am creating an experimental addon that will search for one term on several different art sites at once.

The addon works by doing the following:

  • Opens a window asking for user input (search term)
  • Saves this input
  • Launches a process with a raw loop
  • The loop opens the first tab in the list
  • Loop binding runs the first script in the list (these scripts almost all work the same)
  • This script is attached as worker to the tab
  • The script enters this term into the search box on the tab, then submits it
  • The view initiates the destruction of the worker.
  • Steps 4-8 are repeated until a tab appears in the list to open Note: The number of these steps has nothing to do with the step numbers in the code

Main.js

var data = require("sdk/self").data;
var tabs = require("sdk/tabs");
var journal_entry = require("sdk/panel").Panel({
  contentURL: data.url("DeltaLogPanel.html"),
  contentScriptFile: data.url("get-text.js")
});
// Creates the button
require("sdk/ui/button/action").ActionButton({
  id: "make-post",
  label: "Make post",
  icon: {
    "16": "./icon-16.png",
    "32": "./icon-32.png",
    "64": "./icon-64.png"
  },
  onClick: handleClick
});
var count = 0;
function handleClick(state) {
  journal_entry.show();
}
journal_entry.once("show", function() {
  journal_entry.port.emit("show");
});
function doit() {
    console.log("Step 1, loop "+ count +" started!");;
    if(count < sites.urls.length)
    {
        TabIt(count);
    }
    else
    {
        console.log("loop ended");
    }
}
function TabIt(x) {
    console.log("Step 2, tab "+ count +" is opening!");
   tabs.open(sites.urls[count]);
    handleTab(count);
}
//this is our cargo. There normally more stuff in it
var Cargo = {
    Title: ""
};
var sites = {
urls: ["https://www.sofurry.com/", "https://inkbunny.net/search.php", "http://www.furaffinity.net/search/", "https://www.weasyl.com/search", "http://www.deviantart.com/"],
scripts: ["searchSF.js", "searchIB.js", "searchFA.js", "searchWS.js", "searchDA.js"]
};
function handleTab(X) 
{
    console.log("Step 3, tab  "+ count +" is processing!");
//I tried tabs.on('load'.. and that didn't fix the problem
    tabs.on('ready', function RunPostScript(tab) 
    {
        console.log("The tab is ready");
        worker = tab.attach(
        {
              contentScriptFile: sites.scripts[X],
              contentScriptOptions: 
              {
                Cargo
              }
        });
        worker.port.once("myMessage", function handleMyMessage() 
        {
            console.log("Step 4, search "+ count +" is shuttin down!");
            //tab.close();
            worker.destroy();
        });
        tabs.removeListener("ready", RunPostScript);
    console.log("Step 5, search "+ count +" should be finished!");
    count=count+1;
    console.log("Ready to do it again?");
    doit();
    });
}
journal_entry.port.once("cargo-shipping", function (cargo) {
    Cargo.Title = cargo.title;
    console.log("title: " + Cargo.Title);
    //All data from panel should be imported. So panel is now hidden
    journal_entry.hide();
    //this starts the tab opening process
    TabIt(count);
});

      

DeltaLogPanel.js

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
    <!-- <link type="text/css" rel="stylesheet" href="DeltaLogPanel.css"/> -->
        <style>
            #MainPanel 
            {
            width: 180px;
            height:180px;
            background-color: #ACA1A1;
            }
        </style>
    </head>
    <body id="MainPanel">
        <div id="simpleOptions">
            <textarea id="titleBox" placeholder="Title" rows="1"></textarea>
            <button type="button" id="publishButton">Publish</button>
        </div>
    </body>
</html>

      

Get-text.js

var titleArea = document.getElementById("titleBox");
var finalButton = document.getElementById("publishButton");

//this defines the cargo on the button press then ships it.
finalButton.addEventListener('click', function() {
    // Remove the newline.
    var cargo = {
        title: ""
    };
    cargo.title = titleArea.value;
    self.port.emit("cargo-shipping", cargo);
    titleArea.value = "";
}, false);

//focusses on title box when button is pressed
self.port.on("show", function onShow() {
  titleArea.focus();
});

      

searchFA.js

document.addEventListener("submit", function(event) {
console.log("Unloading now");
self.port.emit("myMessage");
}, false);
//puts words in the input box
var titlePort = function(x){
    var FAzA = document.querySelector("form#search-form fieldset input#q");
    FAzA.value = x;
};
//hits the submit button
var ShipIt = function(){
document.querySelector("form#search-form").submit();
};
var Finalize = function(){
titlePort(self.options.Cargo.Title);
ShipIt();
};
Finalize();

      

searchIB.js

document.addEventListener("submit", function(event) {
console.log("Unloading now");
self.port.emit("myMessage");
}, false);
//puts words in the input box
var titlePort = function(x){
    var IBzA = document.querySelector("#text");
    IBzA.value = x;
};
//hits the submit button
var ShipIt = function(){
    var x = document.querySelector("body > form:nth-child(12)");
    var y =document.querySelector("body > form:nth-child(9)");
    if (x !==null || y !==null)
    {
        if (x !== null) 
        {
            x.submit();
        }
        else 
        {
            y.submit();
        }
    }
};
var Finalize = function(){
titlePort(self.options.Cargo.Title);
ShipIt();
};
Finalize();

      

searchSF.js

document.addEventListener("submit", function(event) {
console.log("Unloading now");
self.port.emit("myMessage");
}, false);
//puts words in the input box
var titlePort = function(x){
    var SFzA = document.querySelector("#headersearch");
    SFzA.value = x;
};
//hits the submit button
var ShipIt = function(){
document.querySelector(".topbar-nav > form:nth-child(3)").submit();
};
var Finalize = function(){
titlePort(self.options.Cargo.Title);
ShipIt();
};
Finalize();

      

searchWS.js

document.addEventListener("submit", function(event) {
console.log("Unloading now");
self.port.emit("myMessage");
}, false);
//puts words in the input box
var titlePort = function(x){
    var WSzA = document.querySelector("form#search-backup-search input");
    WSzA.value = x;
};
//hits the submit button
var ShipIt = function(){
document.querySelector("form#search-backup-search").submit();
};
var Finalize = function(){
titlePort(self.options.Cargo.Title);
ShipIt();
};
Finalize();

      

searchDA.js

document.addEventListener("submit", function(event) {
console.log("Unloading now");
self.port.emit("myMessage");
}, false);
//puts words in the input box
var titlePort = function(x){
    var DAzA = document.querySelector("input.gmbutton2");
    DAzA.value = x;
};
//hits the submit button
var ShipIt = function(){
document.querySelector("#search7").submit();
};
var Finalize = function(){
titlePort(self.options.Cargo.Title);
ShipIt();
};
Finalize();

      

What works

Saves user input. The addon loads all the tabs in the series perfectly. The first script runs smoothly on the first tab. When tested separately, each script runs the search result as it should.

Problem

When you're done, not all tabs display search results. Some of the search boxes are left blank, as if the scripts had never been run on these tabs. The script on the first tab always works, but the scripts on the rest of the tabs have the occasional chance of not working. I noticed it console.log("Step 4, search "+ count +" is shuttin down!");

never gets executed. I think this means that the worker is never destroyed. This can cause these problems. The worker for each script must be destroyed after the script runs the following: self.port.emit("myMessage");

This little segment of code is at the top of each script. But I think the dispatch process is too fast for the event listener to start.

Error message

I am getting the following error for each of the scripts not working:

    Object
- _errorType = TypeError
- message = FAzA is null
- fileName = resource://gre/modules/commonjs/toolkit/loader.js -> resource:/
/gre/modules/commonjs/sdk/loader/sandbox.js -> resource://jid1-tbpzbqttcoeaag-at
-jetpack/my-addon/data/searchFA.js
- lineNumber = 8
- stack = titlePort@resource://gre/modules/commonjs/toolkit/loader.js -> res
ource://gre/modules/commonjs/sdk/loader/sandbox.js -> resource://jid1-     tbpzbqttco
eaag-at-jetpack/my-  addon/data/searchFA.js:8:2|Finalize@resource://gre/modules/co
 mmonjs/toolkit/loader.js ->    resource://gre/modules/commonjs/sdk/loader/sandbox.j
s -> resource://jid1-tbpzbqttcoeaag-at-jetpack/my-  addon/data/searchFA.js:15:1|@r
esource://gre/modules/commonjs/toolkit/loader.js ->   resource://gre/modules/commo
njs/sdk/loader/sandbox.js -> resource://jid1-tbpzbqttcoeaag-at-jetpack/my-  addon/
data/searchFA.js:18:1|
- name = TypeError

      

If only one tab / script runs, this error will never happen. This is true for every tab / script combination. Therefore, I know that they should all work.

My question to you

How can I kill the worker from each script after the script runs the submit function?

+3


source to share


2 answers


Your error:

FAzA is null

indicates that the element was not found on the page in use querySelector()

.

The problem is mostly here:

tabs.on('ready', function RunPostScript(tab) {...});

      

This keeps track of all tabs and creates a race condition when the search is submitted. The following is done:



  • attach a prepared event listener to the tabs awaiting site A
  • open site A
  • page A fires ready event, attach script A to site A
  • remove the prepared event listener
  • number of increments
  • end of loop, call next tab
  • attach a prepared event listener to the tabs awaiting site B
  • meanwhile site A sends a request, it loads the results page and fires the prepared event
  • attach script B to site A
  • site A is now being treated as site B, script B cannot find search input, throws an error
  • remove the prepared event listener
  • number of increments
  • end of loop, call next tab
  • attach a prepared event listener to the tabs awaiting site C
  • etc.

You have to keep track of events at the tab level. Use once()

so you don't need to remove the listener. Something like that:

function TabIt(x) {
    tabs.open({
        url:sites.urls[x],
        onOpen: function(x, tab) {
            // fire once
            // count is available in this scope, no need to setup as arg again
            tab.once('ready', function readyStuff(tab) {
                tab.attach({
                    contentScriptFile: sites.scripts[x],
                    contentScriptOptions:{Cargo}
                });
            });
        }
    });
    ++count;
    doit();
}

      

You also keep using count

instead x

, whereas as possible it will need to bind the value because of the scope it exists in count

. But since you have it x

, just use it.

I also recommended in the comments to use DOMContentLoaded

in the attached scripts. After reading more about the tabbing script it was bad advice.

Your code can be significantly optimized, but let's do one at a time.

+1


source


const { getMostRecentBrowserWindow } = require('sdk/window/utils');
var aDOMWindow = getMostRecentBrowserWindow();
if (aDOMWindow.gBrowser && aDOMWindow.gBrowser.tabContainer) {
    var tabs = aDOMWindow.gBrowser.tabContainer.childNodes;
        for (var i=0; i<tabs.length; i++) {
            // tabs[i].contentWindow.wrappedJSObject.... // not e10s friendly, so to tabs[i].messageManager or something like that
       }
}

      



here are the staffing scripts which are completely simple: https://github.com/mdn/e10s-example-addons/tree/master/run-script-in-all-pages

0


source







All Articles