Merge central to inbound
authorMarco Bonardo <mbonardo@mozilla.com>
Mon, 13 Feb 2012 17:31:26 +0100
changeset 86728 8609c5263f73a03a177677149a837c8cb53b97fb
parent 86727 442b3399ff1a7a4e7b7f54643714675e3c5d29ae (current diff)
parent 86707 ebafee0cea36149bb990849371290f24ec2861e5 (diff)
child 86729 d834ff0f66c1d78937a084e78607156b685f673d
push idunknown
push userunknown
push dateunknown
milestone13.0a1
Merge central to inbound
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -423,8 +423,10 @@ pref("media.realtime_decoder.enabled", t
 // our handling of requestAnimationFrame() listeners, which are
 // supposed to enable this REPEATING_PRECISE_CAN_SKIP behavior.  The
 // secondary bug isn't really worth investigating since it's obseleted
 // by bug 710563.
 pref("layout.frame_rate.precise", true);
 
 // Screen timeout in minutes
 pref("power.screen.timeout", 60);
+
+pref("full-screen-api.enabled", true);
--- a/b2g/chrome/content/webapi.js
+++ b/b2g/chrome/content/webapi.js
@@ -15,38 +15,20 @@ Cu.import('resource://gre/modules/Servic
 XPCOMUtils.defineLazyGetter(Services, 'fm', function() {
   return Cc['@mozilla.org/focus-manager;1']
            .getService(Ci.nsIFocusManager);
 });
 
 (function() {
   function generateAPI(window) {
     let navigator = window.navigator;
-    XPCOMUtils.defineLazyGetter(navigator, 'mozSettings', function() {
-      return new Settings();
-    });
-
-    XPCOMUtils.defineLazyGetter(navigator, 'mozContacts', function() {
-      return ContactsManager;
-    });
-
-    XPCOMUtils.defineLazyGetter(navigator, 'mozApps', function() {
-      let mozApps = {
-        enumerate: function mozAppsEnumerate(callback) {
-          callback(webapps);
-        }
-      };
-      return mozApps;
-    });
 
     XPCOMUtils.defineLazyGetter(navigator, 'mozKeyboard', function() {
       return new MozKeyboard();
     });
-
-    updateApplicationCache(window);
   };
 
   let progressListener = {
     onStateChange: function onStateChange(progress, request,
                                           flags, status) {
     },
 
     onProgressChange: function onProgressChange(progress, request,
@@ -86,32 +68,16 @@ XPCOMUtils.defineLazyGetter(Services, 'f
 
   let flags = Ci.nsIWebProgress.NOTIFY_LOCATION;
   let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
                             .getInterface(Ci.nsIWebProgress);
   flags = Ci.nsIWebProgress.NOTIFY_ALL;
   webProgress.addProgressListener(progressListener, flags);
 })();
 
-
-// WebApps - Application Cache
-function updateApplicationCache(window) {
-  try {
-    var cache = window.applicationCache;
-    cache.update();
-
-    cache.addEventListener('updateready', function updateReady(evt) {
-      // XXX Add a nice UI when an update is ready asking if the user
-      // want to reload the application now.
-      cache.swapCache();
-      window.document.location.reload();
-    });
-  } catch (e) {}
-}
-
 // MozKeyboard
 (function VirtualKeyboardManager() {
   let activeElement = null;
   let isKeyboardOpened = false;
   
   function fireEvent(type, details) {
     let event = content.document.createEvent('CustomEvent');
     event.initCustomEvent(type, true, true, details ? details : {});
@@ -175,1346 +141,16 @@ MozKeyboard.prototype = {
     let utils = content.QueryInterface(Ci.nsIInterfaceRequestor)
                        .getInterface(Ci.nsIDOMWindowUtils);
     ['keydown', 'keypress', 'keyup'].forEach(function sendKey(type) {
       utils.sendKeyEvent(type, keyCode, charCode, null);
     });
   }
 };
 
-// MozSettings - Bug 678695
-function Settings() {
-  content.addEventListener('message', this);
-}
-
-Settings.prototype = {
-  ERROR_SETTING_UNKNOWN: 0x0001,
-
-  _requests: [],
-  handleEvent: function settings_handleEvent(event) {
-    var data = event.data;
-    if (typeof data !== 'string')
-      return;
-
-    var cmd = data.split(':');
-    if (cmd.length < 4 || cmd[0] != 'settings')
-      return;
-
-    var method = cmd[1], id = cmd[2], key = cmd[3], value = cmd[4];
-    var request = this._requests[id];
-    switch (method) {
-      case 'error':
-        request.error = this.ERROR_SETTING_UNKNOWN;
-        this._dispatchEvent(request, request.TYPE_ERROR);
-        break;
-
-      case 'success':
-        request.result = new SettingsMessage(key, value);
-        this._dispatchEvent(request, request.TYPE_SUCCESS);
-        break;
-    }
-  },
-
-  get: function settings_get(key) {
-    var request = new SettingsRequest();
-    var id = this._requests.length;
-    this._requests.push(request);
-
-    var msg = 'settings:get:' + id + ':' + key;
-    content.top.wrappedJSObject.postMessage(msg, '*');
-    return request;
-  },
-
-  set: function settings_set(key, value) {
-    var request = new SettingsRequest();
-    var id = this._requests.length;
-    this._requests.push(request);
-
-    var msg = 'settings:set:' + id + ':' + key + ':' + value;
-    content.top.wrappedJSObject.postMessage(msg, '*');
-    return request;
-  },
-
-  _dispatchEvent: function(target, type) {
-    var evt = content.document.createEvent('CustomEvent');
-    evt.initCustomEvent(type, true, false, null);
-    target.dispatchEvent(evt);
-  }
-};
-
-
-/* ========== nsIDOMMozSettingsRequest ========== */
-function SettingsRequest() {
-  this.readyState = this.STATE_PROCESSING;
-
-  this.error = null;
-  this.onerror = null;
-  // XXX should be an array
-  this._errorCallback = null;
-
-  this.result = null;
-  this.onsuccess = null;
-  // XXX should be an array
-  this._successCallback = null;
-}
-
-SettingsRequest.prototype = {
-  // States of the request
-  STATE_PROCESSING: 'processing',
-  STATE_DONE: 'done',
-
-  // Types of events
-  TYPE_SUCCESS: 'success',
-  TYPE_ERROR: 'error',
-
-  addEventListener: function sr_addEventListener(type, callback) {
-    switch (type) {
-      case this.TYPE_SUCCESS:
-        this._successCallback = callback;
-        break;
-      case this.TYPE_ERROR:
-        this._errorCallback = callback;
-        break;
-    }
-  },
-
-  removeEventListener: function sr_removeEventListener(type, callback) {
-    switch (type) {
-      case this.TYPE_SUCCESS:
-        this._successCallback = null;
-        break;
-      case this.TYPE_ERROR:
-        this._errorCallback = null;
-        break;
-    }
-  },
-
-  dispatchEvent: function sr_dispatchEvent(event) {
-    this.readyState = this.STATE_DONE;
-
-    switch (event.type) {
-      case this.TYPE_SUCCESS:
-        if (this._successCallback)
-          this._successCallback(event);
-
-        if (this.onsuccess)
-          this.onsuccess(event);
-        break;
-      case this.TYPE_ERROR:
-        if (this._errorCallback)
-          this._errorCallback(event);
-
-        if (this.onerror)
-          this.onerror(event);
-        break;
-    }
-  }
-};
-
-/* ========== nsIDOMMozSettingsMessage ========== */
-function SettingsMessage(name, value) {
-  this.name = name;
-  this.value = value;
-}
-
-
-// MozApps - Bug 709015
-var webapps = [
-  { // clock 
-    installOrigin: 'http://gaiamobile.org:8888',
-    origin: '../clock',
-    receipt: null,
-    installTime: 1323339869000,
-    manifest: {
-      'name': 'Clock',
-      'description': 'Gaia Clock',
-      'launch_path': '/clock.html',
-      'developer': {
-        'name': 'The Gaia Team',
-        'url': 'https://github.com/andreasgal/gaia'
-      },
-      'icons': {
-        '120': '/style/icons/Clock.png'
-      }
-    }
-  },
-  { // browser
-    installOrigin: 'http://gaiamobile.org:8888',
-    origin: '../browser',
-    receipt: null,
-    installTime: 1323339869000,
-    manifest: {
-      'name': 'Browser',
-      'description': 'Gaia Web Browser',
-      'launch_path': '/browser.html',
-      'developer': {
-        'name': 'The Gaia Team',
-        'url': 'https://github.com/andreasgal/gaia'
-      },
-      'icons': {
-        '120': '/style/icons/Browser.png'
-      }
-    }
-  },
-  { // camera
-    'installOrigin': 'http://gaiamobile.org:8888',
-    'origin': '../camera',
-    'receipt': null,
-    'installTime': 1323339869000,
-    manifest: {
-      'name': 'Camera',
-      'description': 'Gaia Camera',
-      'launch_path': '/camera.html',
-      'developer': {
-        'name': 'The Gaia Team',
-        'url': 'https://github.com/andreasgal/gaia'
-      },
-      'icons': {
-        '120': '/style/icons/Camera.png'
-      }
-    }
-  },
-  { // dialer
-    'installOrigin': 'http://gaiamobile.org:8888',
-    'origin': '../dialer',
-    'receipt': null,
-    'installTime': 1323339869000,
-    manifest: {
-      'name': 'Dialer',
-      'description': 'Gaia Dialer',
-      'launch_path': '/dialer.html',
-      'developer': {
-        'name': 'The Gaia Team',
-        'url': 'https://github.com/andreasgal/gaia'
-      },
-      'icons': {
-        '120': '/style/icons/Phone.png'
-      }
-    }
-  },
-  { // gallery
-    'installOrigin': 'http://gaiamobile.org:8888',
-    'origin': '../gallery',
-    'receipt': null,
-    'installTime': 1323339869000,
-    manifest: {
-      'name': 'Gallery',
-      'description': 'Gaia Gallery',
-      'launch_path': '/gallery.html',
-      'developer': {
-        'name': 'The Gaia Team',
-        'url': 'https://github.com/andreasgal/gaia'
-      },
-      'icons': {
-        '120': '/style/icons/Gallery.png'
-      }
-    }
-  },
-  { // music
-    'installOrigin': 'http://gaiamobile.org:8888',
-    'origin': '../music',
-    'receipt': null,
-    'installTime': 1323339869000,
-    manifest: {
-      'name': 'Music',
-      'description': 'Gaia Music',
-      'launch_path': '/music.html',
-      'developer': {
-        'name': 'The Gaia Team',
-        'url': 'https://github.com/andreasgal/gaia'
-      },
-      'icons': {
-        '120': '/style/icons/Music.png'
-      }
-    }
-  },
-  { // market
-    'installOrigin': 'http://gaiamobile.org:8888',
-    'origin': '../market',
-    'receipt': null,
-    'installTime': 1323339869000,
-    manifest: {
-      'name': 'Market',
-      'description': 'Market for downloading and installing apps',
-      'launch_path': '/market.html',
-      'developer': {
-        'name': 'The Gaia Team',
-        'url': 'https://github.com/andreasgal/gaia'
-      },
-      'icons': {
-        '120': '/style/icons/Market.png'
-      }
-    }
-  },
-  { // settings
-    'installOrigin': 'http://gaiamobile.org:8888',
-    'origin': '../settings',
-    'receipt': null,
-    'installTime': 1323339869000,
-    manifest: {
-      'name': 'Settings',
-      'description': 'Gaia Settings',
-      'launch_path': '/settings.html',
-      'developer': {
-        'name': 'The Gaia Team',
-        'url': 'https://github.com/andreasgal/gaia'
-      },
-      'icons': {
-        '120': '/style/icons/Settings.png'
-      }
-    }
-  },
-  { // sms
-    'installOrigin': 'http://gaiamobile.org:8888',
-    'origin': '../sms',
-    'receipt': null,
-    'installTime': 1323339869000,
-    manifest: {
-      'name': 'Messages',
-      'description': 'Gaia Messages',
-      'launch_path': '/sms.html',
-      'developer': {
-        'name': 'The Gaia Team',
-        'url': 'https://github.com/andreasgal/gaia'
-      },
-      'icons': {
-        '120': '/style/icons/Messages.png'
-      }
-    }
-  }
-];
-
-
-// Bug 674720 - webContacts
-var contacts = [
-  {
-    id: '3',
-    displayName: 'Coby Newman',
-    name: {
-      familyName: ['Coby'],
-      givenName: ['Newman']
-    },
-    phones: ['1-823-949-7735'],
-    emails: ['posuere.at@hendreritaarcu.com']
-  },
-  {
-    id: '6',
-    displayName: 'Caesar Velasquez',
-    name: {
-      familyName: ['Caesar'],
-      givenName: ['Velasquez']
-    },
-    phones: ['1-355-185-5419'],
-    emails: ['fames@Duis.org']
-  },
-  {
-    id: '9',
-    displayName: 'Hamilton Farrell',
-    name: {
-      familyName: ['Hamilton'],
-      givenName: ['Farrell']
-    },
-    phones: ['1-682-456-9186'],
-    emails: ['sem@Uttinciduntvehicula.com']
-  },
-  {
-    id: '12',
-    displayName: 'Emery Livingston',
-    name: {
-      familyName: ['Emery'],
-      givenName: ['Livingston']
-    },
-    phones: ['1-510-151-9801'],
-    emails: ['orci.luctus.et@massaInteger.com']
-  },
-  {
-    id: '15',
-    displayName: 'Griffith Heath',
-    name: {
-      familyName: ['Griffith'],
-      givenName: ['Heath']
-    },
-    phones: ['1-800-719-3201'],
-    emails: ['dapibus@Inlorem.ca']
-  },
-  {
-    id: '18',
-    displayName: 'Luke Stuart',
-    name: {
-      familyName: ['Luke'],
-      givenName: ['Stuart']
-    },
-    phones: ['1-120-910-1976'],
-    emails: ['congue@nibh.ca']
-  },
-  {
-    id: '21',
-    displayName: 'Brennan Love',
-    name: {
-      familyName: ['Brennan'],
-      givenName: ['Love']
-    },
-    phones: ['1-724-155-2807'],
-    emails: ['interdum.libero.dui@cursusvestibulum.edu']
-  },
-  {
-    id: '24',
-    displayName: 'Lamar Meadows',
-    name: {
-      familyName: ['Lamar'],
-      givenName: ['Meadows']
-    },
-    phones: ['1-976-164-8769'],
-    emails: ['tincidunt@non.com']
-  },
-  {
-    id: '27',
-    displayName: 'Erasmus Flynn',
-    name: {
-      familyName: ['Erasmus'],
-      givenName: ['Flynn']
-    },
-    phones: ['1-488-678-3487'],
-    emails: ['lorem.ut.aliquam@eu.ca']
-  },
-  {
-    id: '30',
-    displayName: 'Aladdin Ellison',
-    name: {
-      familyName: ['Aladdin'],
-      givenName: ['Ellison']
-    },
-    phones: ['1-977-743-6797'],
-    emails: ['sociosqu.ad@sollicitudin.org']
-  },
-  {
-    id: '33',
-    displayName: 'Valentine Rasmussen',
-    name: {
-      familyName: ['Valentine'],
-      givenName: ['Rasmussen']
-    },
-    phones: ['1-265-504-2025'],
-    emails: ['ultrices.iaculis@acsem.edu']
-  },
-  {
-    id: '36',
-    displayName: 'Deacon Murphy',
-    name: {
-      familyName: ['Deacon'],
-      givenName: ['Murphy']
-    },
-    phones: ['1-770-450-1221'],
-    emails: ['varius@erat.edu']
-  },
-  {
-    id: '39',
-    displayName: 'Paul Kennedy',
-    name: {
-      familyName: ['Paul'],
-      givenName: ['Kennedy']
-    },
-    phones: ['1-689-891-3529'],
-    emails: ['ac.arcu@vitae.edu']
-  },
-  {
-    id: '42',
-    displayName: 'Aaron Chase',
-    name: {
-      familyName: ['Aaron'],
-      givenName: ['Chase']
-    },
-    phones: ['1-451-574-7937'],
-    emails: ['tempor.bibendum.Donec@pharetraQuisque.edu']
-  },
-  {
-    id: '45',
-    displayName: 'Geoffrey Dunn',
-    name: {
-      familyName: ['Geoffrey'],
-      givenName: ['Dunn']
-    },
-    phones: ['1-924-387-2395'],
-    emails: ['a.malesuada@tellusPhasellus.com']
-  },
-  {
-    id: '48',
-    displayName: 'Ashton Russo',
-    name: {
-      familyName: ['Ashton'],
-      givenName: ['Russo']
-    },
-    phones: ['1-182-776-5600'],
-    emails: ['Aliquam.vulputate.ullamcorper@faucibusorci.edu']
-  },
-  {
-    id: '51',
-    displayName: 'Owen Noble',
-    name: {
-      familyName: ['Owen'],
-      givenName: ['Noble']
-    },
-    phones: ['1-463-693-1336'],
-    emails: ['et@vulputateveliteu.ca']
-  },
-  {
-    id: '54',
-    displayName: 'Kamal Blake',
-    name: {
-      familyName: ['Kamal'],
-      givenName: ['Blake']
-    },
-    phones: ['1-636-197-1985'],
-    emails: ['tempor@malesuada.edu']
-  },
-  {
-    id: '57',
-    displayName: 'Tyrone Delaney',
-    name: {
-      familyName: ['Tyrone'],
-      givenName: ['Delaney']
-    },
-    phones: ['1-886-920-6283'],
-    emails: ['est@aliquetsemut.com']
-  },
-  {
-    id: '60',
-    displayName: 'Ciaran Sellers',
-    name: {
-      familyName: ['Ciaran'],
-      givenName: ['Sellers']
-    },
-    phones: ['1-315-414-0323'],
-    emails: ['Etiam@Nulla.com']
-  },
-  {
-    id: '63',
-    displayName: 'Bernard Alford',
-    name: {
-      familyName: ['Bernard'],
-      givenName: ['Alford']
-    },
-    phones: ['1-430-958-2651'],
-    emails: ['elementum.lorem.ut@sociisnatoque.edu']
-  },
-  {
-    id: '66',
-    displayName: 'Kamal Cote',
-    name: {
-      familyName: ['Kamal'],
-      givenName: ['Cote']
-    },
-    phones: ['1-666-609-9141'],
-    emails: ['eleifend.egestas@cursus.edu']
-  },
-  {
-    id: '69',
-    displayName: 'Lucius Mckee',
-    name: {
-      familyName: ['Lucius'],
-      givenName: ['Mckee']
-    },
-    phones: ['1-224-590-6780'],
-    emails: ['Fusce.dolor@tellusnon.org']
-  },
-  {
-    id: '72',
-    displayName: 'Dale Coleman',
-    name: {
-      familyName: ['Dale'],
-      givenName: ['Coleman']
-    },
-    phones: ['1-320-245-3036'],
-    emails: ['dapibus.rutrum@ametlorem.org']
-  },
-  {
-    id: '75',
-    displayName: 'Kermit Nguyen',
-    name: {
-      familyName: ['Kermit'],
-      givenName: ['Nguyen']
-    },
-    phones: ['1-247-825-8563'],
-    emails: ['per@risusMorbi.org']
-  },
-  {
-    id: '78',
-    displayName: 'Timon Horton',
-    name: {
-      familyName: ['Timon'],
-      givenName: ['Horton']
-    },
-    phones: ['1-739-233-8981'],
-    emails: ['Etiam@nonummyultriciesornare.ca']
-  },
-  {
-    id: '81',
-    displayName: 'Dale Lamb',
-    name: {
-      familyName: ['Dale'],
-      givenName: ['Lamb']
-    },
-    phones: ['1-640-507-8295'],
-    emails: ['dapibus.id@pedeac.edu']
-  },
-  {
-    id: '84',
-    displayName: 'Owen Acevedo',
-    name: {
-      familyName: ['Owen'],
-      givenName: ['Acevedo']
-    },
-    phones: ['1-403-201-3170'],
-    emails: ['porttitor.tellus.non@dolorFusce.edu']
-  },
-  {
-    id: '87',
-    displayName: 'Richard Mckee',
-    name: {
-      familyName: ['Richard'],
-      givenName: ['Mckee']
-    },
-    phones: ['1-783-513-0684'],
-    emails: ['senectus.et.netus@Vestibulum.com']
-  },
-  {
-    id: '90',
-    displayName: 'Elijah Bass',
-    name: {
-      familyName: ['Elijah'],
-      givenName: ['Bass']
-    },
-    phones: ['1-632-950-0553'],
-    emails: ['erat@sapien.com']
-  },
-  {
-    id: '93',
-    displayName: 'Barrett Wells',
-    name: {
-      familyName: ['Barrett'],
-      givenName: ['Wells']
-    },
-    phones: ['1-112-180-5617'],
-    emails: ['interdum.ligula@varius.edu']
-  },
-  {
-    id: '96',
-    displayName: 'Herman Meyer',
-    name: {
-      familyName: ['Herman'],
-      givenName: ['Meyer']
-    },
-    phones: ['1-296-252-5507'],
-    emails: ['urna@vitaealiquameros.org']
-  },
-  {
-    id: '99',
-    displayName: 'Ashton Hinton',
-    name: {
-      familyName: ['Ashton'],
-      givenName: ['Hinton']
-    },
-    phones: ['1-695-256-8929'],
-    emails: ['lorem@mattisornare.org']
-  },
-  {
-    id: '102',
-    displayName: 'Harrison Marsh',
-    name: {
-      familyName: ['Harrison'],
-      givenName: ['Marsh']
-    },
-    phones: ['1-897-458-1730'],
-    emails: ['pharetra.felis.eget@auctor.com']
-  },
-  {
-    id: '105',
-    displayName: 'Benedict Santana',
-    name: {
-      familyName: ['Benedict'],
-      givenName: ['Santana']
-    },
-    phones: ['1-565-457-4828'],
-    emails: ['amet.metus.Aliquam@Maecenas.org']
-  },
-  {
-    id: '108',
-    displayName: 'David Church',
-    name: {
-      familyName: ['David'],
-      givenName: ['Church']
-    },
-    phones: ['1-179-353-3314'],
-    emails: ['Nullam.enim@Utsagittis.edu']
-  },
-  {
-    id: '111',
-    displayName: 'Colt Wolfe',
-    name: {
-      familyName: ['Colt'],
-      givenName: ['Wolfe']
-    },
-    phones: ['1-587-970-8581'],
-    emails: ['hendrerit.Donec.porttitor@tinciduntaliquam.org']
-  },
-  {
-    id: '114',
-    displayName: 'Carlos Bishop',
-    name: {
-      familyName: ['Carlos'],
-      givenName: ['Bishop']
-    },
-    phones: ['1-963-305-6702'],
-    emails: ['Nam@cursusNunc.org']
-  },
-  {
-    id: '117',
-    displayName: 'Dominic Ware',
-    name: {
-      familyName: ['Dominic'],
-      givenName: ['Ware']
-    },
-    phones: ['1-609-458-5449'],
-    emails: ['Fusce.aliquet@Etiam.ca']
-  },
-  {
-    id: '120',
-    displayName: 'Phillip Whitley',
-    name: {
-      familyName: ['Phillip'],
-      givenName: ['Whitley']
-    },
-    phones: ['1-284-955-1766'],
-    emails: ['per.inceptos.hymenaeos@nequesedsem.ca']
-  },
-  {
-    id: '123',
-    displayName: 'Valentine Sargent',
-    name: {
-      familyName: ['Valentine'],
-      givenName: ['Sargent']
-    },
-    phones: ['1-346-890-6417'],
-    emails: ['nec@dolorFusce.com']
-  },
-  {
-    id: '126',
-    displayName: 'Gabriel Huber',
-    name: {
-      familyName: ['Gabriel'],
-      givenName: ['Huber']
-    },
-    phones: ['1-399-465-0589'],
-    emails: ['pretium.neque@nislsemconsequat.ca']
-  },
-  {
-    id: '129',
-    displayName: 'George Tyler',
-    name: {
-      familyName: ['George'],
-      givenName: ['Tyler']
-    },
-    phones: ['1-739-571-2737'],
-    emails: ['blandit.viverra.Donec@dictum.ca']
-  },
-  {
-    id: '132',
-    displayName: 'Asher Carey',
-    name: {
-      familyName: ['Asher'],
-      givenName: ['Carey']
-    },
-    phones: ['1-477-425-4723'],
-    emails: ['torquent.per.conubia@blanditNamnulla.edu']
-  },
-  {
-    id: '135',
-    displayName: 'Anthony Solomon',
-    name: {
-      familyName: ['Anthony'],
-      givenName: ['Solomon']
-    },
-    phones: ['1-570-753-4296'],
-    emails: ['risus.Nunc@hendreritconsectetuercursus.com']
-  },
-  {
-    id: '138',
-    displayName: 'Griffith Fuller',
-    name: {
-      familyName: ['Griffith'],
-      givenName: ['Fuller']
-    },
-    phones: ['1-779-242-5342'],
-    emails: ['Suspendisse@aliquam.ca']
-  },
-  {
-    id: '141',
-    displayName: 'Beau Brewer',
-    name: {
-      familyName: ['Beau'],
-      givenName: ['Brewer']
-    },
-    phones: ['1-664-184-7334'],
-    emails: ['magna.tellus.faucibus@ultricesposuerecubilia.com']
-  },
-  {
-    id: '144',
-    displayName: 'Jordan Campbell',
-    name: {
-      familyName: ['Jordan'],
-      givenName: ['Campbell']
-    },
-    phones: ['1-593-938-2525'],
-    emails: ['Curae;.Phasellus@Morbiquis.ca']
-  },
-  {
-    id: '147',
-    displayName: 'Cyrus Cabrera',
-    name: {
-      familyName: ['Cyrus'],
-      givenName: ['Cabrera']
-    },
-    phones: ['1-915-748-1349'],
-    emails: ['lorem.tristique@acmetus.edu']
-  },
-  {
-    id: '150',
-    displayName: 'Hamilton Boone',
-    name: {
-      familyName: ['Hamilton'],
-      givenName: ['Boone']
-    },
-    phones: ['1-278-421-9845'],
-    emails: ['non.sapien@quamdignissimpharetra.edu']
-  },
-  {
-    id: '153',
-    displayName: 'Wallace Donovan',
-    name: {
-      familyName: ['Wallace'],
-      givenName: ['Donovan']
-    },
-    phones: ['1-940-175-9334'],
-    emails: ['justo@lacusMaurisnon.org']
-  },
-  {
-    id: '156',
-    displayName: 'Kirk Buckley',
-    name: {
-      familyName: ['Kirk'],
-      givenName: ['Buckley']
-    },
-    phones: ['1-283-177-6304'],
-    emails: ['Cras@Morbinon.edu']
-  },
-  {
-    id: '159',
-    displayName: 'Simon Hall',
-    name: {
-      familyName: ['Simon'],
-      givenName: ['Hall']
-    },
-    phones: ['1-269-202-5174'],
-    emails: ['mus.Proin@dolor.org']
-  },
-  {
-    id: '162',
-    displayName: 'Trevor Rush',
-    name: {
-      familyName: ['Trevor'],
-      givenName: ['Rush']
-    },
-    phones: ['1-865-595-9074'],
-    emails: ['Fusce@Donec.edu']
-  },
-  {
-    id: '165',
-    displayName: 'Todd Mccormick',
-    name: {
-      familyName: ['Todd'],
-      givenName: ['Mccormick']
-    },
-    phones: ['1-398-916-3514'],
-    emails: ['at@ornareelit.org']
-  },
-  {
-    id: '168',
-    displayName: 'Yuli Gay',
-    name: {
-      familyName: ['Yuli'],
-      givenName: ['Gay']
-    },
-    phones: ['1-198-196-4256'],
-    emails: ['Sed.congue.elit@Inornare.edu']
-  },
-  {
-    id: '171',
-    displayName: 'Joseph Frazier',
-    name: {
-      familyName: ['Joseph'],
-      givenName: ['Frazier']
-    },
-    phones: ['1-969-410-7180'],
-    emails: ['faucibus.ut.nulla@massa.org']
-  },
-  {
-    id: '174',
-    displayName: 'Ali Chase',
-    name: {
-      familyName: ['Ali'],
-      givenName: ['Chase']
-    },
-    phones: ['1-598-924-6112'],
-    emails: ['eu.elit@necanteMaecenas.edu']
-  },
-  {
-    id: '177',
-    displayName: 'Guy Simpson',
-    name: {
-      familyName: ['Guy'],
-      givenName: ['Simpson']
-    },
-    phones: ['1-558-377-3714'],
-    emails: ['in@mauriselit.edu']
-  },
-  {
-    id: '180',
-    displayName: 'Ivan Wynn',
-    name: {
-      familyName: ['Ivan'],
-      givenName: ['Wynn']
-    },
-    phones: ['1-274-885-0477'],
-    emails: ['lobortis.quis@Sed.com']
-  },
-  {
-    id: '183',
-    displayName: 'Preston Carpenter',
-    name: {
-      familyName: ['Preston'],
-      givenName: ['Carpenter']
-    },
-    phones: ['1-758-120-5270'],
-    emails: ['elit.Curabitur@vehiculaaliquet.edu']
-  },
-  {
-    id: '186',
-    displayName: 'Demetrius Santos',
-    name: {
-      familyName: ['Demetrius'],
-      givenName: ['Santos']
-    },
-    phones: ['1-913-961-7009'],
-    emails: ['id@magnaPhasellusdolor.com']
-  },
-  {
-    id: '189',
-    displayName: 'Dale Franklin',
-    name: {
-      familyName: ['Dale'],
-      givenName: ['Franklin']
-    },
-    phones: ['1-443-971-0116'],
-    emails: ['velit.Pellentesque@IntegerurnaVivamus.com']
-  },
-  {
-    id: '192',
-    displayName: 'Abraham Randolph',
-    name: {
-      familyName: ['Abraham'],
-      givenName: ['Randolph']
-    },
-    phones: ['1-368-169-0957'],
-    emails: ['egestas@maurisidsapien.com']
-  },
-  {
-    id: '195',
-    displayName: 'Hu Avila',
-    name: {
-      familyName: ['Hu'],
-      givenName: ['Avila']
-    },
-    phones: ['1-311-333-8877'],
-    emails: ['metus@adipiscinglacusUt.com']
-  },
-  {
-    id: '198',
-    displayName: 'Garth Trujillo',
-    name: {
-      familyName: ['Garth'],
-      givenName: ['Trujillo']
-    },
-    phones: ['1-409-494-1231'],
-    emails: ['commodo.hendrerit.Donec@etnunc.ca']
-  },
-  {
-    id: '201',
-    displayName: 'Quamar Buchanan',
-    name: {
-      familyName: ['Quamar'],
-      givenName: ['Buchanan']
-    },
-    phones: ['1-114-992-7225'],
-    emails: ['tellus@consequatpurusMaecenas.ca']
-  },
-  {
-    id: '204',
-    displayName: 'Ulysses Bishop',
-    name: {
-      familyName: ['Ulysses'],
-      givenName: ['Bishop']
-    },
-    phones: ['1-485-518-5941'],
-    emails: ['fermentum.fermentum.arcu@amalesuadaid.com']
-  },
-  {
-    id: '207',
-    displayName: 'Avram Knapp',
-    name: {
-      familyName: ['Avram'],
-      givenName: ['Knapp']
-    },
-    phones: ['1-307-139-5554'],
-    emails: ['est.ac.mattis@ultricesmauris.ca']
-  },
-  {
-    id: '210',
-    displayName: 'Conan Grant',
-    name: {
-      familyName: ['Conan'],
-      givenName: ['Grant']
-    },
-    phones: ['1-331-936-0280'],
-    emails: ['turpis@odio.com']
-  },
-  {
-    id: '213',
-    displayName: 'Chester Kemp',
-    name: {
-      familyName: ['Chester'],
-      givenName: ['Kemp']
-    },
-    phones: ['1-554-119-4848'],
-    emails: ['Aenean.gravida.nunc@eu.org']
-  },
-  {
-    id: '216',
-    displayName: 'Hedley Dudley',
-    name: {
-      familyName: ['Hedley'],
-      givenName: ['Dudley']
-    },
-    phones: ['1-578-607-6287'],
-    emails: ['Nunc@dignissimtemporarcu.ca']
-  },
-  {
-    id: '219',
-    displayName: 'Jermaine Avila',
-    name: {
-      familyName: ['Jermaine'],
-      givenName: ['Avila']
-    },
-    phones: ['1-860-455-2283'],
-    emails: ['accumsan@ametdapibusid.ca']
-  },
-  {
-    id: '222',
-    displayName: 'Kamal Hamilton',
-    name: {
-      familyName: ['Kamal'],
-      givenName: ['Hamilton']
-    },
-    phones: ['1-650-389-0920'],
-    emails: ['Fusce.dolor@nuncsed.ca']
-  },
-  {
-    id: '225',
-    displayName: 'Castor Maxwell',
-    name: {
-      familyName: ['Castor'],
-      givenName: ['Maxwell']
-    },
-    phones: ['1-260-489-7135'],
-    emails: ['diam.lorem@a.ca']
-  },
-  {
-    id: '228',
-    displayName: 'Lyle Burris',
-    name: {
-      familyName: ['Lyle'],
-      givenName: ['Burris']
-    },
-    phones: ['1-250-343-2038'],
-    emails: ['eget.lacus@tempordiamdictum.com']
-  },
-  {
-    id: '231',
-    displayName: 'Merrill Dalton',
-    name: {
-      familyName: ['Merrill'],
-      givenName: ['Dalton']
-    },
-    phones: ['1-851-675-1381'],
-    emails: ['eu.tempor@blanditmattisCras.edu']
-  },
-  {
-    id: '234',
-    displayName: 'Ezekiel Medina',
-    name: {
-      familyName: ['Ezekiel'],
-      givenName: ['Medina']
-    },
-    phones: ['1-389-582-3443'],
-    emails: ['lectus.sit@interdum.ca']
-  },
-  {
-    id: '237',
-    displayName: 'Len Tran',
-    name: {
-      familyName: ['Len'],
-      givenName: ['Tran']
-    },
-    phones: ['1-434-573-6114'],
-    emails: ['turpis.Aliquam.adipiscing@montesnasceturridiculus.com']
-  },
-  {
-    id: '240',
-    displayName: 'Len Dominguez',
-    name: {
-      familyName: ['Len'],
-      givenName: ['Dominguez']
-    },
-    phones: ['1-144-489-7487'],
-    emails: ['augue@Innec.ca']
-  },
-  {
-    id: '243',
-    displayName: 'Paul Lane',
-    name: {
-      familyName: ['Paul'],
-      givenName: ['Lane']
-    },
-    phones: ['1-448-169-4312'],
-    emails: ['lectus.Cum.sociis@dolornonummyac.org']
-  },
-  {
-    id: '246',
-    displayName: 'Eric Horne',
-    name: {
-      familyName: ['Eric'],
-      givenName: ['Horne']
-    },
-    phones: ['1-124-862-6890'],
-    emails: ['commodo.tincidunt.nibh@eleifendnuncrisus.com']
-  },
-  {
-    id: '249',
-    displayName: 'Elton Ellis',
-    name: {
-      familyName: ['Elton'],
-      givenName: ['Ellis']
-    },
-    phones: ['1-492-834-0019'],
-    emails: ['lorem.eu.metus@felis.ca']
-  },
-  {
-    id: '252',
-    displayName: 'Jameson Snyder',
-    name: {
-      familyName: ['Jameson'],
-      givenName: ['Snyder']
-    },
-    phones: ['1-811-590-5893'],
-    emails: ['fermentum@Nuncmaurissapien.org']
-  },
-  {
-    id: '255',
-    displayName: 'Micah Shelton',
-    name: {
-      familyName: ['Micah'],
-      givenName: ['Shelton']
-    },
-    phones: ['1-402-504-4026'],
-    emails: ['Nunc.mauris@malesuada.ca']
-  },
-  {
-    id: '258',
-    displayName: 'Evan Lester',
-    name: {
-      familyName: ['Evan'],
-      givenName: ['Lester']
-    },
-    phones: ['1-535-915-3570'],
-    emails: ['libero@adipiscingfringillaporttitor.org']
-  },
-  {
-    id: '261',
-    displayName: 'Reuben Dalton',
-    name: {
-      familyName: ['Reuben'],
-      givenName: ['Dalton']
-    },
-    phones: ['1-296-598-2504'],
-    emails: ['tincidunt.vehicula.risus@Craseutellus.com']
-  },
-  {
-    id: '264',
-    displayName: 'Beau Baird',
-    name: {
-      familyName: ['Beau'],
-      givenName: ['Baird']
-    },
-    phones: ['1-525-882-9957'],
-    emails: ['urna.suscipit.nonummy@facilisisvitae.com']
-  },
-  {
-    id: '267',
-    displayName: 'Hedley Olsen',
-    name: {
-      familyName: ['Hedley'],
-      givenName: ['Olsen']
-    },
-    phones: ['1-945-295-5863'],
-    emails: ['vulputate.ullamcorper@Vivamusnisi.org']
-  },
-  {
-    id: '270',
-    displayName: 'Oliver Todd',
-    name: {
-      familyName: ['Oliver'],
-      givenName: ['Todd']
-    },
-    phones: ['1-551-447-1296'],
-    emails: ['Donec.egestas@rutrum.edu']
-  },
-  {
-    id: '273',
-    displayName: 'Keegan Mayo',
-    name: {
-      familyName: ['Keegan'],
-      givenName: ['Mayo']
-    },
-    phones: ['1-351-848-2796'],
-    emails: ['ridiculus@Nuncsed.ca']
-  },
-  {
-    id: '276',
-    displayName: 'Wang Cote',
-    name: {
-      familyName: ['Wang'],
-      givenName: ['Cote']
-    },
-    phones: ['1-439-568-2013'],
-    emails: ['Morbi@tinciduntduiaugue.org']
-  },
-  {
-    id: '279',
-    displayName: 'Hyatt Rowe',
-    name: {
-      familyName: ['Hyatt'],
-      givenName: ['Rowe']
-    },
-    phones: ['1-596-765-3807'],
-    emails: ['eu.erat.semper@enimnonnisi.com']
-  },
-  {
-    id: '282',
-    displayName: 'Cade Wyatt',
-    name: {
-      familyName: ['Cade'],
-      givenName: ['Wyatt']
-    },
-    phones: ['1-988-289-5924'],
-    emails: ['erat.nonummy@sedpedeCum.com']
-  },
-  {
-    id: '285',
-    displayName: 'Stephen Vincent',
-    name: {
-      familyName: ['Stephen'],
-      givenName: ['Vincent']
-    },
-    phones: ['1-954-435-1259'],
-    emails: ['nec.euismod@ultricies.ca']
-  },
-  {
-    id: '288',
-    displayName: 'Tobias Cherry',
-    name: {
-      familyName: ['Tobias'],
-      givenName: ['Cherry']
-    },
-    phones: ['1-270-763-1111'],
-    emails: ['Nulla.aliquet@sit.com']
-  },
-  {
-    id: '291',
-    displayName: 'Keane Trevino',
-    name: {
-      familyName: ['Keane'],
-      givenName: ['Trevino']
-    },
-    phones: ['1-794-929-8599'],
-    emails: ['sem.semper.erat@Aliquamnecenim.edu']
-  },
-  {
-    id: '294',
-    displayName: 'Kennedy Cooley',
-    name: {
-      familyName: ['Kennedy'],
-      givenName: ['Cooley']
-    },
-    phones: ['1-725-946-1901'],
-    emails: ['urna.justo@Duismienim.edu']
-  },
-  {
-    id: '297',
-    displayName: 'Lucian Pope',
-    name: {
-      familyName: ['Lucian'],
-      givenName: ['Pope']
-    },
-    phones: ['1-186-946-8356'],
-    emails: ['justo.Proin@dis.com']
-  },
-  {
-    id: '300',
-    displayName: 'Hu Combs',
-    name: {
-      familyName: ['Hu'],
-      givenName: ['Combs']
-    },
-    phones: ['1-398-488-5222'],
-    emails: ['faucibus.lectus@nuncsedpede.com']
-  }
-];
-
-var ContactsManager = {
-  contacts: contacts,
-  find: function contactsManager(fields, successCallback, errorCallback) {
-    var contacts = this.contacts.slice();
-    successCallback(contacts);
-  },
-  create: function contactsCreate(successCallback, errorCallback, contact) {
-    this.contacts.push(contact);
-    successCallback();
-  },
-  delete: function contactsDelete(successCallback, errorCallback, id) {
-    var count = contacts.length;
-    for (var i = 0; i < count; i++) {
-      if (contacts[i].id != id)
-        continue;
-      var oldContact = contacts.slice(i, 1);
-      successCallback(oldContact);
-      return;
-    }
-    errorCallback();
-  }
-};
-
 let { classes: Cc, interfaces: Ci, utils: Cu } = Components;
 
 Cu.import('resource://gre/modules/Geometry.jsm');
 Cu.import('resource://gre/modules/Services.jsm');
 
 const ContentPanning = {
   init: function cp_init() {
     ['mousedown', 'mouseup', 'mousemove'].forEach(function(type) {
--- a/browser/devtools/debugger/DebuggerUI.jsm
+++ b/browser/devtools/debugger/DebuggerUI.jsm
@@ -35,18 +35,16 @@
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 "use strict";
 
-/*global Components, NetUtil, Services, XPCOMUtils */
-/*global DebuggerServer, DebuggerClient, SourceEditor */
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 
 Cu.import("resource://gre/modules/NetUtil.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/devtools/dbg-server.jsm");
 Cu.import("resource://gre/modules/devtools/dbg-client.jsm");
@@ -290,17 +288,17 @@ DebuggerUI.prototype = {
           onDataAvailable: function (aRequest, aContext, aStream, aOffset, aCount) {
             chunks.push(NetUtil.readInputStreamToString(aStream, aCount));
           },
           onStopRequest: function (aRequest, aContext, aStatusCode) {
             if (!Components.isSuccessCode(aStatusCode)) {
               return this.logError(url, aStatusCode);
             }
 
-            this._onSourceLoaded(url, chunks.join(""));
+            this._onSourceLoaded(url, chunks.join(""), channel.contentType);
           }.bind(this)
         };
 
         channel.loadFlags = channel.LOAD_FROM_CACHE;
         channel.asyncOpen(streamListener, null);
         break;
     }
   },
@@ -320,31 +318,32 @@ DebuggerUI.prototype = {
 
   /**
    * Called when source has been loaded.
    *
    * @param string aSourceUrl
    *        The URL of the source script.
    * @param string aSourceText
    *        The text of the source script.
+   * @param string aContentType
+   *        The content type of the source script.
    */
-  _onSourceLoaded: function DebuggerUI__onSourceLoaded(aSourceUrl, aSourceText) {
+  _onSourceLoaded: function DebuggerUI__onSourceLoaded(aSourceUrl,
+                                                       aSourceText,
+                                                       aContentType) {
     let dbg = this.getDebugger(this.aWindow.gBrowser.selectedTab);
-    if (aSourceUrl.slice(-3) == ".js") {
-      dbg.editor.setMode(SourceEditor.MODES.JAVASCRIPT);
-    } else {
-      dbg.editor.setMode(SourceEditor.MODES.HTML);
-    }
+    dbg.debuggerWindow.SourceScripts.setEditorMode(aSourceUrl, aContentType);
     dbg.editor.setText(aSourceText);
     let doc = dbg.frame.contentDocument;
     let scripts = doc.getElementById("scripts");
     let elt = scripts.getElementsByAttribute("value", aSourceUrl)[0];
     let script = elt.getUserData("sourceScript");
     script.loaded = true;
     script.text = aSourceText;
+    script.contentType = aContentType;
     elt.setUserData("sourceScript", script, null);
   }
 };
 
 /**
  * Various debugger UI preferences (currently just the pane height).
  */
 let DebuggerUIPreferences = {
--- a/browser/devtools/debugger/debugger-view.js
+++ b/browser/devtools/debugger/debugger-view.js
@@ -33,19 +33,17 @@
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the LGPL or the GPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  ***** END LICENSE BLOCK *****/
 "use strict";
 
-/*global Components, XPCOMUtils, Services, StackFrames, ThreadState, dump */
 const Cu = Components.utils;
-
 const DBG_STRINGS_URI = "chrome://browser/locale/devtools/debugger.properties";
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import('resource://gre/modules/Services.jsm');
 
 /**
  * Object mediating visual changes and event listeners between the debugger and
  * the html view.
@@ -1066,32 +1064,75 @@ DebuggerView.Scripts = {
    */
   empty: function DVS_empty() {
     while (this._scripts.firstChild) {
       this._scripts.removeChild(this._scripts.firstChild);
     }
   },
 
   /**
+   * Checks whether the script with the specified URL is among the scripts
+   * known to the debugger and shown in the list.
+   *
+   * @param string aUrl
+   *        The script URL.
+   */
+  contains: function DVS_contains(aUrl) {
+    if (this._scripts.getElementsByAttribute("value", aUrl).length > 0) {
+      return true;
+    }
+    return false;
+  },
+
+  /**
+   * Checks whether the script with the specified URL is selected in the list.
+   *
+   * @param string aUrl
+   *        The script URL.
+   */
+  isSelected: function DVS_isSelected(aUrl) {
+    if (this._scripts.selectedItem &&
+        this._scripts.selectedItem.value == aUrl) {
+      return true;
+    }
+    return false;
+  },
+
+  /**
+   * Selects the script with the specified URL from the list.
+   *
+   * @param string aUrl
+   *        The script URL.
+   */
+   selectScript: function DVS_selectScript(aUrl) {
+    for (let i = 0; i < this._scripts.itemCount; i++) {
+      if (this._scripts.getItemAtIndex(i).value == aUrl) {
+        this._scripts.selectedIndex = i;
+        break;
+      }
+    }
+   },
+
+  /**
    * Adds a script to the scripts container.
    * If the script already exists (was previously added), null is returned.
    * Otherwise, the newly created element is returned.
    *
    * @param string aUrl
    *        The script url.
    * @param string aScript
    *        The source script.
    * @param string aScriptNameText
    *        Optional, title displayed instead of url.
    * @return object
    *         The newly created html node representing the added script.
    */
   addScript: function DVS_addScript(aUrl, aSource, aScriptNameText) {
     // make sure we don't duplicate anything
-    if (this._scripts.getElementsByAttribute("value", aUrl).length > 0) {
+    if (this.contains(aUrl)) {
       return null;
     }
 
     let script = this._scripts.appendItem(aScriptNameText || aUrl, aUrl);
     script.setUserData("sourceScript", aSource, null);
     this._scripts.selectedItem = script;
     return script;
   },
--- a/browser/devtools/debugger/debugger.js
+++ b/browser/devtools/debugger/debugger.js
@@ -36,16 +36,18 @@
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 "use strict";
 
+Components.utils.import("resource:///modules/source-editor.jsm");
+
 var gInitialized = false;
 var gClient = null;
 var gTabClient = null;
 
 
 function initDebugger()
 {
   window.removeEventListener("DOMContentLoaded", initDebugger, false);
@@ -250,21 +252,29 @@ var StackFrames = {
       DebuggerView.Stackframes.highlightFrame(this.selectedFrame, false);
     }
 
     this.selectedFrame = aDepth;
     if (aDepth !== null) {
       DebuggerView.Stackframes.highlightFrame(this.selectedFrame, true);
     }
 
-    // Display the local variables.
     let frame = this.activeThread.cachedFrames[aDepth];
     if (!frame) {
       return;
     }
+    // Move the editor's caret to the proper line.
+    if (DebuggerView.Scripts.isSelected(frame.where.url) && frame.where.line) {
+      window.editor.setCaretPosition(frame.where.line - 1);
+    } else if (DebuggerView.Scripts.contains(frame.where.url)) {
+      DebuggerView.Scripts.selectScript(frame.where.url);
+      SourceScripts.onChange({ target: DebuggerView.Scripts._scripts });
+      window.editor.setCaretPosition(frame.where.line - 1);
+    }
+    // Display the local variables.
     let localScope = DebuggerView.Properties.localScope;
     localScope.empty();
     // Add "this".
     if (frame["this"]) {
       let thisVar = localScope.addVar("this");
       thisVar.setGrip({ "type": frame["this"].type,
                         "class": frame["this"].class });
       this._addExpander(thisVar, frame["this"]);
@@ -430,56 +440,95 @@ var SourceScripts = {
    */
   disconnect: function TS_disconnect() {
     this.activeThread.removeListener("paused", this.onPaused);
     this.activeThread.removeListener("scriptsadded", this.onScripts);
     this.activeThread.removeListener("scriptscleared", this.onScriptsCleared);
   },
 
   /**
-   * Handler for the thread client's paused notification.
+   * Handler for the thread client's paused notification. This is triggered only
+   * once, to retrieve the list of scripts known to the server from before the
+   * client was ready to handle new script notifications.
    */
   onPaused: function SS_onPaused() {
+    this.activeThread.removeListener("paused", this.onPaused);
     this.activeThread.fillScripts();
   },
 
   /**
    * Handler for the debugger client's unsolicited newScript notification.
    */
   onNewScript: function SS_onNewScript(aNotification, aPacket) {
     this._addScript({ url: aPacket.url, startLine: aPacket.startLine });
   },
 
   /**
    * Handler for the thread client's scriptsadded notification.
    */
   onScripts: function SS_onScripts() {
-    this.onScriptsCleared();
     for each (let script in this.activeThread.cachedScripts) {
       this._addScript(script);
     }
   },
 
   /**
    * Handler for the thread client's scriptscleared notification.
    */
   onScriptsCleared: function SS_onScriptsCleared() {
     DebuggerView.Scripts.empty();
   },
 
   /**
    * Handler for changes on the selected source script.
    */
-  onChange: function SS_onClick(aEvent) {
+  onChange: function SS_onChange(aEvent) {
     let scripts = aEvent.target;
+    if (!scripts.selectedItem) {
+      return;
+    }
     let script = scripts.selectedItem.getUserData("sourceScript");
+    this.setEditorMode(script.url, script.contentType);
     this._showScript(script);
   },
 
   /**
+   * Sets the proper editor mode (JS or HTML) according to the specified
+   * content type, or by determining the type from the URL.
+   *
+   * @param string aUrl
+   *        The script URL.
+   * @param string aContentType [optional]
+   *        The script content type.
+   */
+  setEditorMode: function SS_setEditorMode(aUrl, aContentType) {
+    if (aContentType) {
+      if (/javascript/.test(aContentType)) {
+        window.editor.setMode(SourceEditor.MODES.JAVASCRIPT);
+      } else {
+        window.editor.setMode(SourceEditor.MODES.HTML);
+      }
+      return;
+    }
+
+    let url = aUrl;
+    // Trim the query part.
+    let q = url.indexOf('?');
+    if (q > -1) {
+      url = url.slice(0, q);
+    }
+
+    if (url.slice(-3) == ".js") {
+      window.editor.setMode(SourceEditor.MODES.JAVASCRIPT);
+    } else {
+      window.editor.setMode(SourceEditor.MODES.HTML);
+    }
+  },
+
+  /**
    * Add the specified script to the list and display it in the editor if the
    * editor is empty.
    */
   _addScript: function SS_addScript(aScript) {
     DebuggerView.Scripts.addScript(aScript.url, aScript);
 
     if (window.editor.getCharCount() == 0) {
       this._showScript(aScript);
--- a/browser/devtools/debugger/test/Makefile.in
+++ b/browser/devtools/debugger/test/Makefile.in
@@ -65,27 +65,31 @@ include $(topsrcdir)/config/rules.mk
 	browser_dbg_panesize.js \
 	browser_dbg_stack-01.js \
 	browser_dbg_stack-02.js \
 	browser_dbg_stack-03.js \
 	browser_dbg_stack-04.js \
 	browser_dbg_location-changes.js \
 	browser_dbg_script-switching.js \
 	browser_dbg_pause-resume.js \
+	browser_dbg_update-editor-mode.js \
+	browser_dbg_select-line.js \
 	head.js \
 	$(NULL)
 
 _BROWSER_TEST_PAGES = \
 	browser_dbg_tab1.html \
 	browser_dbg_tab2.html \
 	browser_dbg_debuggerstatement.html \
 	browser_dbg_stack.html \
 	browser_dbg_script-switching.html \
 	test-script-switching-01.js \
 	test-script-switching-02.js \
 	browser_dbg_frame-parameters.html \
+	browser_dbg_update-editor-mode.html \
+	test-editor-mode \
 	$(NULL)
 
 libs:: $(_BROWSER_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
 
 libs:: $(_BROWSER_TEST_PAGES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
--- a/browser/devtools/debugger/test/browser_dbg_script-switching.js
+++ b/browser/devtools/debugger/test/browser_dbg_script-switching.js
@@ -28,45 +28,34 @@ function test()
 
     testScriptsDisplay();
   });
 }
 
 function testScriptsDisplay() {
   gPane.activeThread.addOneTimeListener("scriptsadded", function() {
     Services.tm.currentThread.dispatch({ run: function() {
-      let count = 0;
-      gDebugger.editor.addEventListener(SourceEditor.EVENTS.TEXT_CHANGED,
-                                        function onScriptLoad() {
-        // Skip the first change event, since we're only interested in the
-        // second.
-        if (count++ < 1) {
-          return;
-        }
-        gDebugger.editor.removeEventListener(SourceEditor.EVENTS.TEXT_CHANGED,
-                                             onScriptLoad);
-        gScripts = gDebugger.DebuggerView.Scripts._scripts;
+      gScripts = gDebugger.DebuggerView.Scripts._scripts;
 
-        is(gDebugger.StackFrames.activeThread.state, "paused",
-          "Should only be getting stack frames while paused.");
+      is(gDebugger.StackFrames.activeThread.state, "paused",
+        "Should only be getting stack frames while paused.");
+
+      is(gScripts.itemCount, 2, "Found the expected number of scripts.");
 
-        is(gScripts.itemCount, 2, "Found the expected number of scripts.");
-
-        ok(gDebugger.editor.getText().search(/debugger/) != -1,
-          "The correct script was loaded initially.");
+      ok(gDebugger.editor.getText().search(/debugger/) != -1,
+        "The correct script was loaded initially.");
 
-        gDebugger.editor.addEventListener(SourceEditor.EVENTS.TEXT_CHANGED,
-                                          function onChange() {
-          gDebugger.editor.removeEventListener(SourceEditor.EVENTS.TEXT_CHANGED,
-                                               onChange);
-          testSwitchPaused();
-        });
-        gScripts.selectedIndex = 0;
-        gDebugger.SourceScripts.onChange({ target: gScripts });
+      gDebugger.editor.addEventListener(SourceEditor.EVENTS.TEXT_CHANGED,
+                                        function onChange() {
+        gDebugger.editor.removeEventListener(SourceEditor.EVENTS.TEXT_CHANGED,
+                                             onChange);
+        testSwitchPaused();
       });
+      gScripts.selectedIndex = 0;
+      gDebugger.SourceScripts.onChange({ target: gScripts });
     }}, 0);
   });
 
   gDebuggee.firstCall();
 }
 
 function testSwitchPaused()
 {
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/browser_dbg_select-line.js
@@ -0,0 +1,81 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Make sure that selecting a stack frame loads the right script in the editor
+ * pane and highlights the proper line.
+ */
+
+const TAB_URL = "http://example.com/browser/browser/devtools/debugger/" +
+                "test/browser_dbg_script-switching.html";
+let tempScope = {};
+Cu.import("resource:///modules/source-editor.jsm", tempScope);
+let SourceEditor = tempScope.SourceEditor;
+
+var gPane = null;
+var gTab = null;
+var gDebuggee = null;
+var gDebugger = null;
+var gScripts = null;
+
+function test()
+{
+  debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
+    gTab = aTab;
+    gDebuggee = aDebuggee;
+    gPane = aPane;
+    gDebugger = gPane.debuggerWindow;
+
+    testSelectLine();
+  });
+}
+
+function testSelectLine() {
+  gPane.activeThread.addOneTimeListener("scriptsadded", function() {
+    Services.tm.currentThread.dispatch({ run: function() {
+      gScripts = gDebugger.DebuggerView.Scripts._scripts;
+
+      is(gDebugger.StackFrames.activeThread.state, "paused",
+        "Should only be getting stack frames while paused.");
+
+      is(gScripts.itemCount, 2, "Found the expected number of scripts.");
+
+      ok(gDebugger.editor.getText().search(/debugger/) != -1,
+        "The correct script was loaded initially.");
+
+      // getCaretPosition is 0-based.
+      is(gDebugger.editor.getCaretPosition().line, 5,
+         "The correct line is selected.");
+
+      gDebugger.editor.addEventListener(SourceEditor.EVENTS.TEXT_CHANGED,
+                                        function onChange() {
+        gDebugger.editor.removeEventListener(SourceEditor.EVENTS.TEXT_CHANGED,
+                                             onChange);
+        ok(gDebugger.editor.getText().search(/debugger/) == -1,
+          "The second script is no longer displayed.");
+
+        ok(gDebugger.editor.getText().search(/firstCall/) != -1,
+          "The first script is displayed.");
+
+        // Yield control back to the event loop so that the debugger has a
+        // chance to highlight the proper line.
+        executeSoon(function(){
+          // getCaretPosition is 0-based.
+          is(gDebugger.editor.getCaretPosition().line, 4,
+             "The correct line is selected.");
+
+          gDebugger.StackFrames.activeThread.resume(function() {
+            removeTab(gTab);
+            finish();
+          });
+        });
+      });
+
+      // Click the oldest stack frame.
+      let element = gDebugger.document.getElementById("stackframe-3");
+      EventUtils.synthesizeMouseAtCenter(element, {}, gDebugger);
+    }}, 0);
+  });
+
+  gDebuggee.firstCall();
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/browser_dbg_update-editor-mode.html
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML>
+<html>
+	<head>
+		<title>Browser Debugger Update Editor Mode Test</title>
+    <!-- Any copyright is dedicated to the Public Domain.
+         http://creativecommons.org/publicdomain/zero/1.0/ -->
+    <script type="text/javascript" src="test-script-switching-01.js?q=a"></script>
+    <script type="text/javascript" src="test-editor-mode?a=b"></script>
+	</head>
+	<body>
+	</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/browser_dbg_update-editor-mode.js
@@ -0,0 +1,78 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Make sure that updating the editor mode sets the right highlighting engine,
+ * and script URIs with extra query parameters also get the right engine.
+ */
+
+const TAB_URL = "http://example.com/browser/browser/devtools/debugger/" +
+                "test/browser_dbg_update-editor-mode.html";
+let tempScope = {};
+Cu.import("resource:///modules/source-editor.jsm", tempScope);
+let SourceEditor = tempScope.SourceEditor;
+
+var gPane = null;
+var gTab = null;
+var gDebuggee = null;
+var gDebugger = null;
+var gScripts = null;
+
+function test()
+{
+  debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
+    gTab = aTab;
+    gDebuggee = aDebuggee;
+    gPane = aPane;
+    gDebugger = gPane.debuggerWindow;
+
+    testScriptsDisplay();
+  });
+}
+
+function testScriptsDisplay() {
+  gPane.activeThread.addOneTimeListener("scriptsadded", function() {
+    Services.tm.currentThread.dispatch({ run: function() {
+      gScripts = gDebugger.DebuggerView.Scripts._scripts;
+
+      is(gDebugger.StackFrames.activeThread.state, "paused",
+        "Should only be getting stack frames while paused.");
+
+      is(gScripts.itemCount, 2, "Found the expected number of scripts.");
+
+      is(gDebugger.editor.getMode(), SourceEditor.MODES.HTML,
+         "Found the expected editor mode.");
+
+      ok(gDebugger.editor.getText().search(/debugger/) != -1,
+        "The correct script was loaded initially.");
+
+      gDebugger.editor.addEventListener(SourceEditor.EVENTS.TEXT_CHANGED,
+                                        function onChange() {
+        gDebugger.editor.removeEventListener(SourceEditor.EVENTS.TEXT_CHANGED,
+                                             onChange);
+        testSwitchPaused();
+      });
+      gScripts.selectedIndex = 0;
+      gDebugger.SourceScripts.onChange({ target: gScripts });
+    }}, 0);
+  });
+
+  gDebuggee.firstCall();
+}
+
+function testSwitchPaused()
+{
+  ok(gDebugger.editor.getText().search(/debugger/) == -1,
+    "The second script is no longer displayed.");
+
+  ok(gDebugger.editor.getText().search(/firstCall/) != -1,
+    "The first script is displayed.");
+
+  is(gDebugger.editor.getMode(), SourceEditor.MODES.JAVASCRIPT,
+     "Found the expected editor mode.");
+
+  gDebugger.StackFrames.activeThread.resume(function() {
+    removeTab(gTab);
+    finish();
+  });
+}
copy from browser/devtools/debugger/test/test-script-switching-02.js
copy to browser/devtools/debugger/test/test-editor-mode
--- a/browser/devtools/debugger/test/test-script-switching-02.js
+++ b/browser/devtools/debugger/test/test-script-switching-02.js
@@ -1,6 +1,7 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 function secondCall() {
+  // This comment is useful for browser_dbg_select-line.js.
   eval("debugger;");
 }
--- a/browser/devtools/tilt/Tilt.jsm
+++ b/browser/devtools/tilt/Tilt.jsm
@@ -241,17 +241,17 @@ Tilt.prototype = {
    * A node was selected in the Inspector.
    * Called from InspectorUI.
    *
    * @param {Element} aNode
    *                  the newly selected node
    */
   update: function T_update(aNode) {
     if (this.currentInstance) {
-      this.currentInstance.presenter.highlightNode(aNode);
+      this.currentInstance.presenter.highlightNode(aNode, "moveIntoView");
     }
   },
 
   /**
    * Add the browser event listeners to handle state changes.
    * Called from InspectorUI.
    */
   setup: function T_setup()
--- a/browser/devtools/tilt/TiltUtils.jsm
+++ b/browser/devtools/tilt/TiltUtils.jsm
@@ -543,28 +543,16 @@ TiltUtils.getWindowId = function TU_getW
   }
 
   return aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
                 .getInterface(Ci.nsIDOMWindowUtils)
                 .currentInnerWindowID;
 };
 
 /**
- * Gets the markup document viewer zoom for the currently selected browser.
- *
- * @param {Window} aChromeWindow
- *                 the top-level browser window
- *
- * @return {Number} the zoom ammount
- */
-TiltUtils.getDocumentZoom = function TU_getDocumentZoom(aChromeWindow) {
-  return aChromeWindow.gBrowser.selectedBrowser.markupDocumentViewer.fullZoom;
-};
-
-/**
  * Sets the markup document viewer zoom for the currently selected browser.
  *
  * @param {Window} aChromeWindow
  *                 the top-level browser window
  *
  * @param {Number} the zoom ammount
  */
 TiltUtils.setDocumentZoom = function TU_setDocumentZoom(aChromeWindow, aZoom) {
--- a/browser/devtools/tilt/TiltVisualizer.jsm
+++ b/browser/devtools/tilt/TiltVisualizer.jsm
@@ -55,16 +55,17 @@ const INVISIBLE_ELEMENTS = {
   "title": true
 };
 
 const STACK_THICKNESS = 15;
 const WIREFRAME_COLOR = [0, 0, 0, 0.25];
 const INTRO_TRANSITION_DURATION = 50;
 const OUTRO_TRANSITION_DURATION = 40;
 const INITIAL_Z_TRANSLATION = 400;
+const MOVE_INTO_VIEW_ACCURACY = 50;
 
 const MOUSE_CLICK_THRESHOLD = 10;
 const MOUSE_INTRO_DELAY = 10;
 const ARCBALL_SENSITIVITY = 0.5;
 const ARCBALL_ROTATION_STEP = 0.15;
 const ARCBALL_TRANSLATION_STEP = 35;
 const ARCBALL_ZOOM_STEP = 0.1;
 const ARCBALL_ZOOM_MIN = -3000;
@@ -245,17 +246,17 @@ TiltVisualizer.Presenter = function TV_P
     v3: vec3.create()
   };
 
   /**
    * Scene transformations, exposing offset, translation and rotation.
    * Modified by events in the controller through delegate functions.
    */
   this.transforms = {
-    zoom: TiltUtils.getDocumentZoom(aChromeWindow),
+    zoom: 1,
     offset: vec3.create(),      // mesh offset, aligned to the viewport center
     translation: vec3.create(), // scene translation, on the [x, y, z] axis
     rotation: quat4.create()    // scene rotation, expressed as a quaternion
   };
 
   /**
    * Variables holding information about the initial and current node selected.
    */
@@ -275,30 +276,36 @@ TiltVisualizer.Presenter = function TV_P
   this.frames = 0;
 
   /**
    * The initialization logic.
    */
   let setup = function TVP_setup()
   {
     let renderer = this.renderer;
+    let inspector = this.chromeWindow.InspectorUI;
 
     // if the renderer was destroyed, don't continue setup
     if (!renderer || !renderer.context) {
       return;
     }
 
     // create the visualization shaders and program to draw the stacks mesh
     this.visualizationProgram = new renderer.Program({
       vs: TiltVisualizer.MeshShader.vs,
       fs: TiltVisualizer.MeshShader.fs,
       attributes: ["vertexPosition", "vertexTexCoord", "vertexColor"],
       uniforms: ["mvMatrix", "projMatrix", "sampler"]
     });
 
+    // get the document zoom to properly scale the visualization
+    if (inspector.highlighter) {
+      this.transforms.zoom = inspector.highlighter.zoom;
+    }
+
     this.setupTexture();
     this.setupMeshData();
     this.setupEventListeners();
     this.canvas.focus();
   }.bind(this);
 
   /**
    * The animation logic.
@@ -541,19 +548,18 @@ TiltVisualizer.Presenter.prototype = {
     if (!this._initialSelection) {
       this._initialSelection = true;
       this.highlightNode(this.chromeWindow.InspectorUI.selection);
     }
 
     if (!this._initialMeshConfiguration) {
       this._initialMeshConfiguration = true;
 
-      let zoom = this.transforms.zoom;
-      let width = Math.min(aData.meshWidth * zoom, renderer.width);
-      let height = Math.min(aData.meshHeight * zoom, renderer.height);
+      let width = renderer.width;
+      let height = renderer.height;
 
       // set the necessary mesh offsets
       this.transforms.offset[0] = -width * 0.5;
       this.transforms.offset[1] = -height * 0.5;
 
       // make sure the canvas is opaque now that the initialization is finished
       this.canvas.style.background = TiltVisualizerStyle.canvas.background;
 
@@ -615,36 +621,38 @@ TiltVisualizer.Presenter.prototype = {
     this.contentWindow.addEventListener("resize", this.onResize, false);
   },
 
   /**
    * Called when the content window of the current browser is resized.
    */
   onResize: function TVP_onResize(e)
   {
-    let zoom = TiltUtils.getDocumentZoom(this.chromeWindow);
+    let zoom = this.chromeWindow.InspectorUI.highlighter.zoom;
     let width = e.target.innerWidth * zoom;
     let height = e.target.innerHeight * zoom;
 
     // handle aspect ratio changes to update the projection matrix
     this.renderer.width = width;
     this.renderer.height = height;
 
     this.redraw = true;
   },
 
   /**
    * Highlights a specific node.
    *
    * @param {Element} aNode
    *                  the html node to be highlighted
+   * @param {String} aFlags
+   *                 flags specifying highlighting options
    */
-  highlightNode: function TVP_highlightNode(aNode)
+  highlightNode: function TVP_highlightNode(aNode, aFlags)
   {
-    this.highlightNodeFor(this.traverseData.nodes.indexOf(aNode));
+    this.highlightNodeFor(this.traverseData.nodes.indexOf(aNode), aFlags);
   },
 
   /**
    * Picks a stacked dom node at the x and y screen coordinates and highlights
    * the selected node in the mesh.
    *
    * @param {Number} x
    *                 the current horizontal coordinate of the mouse
@@ -693,18 +701,20 @@ TiltVisualizer.Presenter.prototype = {
   },
 
   /**
    * Sets the corresponding highlight coordinates and color based on the
    * information supplied.
    *
    * @param {Number} aNodeIndex
    *                 the index of the node in the this.traverseData array
+   * @param {String} aFlags
+   *                 flags specifying highlighting options
    */
-  highlightNodeFor: function TVP_highlightNodeFor(aNodeIndex)
+  highlightNodeFor: function TVP_highlightNodeFor(aNodeIndex, aFlags)
   {
     this.redraw = true;
 
     // if the node was already selected, don't do anything
     if (this._currentSelection === aNodeIndex) {
       return;
     }
 
@@ -739,16 +749,27 @@ TiltVisualizer.Presenter.prototype = {
     vec3.set([x,     y + h, z * STACK_THICKNESS], highlight.v3);
 
     this._currentSelection = aNodeIndex;
 
     this.chromeWindow.InspectorUI.inspectNode(node,
       this.contentWindow.innerHeight < y ||
       this.contentWindow.pageYOffset > 0);
 
+    // if something is highlighted, make sure it's inside the current viewport;
+    // the point which should be moved into view is considered the center [x, y]
+    // position along the top edge of the currently selected node
+
+    if (aFlags && aFlags.indexOf("moveIntoView") !== -1)
+    {
+      this.controller.arcball.moveIntoView(vec3.lerp(
+        vec3.scale(this.highlight.v0, this.transforms.zoom, []),
+        vec3.scale(this.highlight.v1, this.transforms.zoom, []), 0.5));
+    }
+
     Services.obs.notifyObservers(null, this.NOTIFICATIONS.HIGHLIGHTING, null);
   },
 
   /**
    * Deletes a node from the visualization mesh.
    *
    * @param {Number} aNodeIndex
    *                 the index of the node in the this.traverseData array;
@@ -808,17 +829,17 @@ TiltVisualizer.Presenter.prototype = {
         }
       } else {
         if ("function" === typeof aProperties.onfail) {
           aProperties.onfail();
         }
       }
     }, false);
 
-    let zoom = TiltUtils.getDocumentZoom(this.chromeWindow);
+    let zoom = this.chromeWindow.InspectorUI.highlighter.zoom;
     let width = this.renderer.width * zoom;
     let height = this.renderer.height * zoom;
     let mesh = this.meshStacks;
     x *= zoom;
     y *= zoom;
 
     // create a ray following the mouse direction from the near clipping plane
     // to the far clipping plane, to check for intersections with the mesh,
@@ -992,16 +1013,17 @@ TiltVisualizer.Controller = function TV_
    * A canvas overlay on which mouse and keyboard event listeners are attached.
    */
   this.canvas = aCanvas;
 
   /**
    * Save a reference to the presenter to modify its model-view transforms.
    */
   this.presenter = aPresenter;
+  this.presenter.controller = this;
 
   /**
    * The initial controller dimensions and offset, in pixels.
    */
   this.zoom = aPresenter.transforms.zoom;
   this.left = (aPresenter.contentWindow.pageXOffset || 0) * this.zoom;
   this.top = (aPresenter.contentWindow.pageYOffset || 0) * this.zoom;
   this.width = aCanvas.width;
@@ -1251,17 +1273,17 @@ TiltVisualizer.Controller.prototype = {
     this.arcball.cancelKeyEvents();
   },
 
   /**
    * Called when the content window of the current browser is resized.
    */
   onResize: function TVC_onResize(e)
   {
-    let zoom = TiltUtils.getDocumentZoom(this.presenter.chromeWindow);
+    let zoom = this.presenter.chromeWindow.InspectorUI.highlighter.zoom;
     let width = e.target.innerWidth * zoom;
     let height = e.target.innerHeight * zoom;
 
     this.arcball.resize(width, height);
   },
 
   /**
    * Checks if this object was initialized properly.
@@ -1724,16 +1746,35 @@ TiltVisualizer.Arcball.prototype = {
    */
   cancelMouseEvents: function TVA_cancelMouseEvents()
   {
     this._rotating = false;
     this._mouseButton = -1;
   },
 
   /**
+   * Moves a target point into view only if it's outside the currently visible
+   * area bounds (in which case it also resets any additional transforms).
+   *
+   * @param {Arary} aPoint
+   *                the [x, y] point which should be brought into view
+   */
+  moveIntoView: function TVA_moveIntoView(aPoint) {
+    let visiblePointX = -(this._currentTrans[0] + this._additionalTrans[0]);
+    let visiblePointY = -(this._currentTrans[1] + this._additionalTrans[1]);
+
+    if (aPoint[1] - visiblePointY - MOVE_INTO_VIEW_ACCURACY > this.height ||
+        aPoint[1] - visiblePointY + MOVE_INTO_VIEW_ACCURACY < 0 ||
+        aPoint[0] - visiblePointX > this.width ||
+        aPoint[0] - visiblePointX < 0) {
+      this.reset([0, -aPoint[1]]);
+    }
+  },
+
+  /**
    * Resize this implementation to use different bounds.
    * This function is automatically called when the arcball is created.
    *
    * @param {Number} newWidth
    *                 the new width of canvas
    * @param {Number} newHeight
    *                 the new  height of canvas
    * @param {Number} newRadius
--- a/browser/devtools/tilt/test/Makefile.in
+++ b/browser/devtools/tilt/test/Makefile.in
@@ -71,16 +71,17 @@ include $(topsrcdir)/config/rules.mk
 	browser_tilt_math02.js \
 	browser_tilt_math03.js \
 	browser_tilt_math04.js \
 	browser_tilt_math05.js \
 	browser_tilt_math06.js \
 	browser_tilt_math07.js \
 	browser_tilt_picking.js \
 	browser_tilt_picking_delete.js \
+	browser_tilt_picking_highlight01-offs.js \
 	browser_tilt_picking_highlight01.js \
 	browser_tilt_picking_highlight02.js \
 	browser_tilt_picking_highlight03.js \
 	browser_tilt_utils01.js \
 	browser_tilt_utils02.js \
 	browser_tilt_utils03.js \
 	browser_tilt_utils04.js \
 	browser_tilt_utils05.js \
--- a/browser/devtools/tilt/test/browser_tilt_picking.js
+++ b/browser/devtools/tilt/test/browser_tilt_picking.js
@@ -18,17 +18,17 @@ function test() {
     createTilt({
       onTiltOpen: function(instance)
       {
         let presenter = instance.presenter;
         let canvas = presenter.canvas;
 
         presenter.onSetupMesh = function() {
 
-          presenter.pickNode(canvas.width / 2, canvas.height / 2, {
+          presenter.pickNode(canvas.width / 2, 10, {
             onpick: function(data)
             {
               ok(data.index > 0,
                 "Simply picking a node didn't work properly.");
               ok(!presenter.highlight.disabled,
                 "After only picking a node, it shouldn't be highlighted.");
 
               Services.obs.addObserver(cleanup, DESTROYED, false);
--- a/browser/devtools/tilt/test/browser_tilt_picking_delete.js
+++ b/browser/devtools/tilt/test/browser_tilt_picking_delete.js
@@ -19,18 +19,17 @@ function test() {
   createTab(function() {
     createTilt({
       onTiltOpen: function(instance)
       {
         presenter = instance.presenter;
         Services.obs.addObserver(whenNodeRemoved, NODE_REMOVED, false);
 
         presenter.onSetupMesh = function() {
-          presenter.highlightNodeAt(presenter.canvas.width / 2,
-                                    presenter.canvas.height / 2, {
+          presenter.highlightNodeAt(presenter.canvas.width / 2, 10, {
             onpick: function()
             {
               ok(presenter._currentSelection > 0,
                 "Highlighting a node didn't work properly.");
               ok(!presenter.highlight.disabled,
                 "After highlighting a node, it should be highlighted. D'oh.");
 
               presenter.deleteNode();
new file mode 100644
--- /dev/null
+++ b/browser/devtools/tilt/test/browser_tilt_picking_highlight01-offs.js
@@ -0,0 +1,70 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+let presenter;
+
+function test() {
+  if (!isTiltEnabled()) {
+    info("Skipping highlight test because Tilt isn't enabled.");
+    return;
+  }
+  if (!isWebGLSupported()) {
+    info("Skipping highlight test because WebGL isn't supported.");
+    return;
+  }
+
+  requestLongerTimeout(10);
+  waitForExplicitFinish();
+
+  createTab(function() {
+    createTilt({
+      onTiltOpen: function(instance)
+      {
+        presenter = instance.presenter;
+        Services.obs.addObserver(whenHighlighting, HIGHLIGHTING, false);
+
+        presenter.onInitializationFinished = function() {
+          let contentDocument = presenter.contentWindow.document;
+          let div = contentDocument.getElementById("far-far-away");
+
+          presenter.highlightNode(div, "moveIntoView");
+        };
+      }
+    });
+  });
+}
+
+function whenHighlighting() {
+  ok(presenter._currentSelection > 0,
+    "Highlighting a node didn't work properly.");
+  ok(!presenter.highlight.disabled,
+    "After highlighting a node, it should be highlighted. D'oh.");
+  ok(presenter.controller.arcball._resetInterval,
+    "Highlighting a node that's not already visible should trigger a reset!");
+
+  executeSoon(function() {
+    Services.obs.addObserver(whenUnhighlighting, UNHIGHLIGHTING, false);
+    presenter.highlightNode(null);
+  });
+}
+
+function whenUnhighlighting() {
+  ok(presenter._currentSelection < 0,
+    "Unhighlighting a should remove the current selection.");
+  ok(presenter.highlight.disabled,
+    "After unhighlighting a node, it shouldn't be highlighted anymore. D'oh.");
+
+  executeSoon(function() {
+    Services.obs.addObserver(cleanup, DESTROYED, false);
+    InspectorUI.closeInspectorUI();
+  });
+}
+
+function cleanup() {
+  Services.obs.removeObserver(whenHighlighting, HIGHLIGHTING);
+  Services.obs.removeObserver(whenUnhighlighting, UNHIGHLIGHTING);
+  Services.obs.removeObserver(cleanup, DESTROYED);
+  gBrowser.removeCurrentTab();
+  finish();
+}
--- a/browser/devtools/tilt/test/browser_tilt_picking_highlight01.js
+++ b/browser/devtools/tilt/test/browser_tilt_picking_highlight01.js
@@ -22,28 +22,30 @@ function test() {
       {
         presenter = instance.presenter;
         Services.obs.addObserver(whenHighlighting, HIGHLIGHTING, false);
 
         presenter.onSetupMesh = function() {
           let contentDocument = presenter.contentWindow.document;
           let div = contentDocument.getElementById("first-law");
 
-          presenter.highlightNode(div);
+          presenter.highlightNode(div, "moveIntoView");
         };
       }
     });
   });
 }
 
 function whenHighlighting() {
   ok(presenter._currentSelection > 0,
     "Highlighting a node didn't work properly.");
   ok(!presenter.highlight.disabled,
     "After highlighting a node, it should be highlighted. D'oh.");
+  ok(!presenter.controller.arcball._resetInterval,
+    "Highlighting a node that's already visible shouldn't trigger a reset.");
 
   executeSoon(function() {
     Services.obs.addObserver(whenUnhighlighting, UNHIGHLIGHTING, false);
     presenter.highlightNode(null);
   });
 }
 
 function whenUnhighlighting() {
--- a/browser/devtools/tilt/test/browser_tilt_picking_highlight02.js
+++ b/browser/devtools/tilt/test/browser_tilt_picking_highlight02.js
@@ -19,18 +19,17 @@ function test() {
   createTab(function() {
     createTilt({
       onTiltOpen: function(instance)
       {
         presenter = instance.presenter;
         Services.obs.addObserver(whenHighlighting, HIGHLIGHTING, false);
 
         presenter.onSetupMesh = function() {
-          presenter.highlightNodeAt(presenter.canvas.width / 2,
-                                    presenter.canvas.height / 2);
+          presenter.highlightNodeAt(presenter.canvas.width / 2, 10);
         };
       }
     });
   });
 }
 
 function whenHighlighting() {
   ok(presenter._currentSelection > 0,
--- a/browser/devtools/tilt/test/browser_tilt_zoom.js
+++ b/browser/devtools/tilt/test/browser_tilt_zoom.js
@@ -1,23 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 "use strict";
 
 const ZOOM = 2;
 const RESIZE = 50;
 
 function test() {
-  let random = Math.random() * 10;
-
-  TiltUtils.setDocumentZoom(window, random);
-  ok(isApprox(TiltUtils.getDocumentZoom(window), random),
-    "The getDocumentZoom utility function didn't return the expected results.");
-
-
   if (!isTiltEnabled()) {
     info("Skipping controller test because Tilt isn't enabled.");
     return;
   }
   if (!isWebGLSupported()) {
     info("Skipping controller test because WebGL isn't supported.");
     return;
   }
@@ -27,16 +20,19 @@ function test() {
   createTab(function() {
     createTilt({
       onInspectorOpen: function()
       {
         TiltUtils.setDocumentZoom(window, ZOOM);
       },
       onTiltOpen: function(instance)
       {
+        ok(isApprox(InspectorUI.highlighter.zoom, ZOOM),
+          "The Highlighter zoom doesn't have the expected results.");
+
         ok(isApprox(instance.presenter.transforms.zoom, ZOOM),
           "The presenter transforms zoom wasn't initially set correctly.");
 
         let contentWindow = gBrowser.selectedBrowser.contentWindow;
         let initialWidth = contentWindow.innerWidth;
         let initialHeight = contentWindow.innerHeight;
 
         let renderer = instance.presenter.renderer;
@@ -69,20 +65,21 @@ function test() {
           ok(isApprox(contentWindow.innerWidth * ZOOM, arcball.width, 1),
             "The arcball width wasn't set correctly after the resize.");
           ok(isApprox(contentWindow.innerHeight * ZOOM, arcball.height, 1),
             "The arcball height wasn't set correctly after the resize.");
 
 
           window.resizeBy(RESIZE * ZOOM, RESIZE * ZOOM);
 
+
           Services.obs.addObserver(cleanup, DESTROYED, false);
           InspectorUI.closeInspectorUI();
         });
-      },
+      }
     });
   });
 }
 
 function cleanup() {
   Services.obs.removeObserver(cleanup, DESTROYED);
   gBrowser.removeCurrentTab();
   finish();
--- a/browser/devtools/tilt/test/head.js
+++ b/browser/devtools/tilt/test/head.js
@@ -32,16 +32,19 @@ const DEFAULT_HTML = "data:text/html," +
       "<div>" +
         "A robot must obey the orders given to it by human beings, except " +
         "where such orders would conflict with the First Law." +
       "</div>" +
       "<div>" +
         "A robot must protect its own existence as long as such protection " +
         "does not conflict with the First or Second Laws." +
       "</div>" +
+      "<div id='far-far-away' style='position: absolute; top: 250%;'>" +
+        "I like bacon." +
+      "</div>" +
     "<body>" +
   "</html>";
 
 const INSPECTOR_OPENED = InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED;
 const INSPECTOR_CLOSED = InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED;
 
 const INITIALIZING = Tilt.NOTIFICATIONS.INITIALIZING;
 const INITIALIZED = Tilt.NOTIFICATIONS.INITIALIZED;
@@ -132,45 +135,51 @@ function createTilt(callbacks, close) {
 
   function onInspectorOpen() {
     Services.obs.removeObserver(onInspectorOpen, INSPECTOR_OPENED);
 
     executeSoon(function() {
       if ("function" === typeof callbacks.onInspectorOpen) {
         callbacks.onInspectorOpen();
       }
-      Services.obs.addObserver(onTiltOpen, INITIALIZING, false);
-      Tilt.initialize();
+      executeSoon(function() {
+        Services.obs.addObserver(onTiltOpen, INITIALIZING, false);
+        Tilt.initialize();
+      });
     });
   }
 
   function onTiltOpen() {
     Services.obs.removeObserver(onTiltOpen, INITIALIZING);
 
     executeSoon(function() {
       if ("function" === typeof callbacks.onTiltOpen) {
         callbacks.onTiltOpen(Tilt.visualizers[Tilt.currentWindowId]);
       }
       if (close) {
-        Services.obs.addObserver(onTiltClose, DESTROYED, false);
-        Tilt.destroy(Tilt.currentWindowId);
+        executeSoon(function() {
+          Services.obs.addObserver(onTiltClose, DESTROYED, false);
+          Tilt.destroy(Tilt.currentWindowId);
+        });
       }
     });
   }
 
   function onTiltClose() {
     Services.obs.removeObserver(onTiltClose, DESTROYED);
 
     executeSoon(function() {
       if ("function" === typeof callbacks.onTiltClose) {
         callbacks.onTiltClose();
       }
       if (close) {
-        Services.obs.addObserver(onInspectorClose, INSPECTOR_CLOSED, false);
-        InspectorUI.closeInspectorUI();
+        executeSoon(function() {
+          Services.obs.addObserver(onInspectorClose, INSPECTOR_CLOSED, false);
+          InspectorUI.closeInspectorUI();
+        });
       }
     });
   }
 
   function onInspectorClose() {
     Services.obs.removeObserver(onInspectorClose, INSPECTOR_CLOSED);
 
     executeSoon(function() {
--- a/content/events/src/nsDOMEventTargetHelper.cpp
+++ b/content/events/src/nsDOMEventTargetHelper.cpp
@@ -38,16 +38,17 @@
 
 #include "nsDOMEventTargetHelper.h"
 #include "nsContentUtils.h"
 #include "nsEventDispatcher.h"
 #include "nsGUIEvent.h"
 #include "nsIDocument.h"
 #include "nsIJSContextStack.h"
 #include "nsDOMJSUtils.h"
+#include "prprf.h"
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMEventListenerWrapper)
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMEventListenerWrapper)
   NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
 NS_INTERFACE_MAP_END_AGGREGATED(mListener)
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMEventListenerWrapper)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMEventListenerWrapper)
@@ -67,17 +68,31 @@ nsDOMEventListenerWrapper::HandleEvent(n
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMEventTargetHelper)
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsDOMEventTargetHelper)
   NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDOMEventTargetHelper)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsDOMEventTargetHelper)
+  if (NS_UNLIKELY(cb.WantDebugInfo())) {
+    char name[512];
+    nsAutoString uri;
+    if (tmp->mOwner && tmp->mOwner->GetExtantDocument()) {
+      tmp->mOwner->GetExtantDocument()->GetDocumentURI(uri);
+    }
+    PR_snprintf(name, sizeof(name), "nsDOMEventTargetHelper %s",
+                NS_ConvertUTF16toUTF8(uri).get());
+    cb.DescribeRefCountedNode(tmp->mRefCnt.get(), sizeof(nsDOMEventTargetHelper),
+                              name);
+  } else {
+    NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsDOMEventTargetHelper, tmp->mRefCnt.get())
+  }
+
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_MEMBER(mListenerManager,
                                                   nsEventListenerManager)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mScriptContext)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOwner)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDOMEventTargetHelper)
--- a/toolkit/devtools/debugger/server/dbg-script-actors.js
+++ b/toolkit/devtools/debugger/server/dbg-script-actors.js
@@ -1090,16 +1090,20 @@ FrameActor.prototype = {
     }
 
     let envActor = this.threadActor
                        .createEnvironmentActor(this.frame,
                                                this.frameLifetimePool);
     grip.environment = envActor ? envActor.grip() : envActor;
     grip["this"] = this.threadActor.createValueGrip(this.frame["this"]);
     grip.arguments = this._args();
+    if (this.frame.script) {
+      grip.where = { url: this.frame.script.url,
+                     line: this.frame.script.getOffsetLine(this.frame.offset) };
+    }
 
     if (!this.frame.older) {
       grip.oldest = true;
     }
 
     return grip;
   },