0

I'm working on an add-on for Firefox using the SDK (previously known as Jetpack).

In my add-on I need to access some data via JSONP requests to a PHP script on a remote server (which I'm in control of), and then use the data returned from the server to update certain web page elements.

In my main.js file I use a page-mod and contentScriptFile to inject a copy of the JQuery library, and my own javascript file into the web page. My javascript file uses JQuery's "$.ajax" function to access the remote PHP script.

The problem: the responses are never received.

I stumbled across this StackOverflow question, which deals with the same issue: error in jsonp call ONLY FROM firefox-extension

I've followed some of the advice there, and changed my $.ajax request by specifying my own callback, and added the callback function to my file using "unsafeWindow.callback" - I don't fully understand what's happening, but it seems the "window" reference acquired by JQuery is not the the same as the real underlying window object (I thought this sort of thing was meant to be handled invisibly by the XRayWrapper, so confused about that).

That's OK. I get a single parameter passed to my callback (I've labelled the parameter "data"), which contains the entire server response - everything that I need.

The problem is, when I was previously using success: within the .ajax function, "this" referred to the ajax object (I think - Javascript beginner), and thence to the page element I wanted to update with the response data. I lose access to "this" in my custom callback.

What I've found is that even though I always get a response from the server, and my callback fires, JQuery reports an error - if I specify error:, then this will fire. If I add a complete:, this fires too, but success: never fires.

I thought that I could use :complete in place of my custom callback, but it appears that when the error handling code is invoked, it wipes out the server response - I can't access it in complete: (The XHR object .responseText property is "undefined" by the time processing reaches complete:).

So, my cobbled together solution to all of this is as follows. I have my custom callback, which is passed the full server response. I store this in a global variable. I specify complete: - which fires next (or later) - and then access the global variable from there, but ALSO have access to the page element I want to update via "this".

This concerns me because several AJAX requests may be made in quick succession, so my solution depends upon there being no "interleaving" of response handling - in other words, I'm making an assumption (which I don't know to be true) that when a response is received by JQuery, it will fire all of the event handlers for that response before beginning processing of the next received response. If this ISN'T so, then the data I store in my global variable may not correspond to the page element I want to use it with in success:. It seems to be working so far in testing, but it may just be that I've been lucky.

(Side note: I also use QTip2, and it's AJAX plugin - that always throws an error too, and not a silent one. But I found I could suppress this by adding error: and setting the xhr.status = 0 - I'll post a snippet below).

But this seems like such a horrible messy way of doing things. I don't understand why JQuery thinks there's been an error when the server response is received successfully. And it's very inconvenient that its error handling destroys this response which means I can't get at it in success:.

Does anyone have a neater solution? Thank you in advance - snippets to follow (by the way, these problems ONLY exist when I do all of this within my add-on. If I do the same stuff in a stand-alone page/script, it executes without errors being raised, and exactly as expected).

unsafeWindow.callback = function(data)
{
  MyGlobalServerResponseVariable = data;
}

$.ajax(
{
  url: 'http://nottherealurl.com/',
  type: 'GET',
  data: params,
  async: true,
  cache: false,
  contentType: "text/json; charset=utf-8",
  dataType: 'jsonp',
  crossDomain: true,
  jsonpCallback: "callback",

  complete: function(xhr, textStatus)
  {
    var data = MyGlobalServerResponseVariable;
    this.PageElementIWantToUpdate.attr("title", data.somevalue);
    ...
  }
...
}

QTip2 error suppression:

error: function(xhr, status, error)
{
  xhr.status = 0;
}

Final note, not directly related to the above - when I do "File->Open File" in Firefox and select a web page, my add-on is not executed. Is this by design?

Community
  • 1
  • 1
AMarch
  • 153
  • 2
  • 14
  • I'd moved on to working on a Chrome equivalent in the mean time when I discovered something. Chrome doesn't have an equivalent of unsafeWindow, and the "hacks" that have previously been employed to access the un-sandboxed window were made impotent by release 27. So it seemed there was no way I'd be able to use AJAX/JSONP requests from my Chrome plugin. – AMarch Aug 15 '13 at 07:35
  • These StackOverflow discussions gave some hope: http://stackoverflow.com/questions/8495825/jsonp-request-in-chrome-extension-callback-function-doesnt-exist http://stackoverflow.com/questions/1622145/how-can-i-mimic-greasemonkey-firefoxs-unsafewindow-functionality-in-chrome http://stackoverflow.com/questions/2619827/using-jquery-getjson-in-chrome-extension http://stackoverflow.com/questions/8977720/jsonp-communication-in-a-google-chrome-extension – AMarch Aug 15 '13 at 07:35
  • But I wasn't at all sure it would work. What I subsequently discovered was Chrome's Cross Origin XMLHttpRequest: http://developer.chrome.com/extensions/xhr.html What does this mean? I can use JSON instead of JSONP and STILL call across domain boundaries. That's fine because I'm in charge of the remote server PHP script, so I can conditionally have it return JSON instead of JSONP. – AMarch Aug 15 '13 at 07:35
  • And the bonus part is that I've no retrospectively realised Firefox supports exactly the same thing: https://addons.mozilla.org/en-US/developers/docs/sdk/Firefox-24/dev-guide/guides/content-scripts/cross-domain.html So I can do that in Firefox too - and ditch my horrible JSONP solution. – AMarch Aug 15 '13 at 07:36

2 Answers2

1

I would suggest moving the data loading logic to main.js, use the Request API available in this context to load the data (see https://addons.mozilla.org/en-US/developers/docs/sdk/latest/modules/sdk/request.html ).

When the data loaded, you can send the data to the page via extension -> page messaging ( https://addons.mozilla.org/en-US/developers/docs/sdk/latest/dev-guide/guides/content-scripts/using-port.html ).

hallvors
  • 6,069
  • 1
  • 25
  • 43
  • Thank you very much for your reply; I'm currently pre-occupied with another unrelated part of the project, but I'll certainly revisit your suggestions when I come back to round that bit off. – AMarch Oct 05 '13 at 16:57
1

when jQuery.ajax receive, firefox load the callback from content codes to page codes(because the design of window/unsafeWindow?), so you do not in your codes any more,but in the site's js. It is definitely can not find the callback function in site's js. So you should export the function in your codes to site's codes. Like this :

function your_fuc(){}
exportFunction(your_fuc, unsafeWindow, {defineAs: "your_fuc"});
//  ....
jQuery.ajax({url:url,data:mdata,jsonp:'callback', jsonpCallback:'window.your_fuc', dataType:"jsonp",crossDomain:true});
but since mozilla said

Also, unsafeWindow isn't a supported API, so it could be removed or changed in a future version of the SDK.

the best way to use jsonp,is inject your code into site's page by insert

<srcipt src="yoursite.com/your.js">
chen x
  • 43
  • 4
  • Thank you kindly chen x for contributing this answer. It's been a while since I worked on that part of my project, but I'm going to have to go back and update it at some point, so I'll make reference to your answer then. – AMarch Apr 23 '15 at 07:49