0

I'm writing a WebExtension for a text editor. When the user clicks a button, format the editor fires an ajax request and then returns the formatted text. I need my addon to listen for a change in the textarea.

I've tried using onchange or oninput, but when the response is received, these events aren't triggered. The ajax response sets the body with this function (not my own):

function setBody(text) {
  $(opts.codeEl).val(text); // opts.codeEl is the textarea in question
}

the ajax response looks like this:

{"Body":"Hello World","Error":""}

Is it possible to handle this ajax request/response from a WebExtension Content Script? Somehow listen for a change in val of the textarea in the DOM? Can I listen and intercept the response?

The ajax request is being sent and received in the website's javascript code, isolated from the content script which I'm using (the WebExtension code) for the editor. the request is this

function fmt() {
  loading();
  var data = {"body": body()};
  if ($(opts.fmtImportEl).is(":checked")) {
    data["imports"] = "true";
  }
  $.ajax("/fmt", {
    data: data,
    type: "POST",
    dataType: "json",
    success: function(data) {
      if (data.Error) {
        setError(data.Error);
      } else {
        setBody(data.Body);
        setError("");
      }
    }
  });
}

Edit

Injecting a ajaxComplete handler:

  $(document).ajaxComplete(function(event, jqxhr, options) {
    if(options.url === "/fmt") {
      console.log("fmtted");
      $('#code').change();
    }
  });

