       req.setRequestHeader 'Content-Type', service.contentType
       # Setup the OAuth header, if required.
       if service.oauth and service.isOAuthEnabled()
+        oauth = yes
         req.setRequestHeader 'Authorization',
           service.oauth.getAuthorizationHeader sUrl, service.method, params
       # Wait for the response and let the service handle it before passing the
-      # result to `callback`.
+      # result to `callback` via the runner.
       req.onreadystatechange = ->
         if req.readyState is 4
           if req.status is 200
-            updateUrlShortenerUsage service.name
-            callback?(
+            updateUrlShortenerUsage service.name, oauth
+            runner.finish
               shortUrl:  service.output req.responseText
               shortener: title
               success:   yes
-            )
           else
             # Something went wrong so let's tell the user.
-            callback?(
+            runner.finish
               message:   i18n.get 'shortener_error', title
               shortener: title
               success:   no
-            )
       # Finally, send the HTTP request.
       req.send service.input url
     catch error
       # Aw snap! Notify the user via `callback`.
       log.error error
-      callback?(
+      runner.finish
         message:   i18n.get 'shortener_error', title
         shortener: title
         success:   no
-      )
-
-# Determine when `callback` is called depending on whether or not the active
-# URL Shortener supports [OAuth](http://oauth.net).
-callUrlShortenerHelper = (callback) ->
-  service = getActiveUrlShortener()
-  if service.oauth and service.isOAuthEnabled()
-    service.oauth.authorize ->
-      callback? service
-  else
-    callback? service
+  runner.run (result = {}) ->
+    callback? result
 
 # Retrieve the active URL shortener service.
 getActiveUrlShortener = ->

showNotification = ->
  if store.get 'notifications.enabled'
    webkitNotifications.createHTMLNotification(
      chrome.extension.getURL 'pages/notification.html'
    ).show()
  else
    ext.reset()

# Increment the usage for the template with the specified `key` and persist the
# changes.
updateTemplateUsage = (key) ->
  for template in ext.templates when template.key is key
    template.usage++
    break
  store.set 'templates', ext.templates
  analytics.track 'Templates', 'Used', template.title,
    if template.readOnly then 1 else 0

# Increment the usage for the URL shortener service with the specified `name`
# and persist the changes.
updateUrlShortenerUsage = (name, oauth) ->
  store.modify name, (shortener) ->
    shortener.usage++
  shortener = ext.queryUrlShortener (shortener) ->
    shortener.name is name
  analytics.track 'Shorteners', 'Used', shortener.title, if oauth then 1 else 0

# Data functions
# --------------

# Extract additional information from `tab` and add it to `data`.
addAdditionalData = (tab, data, callback) ->
  # Create a runner to simplify this process.
  runner = new utils.Runner()
  runner.push chrome.cookies, 'getAll', url: data.url, (cookies = []) ->
    names = []
    # Extract the names of each cookie.
    names.push cookie.name for cookie in cookies
    $.extend data,
      cookie:  ->
        (text, render) ->
          name = render text
          # Attempt to find the value for the cookie name.
          return cookie.value for cookie in cookies when cookie.name is name
          ''
      cookies: names
    # Try to prevent pages hanging because content script should not have been
    # executed.
    if isProtectedPage tab then runner.finish() else runner.next()
  runner.push chrome.tabs, 'sendRequest', tab.id, {}, (response) ->
    runner.finish response
  runner.run (result = {}) ->
    $.extend data,
      selection:      result.text ? ''
      selectionlinks: result.urls ? []
    callback?()

# Creates an object containing data based on information derived from the
# specified tab and menu item data.
