File:  [mozdev] / greasemonkey / www / authoring.html
Revision 1.16: download - view: text, annotated - select for diffs - revision graph
Fri Dec 30 22:24:15 2005 UTC (14 years, 4 months ago) by boogs
Branches: MAIN
CVS tags: HEAD
*** empty log message ***

<!-- MAIN CONTENT -->
<h5 class="page-header"><a id="content" name="content">Writing User 
Scripts</a></h5>

<p>Mark Pilgrim has given the Greasemonkey community <a 
href="http://diveintogreasemonkey.org/">Dive into Greasemonkey</a>, the best 
reference imaginable. The stuff below might be slightly more up-to-date.</p>

<hr/>

<p>You can write your very own shiny user script with just a few steps:</p>

<h6>1. Create the file</h6>
<p>Open a new text file in your favorite editor and throw some javascript in 
there. Note that there are <a 
href="http://www.amazon.com/exec/obidos/tg/detail/-/1565923928/103-6951853-41030
63?v=glance">many</a> good <a 
href="http://www.google.com/search?q=dhtml+tutorial">resources</a> available if 
you're not sure exactly what javascript you need.</p> 
<p>For example, you might create this simple script:</p>
<pre><code>alert("Hello, world!");</code></pre>

<h6>2. Save the file</h6>
<p>You can choose any name, <em>but it has to end with 
<strong>.user.js</strong></em>.</p>

<h6>3. Test the file</h6>
<p>Drag the file onto Firefox (or open it any other way) and select "Install 
User Script..." from the Tools menu. <em>Note that this option will be disabled 
if you forgot to name your file ending with ".user.js".</em> Refresh the browser 
to see any changes to the current page.</p>

<h6>4. Debug the file</h6>
<p>Press the Edit button in the Manage Userscripts screen to edit a live copy of 
the script. When you save changes, you will be able to reload and see their 
effects immediately. You can also use the <a href="#GM_Log">GM_log</a> function 
to write output to the JavaScript Console.</p>

<h6>5. (optional) Add metadata</h6>
<p>By default, scripts will be installed with a name based on their filename, a 
namespace based on the URL they were downloaded from, and will be executed on 
every webpage. You can override these settings by adding metadata to your 
script.</p>
<p>This is done with special javascript comments, like so:</p>
<pre>// ==UserScript==
// @name          Say Hello!
// @namespace     http://youngpup.net/userscripts
// @description	  Greets the world
// @include       http://google.com/*
// @include       http://www.google.com/*
// @include       http://maps.google.tld/*
// @exclude       http://gmail.google.com/*
// ==/UserScript==
// Notes:
//   * is a wildcard character
//   .tld is magic that matches all top-level domains (e.g. .com, .co.uk, .us, 
etc.)

alert("Hello, world!");</pre>
<p>Here is a short reference of the available metadata tags:</p>
<p>
  <table border="1">
    <thead>
      <tr>
        <th>Property</th>
        <th>Description</th>
        <th>Multiplicity</th>
        <th>Default</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>@name</td>
        <td>A friendly name for the script.</td>
        <td>0-1</td>
        <td>The script's filename without the extension.</td>
      </tr>
      <tr>
        <td>@namespace</td>
        <td>A scope within which @name should be unique.</td>
        <td>0-1</td>
        <td>The domain of the script's file.</td>
      </tr>
      <tr>
        <td>@description</td>
        <td>A, uhm, description.</td>
        <td>0-1</td>
        <td>none</td>
      </tr>
      <tr>
        <td>@include</td>
        <td>A url to include the script with. There can be more than one.</td>
        <td>0+</td>
        <td>one, set to *</td>
      </tr>
      <tr>
        <td>@exclude</td>
        <td>A url to explicitly exclude the script from. There can be more than 
one.</td>
        <td>0+</td>
        <td>none</td>
      </tr>
    </tbody>
  </table>
</p>
<br />
<h6>Tips</h6>
<ul>
  <li>User scripts are executed after the DOM is fully loaded, but before onload 