Into the target site header will be called when the ajax response is received. However, when trying to modify the DOM using injected scripts (by, for example, calling change() or onchange(), for security reasons, it raises an Error: Permission denied to access property "apply" error.

Nevermore
  • 7,141
  • 5
  • 42
  • 64
  • Are you trying to check response of server to `$.ajax()` call or `change` event of `textarea`? – guest271314 Jan 05 '17 at 16:14
  • 1. Does this text editor provide its own js API and event subscription? 2. You may have to [spoof `val` method](https://stackoverflow.com/a/40908082) using a [DOM-injected script](https://stackoverflow.com/a/9517879) – wOxxOm Jan 05 '17 at 16:18
  • @guest271314 check the `change` event – Nevermore Jan 05 '17 at 16:22
  • @wOxxOm yes... I think. I'm going to look into those links now – Nevermore Jan 05 '17 at 16:23
  • @Nevermore `options.url` would not be equal to `"/fmt"`, but rather, the full URL. – guest271314 Jan 05 '17 at 18:11
  • @guest271314 when I log simply `options.url`, or `options` the result is `"/fmt"` – Nevermore Jan 05 '17 at 18:19
  • @Nevermore Ok. Still not sure what issue is? – guest271314 Jan 05 '17 at 18:21
  • @guest271314 I think I get it - I'm trying to call either `change()` or `onchange()` using injected js, and so I'm denied because of my permissions (to protect against cross site scripting). Either I have to send a message to my content script another way (than activating the `change` event) or detect the change another way (`MutationObservers` wont work on forms) – Nevermore Jan 05 '17 at 18:25
  • @Nevermore _"(`MutationObservers` wont work on forms) "_ Was not aware of this; are you sure? – guest271314 Jan 05 '17 at 18:32
  • 1
    @guest271314 there is a lot I'm not sure about javascript in general, but editing the textarea doesnt change the DOM AFAIK and so `MutationObservers` doesnt register anything – Nevermore Jan 05 '17 at 18:37
  • @Nevermore The original Question appears to be becoming broader? Can you create a gist including all relevant `html`, `javascript`? Not certain how to reproduce `Error: Permission denied to access property "apply"`? – guest271314 Jan 05 '17 at 18:59
  • @guest271314 I think the question has expanded since I've pursued one solution (which is very close to working!), but ultimately I'm still interested in putting a listener in a `content_script` for when an `ajax` call changes a `textarea` (and doesn't activate the `onchange`) - now the trick is get `ajaxComplete`, injected code, to register a `change`. Here is a gist of most things, [gist](https://gist.github.com/polypmer/9e413616a299904f8f9288d2bb700d09) I hope it helps explain... otherwise the website i"m injecting into is here https://play.golang.org/ – Nevermore Jan 05 '17 at 19:24
  • Why do you call `script.remove()`? Where is `jQuery` defined at `html` `document`? – guest271314 Jan 05 '17 at 19:27
  • @guest271314 jquery is defined at the beginning of the header, here I"ll include the header in a gist as well. I call `script.remove()`.. well I don't know, I'll try otherwise (I was injecting using a stackoverflow answer...) – Nevermore Jan 05 '17 at 19:32

1 Answers1

0

jQuery .val(value) does does dispatch change event. Call .change() or .trigger("change").

If you do not know when setBody will be called you can use .ajaxStop() to call .change() on $(opts.codeEl) when last $.ajax() call completes.

$(function() {

  let opts = {
    codeEl: document.querySelector("textarea")
  }

  $(opts.codeEl).on("change", function(event) {
    console.log("changed")
  });

  function setBody(text) {
    $(opts.codeEl).val(text)
  }

  let url = URL.createObjectURL(
    new Blob([
      JSON.stringify({
        "Body": "Hello World",
        "Error": ""
      })
    ], {
      type: "application/json"
    })
  );

  $.get(url)
  .then(function(data) {
    URL.revokeObjectURL(url);
    setBody(JSON.stringify(data))
  });
  
  $(document).ajaxComplete(function(event, jqxhr, options) {
    if (options.url === url) {
      console.log("ajaxStop");
      $(opts.codeEl).change(); // trigger `change` event
    }
    
  });

})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js">
</script>
<textarea></textarea>
guest271314
  • 1
  • 15
  • 104
  • 177
  • @guest271314 so `ajaxComplete` will trigger whenever an ajax request comes through? Even when the ajax request is sent and received in foreign code? – Nevermore Jan 05 '17 at 16:31
  • 1
    @Nevermore What do you mean by "foreign code"? Can you include all relevant `javascript` at Question? – guest271314 Jan 05 '17 at 16:32
  • @Nevermore, yes it'll always execute but you can simply compare the old textarea value with the current one. Also, you'll have to hook ajaxComplete in a [DOM-injected script](https://stackoverflow.com/a/9517879). – wOxxOm Jan 05 '17 at 16:34
  • @guest271314 so I"ve updated my question to specify that the ajax call is happening in an isolated script from my content script -- I think wOxxOm is onto something, but I need to first understand the code you put up before I can really get what "hook"ing an `ajaxComplete` function means... Basically, I need to inject that listener you've written above into the isolated script? – Nevermore Jan 05 '17 at 16:38
  • 2
    If you have control over the `html` `document`, you can inject the `.ajaxComplete()` portion of Answer at the `document`. Or, simply chain `.change()` to `.val()`. You could also try using [`chrome.webRequest`](https://developer.chrome.com/extensions/webRequest), [WebRequest API](http://www.adambarth.com/experimental/crx/docs/webRequest.html) from extension. – guest271314 Jan 05 '17 at 16:43
  • So, I've injected the ajax handler, and I successfully can check for the response, but I get an error: `Error: Permission denied to access property apply` when I call change. 2. I would like to simply chain `.change()` to `.val()` but the val change is happening inside a `src` script, so I don't know how to change it – Nevermore Jan 05 '17 at 17:04
  • _"but the val change is happening inside a src script, so I don't know how to change it"_ Not sure what you mean? Can you create a jsfiddle http://jsfiddle.net or plnkr http://plnkr.co to demonstrate? Did you call `.change()` on ` – guest271314 Jan 05 '17 at 17:08
  • I'm sorry but I'm not sure how to create a fiddle with an ajax request; I am calling `.change()` on the div in question on the `ajaxComplete()`, and it works, but I get a permission denied error which has something to do with cross site scripting I think. I'll update my question with the modified code, if that helps – Nevermore Jan 05 '17 at 17:10
  • the problem with jsfiddle is that I'm injecting the js; It does work, but I'm unable to call `.change()` with injected code. I think. I;ll continue trying to make a demo with `echo`. Thanks – Nevermore Jan 05 '17 at 17:19