{"id":7102,"date":"2014-04-10T21:07:27","date_gmt":"2014-04-11T04:07:27","guid":{"rendered":"http:\/\/blog.mozilla.org\/addons\/?p=7102"},"modified":"2014-04-11T12:15:07","modified_gmt":"2014-04-11T19:15:07","slug":"changes-to-unsafewindow-for-the-add-on-sdk","status":"publish","type":"post","link":"https:\/\/blog.mozilla.org\/addons\/2014\/04\/10\/changes-to-unsafewindow-for-the-add-on-sdk\/","title":{"rendered":"Changes to unsafeWindow for the Add-on SDK"},"content":{"rendered":"<p>In Firefox 30 the Add-on SDK is making a change to the execution environment for content scripts. While this change will not affect most add-ons, some patterns will no longer work. Specifically, content scripts will no longer be able to use <code>unsafeWindow<\/code> or <code>window.wrappedJSObject<\/code> to make JavaScript objects available to content. The more common pattern of accessing objects in the page directly via <code>unsafeWindow<\/code> will continue to work as expected.<\/p>\n<p>What we&#8217;re introducing instead are some new APIs that you can use to share functions and objects with content explicitly. While you&#8217;re working on migrating to these new APIs, there&#8217;s a mechanism you can use to switch your add-on back to the old behavior as a short-term measure.<\/p>\n<h2><a href=\"https:\/\/github.com\/wbamberg\/slaughterhouse\/blob\/master\/blog.md#whos-affected\" name=\"user-content-whos-affected\"><\/a>Who&#8217;s affected?<\/h2>\n<p><em>Add-on SDK-based add-ons that use <code>unsafeWindow<\/code> or <code>window.wrappedJSObject<\/code> in content scripts to share JavaScript objects with the scripts loaded by a web page will be broken by this change.<\/em><\/p>\n<p>By default, content scripts and the scripts loaded into web pages are insulated from each other. They can both see the DOM, but content scripts can&#8217;t access objects defined by page scripts, and page scripts can&#8217;t access objects defined by content scripts:<\/p>\n<pre lang=\"javascript\">\/\/ content-script.js\r\nvar button = document.getElementById(\"show-page-script-var\");\r\nbutton.addEventListener(\"click\", function() {\r\n  \/\/ access object defined in page script\r\n  console.log(window.pageScriptObject.greeting);        \/\/ undefined\r\n}, false);\r\n\r\nwindow.contentScriptObject = {\"greeting\" : \"hello from add-on\"};<\/pre>\n<pre lang=\"javascript\">\/\/ page-script.js\r\nvar button = document.getElementById(\"show-content-script-var\");\r\n\r\nbutton.addEventListener(\"click\", function() {\r\n  \/\/ access object defined by content script\r\n  console.log(window.contentScriptObject.greeting);     \/\/ undefined\r\n}, false);\r\n\r\n\/\/ define object to be accessed by content script\r\nwindow.pageScriptObject = {\"greeting\" : \"hello from web page\"};<\/pre>\n<p>Sometimes a content script wants to break through this insulation, and to do that it can use <code>unsafeWindow<\/code> (or <code>window.wrappedJSObject<\/code>, which is identical):<\/p>\n<pre lang=\"javascript\">\/\/ content-script.js\r\nvar button = document.getElementById(\"show-page-script-var\");\r\nbutton.addEventListener(\"click\", function() {\r\n  \/\/ access object defined in page script\r\n  console.log(unsafeWindow.pageScriptObject.greeting);  \/\/ \"hello from web page\"\r\n}, false);\r\n\r\nunsafeWindow.contentScriptObject = {\"greeting\" : \"hello from add-on\"};<\/pre>\n<pre lang=\"javascript\">\/\/ page-script.js\r\nvar button = document.getElementById(\"show-content-script-var\");\r\n\r\nbutton.addEventListener(\"click\", function() {\r\n  \/\/ access object defined by content script\r\n  console.log(window.contentScriptObject.greeting);     \/\/ \"hello from add-on\"\r\n}, false);\r\n\r\nwindow.pageScriptObject = {\"greeting\" : \"hello from web page\"};<\/pre>\n<p>From Firefox 30 onwards, this mechanism won&#8217;t work any more to share objects from the content script to the page script:<\/p>\n<pre lang=\"javascript\">\/\/ content-script.js\r\nvar button = document.getElementById(\"show-page-script-var\");\r\nbutton.addEventListener(\"click\", function() {\r\n  \/\/ access object defined in page script\r\n  console.log(unsafeWindow.pageScriptObject.greeting);  \/\/ \"hello from web page\"\r\n}, false);\r\n\r\nunsafeWindow.contentScriptObject = {\"greeting\" : \"hello from add-on\"};\r\nunsafeWindow.contentScriptPrimitive = 42;<\/pre>\n<pre lang=\"javascript\">\/\/ page-script.js\r\nvar button = document.getElementById(\"show-content-script-var\");\r\n\r\nbutton.addEventListener(\"click\", function() {\r\n  \/\/ access primitive defined by content script\r\n  console.log(window.contentScriptPrimitive);            \/\/ 42\r\n  \/\/ access object defined by content script\r\n  console.log(window.contentScriptObject.greeting);     \/\/ undefined\r\n}, false);\r\n\r\nwindow.pageScriptObject = {\"greeting\" : \"hello from web page\"};<\/pre>\n<p>Again, the use of <code>unsafeWindow<\/code> to access variables defined in the page script is unaffected, so this change is asymmetric. <em>Content scripts can still access page objects using <code>unsafeWindow<\/code>, but not vice versa.<\/em><\/p>\n<p>Also note that the content script will still be able to share primitive values with the page using <code>unsafeWindow<\/code>, just not objects.<\/p>\n<p>If the page script tries to read a variable defined using <code>unsafeWindow<\/code>, it will get the value <code>undefined<\/code>. If it tries to write to such a variable, the browser will throw an exception with this message: &#8220;Permission denied to access property [name of the property]&#8221;.<\/p>\n<h2>The workaround<\/h2>\n<p>In the short term, there&#8217;s a new option in <code>package.json<\/code> called &#8220;unsafe-content-script&#8221; under the &#8220;permissions&#8221; key. By setting it to <code>true<\/code> you can revert to the old behavior:<\/p>\n<pre><code>\"permissions\": {\r\n  \"unsafe-content-script\": true\r\n}\r\n<\/code><\/pre>\n<p>However, this is only a temporary fix to enable your add-on to keep working while you update to the new APIs. We will deprecate and eventually remove this flag.<\/p>\n<h2>The real fix<\/h2>\n<p>The real fix is to use three new APIs to share objects and functions with web content: <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Components.utils.cloneInto\"><code>cloneInto()<\/code><\/a>, <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Components.utils.exportFunction\"><code>exportFunction()<\/code><\/a>, and <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Components.utils.createObjectIn\"><code>createObjectIn()<\/code><\/a>. All three of these functions are made available to content scripts as globals.<\/p>\n<h3><a href=\"https:\/\/github.com\/wbamberg\/slaughterhouse\/blob\/master\/blog.md#cloneinto\" name=\"user-content-cloneinto\"><\/a>cloneInto()<\/h3>\n<p>You can use <code>cloneInto()<\/code> to clone an object from the content script&#8217;s context into the page script&#8217;s context. <code>cloneInto()<\/code> creates a <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/Guide\/API\/DOM\/The_structured_clone_algorithm\">structured clone<\/a> of the object in the target context, and returns a reference to the clone. You can then assign that to a property of the target window, and the page script can access it:<\/p>\n<pre lang=\"javascript\">\/\/ content-script.js\r\ncontentScriptObject = {\"greeting\" : \"hello from add-on\"};\r\nunsafeWindow.contentScriptObject = cloneInto(contentScriptObject, unsafeWindow);<\/pre>\n<pre lang=\"javascript\">\/\/ page-script.js\r\nvar button = document.getElementById(\"show-content-script-var\");\r\n\r\nbutton.addEventListener(\"click\", function() {\r\n  \/\/ access object defined by content script\r\n  console.log(window.contentScriptObject.greeting);     \/\/ \"hello from add-on\"\r\n}, false);<\/pre>\n<p>You can also assign the reference to an object you&#8217;ve created using <code>createObjectIn()<\/code>:<\/p>\n<pre lang=\"javascript\">var foo = createObjectIn(unsafeWindow, {defineAs: \"foo\"});\r\nvar contentScriptObject = {\"greeting\" : \"hello from add-on\"};\r\nunsafeWindow.foo.contentScriptObject = cloneInto(contentScriptObject, unsafeWindow);<\/pre>\n<pre lang=\"javascript\">\/\/ page-script.js\r\nvar button = document.getElementById(\"show-content-script-var\");\r\n\r\nbutton.addEventListener(\"click\", function() {\r\n  \/\/ access object defined by content script\r\n  console.log(window.foo.contentScriptObject.greeting); \/\/ \"hello from add-on\"\r\n}, false);<\/pre>\n<h3><a href=\"https:\/\/github.com\/wbamberg\/slaughterhouse\/blob\/master\/blog.md#exportfunction\" name=\"user-content-exportfunction\"><\/a>exportFunction()<\/h3>\n<p>You can use <code>exportFunction()<\/code> to expose a function from a content script to a page script. In this way a function defined in a content script can be called by the page script. Any non-native arguments, and the return value, are <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/Guide\/API\/DOM\/The_structured_clone_algorithm\">cloned<\/a> into the page script&#8217;s context.<\/p>\n<p>You can export the function to the target&#8217;s <code>window<\/code> object:<\/p>\n<pre lang=\"javascript\">\/\/ content-script.js\r\nvar salutation = \"hello, \";\r\nfunction greetme(user) {\r\n  return salutation + user;\r\n}\r\n\r\nexportFunction(greetme, unsafeWindow, {defineAs: \"greetme\"});<\/pre>\n<pre lang=\"javascript\">\/\/ page-script.js\r\nvar button = document.getElementById(\"call-content-script-function\");\r\n\r\nbutton.addEventListener(\"click\", function() {\r\n  console.log(window.greetme(\"page script\"));           \/\/ \"hello, page script\"\r\n}, false);<\/pre>\n<p>You can also export the function to an object you&#8217;ve created in the page context using <code>createObjectIn()<\/code>. This code creates a new object in the page&#8217;s context called <code>foo<\/code>, and attaches the <code>greetme()<\/code> function to that object:<\/p>\n<pre lang=\"javascript\">\/\/ content-script.js\r\nvar salutation = \"hello, \";\r\nfunction greetme(user) {\r\n  return salutation + user;\r\n}\r\n\r\nvar foo = createObjectIn(unsafeWindow, {defineAs: \"foo\"});\r\nexportFunction(greetme, foo, {defineAs: \"greetme\"});<\/pre>\n<pre lang=\"javascript\">\/\/ page-script.js\r\nvar button = document.getElementById(\"call-content-script-function\");\r\n\r\nbutton.addEventListener(\"click\", function() {\r\n  console.log(window.foo.greetme(\"page script\"));\r\n}, false);<\/pre>\n<h3><a href=\"https:\/\/github.com\/wbamberg\/slaughterhouse\/blob\/master\/blog.md#createobjectin\" name=\"user-content-createobjectin\"><\/a>createObjectIn()<\/h3>\n<p>You can use <code>createObjectIn()<\/code> to create a new object in the page script&#8217;s context. You can then export objects and functions to that object, instead of the target scope&#8217;s global window. See the previous sections for some examples of this.<\/p>\n<h3><a href=\"https:\/\/github.com\/wbamberg\/slaughterhouse\/blob\/master\/blog.md#structured-cloning\" name=\"user-content-structured-cloning\"><\/a>Structured cloning<\/h3>\n<p>These functions use the <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/Guide\/API\/DOM\/The_structured_clone_algorithm\">structured clone algorithm<\/a> to clone objects into the page context. This is more capable than JSON serialization, but still has some serious limitations. Most notably, you can&#8217;t clone functions, so you can&#8217;t export functions that take functions as arguments (such as callbacks) and functions that return functions. However, we&#8217;re working on adding better support at least for common patterns like callbacks.<\/p>\n<h2><a href=\"https:\/\/github.com\/wbamberg\/slaughterhouse\/blob\/master\/blog.md#more-details\" name=\"user-content-more-details\"><\/a>More details<\/h2>\n<p>To learn more about the new APIs see the reference documentation:<\/p>\n<ul>\n<li><a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Components.utils.cloneInto\"><code>cloneInto()<\/code><\/a><\/li>\n<li><a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Components.utils.exportFunction\"><code>exportFunction()<\/code><\/a><\/li>\n<li><a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Components.utils.createObjectIn\"><code>createObjectIn()<\/code><\/a><\/li>\n<\/ul>\n<p>If you have any questions, please ask in the <a href=\"https:\/\/groups.google.com\/forum\/#%21forum\/mozilla-labs-jetpack\">Jetpack mailing list<\/a> or the #jetpack channel on irc.mozilla.org.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In Firefox 30 the Add-on SDK is making a change to the execution environment for content scripts. While this change will not affect most add-ons, some patterns will no longer &hellip; <a class=\"go\" href=\"https:\/\/blog.mozilla.org\/addons\/2014\/04\/10\/changes-to-unsafewindow-for-the-add-on-sdk\/\">Read more<\/a><\/p>\n","protected":false},"author":303,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[388,44,121,588,742],"tags":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v22.5 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Changes to unsafeWindow for the Add-on SDK - Mozilla Add-ons Community Blog<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/blog.mozilla.org\/addons\/2014\/04\/10\/changes-to-unsafewindow-for-the-add-on-sdk\/\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Will Bamberg\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"6 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/blog.mozilla.org\/addons\/2014\/04\/10\/changes-to-unsafewindow-for-the-add-on-sdk\/\",\"url\":\"https:\/\/blog.mozilla.org\/addons\/2014\/04\/10\/changes-to-unsafewindow-for-the-add-on-sdk\/\",\"name\":\"Changes to unsafeWindow for the Add-on SDK - Mozilla Add-ons Community Blog\",\"isPartOf\":{\"@id\":\"https:\/\/blog.mozilla.org\/addons\/#website\"},\"datePublished\":\"2014-04-11T04:07:27+00:00\",\"dateModified\":\"2014-04-11T19:15:07+00:00\",\"author\":{\"@id\":\"https:\/\/blog.mozilla.org\/addons\/#\/schema\/person\/bb34f381b59245c1d814a91bb4d3c73f\"},\"breadcrumb\":{\"@id\":\"https:\/\/blog.mozilla.org\/addons\/2014\/04\/10\/changes-to-unsafewindow-for-the-add-on-sdk\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/blog.mozilla.org\/addons\/2014\/04\/10\/changes-to-unsafewindow-for-the-add-on-sdk\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/blog.mozilla.org\/addons\/2014\/04\/10\/changes-to-unsafewindow-for-the-add-on-sdk\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/blog.mozilla.org\/addons\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Changes to unsafeWindow for the Add-on SDK\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/blog.mozilla.org\/addons\/#website\",\"url\":\"https:\/\/blog.mozilla.org\/addons\/\",\"name\":\"Mozilla Add-ons Community Blog\",\"description\":\"\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/blog.mozilla.org\/addons\/?s={search_term_string}\"},\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/blog.mozilla.org\/addons\/#\/schema\/person\/bb34f381b59245c1d814a91bb4d3c73f\",\"name\":\"Will Bamberg\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/blog.mozilla.org\/addons\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/576b94c81f8ad97e58d63f12e5fdb00c?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/576b94c81f8ad97e58d63f12e5fdb00c?s=96&d=mm&r=g\",\"caption\":\"Will Bamberg\"},\"description\":\"Will is a technical writer working on MDN.\",\"url\":\"https:\/\/blog.mozilla.org\/addons\/author\/wbambergmozilla-com\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Changes to unsafeWindow for the Add-on SDK - Mozilla Add-ons Community Blog","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/blog.mozilla.org\/addons\/2014\/04\/10\/changes-to-unsafewindow-for-the-add-on-sdk\/","twitter_misc":{"Written by":"Will Bamberg","Est. reading time":"6 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/blog.mozilla.org\/addons\/2014\/04\/10\/changes-to-unsafewindow-for-the-add-on-sdk\/","url":"https:\/\/blog.mozilla.org\/addons\/2014\/04\/10\/changes-to-unsafewindow-for-the-add-on-sdk\/","name":"Changes to unsafeWindow for the Add-on SDK - Mozilla Add-ons Community Blog","isPartOf":{"@id":"https:\/\/blog.mozilla.org\/addons\/#website"},"datePublished":"2014-04-11T04:07:27+00:00","dateModified":"2014-04-11T19:15:07+00:00","author":{"@id":"https:\/\/blog.mozilla.org\/addons\/#\/schema\/person\/bb34f381b59245c1d814a91bb4d3c73f"},"breadcrumb":{"@id":"https:\/\/blog.mozilla.org\/addons\/2014\/04\/10\/changes-to-unsafewindow-for-the-add-on-sdk\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/blog.mozilla.org\/addons\/2014\/04\/10\/changes-to-unsafewindow-for-the-add-on-sdk\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/blog.mozilla.org\/addons\/2014\/04\/10\/changes-to-unsafewindow-for-the-add-on-sdk\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/blog.mozilla.org\/addons\/"},{"@type":"ListItem","position":2,"name":"Changes to unsafeWindow for the Add-on SDK"}]},{"@type":"WebSite","@id":"https:\/\/blog.mozilla.org\/addons\/#website","url":"https:\/\/blog.mozilla.org\/addons\/","name":"Mozilla Add-ons Community Blog","description":"","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/blog.mozilla.org\/addons\/?s={search_term_string}"},"query-input":"required name=search_term_string"}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/blog.mozilla.org\/addons\/#\/schema\/person\/bb34f381b59245c1d814a91bb4d3c73f","name":"Will Bamberg","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/blog.mozilla.org\/addons\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/576b94c81f8ad97e58d63f12e5fdb00c?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/576b94c81f8ad97e58d63f12e5fdb00c?s=96&d=mm&r=g","caption":"Will Bamberg"},"description":"Will is a technical writer working on MDN.","url":"https:\/\/blog.mozilla.org\/addons\/author\/wbambergmozilla-com\/"}]}},"_links":{"self":[{"href":"https:\/\/blog.mozilla.org\/addons\/wp-json\/wp\/v2\/posts\/7102"}],"collection":[{"href":"https:\/\/blog.mozilla.org\/addons\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.mozilla.org\/addons\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.mozilla.org\/addons\/wp-json\/wp\/v2\/users\/303"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.mozilla.org\/addons\/wp-json\/wp\/v2\/comments?post=7102"}],"version-history":[{"count":0,"href":"https:\/\/blog.mozilla.org\/addons\/wp-json\/wp\/v2\/posts\/7102\/revisions"}],"wp:attachment":[{"href":"https:\/\/blog.mozilla.org\/addons\/wp-json\/wp\/v2\/media?parent=7102"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.mozilla.org\/addons\/wp-json\/wp\/v2\/categories?post=7102"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.mozilla.org\/addons\/wp-json\/wp\/v2\/tags?post=7102"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}