occurs. This means that your scripts can begin immediately and don't need to 
wait for onload.  However, replacing large parts of the DOM (e.g. using 
innerHTML or outerHTML) at this early stage of rendering is <a 
href="http://www.kryogenix.org/days/2005/03/21/textplain">known</a> to cause 
Firefox some trouble.  In this case, you'll have more success if you call your 
code in response to the load event instead: 
<pre><code>window.addEventListener("load", function(e) {
  document.innerHTML = "Hello, world!";
}, false);</code></pre></li>
  <li>Mozilla has a bunch of advanced features that you can take advantage of to 
make your job easier. These typically aren't practical for DHTML authors 
because they don't exist in any other browser, but since your user scripts 
will only run on firefox, you're free to use such goodies as <a 
href="http://www-jcsu.jesus.cam.ac.uk/~jg307/mozilla/xpath-tutorial.html">xpat
h</a> support (for HTML as well as XHTML), <a 
href="http://www.w3.org/TR/DOM-Level-2-Traversal-Range/traversal.html">treewal
kers</a>, <a 
href="http://www.w3.org/TR/DOM-Level-2-Events/events.html">mutation 
events</a>, <a 
href="http://xulplanet.com/references/objref/XMLHttpRequest.html">xmlhttp</a>, 
etc.</li>
</ul>

<h6 id="API">API</h6>

<p>Greasemonkey scripts are executed in an environment very much like regular 
DHTML scripts are. With a <a 
href="http://dunck.us/collab/GreaseMonkeyUserScripts?action=show&redirect=Grease
monkeyUserScripts#head-4ac4d1e80f8bbd66bf4f1fbea77ea2390b6a2870">few 
exceptions</a>, Greasemonkey scripts have access to the exact same DOM API that 
the scripts on the website do. See the <a 
href="http://www.mozilla.org/docs/dom/domref/dom_shortTOC.html">Gecko DOM 
Reference</a> for an introduction to this API.

<p>As of <a href="changes/0.6.4.html">Greasemonkey 0.6.4</a>, however, user 
scripts now have their own JavaScript context and execute completely separately 
from the content document. This means that except by noticing changes the 
changes which they make to the DOM, content scripts can neither detect 
Greasemonkey scripts, nor interfere with them. 

<p>Sometimes, you will want to access the global variables in the 
content document. For example, maybe the content document defines a function 
which you want to call. In these cases, you can access the content document's 
global scope with the <a href="#unsafeWindow">unsafeWindow</a> variable. Just be 
aware that accessing the members of this variable means that content scripts can 
detect your script and potentially interfere with it if they choose.

<p>Greasemonkey also defines several special APIs which are not available to 
content scripts.

<p id="GM_registerMenuCommand"><strong>GM_registerMenuCommand</strong></p>
<p>Userscripts can call GM_registerMenuCommand(commandName, commandFunc, 
accelKey, accelModifiers, accessKey) to add a menu command to the tools > User 
Script Commands submenu.  The first two arguments are required; the reamining 
ones are optional.<dl>
<dt>commandName</dt>
<dd>Name to display in the menu</dd>
<dt>commandFunc</dt>
<dd>Function to call</dd>
<dt>accelKey</dt>
<dd>A single character (e.g. 'g') or keycode that can trigger the command</dd>
<dt>accelKey</dt>
<dd>A string listing modifiers that must be pressed with the accelKey. If 
there's more than one, then they should be separated with spaces. For example, 
'shift' or 'ctrl alt'. Available modifiers are: shift, alt, meta, control, and 
accel.</dd>
<dt>accessKey</dt>
<dd>A single character (e.g. 'g') that can be used to jump to the command when 
the menu is open. It should be a letter in commandName</dd>
</dl>
Example calls:<br />
<code>GM_registerMenuCommand( "Hello world!", hello, "e", "control", "h" 
);</code><br />
<code>GM_registerMenuCommand( "Hello world! (again)", hello2, "e", "shift alt", 
"w" );</code><br />
<code>GM_registerMenuCommand( "Hello world (simple)", helloSimple );</code>
</p>
<p id="GM_xmlhttpRequest"><strong>GM_xmlhttpRequest</strong></p>
<p>Userscripts can call GM_xmlhttpRequest(details) to make an xmlhttp request to 
other domains.</p>
<p>The details parameter is an object having the following fields: method, url, 
headers, onload, onreadystatechange, onerror, data. All fields are optional 
except method and url. The headers field is an object which should be the 
name-value pairs of the headers to send, for instance {"User-Agent":"Mozilla"}. 
The onload, onreadystatechange, and onerror parameters are callback functions 
which are called when the corresponding events occur.</p>
<p>Callback functions should accept a single object parameter having the 
following fields: responseText, status, statusText, readyState, and 
responseHeaders. The responseHeaders is the string representation of response 
headers returned by XMLHTTPRequest.getAllResponseHeaders().</p>
<p>Here's an example:</p>
<pre>GM_xmlhttpRequest({
  method:"GET",
  url:"http://youngpup.net/",
  headers:{
    "User-Agent":"monkeyagent",
    "Accept":"text/monkey,text/xml",
    },
  onload:function(details) {
    alert([
      details.status,
      details.statusText,
      details.readyState,
      details.responseHeaders,
      details.responseText
    ].join("\n"))
  }
});</pre>

