Skip to content

Commit

Permalink
Fixed bug with Firefox 23 (current Aurora): Options calls from conten…
Browse files Browse the repository at this point in the history
…t scripts (i.e.,

options page) were failing.
This problem resulted from the recent removal of node.getUserData and node.setUserData. See:
https://bugzilla.mozilla.org/show_bug.cgi?id=749981 The MDN documentation for the correct
way to pass data between privileged and unprivileged scripts had been updated and the new
method is used. See:
https://developer.mozilla.org/en-US/docs/Code_snippets/Interaction_between_privileged_and_non-privileged_pages?redirectlocale=en-US&redirectslug=Code_snippets%3AInteraction_between_privileged_and_non-privileged_pages#Chromium-like_messaging.3A_json_request_with_json_callback
  • Loading branch information
adam-p committed May 18, 2013
1 parent 03c22a7 commit c20b7e4
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 88 deletions.
63 changes: 26 additions & 37 deletions src/common/options-store.js
Original file line number Diff line number Diff line change
Expand Up @@ -319,47 +319,36 @@ var MozillaOptionsStore = {
}
}
catch (ex) {
request = document.createTextNode('');
request.setUserData('data', data, null);
if (callback) {
request.setUserData('callback', callback, null);

var optionsResponseHandler = function(event) {
var node, callback, response;
node = event.target;
callback = node.getUserData('callback');

// May be undefined if there is no response data.
response = node.getUserData('response');

document.documentElement.removeChild(node);
// Note that if there's no callback, then the node gets removed by
// background service: firefox/chrome/content/options.js:listenRequest()
// TODO: That's pretty badly hacky. Can we do the node removal after
// the `dispatchEvent()` call below?

document.removeEventListener('markdown_here-options-response', optionsResponseHandler, false);

// We need to return a clone of the response.
// This is because the response came from the background script, and
// if later content-script code tries to modify it, an error will
// result (silently). I belive that this is related to
// `__exposedProps__` -- see `addExposedProps()` for details.
if (response) {
response = JSON.parse(JSON.stringify(response));
}
// This exception was thrown by the Components.classes stuff above, and
// means that this code is being called from a content script.
// We need to send a request from this non-privileged context to the
// privileged background script.
// See: https://developer.mozilla.org/en-US/docs/Code_snippets/Interaction_between_privileged_and_non-privileged_pages?redirectlocale=en-US&redirectslug=Code_snippets%3AInteraction_between_privileged_and_non-privileged_pages#Chromium-like_messaging.3A_json_request_with_json_callback

request = document.createTextNode(JSON.stringify(data));

var optionsResponseHandler = function(event) {
var response = null;

// There may be no response data.
if (request.nodeValue) {
response = JSON.parse(request.nodeValue);
}

request.parentNode.removeChild(request);

if (callback) {
callback(response);
return;
};
}
};

document.addEventListener('markdown_here-options-response', optionsResponseHandler, false);
}
document.documentElement.appendChild(request);
request.addEventListener('markdown_here-options-response', optionsResponseHandler, false);

document.head.appendChild(request);

sender = document.createEvent('HTMLEvents');
sender.initEvent('markdown_here-options-query', true, false);
request.dispatchEvent(sender);
var event = document.createEvent('HTMLEvents');
event.initEvent('markdown_here-options-query', true, false);
request.dispatchEvent(event);
}
}
};
Expand Down
2 changes: 0 additions & 2 deletions src/common/test/options-store-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,6 @@ describe('OptionsStore', function() {
OptionsStore.get(function(newOptions) {
expect(newOptions).to.have.property(testKeys[0]);
expect(newOptions[testKeys[0]]).to.eql(obj[testKeys[0]]);

// Note that these will fail on Firefox:
expect(Array.isArray(newOptions[testKeys[0]])).to.be.true;
expect(newOptions[testKeys[0]]).to.have.property('length');

Expand Down
62 changes: 13 additions & 49 deletions src/firefox/chrome/content/options.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,61 +18,27 @@

var MozillaOptionsService = {
listenRequest: function(callback) { // analogue of chrome.extension.onRequest.addListener
// https://developer.mozilla.org/en-US/docs/Code_snippets/Interaction_between_privileged_and_non-privileged_pages?redirectlocale=en-US&redirectslug=Code_snippets%3AInteraction_between_privileged_and_non-privileged_pages#Chromium-like_messaging.3A_json_request_with_json_callback

return document.addEventListener('markdown_here-options-query', function(event) {
var node = event.target, doc = node.ownerDocument;
var node = event.target;
if (!node || node.nodeType != Node.TEXT_NODE) {
return;
}

return callback(node.getUserData('data'), doc, function(data) {
if (!node.getUserData('callback')) {
return doc.documentElement.removeChild(node);
}
var doc = node.ownerDocument;

node.setUserData('response', data, null);
return callback(JSON.parse(node.nodeValue), doc, function(response) {
node.nodeValue = JSON.stringify(response);

var listener = doc.createEvent('HTMLEvents');
listener.initEvent('markdown_here-options-response', true, false);
return node.dispatchEvent(listener);
var event = doc.createEvent('HTMLEvents');
event.initEvent('markdown_here-options-response', true, false);
return node.dispatchEvent(event);
});
}, false, true);
},

requestHandler: function(request, sender, callback) {

/*
For information about what `addExposedProps()` is doing and why, see:
https://blog.mozilla.org/addons/2012/08/20/exposing-objects-to-content-safely/
The short version is that in Mozilla v17 a security feature was introduced
(well, enforced) whereby objects shared between background scripts (i.e.,
this code) and content scripts (i.e., the options page) have to specifically
indicate which properties are accessible.
This function basically undoes that by making all properties readable/writable,
but for our purposes that's okay (this code is effectively relinquishing
control of the object to the content script).
This fixes issue #37 (https://github.com/adam-p/markdown-here/issues/37).
*/
function addExposedProps(obj) {
var key, i;

if (typeof(obj) === 'undefined' || obj === null) {
return;
}

// Note that this code path is for Objects and Arrays
if (typeof(obj) === 'object') {
if (!('__exposedProps__' in obj)) {
obj['__exposedProps__'] = {};
}

for (key in obj) {
if (key === '__exposedProps__') {
continue;
}
obj['__exposedProps__'][key] = 'rw';
addExposedProps(obj[key]);
}
}
}

var prefs, prefKeys, prefsObj, i;

prefs = Components.classes['@mozilla.org/preferences-service;1']
Expand All @@ -93,13 +59,11 @@ var MozillaOptionsService = {
}
}

addExposedProps(prefsObj);

return callback(prefsObj);
}
else if (request.action === 'set') {
for (i in request.obj) {
prefs.setCharPref(i, JSON.stringify(request.obj[i]));
for (var key in request.obj) {
prefs.setCharPref(key, JSON.stringify(request.obj[key]));
}

if (callback) return callback();
Expand Down

0 comments on commit c20b7e4

Please sign in to comment.