Looking for Help on Message Passing and Program Structure

Looking for Help on Message Passing and Program Structure
I am working on an addon that uses the context menu to modify selected text in an edit area I have some fairly basic questions on how to handle certain aspects of this.

This will seem fairly basic to a lot of people, but this is my first foray into extension writing and into Javascript programming.

Assume that the following text is in an edit box:
I will not buy this record, it is scratched
My hovercraft is full of eels
I have highlighted the word “full”.
The addon should proceed as follows:

  1. Select text and right click on menu item.
  2. This has the background script run a function, let’s call it “Moo”, sending a signal to the content script. The function argument in this would be something like Moo ("[b]",ZZSELECT,"[/b]");
  3. The content script calls a function which takes the content of the edit box, and reduces it to three variables, the text before the selection, the selected text, and the text after the selection. (I already have a javascript function which does break-up of the text running a web page. This leads to my first question, how do I send a signal to the content page that specifically invokes this function and not others on the page?
  4. The content script sends message back to the background script with these three variables. This is my next question, I cannot see from the documentation how to pass three variables back to the background script page and into function moo.
  5. The background script page takes these variables, along with other modifiers included in the case statement for the specific menu item, and creates a string, which is passed back to the content script of this form:
    I will not buy this record, it is scratched
    My hovercraft is [b]full[/b] of eels
  6. The content script reads in this variable, and replaces the content of the edit box.

My questions are:

  1. What is the syntax for this message passing, the documentation leaves me clueless.
  2. Have I allocated the functions between the scripts properly?
    Please note that I am operating from a position of deep and profound ignorance.

You can send one thing at a time as the first or second arg, as documented and exemplified on https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/runtime/sendMessage.

Since you want to send three things, you probably want to send an object or an array:

browser.runtime.sendMessage({
  command: "dothing",
  someData: data1,
  someMoreData: data2,
  evenMore: data3
}).then((response) => { ... handle result });

On the other side, in the background script you want an onMessage listener that handles the message, calls the function with its args and returns the end result (possibly as promise).

Of course you’d do something similar for step 2.

If you can avoid the round trip to the background script in step 4, I’d suggest to do that. Apart from that it sounds fine.

You want to call a function that the page defines and not the content script from the content script? If that’s the case, please consult https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Sharing_objects_with_page_scripts#Accessing_page_script_objects_from_content_scripts

OK, so when you are talking about avoiding a second round trip, you are saying that I could send (for example) send everything once from the background script, of a form like:

(Before Selection),"\[b]",(selection),"\[/b]",(after selection)

Where the items in parenthesis are tokens representing what the content script has pulled out of the selection, rather than values, which the function in the background script would call.

Got it.

Thanks also for the call SendMessage syntax. It had me flummoxed.

I’m talking about not even sending the data from the content script to the BG script if not absolutely required. Since replacing is most likely a purely content-script action except for getting the context menu event.

This is example is probably highly simplified, but if this is an important use case: you don’t need a background script for this. Consider how the buttons work on your favorite BBCode site. The B button has onclick=“bbstyle(0);” and you can find the bbstyle() and bbfontstyle() functions here:

http://forums.mozillazine.org/styles/prosilver/template/editor.js

Numeric indexing seems too fragile; you’ll way to store and reference your opening and closing tag code in some other way.

I believe that the background script is required to to use this with a context menu, which is my intent.

Let me give you a couple of cases:
In the first, you have copied a Url, and have selected a text, and you are generating a bbcode link: (double curly braces represent a tag)
So we would pass an argument to a function which would look like this:
"[url=",{{clipboard,"}}]",{{selection}},"[/url]"
This would render like:
[url=https://discourse.mozilla.org/]my hovercraft is full of eels[/url]

In the second case, arguably the most complex case, we place a linked image in a box with a text wrap and a pop up asking for the caption, so the argument would look like: (Same double braces with quotes escaped because it generates html, not bbCode.)
"<div style=\"border: 1px solid black; float: right; margin: 0px 10px; padding: 5px; text-align: center; width: 330px;\"><a href=\"",{{clipboard}},"\"><img border=\"0\" bordercolor=\"white\" src=\"",{{clipboard}},"\" width=\"320\" /></a><br /><i>{{popup}}</i></div>"

Which would resolve to:
<div style="border: 1px solid black; float: right; margin: 0px 10px; padding: 5px; text-align: center; width: 330px;"><a href="{{clipboard}}"><img border="0" bordercolor="white" src="{{clipboard}}" width="320" /></a><br /><i>{{popup}}</i></div>

What I would prefer is to generate a string that is then parsed by the function that actually pastes the code in the edit box, as opposed to passing an indeterminate number of variables through the function, but I am not sure which is a better way of handling it in Javascript.

OK, I’ve got the basic click working, but trying to pass a 2nd variable along as an object has me stumped.

If my background script does this:

browser.tabs.sendMessage(tab.id, "zzBBCode");

and my content script does this:

browser.runtime.onMessage.addListener(function(RunParse, CommandString, sendResponse) { / if(RunParse == "zzBBCode") { ....... } });

It works fine, though I still have not passed the requisite instructions to be operated on.

But if my content script attempts to pass an object:

browser.tabs.sendMessage(tab.id,{runwhat: "zzBBCode", zzargument: "[a][b][c][d]"});

and I try to read the “runwhat” portion of that object:

browser.runtime.onMessage.addListener(function(RunParse, CommandString, sendResponse) { if(RunParse.runwhat == "zzBBCode") { .... } });

The if statement is never triggered.

(I want to use runwhat to call different functions in the content script (specifically, at some point, I want to add capitalization, and search & replace, and I want to use zzargument to provide instructions as to what to do).

Have you checked that the message is even received? Because your code sure does look correct. And in case your code has line breaks, using three ` lets you insert code blocks.

The message passing was fine.

I inadvertently deleted a curly brace on the if statement when I made the change.

D’oh!