<p id="GM_setValue"><strong>GM_setValue(name, value)</strong></p>
<p>Allows user script authors to persist simple values locally. Strings, 
booleans, and integers are the only allowed datatypes. For example:</p>
<pre>GM_setValue("foo", "bar");</pre>

<p id="GM_getValue"><strong>GM_getValue(name, default)</strong></p>
<p>Retrieve a value set with setValue. If the value is not found, default is 
returned instead. If default is not supplied, <code>undefined</code> is 
returned.</p>
<pre>alert(GM_getValue("foo"));</pre>

<p id="GM_log"><strong>GM_log(message, level)</strong></p>
<p>Allows script authors simple access to logging informational messages in the 
JS Console. This can be helpful for debugging. Level is optional and defaults to 
0. Valid values are: 0 - info, 1 - warning, 2 - error.</p>
<pre>GM_log("Hello, World!")</pre>

<p id="GM_openInTab"><strong>GM_openInTab(url)</strong></p>
<p>Opens the specified URL in a new tab.</p>
<pre>GM_openInTab("http://greasemonkey.mozdev.org/")</pre>

<p id="GM_addStyle"><strong>GM_addStyle(css)</strong></p>
<p>Adds a string of CSS to the document.</p>
<pre>GM_addStyle("body { color:red; }");</pre>

<p id="unsafeWindow"><strong>unsafeWindow</strong></p>

<p>A reference to the content document's javascript window object. This is where 
the content document's global variable and functions are defined.</p>

<pre>// list all the variables in the content's global scope
for (var p in unsafeWindow) {
  GM_log(p + ": " + unsafeWindow[p] + "\n");
}</pre>

<p>Note that accessing any variables in unsafeWindow yields control to the 
page's scripts. This means they can get acccess to the user script's source. 

<p>If you call browser APIs from this window, they might be redefined to mean 
something different than you expect. For example, a content script which wanted 
to trip up a Greasemonkey script could redefine 
unsafeWindow.document.getElementById to do nothing, or throw an error. This is 
one reason why you should not use unsafeWindow unless there is no other way to 
accomplish what you want to.

<p>Also, content functions you call can walk back up the call stack to get any 
parameters you passed to higher-level calling functions. Consider this 
hypothetical example:

<pre>// DONT DO THIS.
function a(gmxhr) {
 // do something with gmxhr
 unsafeWindow.foo();
}

function b() {
  a(GM_xmlhttpRequest);
}

b();</pre>

<p>Here, we pass a reference to GM_xmlhttpRequest to a user script function, 
which calls a content function. In this example, the content function 
<code>foo</code> could walk up the call stack to retrieve a reference to 
GM_xmlhttpRequest, which would be bad.

<p><strong>In general, it's probably best practice to avoid passing GM_* to any 
functions as parameters.</strong> In the future, there will be a better way to 
call content functions.</p>

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>