Developing a Snippet Template¶
The following document explains how to develop and test a snippet template for use in the Snippets Service. It is assumed that you’ve already filed a bug in the Snippets Campaign component in Bugzilla to inform the snippets team that you want to create a snippet template.
Setting up your development environment¶
In order to develop a snippet template you need to setup have a snippets server. You can either setup the full Snippets Service or the Simple Snippets Server for Template Development. The latter is strongly recommended and it will be all you’ll need for template development. It needs no configuration and the code served to the browsers is the same the original Snippets Service serves. The major difference is that all the Client Matching rules are ignored. All snippets are served all the time and on every request which makes this ideal for template development.
Template or snippet?¶
A snippet is an individual piece of code that is displayed on about:home to users. Snippets are, in turn, generated from templates, which contain code shared between multiple snippets. Templates are useful for snippets where you want an admin to be able to substitute different text for localization or other customization purposes. Templates are written in the Jinja2 template language.
How are snippets loaded?¶
When a user requests snippets from the snippets service, the service finds all snippets that match the user’s client, generates the code for each snippet, and concatenates all of the code together, along with some default CSS and JavaScript.
When the user views about:home, this chunk of code is injected into the page.
The default CSS hides all tags with the class snippet
. Once injected, the
default JavaScript selects a snippet to show and un-hides the hidden tag.
Finally, a show_snippet
event is triggered on the snippet
tag to signal
to the snippet that it is being displayed.
Snippet requirements¶
All snippets must have an outermost tag with the class
snippet
, and no tags outside of that exceptstyle
orscript
tags:<div class="snippet"> <img class="icon" src="some-data-uri" /> <p>Foo bar baz biz</p> </div>
Snippet code must be valid XML. This includes:
Closing all tags, and using self-closing tags like
<br />
.Avoiding HTML entities; use their numeric entities instead, e.g.
»
instead of»
.Using
CDATA
comments within allscript
tags:<script> // <![CDATA[ alert('LOOK BEHIND YOU'); // ]]> </script>
Avoid loading remote resources if possible. For images and other media that you must include, use data URIs to include them directly in your snippet code.
Due to performance concerns, avoid going over 500 kilobytes in filesize for a snippet. Snippets over 500 kilobytes large must be cleared with the development team first.
Helpers¶
Accessing snippet id¶
To get the snippet id within a snippet template use snippet_id Jinja2 variable like this:
<div class="snippet"> This is snippet id {{ snippet_id }}. </div>
The syntax in a snippet is slightly different and uses square brackets [[snippet_id]]. Here is an example that uses the Raw Template:
<div class="snippet"> This is snippet id [[snippet_id]]. </div>Warning
Beware that in this case spacing matters and [[ snippet_id ]] will not work.
Custom Metric Pings¶
Snippet events can be captured and sent to our metrics server. By default snippet impressions get captured and sent to our metrics server tagged as impression. Clicks on <a> elements with defined href get captured too and get sent back as click.
Snippet developers can customize the metric name of clicks by setting the metric data attribute on the link. For example clicking on the link of the following snippet:
<div class="snippet"> <p class="message"> Click this <a href="http://example.com" data-metric="custom-click">link!</a> </p> </div>
will send back a custom-click ping instead of a click ping.
Warning
Avoid setting up event listeners on links for click events and manually sending metric pings, or pings may get sent both by your click handler and the global click handler resulting in inaccurate numbers.
In addition to impressions and clicks snippet developers can send custom pings to capture interactions using the sendMetric function like this:
<!-- Use Raw Template to try this out --> <div class="snippet" id="ping-snippet-[[snippet_id]]"> <p class="message">Foo!</p> </div> <script type="text/javascript"> //<![CDATA[ (function() { var snippet = document.getElementById('ping-snippet-[[snippet_id]]'); snippet.addEventListener('show_snippet', function() { (function () { var callback = function() { alert('Success!'); }; var metric_name = 'success-ping-[[snippet_id]]'; sendMetric(metric_name, callback); })(); }, false); })(); //]]> </script>Note
Callback function is optional.
Note
Only 10% of the pings reach the server. We sample at the browser level. See sendMetric function for implementation details.
Using MozUITour¶
Snippets and snippet templates can use MozUiTour to interact with the browser. Developer can directly use the following MozUITour functions:
Mozilla.UITour.showHighlight
Mozilla.UITour.hideHighlight
Mozilla.UITour.showMenu
Mozilla.UITour.hideMenu
Mozilla.UITour.getConfiguration
Mozilla.UITour.setConfiguration
For example to determine whether Firefox is the default browser can you use the following function in a snippet:
function isDefault (yesDefault, noDefault) { Mozilla.UITour.getConfiguration('appinfo', function(config) { if (config && config.defaultBrowser === true) { firefoxIsDefault(); } else if (config && config.defaultBrowser === false) { firefoxIsNotDefault(); } else { firefoxIsDefault(); } }); }
You can even use the low level MozUITour functions:
_sendEvent
_generateCallbackID
_waitForCallback
to trigger more events. For example to trigger Firefox Accounts:
var fire_event = function() { var event = new CustomEvent( 'mozUITour', { bubbles: true, detail: { action:'showFirefoxAccounts', data: {}}} ); document.dispatchEvent(event); };
Snippet Block List¶
Snippets can be prevented from showing using a block list. By default the block list is empty and the intention is to allow users to block specific snippets from showing by taking an action. Snippet service automatically assigns the block functionality to all elements of snippet with class block-snippet-button. For example a disruptive snippet can include a special Do not display again link that adds the snippet into the block list:
<!-- Use Raw Template to try this out --> <div class="snippet" id="block-snippet-[[snippet_id]]"> Foo! <a href="#" class="block-snippet-button">Do not show again</a> </div>
By default the system reports blocked snippets with the snippet-blocked metric. You can customize the reported metric with the data-metric attribute like this:
<button type="button" data-metric="snippet-scene2-blocked" class="block-snippet-button"> Remove this snippet </button>
If you need more control you can directly access the low-level function addToBlockList:
<!-- Use Raw Template to try this out --> <div class="snippet" id="block-snippet-[[snippet_id]]"> Foo! <a href="#" id="block-snippet-link">Do not show again</a> </div> <script type="text/javascript"> //<![CDATA[ (function() { var snippet = document.getElementById('block-snippet-[[snippet_id]]'); snippet.addEventListener('show_snippet', function() { (function () { var link = document.getElementById('block-snippet-link'); link.onclick = function() { // Add currently showing snippet to block list addToBlockList(); window.location.reload(); } })(); }, false); })(); //]]> </script>Note
In this case we don’t utilize the special block-snippet-button class.
More low level functions are popFromBlockList and getBlockList.
Function addToBlockList will by default add to block list the campaign of the current snippet thus preventing all snippets -even the ones created in the future- with the same campaign name to get blocked. This is particularly useful when we do A/B testing. A user who blocked a variation of a snippet will not see any of the variations either, as long as they share the same snippet campaign.
If there’s no campaign set for showing snippet addToBlockList will block the ID from the snippet.
The function also accepts an argument to explicitly set the blocking value:
.. code-html::
addToBlockList('foo-bar');
Will add foo-bar to block list instead of the showing snippet’s campaign or ID.
In bug 1172579 close button assets are provided to build a image button in your snippet. Refer to the simple snippet code on how to do this.
Testing¶
Once your snippet is done and ready for testing, you can use the
snippet-switcher add-on to set
the host for your about:home snippets to point to
https://snippets.allizom.org
or http://localhost:8000
, depending on
which server you are using for development.
Alternatively to using the add-on you can change the browser.aboutHomeSnippets.updateUrl perf from about:config to point to your server. For example
http://localhost:8000/%STARTPAGE_VERSION%/%NAME%/%VERSION%/%APPBUILDID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/
If you are using the staging server, the developer who set up your account and snippet should give you instructions on a Name value to use in the add-on’s settings in order to view your snippet specifically.
With the add-on installed or the perf change made, your about:home should load the latest snippet code from your local snippets instance (after a short delay). If the code doesn’t seem to update, try force-refreshing with Cmd-Shift-R or Ctrl-Shift-R and deleting local snippet storage by typing in a web console:
gSnippetsMap.clear()
What versions of Firefox should I test?¶
Depending on the complexity of your snippet, you should choose the oldest reasonable version of Firefox you want to support for your snippet, and test roughly every other version from that up until the latest released Firefox, and probably Nightly as well.
So, for example, if you wanted to support Firefox 26 and up, and the latest version was Firefox 30, you’d test Firefox 26, 28, 30, and Nightly.
What should I test for?¶
Basic functionality of your snippet. Make sure it works as you expect it to do.
Ensure that your snippet does not interfere with other snippets. The staging server has a normal text+icon snippet that is sent to all clients, which will help you ensure that the normal snippet can be shown without being altered by your snippet.
Ensure that your snippet can run alongside multiple instances of itself.
Ensure that the normal about:home functionality, such as the search box, links at the bottom, and session restore function properly.
Code review¶
There is a snippets Github repo that keeps track of the code for snippets we’ve run. Once your snippet is finished, you should submit a pull request to the snippets repo adding your snippet or template code for a code review. A snippets developer should respond with a review or direct your PR to the right person for review. If your snippet is already on the staging server, include the URL for editing it to make it easier for the reviewer to test it.