From ea02f058146eb8f49e969d8f657c1925cf81e2f9 Mon Sep 17 00:00:00 2001 From: Libor Polčák Date: Jun 04 2020 14:41:31 +0000 Subject: Handle wrapping of canvas accessed through iframes This fixes #34 In environment affected by Firefox bug 1267027 (see also #25), we do not wrap HTMLIFrameElement. --- diff --git a/common/code_builders.js b/common/code_builders.js index 159ffad..86c2316 100644 --- a/common/code_builders.js +++ b/common/code_builders.js @@ -32,19 +32,31 @@ function enclose_wrapping(code, ...args) { } /** + * Create wrapping that might be IIFE or a function that is immediately called and also available + * for future. + */ +function enclose_wrapping2(code, name, params, call_with_window) { + if (name === undefined) { + return enclose_wrapping(code); + } + return `function ${name}(${params}) {${code}} + ${name}(${call_with_window ? "window" : ""});` +} + +/** * This function create code (as string) that creates code that can be used to inject (or overwrite) * a function in the page context. */ function define_page_context_function(wrapper) { var originalF = wrapper["original_function"] || `${wrapper.parent_object}.${wrapper.parent_object_property}`; - return enclose_wrapping(`var originalF = ${originalF}; + return enclose_wrapping2(`var originalF = ${originalF}; var replacementF = function(${wrapper.wrapping_function_args}) { ${wrapper.wrapping_function_body} }; - ${wrapper.parent_object}.${wrapper.parent_object_property} = replacementF; + ${wrapper.replace_original_function ? wrapper.original_function : `${wrapper.parent_object}.${wrapper.parent_object_property}`} = replacementF; original_functions[replacementF.toString()] = originalF.toString(); ${wrapper.post_replacement_code || ''} - `); + `, wrapper.wrapping_code_function_name, wrapper.wrapping_code_function_params, wrapper.wrapping_code_function_call_window); } /** @@ -56,12 +68,36 @@ function generate_assign_function_code(code_spec_obj) { } /** + * This function wraps object properties using Object.defineProperties. + */ +function generate_object_properties(code_spec_obj) { + var code = ""; + for (assign of code_spec_obj.wrapped_objects) { + code += `var ${assign.wrapped_name} = ${assign.original_name};`; + } + code += ` + Object.defineProperties( + ${code_spec_obj.parent_object}, + { + ${code_spec_obj.parent_object_property}: {` + for (wrap_spec of code_spec_obj.wrapped_properties) { + code += `${wrap_spec.property_name}: ${wrap_spec.property_value}`; + } + code += ` + } + } + );`; + return code; +} + +/** * This function builds the wrapping code. */ var build_code = function(wrapper, ...args) { var post_wrapping_functions = { function_define: define_page_context_function, function_export: generate_assign_function_code, + object_properties: generate_object_properties, }; var code = ""; for (wrapped of wrapper.wrapped_objects) { diff --git a/common/ffbug1267027.js b/common/ffbug1267027.js index 4575549..f1b3b68 100644 --- a/common/ffbug1267027.js +++ b/common/ffbug1267027.js @@ -49,6 +49,16 @@ function generate_assign_function_code_ffbug(code_spec_obj) { } /** + * This function wraps object properties using Object.defineProperties. Supporting + * code for dealing with bug https://bugzilla.mozilla.org/show_bug.cgi?id=1267027. + + */ +function generate_object_properties_ffbug(code_spec_obj) { + console.log(`Generate_object_properties for bug 1267027 not defined. Please go to https://bugzilla.mozilla.org/show_bug.cgi?id=1267027 and report that you are affected by the bug. Affected wrapping: ${code_spec_obj.parent_object}.${code_spec_obj.parent_object_property}`); + return ""; +} + +/** * Alternative definition of the build_code function. * * FIXME:this code needs improvements, see bug #25 @@ -57,6 +67,7 @@ function build_code_ffbug(wrapper, ...args) { var post_wrapping_functions = { function_define: define_page_context_function_ffbug, function_export: generate_assign_function_code_ffbug, + object_properties: generate_object_properties_ffbug, }; var code = ""; for (wrapped of wrapper.wrapped_objects) { diff --git a/common/wrappingS-H-C.js b/common/wrappingS-H-C.js index 536a733..948fc2b 100644 --- a/common/wrappingS-H-C.js +++ b/common/wrappingS-H-C.js @@ -23,6 +23,68 @@ * Create private namespace */ (function() { + + function create_post_wrappers(parent_object) { + return [ + { + code_type: "object_properties", + parent_object: parent_object, + parent_object_property: "contentWindow", + wrapped_objects: [ + { + original_name: "HTMLIFrameElement.prototype.__lookupGetter__('contentWindow')", + wrapped_name: "cw", + }, + ], + wrapped_properties: [ + { + property_name: "get", + property_value: ` + function() { + var parent=cw.call(this); + try { + parent.HTMLCanvasElement; + } + catch(d) { + return; // HTMLIFrameElement.contentWindow properties could not be accessed anyway + } + wrapping(parent); + return parent; + }`, + }, + ], + }, + { + code_type: "object_properties", + parent_object: parent_object, + parent_object_property: "contentDocument", + wrapped_objects: [ + { + original_name: "HTMLIFrameElement.prototype.__lookupGetter__('contentDocument')", + wrapped_name: "cd", + }, + ], + wrapped_properties: [ + { + property_name: "get", + property_value: ` + function() { + var parent=cw.call(this); + try{ + parent.HTMLCanvasElement; + } + catch(d) { + return; // HTMLIFrameElement.contentDocument properties could not be accessed anywaya + } + wrapping(parent); + return cd.call(this); + }`, + }, + ], + }, + ]; + } + var wrappers = [ { parent_object: "HTMLCanvasElement.prototype", @@ -34,24 +96,36 @@ } ], helping_code: "", + wrapping_code_function_name: "wrapping", + wrapping_code_function_params: "parent", + wrapping_code_function_call_window: true, + original_function: "parent.HTMLCanvasElement.prototype.toDataURL", + replace_original_function: true, wrapping_function_args: "...args", wrapping_function_body: ` var ctx = this.getContext("2d"); ctx.fillStyle = "white"; ctx.fillRect(0, 0, this.width, this.height); return origToDataURL.call(this, ...args); - ` + `, + post_wrapping_code: create_post_wrappers("HTMLIFrameElement.prototype"), }, { parent_object: "CanvasRenderingContext2D.prototype", parent_object_property: "getImageData", wrapped_objects: [], helping_code: "", + wrapping_code_function_name: "wrapping", + wrapping_code_function_params: "parent", + wrapping_code_function_call_window: true, + original_function: "parent.CanvasRenderingContext2D.prototype.getImageData", + replace_original_function: true, wrapping_function_args: "sx, sy, sw, sh", wrapping_function_body: ` var fake_image = new ImageData(sw, sh); return fake_image; - ` + `, + post_wrapping_code: create_post_wrappers("HTMLIFrameElement.prototype"), }, { parent_object: "HTMLCanvasElement.prototype", @@ -63,13 +137,19 @@ } ], helping_code: "", + wrapping_code_function_name: "wrapping", + wrapping_code_function_params: "parent", + wrapping_code_function_call_window: true, + original_function: "parent.HTMLCanvasElement.prototype.toBlob", + replace_original_function: true, wrapping_function_args: "callback, mimeType, qualityArgument", wrapping_function_body: ` var fake = document.createElement("canvas"); fake.setAttribute("width", this.width); fake.setAttribute("height", this.height); return origToBlob.call(fake, callback, mimeType, qualityArgument); - ` + `, + post_wrapping_code: create_post_wrappers("HTMLIFrameElement.prototype"), } ] add_wrappers(wrappers);