How To Use Cloneinto In A Firefox Web Extension?
Solution 1:
I found an alternative solution. I didn't solve the question of how to use cloneInto but I am able to pass objects to my page script now, by using sendMessage
and creating an onMessage
listener.
Content script:
browser.runtime.sendMessage({theBlobs: myBlobObjects});
Page script:
browser.runtime.onMessage.addListener(function(request, sender, sendResponse) {
var blobs = request.theBlobs;
for (var i = blobs.length - 1; i >= 0; i--) {
// do clever things with the blobs
}
}
An advantage of this approach is that it doesn't use Firefox-specific functions.
https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/runtime/sendMessagehttps://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/runtime/onMessage
Solution 2:
I avoid using cloneInto()
and exportFunction()
, when possible
The functions cloneInto()
and exportFunction()
are Firefox-only. One of the significant advantages of WebExtensions is the ability to write extensions which work in multiple browsers. Thus, I prefer to avoid using those functions, where possible.
What I would do instead, is use a utility function I wrote, executeInPage()
, to copy the Object
, Array
, function
, RegExp
, Date
, and/or other primitives (Boolean
, null
, undefined, Number
, String
, but not Symbol
) into the page. Another simple function inPageAssignToWindowProperty()
can assign the value passed to a variable in the global scope.
This is a somewhat more limited than cloneInto()
, as executeInPage()
does not, currently, support: Blob
, File
, FileList
, ArrayBuffer
, ArrayBufferView
, ImageData
, Map
, and Set
. These are not currently supported, as I have not had a need to pass them into the page context.
var messenger = {
notify: function(message) {
browser.runtime.sendMessage({
content: "Object method call: " + message
});
}
};
functioninPageAssignToWindowProperty(property, value) {
window[property] = value;
}
executeInPage(inPageAssignToWindowProperty, false, '', 'messenger', messenger);
console.log('window.messenger:', window.messenger);
<script>/* executeInPage takes a function defined in this context, converts it to a string
* and inserts it into the page context inside a <script>. It is placed in an IIFE and
* passed all of the additional parameters passed to executeInPage.
* Parameters:
* func The function which you desire to execute in the page.
* leaveInPage If this does not evaluate to a truthy value, then the <script> is
* immediately removed from the page after insertion. Immediately
* removing the script can normally be done. In some corner cases,
* it's desirable for the script to remain in the page. However,
* even for asynchronous functionality it's usually not necessary, as
* the context containing the code will be kept with any references
* (e.g. the reference to a callback function).
* id If this is a non-blank string, it is used as the ID for the <script>
* All additional parameters are passed to the function executing in the page.
* This is done by converting them to JavaScript code-text and back.
* All such parameters must be Object, Array, functions, RegExp,
* Date, and/or other primitives (Boolean, null, undefined, Number,
* String, but not Symbol). Circular references are not supported.
* If you need to communicate DOM elements, you will need to
* pass selectors, or other descriptors of them (e.g. temporarily
* assign them a unique class), or otherwise communicate them to the
* script (e.g. you could dispatch a custom event once the script is
* inserted into the page context).
*/functionexecuteInPage(functionToRunInPage, leaveInPage, id) {
//Execute a function in the page context.// Any additional arguments passed to this function are passed into the page to the// functionToRunInPage.// Such arguments must be JSON-ifiable (also Date, Function, and RegExp) (prototypes// are not copied).// Using () => doesn't set arguments, so can't use it to define this function.// This has to be done without jQuery, as jQuery creates the script// within this context, not the page context, which results in// permission denied to run the function.functionconvertToText(args) {
//This uses the fact that the arguments are converted to text which is// interpreted within a <script>. That means we can create other types of// objects by recreating their normal JavaScript representation.// It's actually easier to do this without JSON.strigify() for the whole// Object/Array.var asText = '';
var level = 0;
functionlineSeparator(adj, isntLast) {
level += adj - ((typeof isntLast === 'undefined' || isntLast) ? 0 : 1);
asText += (isntLast ? ',' : '') +'\n'+ (newArray(level * 2 + 1)).join('');
}
functionrecurseObject(obj) {
if (Array.isArray(obj)) {
asText += '[';
lineSeparator(1);
obj.forEach(function(value, index, array) {
recurseObject(value);
lineSeparator(0, index !== array.length - 1);
});
asText += ']';
} elseif (obj === null) {
asText +='null';
//undefined
} elseif (obj === void(0)) {
asText +='void(0)';
//Special cases for Number
} elseif (Number.isNaN(obj)) {
asText +='Number.NaN';
} elseif (obj === 1/0) {
asText +='1/0';
} elseif (obj === 1/-0) {
asText +='1/-0';
//function
} elseif (obj instanceofRegExp || typeof obj === 'function') {
asText += obj.toString();
} elseif (obj instanceofDate) {
asText += 'new Date("' + obj.toJSON() + '")';
} elseif (typeof obj === 'object') {
asText += '{';
lineSeparator(1);
Object.keys(obj).forEach(function(prop, index, array) {
asText += JSON.stringify(prop) + ': ';
recurseObject(obj[prop]);
lineSeparator(0, index !== array.length - 1);
});
asText += '}';
} elseif (['boolean', 'number', 'string'].indexOf(typeof obj) > -1) {
asText += JSON.stringify(obj);
} else {
console.log('Didn\'t handle: typeof obj:', typeof obj, ':: obj:', obj);
}
}
recurseObject(args);
return asText;
}
var newScript = document.createElement('script');
if(typeof id === 'string' && id) {
newScript.id = id;
}
var args = [];
//using .slice(), or other Array methods, on arguments prevents optimizationfor(var index=3;index<arguments.length;index++){
args.push(arguments[index]);
}
newScript.textContent = '(' + functionToRunInPage.toString() + ').apply(null,'
+ convertToText(args) + ");";
(document.head || document.documentElement).appendChild(newScript);
if(!leaveInPage) {
//Synchronous scripts are executed immediately and can be immediately removed.//Scripts with asynchronous functionality of any type must remain in the page// until complete.document.head.removeChild(newScript);
}
return newScript;
};
</script>
Post a Comment for "How To Use Cloneinto In A Firefox Web Extension?"