{"id":5317,"date":"2012-09-12T10:40:05","date_gmt":"2012-09-12T17:40:05","guid":{"rendered":"http:\/\/blog.mozilla.org\/addons\/?p=5317"},"modified":"2012-09-12T10:40:05","modified_gmt":"2012-09-12T17:40:05","slug":"introducing-page-mods-attachto","status":"publish","type":"post","link":"https:\/\/blog.mozilla.org\/addons\/2012\/09\/12\/introducing-page-mods-attachto\/","title":{"rendered":"Introducing page-mod&#8217;s &#8220;attachTo&#8221;"},"content":{"rendered":"<p><em>This post is written by guest blogger and SDK developer <a href=\"https:\/\/github.com\/ZER0\/\" title=\"ZER0\">Matteo Ferretti<\/a>.<\/em><\/p>\n<p>We&#8217;re pleased to announce a new feature for the <code>page-mod<\/code> module that&#8217;s coming in Add-on SDK 1.11: the <code>attachTo<\/code> option!<\/p>\n<h2>What PageMod does<\/h2>\n<p>With PageMod you can dynamically modify the content of certain pages: the add-on developer supplies a set of rules to select the desired subset of web pages based on their URL.<\/p>\n<p>Here the first important thing: a PageMod affects <em>any<\/em> document whose URL matches the rules supplied. Any. So if your rule matches a specific hostname and path, and the topmost document that satisfies the rule includes ten iframes using a relative URL, then your PageMod is applied eleven times. And that&#8217;s probably not what you expected.<\/p>\n<p>But that&#8217;s not all: <a href=\"https:\/\/addons.mozilla.org\/en-US\/developers\/docs\/sdk\/latest\/packages\/addon-kit\/page-mod.html\"><em>&#8220;A page mod does not modify its pages until those pages are loaded or reloaded. In other words, if your add-on is loaded while the user&#8217;s browser is open, the user will have to reload any open pages that match the mod for the mod to affect them&#8221;<\/em><\/a>. That&#8217;s annoying, isn&#8217;t it? <\/p>\n<h2>Workarounds<\/h2>\n<h3>Excluding frames<\/h3>\n<p>Before the <code>attachTo<\/code> option, the only way to attach a PageMod to only the topmost document was\u2026 to not use PageMod. Instead, use the <code>tabs<\/code> module to attach a content script to the desired subset of pages, simulating partially \u2013 and manually \u2013 what PageMod does. Unfortunately, beside the boilerplate code, there are some downsides: <code>tabs<\/code> doesn&#8217;t support <code>contentStyle*<\/code>, and if you want  to target the frames instead of the topmost document, you can&#8217;t.<\/p>\n<p>Alternatively, you can still use PageMod, and in the content script have a check to figure out if the document is loaded in a frame or not, like testing <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/DOM\/window.frameElement\">window.frameElement<\/a> for example.<br \/>\nBut this means that the PageMod has to be attached to and executed in every document, which can have a bad effect on memory consumption.<\/p>\n<h3>Attaching to existing documents<\/h3>\n<p>To attach the PageMod to existing documents, you can have a similar approach, this time using both <code>PageMod<\/code> and <code>tabs<\/code> together. You basically create your regular <code>PageMod<\/code>, and iterating the tabs already opened with <code>tabs<\/code> module to attach the same content script, if the document&#8217;s URL satisfies the rule specified.<\/p>\n<p>However, as we already saw, a content script attached in that way is not exactly equivalent to the PageMod&#8217;s one: you can&#8217;t use <code>contentStyle*<\/code>, and the content script will be attached only to the tab&#8217;s document, ignoring any sub frames.<\/p>\n<h2>attachTo FTW!<\/h2>\n<p>This new option addresses all these issues.<br \/>\nWith <code>attachTo<\/code>, if you want to attach your PageMod only to the topmost document, you can simply have:<\/p>\n<pre lang=\"javascript\">var { PageMod } = require(\"page-mod\");\r\nvar script = require(\"self\").data.url(\"content.js\");\r\n\r\nPageMod({\r\n  include: \"*\",\r\n  contentScriptFile: script,\r\n  attachTo: [\"top\"]\r\n});\r\n<\/pre>\n<p>Likewise, if you want to attach your PageMod to all documents \u2013 topmost, frames, and existing ones &#8211; you will have:<\/p>\n<pre lang=\"javascript\">var { PageMod } = require(\"page-mod\");\r\nvar script = require(\"self\").data.url(\"content.js\");\r\n\r\nPageMod({\r\n  include: \"*\",\r\ncontentScriptFile: script,\r\nattachTo: [\"top\", \"frames\", \"existing\"]\r\n});\r\n<\/pre>\n<h2>But wait, there&#8217;s more!<\/h2>\n<p>As you saw, <code>attachTo<\/code> accepts an array of strings with three possible values: <code>\"existing\"<\/code>, <code>\"top\"<\/code>, and <code>\"frames\"<\/code>. You can combine them, to open a whole new set of behaviors for PageMods. Here&#8217;s a comprehensive list:<\/p>\n<pre lang=\"javascript\">\/\/ Applied to: new top documents\r\n\/\/ Not applied to: frames' documents; existing documents\r\nattachTo: [\"top\"]\r\n\r\n\/\/ Applied to: new frames' documents\r\n\/\/ Not applied to: top documents; existing documents\r\nattachTo: [\"frames\"]\r\n\r\n\/\/ Applied to: new top and frames' documents\r\n\/\/ Not applied to: existing documents\r\n\/\/ NOTE: it's equivalent to omit `attachTo` option at all\r\nattachTo: [\"top\", \"frames\"]\r\n\r\n\/\/ Applied to: all top documents\r\n\/\/ Not applied to: frames' documents\r\nattachTo: [\"top\", \"existing\"]\r\n\r\n\/\/ Applied to: all frames' documents\r\n\/\/ Not applied to: top documents\r\nattachTo: [\"frames\", \"existing\"]\r\n\r\n\/\/ Applied to: all documents\r\nattachTo: [\"top\", \"frames\", \"existing\"]\r\n<\/pre>\n<p>As you probably noticed, <code>\"existing\"<\/code> alone is not listed. That&#8217;s because <code>attachTo<\/code>, when specified, requires at least one of <code>\"top\"<\/code> and <code>\"frames\"<\/code> to determine the document target.<\/p>\n<h2>Style-challenged<\/h2>\n<p>The current implementation of <code>contentStyle*<\/code> is independent from the <code>contentScript*<\/code> and therefore <code>attachTo<\/code>. Because the style is registered as a global stylesheet, it will always be applied to all documents, whatever <code>attachTo<\/code> specifies. We&#8217;re working on addressing this difference. If you are interested, you can have a look at our <a href=\"https:\/\/github.com\/mozilla\/addon-sdk\/wiki\/contentStyle-issues\">contentStyle issues<\/a> wiki page.<\/p>\n<h2>Cleaning up<\/h2>\n<p>I personally believe that this feature is a great step forward for PageMods. But &#8220;with great power comes great responsibility&#8221;: a PageMod that can be attached to existing documents means that you could have a scenario where it is attached twice: a user could install your add-on, disable it and then re-enable it.<\/p>\n<p>So play safe! If you add some DOM elements to a page, check they&#8217;re not already there, or clean up what you have done when your PageMod is destroyed.<\/p>\n<p>If this sounds complicated, don&#8217;t worry! In my next post I&#8217;ll describe how to clean up your PageMod&#8217;s changes nicely.<\/p>\n<h2>Try it out<\/h2>\n<p>If you&#8217;re interested in trying this feature out now, check out the <code>master<\/code> branch of the addon-sdk repository:<\/p>\n<pre>git clone git:\/\/github.com\/mozilla\/addon-sdk.git<\/pre>\n<p>Or download the current master&#8217;s zip archive:<\/p>\n<p><a href=\"https:\/\/github.com\/mozilla\/addon-sdk\/zipball\/master\"><\/p>\n<pre>https:\/\/github.com\/mozilla\/addon-sdk\/zipball\/master<\/pre>\n<p><\/a><\/p>\n<h2>Bug reference<\/h2>\n<p><a href=\"http:\/\/bugzil.la\/708190\">Bug 708190 &#8211; page-mod should be able to apply to existing tabs automatically<\/a><\/p>\n<p><a href=\"http:\/\/bugzil.la\/684047\">Bug 684047 &#8211; page-mod: contentScripts being injected in all frames of a page<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>This post is written by guest blogger and SDK developer Matteo Ferretti. We&#8217;re pleased to announce a new feature for the page-mod module that&#8217;s coming in Add-on SDK 1.11: the &hellip; <a class=\"go\" href=\"https:\/\/blog.mozilla.org\/addons\/2012\/09\/12\/introducing-page-mods-attachto\/\">Read more<\/a><\/p>\n","protected":false},"author":303,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[7117,44,588,742],"tags":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v22.5 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Introducing page-mod&#039;s &quot;attachTo&quot; - 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\/2012\/09\/12\/introducing-page-mods-attachto\/\" \/>\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=\"4 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/blog.mozilla.org\/addons\/2012\/09\/12\/introducing-page-mods-attachto\/\",\"url\":\"https:\/\/blog.mozilla.org\/addons\/2012\/09\/12\/introducing-page-mods-attachto\/\",\"name\":\"Introducing page-mod's \\\"attachTo\\\" - Mozilla Add-ons Community Blog\",\"isPartOf\":{\"@id\":\"https:\/\/blog.mozilla.org\/addons\/#website\"},\"datePublished\":\"2012-09-12T17:40:05+00:00\",\"dateModified\":\"2012-09-12T17:40:05+00:00\",\"author\":{\"@id\":\"https:\/\/blog.mozilla.org\/addons\/#\/schema\/person\/bb34f381b59245c1d814a91bb4d3c73f\"},\"breadcrumb\":{\"@id\":\"https:\/\/blog.mozilla.org\/addons\/2012\/09\/12\/introducing-page-mods-attachto\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/blog.mozilla.org\/addons\/2012\/09\/12\/introducing-page-mods-attachto\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/blog.mozilla.org\/addons\/2012\/09\/12\/introducing-page-mods-attachto\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/blog.mozilla.org\/addons\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Introducing page-mod&#8217;s &#8220;attachTo&#8221;\"}]},{\"@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":"Introducing page-mod's \"attachTo\" - 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\/2012\/09\/12\/introducing-page-mods-attachto\/","twitter_misc":{"Written by":"Will Bamberg","Est. reading time":"4 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/blog.mozilla.org\/addons\/2012\/09\/12\/introducing-page-mods-attachto\/","url":"https:\/\/blog.mozilla.org\/addons\/2012\/09\/12\/introducing-page-mods-attachto\/","name":"Introducing page-mod's \"attachTo\" - Mozilla Add-ons Community Blog","isPartOf":{"@id":"https:\/\/blog.mozilla.org\/addons\/#website"},"datePublished":"2012-09-12T17:40:05+00:00","dateModified":"2012-09-12T17:40:05+00:00","author":{"@id":"https:\/\/blog.mozilla.org\/addons\/#\/schema\/person\/bb34f381b59245c1d814a91bb4d3c73f"},"breadcrumb":{"@id":"https:\/\/blog.mozilla.org\/addons\/2012\/09\/12\/introducing-page-mods-attachto\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/blog.mozilla.org\/addons\/2012\/09\/12\/introducing-page-mods-attachto\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/blog.mozilla.org\/addons\/2012\/09\/12\/introducing-page-mods-attachto\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/blog.mozilla.org\/addons\/"},{"@type":"ListItem","position":2,"name":"Introducing page-mod&#8217;s &#8220;attachTo&#8221;"}]},{"@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\/5317"}],"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=5317"}],"version-history":[{"count":0,"href":"https:\/\/blog.mozilla.org\/addons\/wp-json\/wp\/v2\/posts\/5317\/revisions"}],"wp:attachment":[{"href":"https:\/\/blog.mozilla.org\/addons\/wp-json\/wp\/v2\/media?parent=5317"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.mozilla.org\/addons\/wp-json\/wp\/v2\/categories?post=5317"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.mozilla.org\/addons\/wp-json\/wp\/v2\/tags?post=5317"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}