Merge m-c to s-c.
authorRichard Newman <rnewman@mozilla.com>
Sat, 14 Apr 2012 01:44:24 -0700
changeset 93450 2e20bc3aa6506014c132a0fed376451a018ad548
parent 93449 e24ad9cee74116f513685d8babde06d43b9ac2af (current diff)
parent 91593 364f0a5a1d2dc959bba2dfada7bb2eade016d06a (diff)
child 93451 18b6a2def113996466fdc22734bce1555e717c47
push id22635
push usergszorc@mozilla.com
push dateTue, 08 May 2012 16:52:49 +0000
treeherdermozilla-central@40b7177ea13a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone14.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to s-c.
browser/components/places/tests/browser/browser_457473_no_copy_guid.js
browser/devtools/debugger/debugger.js
dom/bindings/parser/__init__.py
js/src/v8-dtoa/README
js/src/v8-dtoa/cached-powers.h
js/src/v8-dtoa/checks.cc
js/src/v8-dtoa/checks.h
js/src/v8-dtoa/conversions.cc
js/src/v8-dtoa/conversions.h
js/src/v8-dtoa/diy-fp.cc
js/src/v8-dtoa/diy-fp.h
js/src/v8-dtoa/double.h
js/src/v8-dtoa/dtoa.h
js/src/v8-dtoa/fast-dtoa.cc
js/src/v8-dtoa/fast-dtoa.h
js/src/v8-dtoa/globals.h
js/src/v8-dtoa/include-v8.h
js/src/v8-dtoa/platform.cc
js/src/v8-dtoa/platform.h
js/src/v8-dtoa/powers-ten.h
js/src/v8-dtoa/utils.cc
js/src/v8-dtoa/utils.h
js/src/v8-dtoa/v8-dtoa.cc
js/src/v8-dtoa/v8.h
toolkit/components/ctypes/tests/jsctypes-test-finalizer.cpp
toolkit/components/ctypes/tests/jsctypes-test-finalizer.h
toolkit/components/ctypes/tests/unit/test_finalizer.js
toolkit/components/ctypes/tests/unit/test_finalizer_shouldaccept.js
toolkit/components/ctypes/tests/unit/test_finalizer_shouldfail.js
toolkit/components/places/tests/bookmarks/test_360134.js
toolkit/components/places/tests/bookmarks/test_484026.js
toolkit/components/places/tests/bookmarks/test_restore_guids.js
toolkit/components/places/tests/unit/test_txnGUIDs.js
--- a/accessible/src/mac/mozAccessible.mm
+++ b/accessible/src/mac/mozAccessible.mm
@@ -227,17 +227,17 @@ GetNativeFromGeckoAccessible(nsIAccessib
   if ([attribute isEqualToString:NSAccessibilityEnabledAttribute])
     return [NSNumber numberWithBool:[self isEnabled]];
   if ([attribute isEqualToString:NSAccessibilityValueAttribute])
     return [self value];
   if ([attribute isEqualToString:NSAccessibilityRoleDescriptionAttribute]) {
     if (mRole == roles::DOCUMENT)
       return utils::LocalizedString(NS_LITERAL_STRING("htmlContent"));
 
-    return NSAccessibilityRoleDescription([self role], nil);
+    return NSAccessibilityRoleDescription([self role], [self subrole]);
   }
   
   if ([attribute isEqualToString:NSAccessibilityDescriptionAttribute])
     return [self customDescription];
   if ([attribute isEqualToString:NSAccessibilityFocusedAttribute])
     return [NSNumber numberWithBool:[self isFocused]];
   if ([attribute isEqualToString:NSAccessibilitySizeAttribute])
     return [self size];
--- a/accessible/src/mac/mozTextAccessible.mm
+++ b/accessible/src/mac/mozTextAccessible.mm
@@ -285,17 +285,19 @@ ToNSString(id aValue)
 
   [super accessibilitySetValue:value forAttribute:attribute];
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
 - (NSString*)subrole
 {
-  // TODO: text accessibles have two different subroles in Cocoa: secure textfield (passwords) and search field
+  if(mRole == roles::PASSWORD_TEXT)
+    return NSAccessibilitySecureTextFieldSubrole;
+
   return nil;
 }
 
 - (void)expire
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
   NS_IF_RELEASE(mGeckoTextAccessible);
@@ -342,17 +344,21 @@ ToNSString(id aValue)
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
 - (NSString*)text
 {
   if (!mGeckoTextAccessible)
     return nil;
-    
+
+  // A password text field returns an empty value
+  if (mRole == roles::PASSWORD_TEXT)
+    return @"";
+
   nsAutoString text;
   nsresult rv = mGeckoTextAccessible->
     GetText(0, nsIAccessibleText::TEXT_OFFSET_END_OF_TEXT, text);
   NS_ENSURE_SUCCESS(rv, @"");
 
   return nsCocoaUtils::ToNSString(text);
 }
 
--- a/accessible/src/mac/nsAccessibleWrap.mm
+++ b/accessible/src/mac/nsAccessibleWrap.mm
@@ -116,16 +116,17 @@ nsAccessibleWrap::GetNativeType ()
     case roles::PAGETABLIST:
       return [mozTabsAccessible class];
       
     case roles::ENTRY:
     case roles::STATICTEXT:
     case roles::CAPTION:
     case roles::ACCEL_LABEL:
     case roles::TEXT_LEAF:
+    case roles::PASSWORD_TEXT:
       // normal textfield (static or editable)
       return [mozTextAccessible class]; 
 
     case roles::LINK:
       return [mozLinkAccessible class];
 
     case roles::COMBOBOX:
       return [mozPopupButtonAccessible class];
--- a/accessible/tests/mochitest/events/test_focus_menu.xul
+++ b/accessible/tests/mochitest/events/test_focus_menu.xul
@@ -17,17 +17,17 @@
           src="../role.js" />
   <script type="application/javascript"
           src="../states.js" />
   <script type="application/javascript"
           src="../events.js" />
 
   <script type="application/javascript">
     //gA11yEventDumpID = "eventdump"; // debug stuff
-    //gA11yEventDumpToConsole = true; // debug stuff
+    gA11yEventDumpToConsole = true; // debug stuff
 
     var gQueue = null;
     function doTests()
     {
       // Test focus events.
       gQueue = new eventQueue();
 
       if (WIN) {
new file mode 100644
--- /dev/null
+++ b/b2g/chrome/content/dbg-browser-actors.js
@@ -0,0 +1,301 @@
+/* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+'use strict';
+
+/**
+ * The function that creates the root actor. DebuggerServer expects to find this
+ * function in the loaded actors in order to initialize properly.
+ */
+function createRootActor(connection) {
+  return new DeviceRootActor(connection);
+}
+
+/**
+ * Creates the root actor that client-server communications always start with.
+ * The root actor is responsible for the initial 'hello' packet and for
+ * responding to a 'listTabs' request that produces the list of currently open
+ * tabs.
+ *
+ * @param connection DebuggerServerConnection
+ *        The conection to the client.
+ */
+function DeviceRootActor(connection) {
+  this.conn = connection;
+  this._tabActors = new WeakMap();
+  this._tabActorPool = null;
+  this._actorFactories = null;
+  this.browser = Services.wm.getMostRecentWindow('navigator:browser');
+}
+
+DeviceRootActor.prototype = {
+  /**
+   * Return a 'hello' packet as specified by the Remote Debugging Protocol.
+   */
+  sayHello: function DRA_sayHello() {
+    return {
+      from: 'root',
+      applicationType: 'browser',
+      traits: []
+    };
+  },
+
+  /**
+   * Disconnects the actor from the browser window.
+   */
+  disconnect: function DRA_disconnect() {
+    let actor = this._tabActors.get(this.browser);
+    if (actor) {
+      actor.exit();
+    }
+  },
+
+  /**
+   * Handles the listTabs request.  Builds a list of actors for the single
+   * tab (window) running in the process. The actors will survive
+   * until at least the next listTabs request.
+   */
+  onListTabs: function DRA_onListTabs() {
+    let actor = this._tabActors.get(this.browser);
+    if (!actor) {
+      actor = new DeviceTabActor(this.conn, this.browser);
+      // this.actorID is set by ActorPool when an actor is put into one.
+      actor.parentID = this.actorID;
+      this._tabActors.set(this.browser, actor);
+    }
+
+    let actorPool = new ActorPool(this.conn);
+    actorPool.addActor(actor);
+
+    // Now drop the old actorID -> actor map. Actors that still mattered were
+    // added to the new map, others will go away.
+    if (this._tabActorPool) {
+      this.conn.removeActorPool(this._tabActorPool);
+    }
+    this._tabActorPool = actorPool;
+    this.conn.addActorPool(this._tabActorPool);
+
+    return {
+      'from': 'root',
+      'selected': 0,
+      'tabs': [actor.grip()]
+    };
+  }
+
+};
+
+/**
+ * The request types this actor can handle.
+ */
+DeviceRootActor.prototype.requestTypes = {
+  'listTabs': DeviceRootActor.prototype.onListTabs
+};
+
+/**
+ * Creates a tab actor for handling requests to the single tab, like attaching
+ * and detaching.
+ *
+ * @param connection DebuggerServerConnection
+ *        The connection to the client.
+ * @param browser browser
+ *        The browser instance that contains this tab.
+ */
+function DeviceTabActor(connection, browser) {
+  this.conn = connection;
+  this._browser = browser;
+}
+
+DeviceTabActor.prototype = {
+  get browser() {
+    return this._browser;
+  },
+
+  get exited() {
+    return !this.browser;
+  },
+
+  get attached() {
+    return !!this._attached
+  },
+
+  _tabPool: null,
+  get tabActorPool() {
+    return this._tabPool;
+  },
+
+  _contextPool: null,
+  get contextActorPool() {
+    return this._contextPool;
+  },
+
+  actorPrefix: 'tab',
+
+  grip: function DTA_grip() {
+    dbg_assert(!this.exited,
+               'grip() should not be called on exited browser actor.');
+    dbg_assert(this.actorID,
+               'tab should have an actorID.');
+    return {
+      'actor': this.actorID,
+      'title': this.browser.contentTitle,
+      'url': this.browser.document.documentURI
+    }
+  },
+
+  /**
+   * Called when the actor is removed from the connection.
+   */
+  disconnect: function DTA_disconnect() {
+    this._detach();
+  },
+
+  /**
+   * Called by the root actor when the underlying tab is closed.
+   */
+  exit: function DTA_exit() {
+    if (this.exited) {
+      return;
+    }
+
+    if (this.attached) {
+      this._detach();
+      this.conn.send({
+        'from': this.actorID,
+        'type': 'tabDetached'
+      });
+    }
+
+    this._browser = null;
+  },
+
+  /**
+   * Does the actual work of attaching to a tab.
+   */
+  _attach: function DTA_attach() {
+    if (this._attached) {
+      return;
+    }
+
+    // Create a pool for tab-lifetime actors.
+    dbg_assert(!this._tabPool, 'Should not have a tab pool if we were not attached.');
+    this._tabPool = new ActorPool(this.conn);
+    this.conn.addActorPool(this._tabPool);
+
+    // ... and a pool for context-lifetime actors.
+    this._pushContext();
+
+    this._attached = true;
+  },
+
+  /**
+   * Creates a thread actor and a pool for context-lifetime actors. It then sets
+   * up the content window for debugging.
+   */
+  _pushContext: function DTA_pushContext() {
+    dbg_assert(!this._contextPool, "Can't push multiple contexts");
+
+    this._contextPool = new ActorPool(this.conn);
+    this.conn.addActorPool(this._contextPool);
+
+    this.threadActor = new ThreadActor(this);
+    this._addDebuggees(this.browser.content.wrappedJSObject);
+    this._contextPool.addActor(this.threadActor);
+  },
+
+  /**
+   * Add the provided window and all windows in its frame tree as debuggees.
+   */
+  _addDebuggees: function DTA__addDebuggees(content) {
+    this.threadActor.addDebuggee(content);
+    let frames = content.frames;
+    for (let i = 0; i < frames.length; i++) {
+      this._addDebuggees(frames[i]);
+    }
+  },
+
+  /**
+   * Exits the current thread actor and removes the context-lifetime actor pool.
+   * The content window is no longer being debugged after this call.
+   */
+  _popContext: function DTA_popContext() {
+    dbg_assert(!!this._contextPool, 'No context to pop.');
+
+    this.conn.removeActorPool(this._contextPool);
+    this._contextPool = null;
+    this.threadActor.exit();
+    this.threadActor = null;
+  },
+
+  /**
+   * Does the actual work of detaching from a tab.
+   */
+  _detach: function DTA_detach() {
+    if (!this.attached) {
+      return;
+    }
+
+    this._popContext();
+
+    // Shut down actors that belong to this tab's pool.
+    this.conn.removeActorPool(this._tabPool);
+    this._tabPool = null;
+
+    this._attached = false;
+  },
+
+  // Protocol Request Handlers
+
+  onAttach: function DTA_onAttach(aRequest) {
+    if (this.exited) {
+      return { type: 'exited' };
+    }
+
+    this._attach();
+
+    return { type: 'tabAttached', threadActor: this.threadActor.actorID };
+  },
+
+  onDetach: function DTA_onDetach(aRequest) {
+    if (!this.attached) {
+      return { error: 'wrongState' };
+    }
+
+    this._detach();
+
+    return { type: 'detached' };
+  },
+
+  /**
+   * Prepare to enter a nested event loop by disabling debuggee events.
+   */
+  preNest: function DTA_preNest() {
+    let windowUtils = this.browser.content
+                          .QueryInterface(Ci.nsIInterfaceRequestor)
+                          .getInterface(Ci.nsIDOMWindowUtils);
+    windowUtils.suppressEventHandling(true);
+    windowUtils.suspendTimeouts();
+  },
+
+  /**
+   * Prepare to exit a nested event loop by enabling debuggee events.
+   */
+  postNest: function DTA_postNest(aNestData) {
+    let windowUtils = this.browser.content
+                          .QueryInterface(Ci.nsIInterfaceRequestor)
+                          .getInterface(Ci.nsIDOMWindowUtils);
+    windowUtils.resumeTimeouts();
+    windowUtils.suppressEventHandling(false);
+  }
+
+};
+
+/**
+ * The request types this actor can handle.
+ */
+DeviceTabActor.prototype.requestTypes = {
+  'attach': DeviceTabActor.prototype.onAttach,
+  'detach': DeviceTabActor.prototype.onDetach
+};
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -40,16 +40,21 @@ XPCOMUtils.defineLazyGetter(Services, 'a
 #endif
 });
 
 XPCOMUtils.defineLazyServiceGetter(Services, 'fm', function() {
   return Cc['@mozilla.org/focus-manager;1']
            .getService(Ci.nsFocusManager);
 });
 
+XPCOMUtils.defineLazyGetter(this, 'DebuggerServer', function() {
+  Cu.import('resource://gre/modules/devtools/dbg-server.jsm');
+  return DebuggerServer;
+});
+
 // FIXME Bug 707625
 // until we have a proper security model, add some rights to
 // the pre-installed web applications
 // XXX never grant 'content-camera' to non-gaia apps
 function addPermissions(urls) {
   let permissions = [
     'indexedDB', 'indexedDB-unlimited', 'webapps-manage', 'offline-app', 'pin-app',
     'websettings-read', 'websettings-readwrite',
@@ -536,8 +541,29 @@ var WebappsHelper = {
         break;
       case "webapps-ask-install":
         let id = this.registerInstaller(json);
         shell.sendEvent(content, "mozChromeEvent", { type: "webapps-ask-install", id: id, app: json.app } );
         break;
     }
   }
 }
+
+// Start the debugger server.
+function startDebugger() {
+  if (!DebuggerServer.initialized) {
+    DebuggerServer.init();
+    DebuggerServer.addActors('chrome://browser/content/dbg-browser-actors.js');
+  }
+
+  let port = Services.prefs.getIntPref('devtools.debugger.port') || 6000;
+  try {
+    DebuggerServer.openListener(port, false);
+  } catch (e) {
+    dump('Unable to start debugger server: ' + e + '\n');
+  }
+}
+
+window.addEventListener('ContentStart', function(evt) {
+  if (Services.prefs.getBoolPref('devtools.debugger.enabled')) {
+    startDebugger();
+  }
+}, false);
--- a/b2g/chrome/jar.mn
+++ b/b2g/chrome/jar.mn
@@ -1,14 +1,15 @@
 #filter substitution
 
 chrome.jar:
 % content branding %content/branding/
 % content browser %content/
 
+  content/dbg-browser-actors.js         (content/dbg-browser-actors.js)
 * content/shell.xul                     (content/shell.xul)
 * content/shell.js                      (content/shell.js)
 #ifndef MOZ_TOUCH
   content/touch.js                      (content/touch.js)
 #endif
   content/webapi.js                     (content/webapi.js)
   content/content.css                   (content/content.css)
 
--- a/browser/base/content/browser-appmenu.inc
+++ b/browser/base/content/browser-appmenu.inc
@@ -265,18 +265,18 @@
       <splitmenu id="appmenu_bookmarks"
                  iconic="true"
                  label="&bookmarksMenu.label;"
                  command="Browser:ShowAllBookmarks">
           <menupopup id="appmenu_bookmarksPopup"
                      placespopup="true"
                      context="placesContext"
                      openInTabs="children"
-                     oncommand="BookmarksEventHandler.onCommand(event);"
-                     onclick="BookmarksEventHandler.onClick(event);"
+                     oncommand="BookmarksEventHandler.onCommand(event, this.parentNode._placesView);"
+                     onclick="BookmarksEventHandler.onClick(event, this.parentNode._placesView);"
                      onpopupshowing="BookmarksMenuButton.onPopupShowing(event);
                                      if (!this.parentNode._placesView)
                                        new PlacesMenu(event, 'place:folder=BOOKMARKS_MENU');"
                      tooltip="bhTooltip"
                      popupsinherittooltip="true">
             <menuitem id="appmenu_showAllBookmarks"
                       label="&showAllBookmarks2.label;"
                       command="Browser:ShowAllBookmarks"
--- a/browser/base/content/browser-thumbnails.js
+++ b/browser/base/content/browser-thumbnails.js
@@ -21,16 +21,21 @@ let gBrowserThumbnails = {
   _pageThumbs: null,
 
   /**
    * List of tab events we want to listen for.
    */
   _tabEvents: ["TabClose", "TabSelect"],
 
   init: function Thumbnails_init() {
+    try {
+      if (Services.prefs.getBoolPref("browser.pagethumbnails.capturing_disabled"))
+        return;
+    } catch (e) {}
+
     gBrowser.addTabsProgressListener(this);
 
     this._tabEvents.forEach(function (aEvent) {
       gBrowser.tabContainer.addEventListener(aEvent, this, false);
     }, this);
 
     this._timeouts = new WeakMap();
 
@@ -88,16 +93,19 @@ let gBrowserThumbnails = {
       this._clearTimeout(aBrowser);
       this._capture(aBrowser);
     }.bind(this), this._captureDelayMS);
 
     this._timeouts.set(aBrowser, timeout);
   },
 
   _shouldCapture: function Thumbnails_shouldCapture(aBrowser) {
+    if (gPrivateBrowsingUI.privateBrowsingEnabled)
+      return false;
+
     let doc = aBrowser.contentDocument;
 
     // FIXME Bug 720575 - Don't capture thumbnails for SVG or XML documents as
     //       that currently regresses Talos SVG tests.
     if (doc instanceof SVGDocument || doc instanceof XMLDocument)
       return false;
 
     // There's no point in taking screenshot of loading pages.
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -3599,22 +3599,22 @@ function FillHistoryMenu(aParent) {
     let entry = sessionHistory.getEntryAtIndex(j, false);
     let uri = entry.URI.spec;
 
     item.setAttribute("uri", uri);
     item.setAttribute("label", entry.title || uri);
     item.setAttribute("index", j);
 
     if (j != index) {
-      function FHM_getFaviconURLCallback(aURI) {
-        let iconURL = PlacesUtils.favicons.getFaviconLinkForIcon(aURI).spec;
-        item.style.listStyleImage = "url(" + iconURL + ")";
-      }
-      PlacesUtils.favicons.getFaviconURLForPage(entry.URI,
-                                                FHM_getFaviconURLCallback);
+      PlacesUtils.favicons.getFaviconURLForPage(entry.URI, function (aURI) {
+        if (aURI) {
+          let iconURL = PlacesUtils.favicons.getFaviconLinkForIcon(aURI).spec;
+          item.style.listStyleImage = "url(" + iconURL + ")";
+        }
+      });
     }
 
     if (j < index) {
       item.className = "unified-nav-back menuitem-iconic menuitem-with-favicon";
       item.setAttribute("tooltiptext", tooltipBack);
     } else if (j == index) {
       item.setAttribute("type", "radio");
       item.setAttribute("checked", "true");
--- a/browser/base/content/highlighter.css
+++ b/browser/base/content/highlighter.css
@@ -44,30 +44,39 @@
 }
 
 #highlighter-nodeinfobar-container:not([disable-transitions]) {
   -moz-transition-property: top, left;
   -moz-transition-duration: 0.1s;
   -moz-transition-timing-function: linear;
 }
 
-#highlighter-nodeinfobar {
-  display: block;
+#highlighter-nodeinfobar-text {
+  overflow: hidden;
   white-space: nowrap;
+  text-overflow: ellipsis;
   direction: ltr;
-  overflow: hidden;
-  text-overflow: ellipsis;
 }
 
-#highlighter-nodeinfobar-container[locked] > #highlighter-nodeinfobar {
+.highlighter-nodeinfobar-button > .toolbarbutton-text {
+  display: none;
+}
+
+#highlighter-nodeinfobar-container:not([locked]):not(:hover) > #highlighter-nodeinfobar > .highlighter-nodeinfobar-button {
+  visibility: hidden;
+}
+
+#highlighter-nodeinfobar-container[locked] > #highlighter-nodeinfobar,
+#highlighter-nodeinfobar-container:not([locked]):hover > #highlighter-nodeinfobar {
   pointer-events: auto;
 }
 
 html|*#highlighter-nodeinfobar-id,
 html|*#highlighter-nodeinfobar-classes,
+html|*#highlighter-nodeinfobar-pseudo-classes,
 html|*#highlighter-nodeinfobar-tagname {
   -moz-user-select: text;
   cursor: text;
 }
 
 .highlighter-nodeinfobar-arrow {
   display: none;
 }
--- a/browser/components/places/tests/browser/Makefile.in
+++ b/browser/components/places/tests/browser/Makefile.in
@@ -46,17 +46,16 @@ include $(topsrcdir)/config/rules.mk
 _BROWSER_TEST_FILES = \
 	head.js \
 	browser_0_library_left_pane_migration.js \
 	browser_library_left_pane_fixnames.js \
 	browser_425884.js \
 	browser_475045.js \
 	browser_423515.js \
 	browser_410196_paste_into_tags.js \
-	browser_457473_no_copy_guid.js \
 	browser_sort_in_library.js \
 	browser_library_open_leak.js \
 	browser_library_panel_leak.js \
 	browser_library_search.js \
 	browser_history_sidebar_search.js \
 	browser_bookmarksProperties.js \
 	$(warning browser_forgetthissite_single.js temporarily disabled because of very frequent oranges, see bug 551540) \
 	browser_library_left_pane_commands.js \
deleted file mode 100644
--- a/browser/components/places/tests/browser/browser_457473_no_copy_guid.js
+++ /dev/null
@@ -1,146 +0,0 @@
-/* vim:set ts=2 sw=2 sts=2 et: */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is Places test code.
- *
- * The Initial Developer of the Original Code is the Mozilla Foundation.
- * Portions created by the Initial Developer are Copyright (C) 2008
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *  Dietrich Ayala <dietrich@mozilla.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * 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 ***** */
-
-function test() {
-  // sanity check
-  ok(PlacesUtils, "checking PlacesUtils, running in chrome context?");
-  ok(PlacesUIUtils, "checking PlacesUIUtils, running in chrome context?");
-
-  /*
-  - create, a test folder, add bookmark, separator to it
-  - fetch guids for all
-  - copy the folder
-  - test that guids are all different
-  - undo copy
-  - redo copy
-  - test that guids for the copy stay the same
-  */
-
-  var toolbarId = PlacesUtils.toolbarFolderId;
-  var toolbarNode = PlacesUtils.getFolderContents(toolbarId).root;
-
-  var oldCount = toolbarNode.childCount;
-  var testRootId = PlacesUtils.bookmarks.createFolder(toolbarId, "test root", -1);
-  is(toolbarNode.childCount, oldCount+1, "confirm test root node is a container, and is empty");
-  var testRootNode = toolbarNode.getChild(toolbarNode.childCount-1);
-  PlacesUtils.asContainer(testRootNode);
-  testRootNode.containerOpen = true;
-  is(testRootNode.childCount, 0, "confirm test root node is a container, and is empty");
-
-  // create folder A, fill it w/ each item type
-  var folderAId = PlacesUtils.bookmarks.createFolder(testRootId, "A", -1);
-  PlacesUtils.bookmarks.insertBookmark(folderAId, PlacesUtils._uri("http://foo"),
-                                       -1, "test bookmark");
-  PlacesUtils.bookmarks.insertSeparator(folderAId, -1);
-  var folderANode = testRootNode.getChild(0);
-  var folderAGUIDs = getGUIDs(folderANode);
-
-  // test the test function
-  ok(checkGUIDs(folderANode, folderAGUIDs, true), "confirm guid test works");
-
-  // serialize the folder
-  var serializedNode = PlacesUtils.wrapNode(folderANode, PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER);
-  var rawNode = PlacesUtils.unwrapNodes(serializedNode, PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER).shift();
-  ok(rawNode.type, "confirm json node was made");
-
-  // Create a copy transaction from the serialization.
-  // this exercises the guid-filtering
-  var transaction = PlacesUIUtils.makeTransaction(rawNode,
-                                                  PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER,
-                                                  testRootId, -1, true);
-  ok(transaction, "create transaction");
-
-  // execute it, copying to the test root folder
-  PlacesUtils.transactionManager.doTransaction(transaction);
-  is(testRootNode.childCount, 2, "create test folder via copy");
-
-  // check GUIDs are different
-  var folderBNode = testRootNode.getChild(1);
-  ok(checkGUIDs(folderBNode, folderAGUIDs, false), "confirm folder A GUIDs don't match folder B GUIDs");
-  var folderBGUIDs = getGUIDs(folderBNode);
-  ok(checkGUIDs(folderBNode, folderBGUIDs, true), "confirm test of new GUIDs");
-
-  // undo the transaction, confirm the removal
-  PlacesUtils.transactionManager.undoTransaction();
-  is(testRootNode.childCount, 1, "confirm undo removed the copied folder");
-
-  // redo the transaction
-  // confirming GUIDs persist through undo/redo
-  PlacesUtils.transactionManager.redoTransaction();
-  is(testRootNode.childCount, 2, "confirm redo re-copied the folder");
-  folderBNode = testRootNode.getChild(1);
-  ok(checkGUIDs(folderBNode, folderAGUIDs, false), "folder B GUIDs after undo/redo don't match folder A GUIDs"); // sanity check
-  ok(checkGUIDs(folderBNode, folderBGUIDs, true), "folder B GUIDs after under/redo should match pre-undo/redo folder B GUIDs");
-
-  // Close containers, cleaning up their observers.
-  testRootNode.containerOpen = false;
-  toolbarNode.containerOpen = false;
-
-  // clean up
-  PlacesUtils.transactionManager.undoTransaction();
-  PlacesUtils.bookmarks.removeItem(testRootId);
-}
-
-function getGUIDs(aNode) {
-  PlacesUtils.asContainer(aNode);
-  aNode.containerOpen = true;
-  var GUIDs = {
-    folder: PlacesUtils.bookmarks.getItemGUID(aNode.itemId),
-    bookmark: PlacesUtils.bookmarks.getItemGUID(aNode.getChild(0).itemId),
-    separator: PlacesUtils.bookmarks.getItemGUID(aNode.getChild(1).itemId)
-  };
-  aNode.containerOpen = false;
-  return GUIDs;
-}
-
-function checkGUIDs(aFolderNode, aGUIDs, aShouldMatch) {
-
-  function check(aNode, aGUID, aEquals) {
-    var nodeGUID = PlacesUtils.bookmarks.getItemGUID(aNode.itemId);
-    return aEquals ? (nodeGUID == aGUID) : (nodeGUID != aGUID);
-  }
-
-  PlacesUtils.asContainer(aFolderNode);
-  aFolderNode.containerOpen = true;
-
-  var allMatch = check(aFolderNode, aGUIDs.folder, aShouldMatch) &&
-                 check(aFolderNode.getChild(0), aGUIDs.bookmark, aShouldMatch) &&
-                 check(aFolderNode.getChild(1), aGUIDs.separator, aShouldMatch)
-
-  aFolderNode.containerOpen = false;
-  return allMatch;
-}
--- a/browser/components/places/tests/unit/test_457441-import-export-corrupt-bookmarks-html.js
+++ b/browser/components/places/tests/unit/test_457441-import-export-corrupt-bookmarks-html.js
@@ -57,17 +57,17 @@ var ies = Cc["@mozilla.org/browser/place
           getService(Ci.nsIPlacesImportExportService);
 Cu.import("resource://gre/modules/BookmarkHTMLUtils.jsm");
 
 const DESCRIPTION_ANNO = "bookmarkProperties/description";
 const LOAD_IN_SIDEBAR_ANNO = "bookmarkProperties/loadInSidebar";
 const POST_DATA_ANNO = "bookmarkProperties/POSTData";
 
 const TEST_FAVICON_PAGE_URL = "http://en-US.www.mozilla.com/en-US/firefox/central/";
-const TEST_FAVICON_DATA_URL = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHWSURBVHjaYvz//z8DJQAggJiQOe/fv2fv7Oz8rays/N+VkfG/iYnJfyD/1+rVq7ffu3dPFpsBAAHEAHIBCJ85c8bN2Nj4vwsDw/8zQLwKiO8CcRoQu0DxqlWrdsHUwzBAAIGJmTNnPgYa9j8UqhFElwPxf2MIDeIrKSn9FwSJoRkAEEAM0DD4DzMAyPi/G+QKY4hh5WAXGf8PDQ0FGwJ22d27CjADAAIIrLmjo+MXA9R2kAHvGBA2wwx6B8W7od6CeQcggKCmCEL8bgwxYCbUIGTDVkHDBia+CuotgACCueD3TDQN75D4xmAvCoK9ARMHBzAw0AECiBHkAlC0Mdy7x9ABNA3obAZXIAa6iKEcGlMVQHwWyjYuL2d4v2cPg8vZswx7gHyAAAK7AOif7SAbOqCmn4Ha3AHFsIDtgPq/vLz8P4MSkJ2W9h8ggBjevXvHDo4FQUQg/kdypqCg4H8lUIACnQ/SOBMYI8bAsAJFPcj1AAEEjwVQqLpAbXmH5BJjqI0gi9DTAAgDBBCcAVLkgmQ7yKCZxpCQxqUZhAECCJ4XgMl493ug21ZD+aDAXH0WLM4A9MZPXJkJIIAwTAR5pQMalaCABQUULttBGCCAGCnNzgABBgAMJ5THwGvJLAAAAABJRU5ErkJggg==";
+const TEST_FAVICON_DATA_SIZE = 580;
 
 function run_test() {
   do_test_pending();
 
   // avoid creating the places smart folder during tests
   ps.setIntPref("browser.places.smartBookmarksVersion", -1);
 
   // import bookmarks from corrupt file
@@ -79,18 +79,17 @@ function run_test() {
 
 function after_import(success) {
   if (!success) {
     do_throw("Couldn't import corrupt bookmarks file.");
   }
 
   // Check that every bookmark is correct
   // Corrupt bookmarks should not have been imported
-  database_check();
-  waitForAsyncUpdates(function() {
+  database_check(function () {
     // Create corruption in database
     var corruptItemId = bs.insertBookmark(bs.toolbarFolder,
                                           uri("http://test.mozilla.org"),
                                           bs.DEFAULT_INDEX, "We love belugas");
     var stmt = dbConn.createStatement("UPDATE moz_bookmarks SET fk = NULL WHERE id = :itemId");
     stmt.params.itemId = corruptItemId;
     stmt.execute();
     stmt.finalize();
@@ -107,32 +106,33 @@ function after_import(success) {
       ies.exportHTMLToFile(bookmarksFile);
     } catch(ex) { do_throw("couldn't export to bookmarks.exported.html: " + ex); }
 
     // Clear all bookmarks
     remove_all_bookmarks();
 
     // Import bookmarks
     try {
-      BookmarkHTMLUtils.importFromFile(bookmarksFile, true, before_database_check);
+    BookmarkHTMLUtils.importFromFile(bookmarksFile, true, before_database_check);
     } catch(ex) { do_throw("couldn't import the exported file: " + ex); }
   });
 }
 
 function before_database_check(success) {
-    // Check that every bookmark is correct
-    database_check();
-
-    waitForAsyncUpdates(do_test_finished);
+  // Check that every bookmark is correct
+  database_check(do_test_finished);
 }
 
 /*
  * Check for imported bookmarks correctness
+ *
+ * @param aCallback
+ *        Called when the checks are finished.
  */
-function database_check() {
+function database_check(aCallback) {
   // BOOKMARKS MENU
   var query = hs.getNewQuery();
   query.setFolders([bs.bookmarksMenuFolder], 1);
   var options = hs.getNewQueryOptions();
   options.queryType = Ci.nsINavHistoryQueryOptions.QUERY_TYPE_BOOKMARKS;
   var result = hs.executeQuery(query, options);
   var rootNode = result.root;
   rootNode.containerOpen = true;
@@ -221,12 +221,19 @@ function database_check() {
   query.setFolders([bs.unfiledBookmarksFolder], 1);
   result = hs.executeQuery(query, hs.getNewQueryOptions());
   var unfiledBookmarks = result.root;
   unfiledBookmarks.containerOpen = true;
   do_check_eq(unfiledBookmarks.childCount, 1);
   unfiledBookmarks.containerOpen = false;
 
   // favicons
-  var faviconURI = icos.getFaviconForPage(uri(TEST_FAVICON_PAGE_URL));
-  var dataURL = icos.getFaviconDataAsDataURL(faviconURI);
-  do_check_eq(TEST_FAVICON_DATA_URL, dataURL);
+  icos.getFaviconDataForPage(uri(TEST_FAVICON_PAGE_URL),
+    function DC_onComplete(aURI, aDataLen, aData, aMimeType) {
+      // aURI should never be null when aDataLen > 0.
+      do_check_neq(aURI, null);
+      // Favicon data is stored in the bookmarks file as a "data:" URI.  For
+      // simplicity, instead of converting the data we receive to a "data:" URI
+      // and comparing it, we just check the data size.
+      do_check_eq(TEST_FAVICON_DATA_SIZE, aDataLen);
+      aCallback();
+    });
 }
--- a/browser/components/places/tests/unit/test_bookmarks_html.js
+++ b/browser/components/places/tests/unit/test_bookmarks_html.js
@@ -178,21 +178,24 @@ add_test(function test_import_new()
     });
   } catch(ex) { do_throw("couldn't import the exported file: " + ex); }
 });
 
 add_test(function test_emptytitle_export()
 {
   // Test exporting and importing with an empty-titled bookmark.
   // 1. import bookmarks
-  // 1. create an empty-titled bookmark.
-  // 2. export to bookmarks.exported.html
-  // 3. empty bookmarks db
-  // 4. import bookmarks.exported.html
-  // 5. run the test-suite
+  // 2. create an empty-titled bookmark.
+  // 3. export to bookmarks.exported.html
+  // 4. empty bookmarks db
+  // 5. import bookmarks.exported.html
+  // 6. run the test-suite
+  // 7. remove the empty-titled bookmark
+  // 8. export to bookmarks.exported.html
+  // 9. empty bookmarks db and continue
 
   try {
     BookmarkHTMLUtils.importFromFile(gBookmarksFileNew, true, function(success) {
       if (success) {
         const NOTITLE_URL = "http://notitle.mozilla.org/";
         let id = PlacesUtils.bookmarks.insertBookmark(PlacesUtils.unfiledBookmarksFolderId,
                                                       NetUtil.newURI(NOTITLE_URL),
                                                       PlacesUtils.bookmarks.DEFAULT_INDEX,
@@ -231,16 +234,93 @@ add_test(function test_emptytitle_export
         } catch(ex) { do_throw("couldn't import the exported file: " + ex); }
       } else {
         do_throw("couldn't import the exported file.");
       }
     });
   } catch(ex) { do_throw("couldn't import the exported file: " + ex); }
 });
 
+add_test(function test_import_chromefavicon()
+{
+  // Test exporting and importing with a bookmark pointing to a chrome favicon.
+  // 1. import bookmarks
+  // 2. create a bookmark pointing to a chrome favicon.
+  // 3. export to bookmarks.exported.html
+  // 4. empty bookmarks db
+  // 5. import bookmarks.exported.html
+  // 6. run the test-suite
+  // 7. remove the bookmark pointing to a chrome favicon.
+  // 8. export to bookmarks.exported.html
+  // 9. empty bookmarks db and continue
+
+  const PAGE_URI = NetUtil.newURI("http://example.com/chromefavicon_page");
+  const CHROME_FAVICON_URI = NetUtil.newURI("chrome://global/skin/icons/information-16.png");
+  const CHROME_FAVICON_URI_2 = NetUtil.newURI("chrome://global/skin/icons/error-16.png");
+
+  try {
+    BookmarkHTMLUtils.importFromFile(gBookmarksFileNew, true, function(success) {
+      if (!success) {
+        do_throw("couldn't import the exported file.");
+      }
+      let id = PlacesUtils.bookmarks.insertBookmark(PlacesUtils.unfiledBookmarksFolderId,
+                                                    PAGE_URI,
+                                                    PlacesUtils.bookmarks.DEFAULT_INDEX,
+                                                    "Test");
+
+      PlacesUtils.favicons.setAndFetchFaviconForPage(
+        PAGE_URI, CHROME_FAVICON_URI, true, function () {
+          PlacesUtils.favicons.getFaviconDataForPage(
+            PAGE_URI, function (aURI, aDataLen, aData, aMimeType) {
+              let base64Icon = "data:image/png;base64," +
+                  base64EncodeString(String.fromCharCode.apply(String, aData));
+
+              test_bookmarks.unfiled.push(
+                { title: "Test", url: PAGE_URI.spec, icon: base64Icon });
+
+              try {
+                exporter.exportHTMLToFile(gBookmarksFileNew);
+              } catch(ex) { do_throw("couldn't export to file: " + ex); }
+
+              // Change the favicon to check it's really imported again later.
+              PlacesUtils.favicons.setAndFetchFaviconForPage(
+                PAGE_URI, CHROME_FAVICON_URI_2, true, function () {
+
+                  remove_all_bookmarks();
+
+                  try {
+                    BookmarkHTMLUtils.importFromFile(gBookmarksFileNew, true, function(success) {
+                     if (!success) {
+                        do_throw("couldn't import the exported file.");
+                      }
+                      waitForAsyncUpdates(function () {
+                        testImportedBookmarks();
+
+                        // Cleanup.
+                        test_bookmarks.unfiled.pop();
+                        PlacesUtils.bookmarks.removeItem(id);
+
+                        try {
+                          exporter.exportHTMLToFile(gBookmarksFileNew);
+                        } catch(ex) { do_throw("couldn't export to file: " + ex); }
+
+                        waitForAsyncUpdates(function () {
+                          remove_all_bookmarks();
+                          run_next_test();
+                        });
+                      });
+                    });
+                  } catch(ex) { do_throw("couldn't import the exported file: " + ex); }
+                });
+            });
+        });
+    });
+  } catch(ex) { do_throw("couldn't import the exported file: " + ex); }
+});
+
 add_test(function test_import_ontop()
 {
   // Test importing the exported bookmarks.html file *on top of* the existing
   // bookmarks.
   // 1. empty bookmarks db
   // 2. import the exported bookmarks file
   // 3. export to file
   // 3. import the exported bookmarks file
--- a/browser/components/preferences/aboutPermissions.js
+++ b/browser/components/preferences/aboutPermissions.js
@@ -87,38 +87,38 @@ Site.prototype = {
   /**
    * Gets the favicon to use for the site. The callback only gets called if
    * a favicon is found for either the http URI or the https URI.
    *
    * @param aCallback
    *        A callback function that takes a favicon image URL as a parameter.
    */
   getFavicon: function Site_getFavicon(aCallback) {
-    let callbackExecuted = false;
-    function faviconDataCallback(aURI, aDataLen, aData, aMimeType) {
-      // We don't need a second callback, so we can ignore it to avoid making
-      // a second database query for the favicon data.
-      if (callbackExecuted) {
-        return;
-      }
+    function invokeCallback(aFaviconURI) {
       try {
         // Use getFaviconLinkForIcon to get image data from the database instead
         // of using the favicon URI to fetch image data over the network.
-        aCallback(gFaviconService.getFaviconLinkForIcon(aURI).spec);
-        callbackExecuted = true;
+        aCallback(gFaviconService.getFaviconLinkForIcon(aFaviconURI).spec);
       } catch (e) {
         Cu.reportError("AboutPermissions: " + e);
       }
     }
 
-    // Try to find favicion for both URIs. Callback will only be called if a
-    // favicon URI is found. We'll ignore the second callback if it is called,
-    // so this means we'll always prefer the https favicon.
-    gFaviconService.getFaviconURLForPage(this.httpsURI, faviconDataCallback);
-    gFaviconService.getFaviconURLForPage(this.httpURI, faviconDataCallback);
+    // Try to find favicon for both URIs, but always prefer the https favicon.
+    gFaviconService.getFaviconURLForPage(this.httpsURI, function (aURI) {
+      if (aURI) {
+        invokeCallback(aURI);
+      } else {
+        gFaviconService.getFaviconURLForPage(this.httpURI, function (aURI) {
+          if (aURI) {
+            invokeCallback(aURI);
+          }
+        });
+      }
+    }.bind(this));
   },
 
   /**
    * Gets the number of history visits for the site.
    *
    * @param aCallback
    *        A function that takes the visit count (a number) as a parameter.
    */
--- a/browser/components/tabview/groupitems.js
+++ b/browser/components/tabview/groupitems.js
@@ -1187,17 +1187,17 @@ GroupItem.prototype = Utils.extend(new I
   //   options - change how the app tab is added.
   //
   // Options:
   //   position - the position of the app tab should be added to.
   //   dontAdjustTray - (boolean) if true, do not adjust the tray.
   addAppTab: function GroupItem_addAppTab(xulTab, options) {
     GroupItems.getAppTabFavIconUrl(xulTab, function(iconUrl) {
       // The tab might have been removed or unpinned while waiting.
-      if (xulTab.closing || !xulTab.parentNode || !xulTab.pinned)
+      if (!Utils.isValidXULTab(xulTab) || !xulTab.pinned)
         return;
 
       let self = this;
       let $appTab = iQ("<img>")
         .addClass("appTabIcon")
         .attr("src", iconUrl)
         .data("xulTab", xulTab)
         .mousedown(function GroupItem_addAppTab_onAppTabMousedown(event) {
--- a/browser/components/tabview/modules/utils.jsm
+++ b/browser/components/tabview/modules/utils.jsm
@@ -632,16 +632,25 @@ let Utils = {
   // ----------
   // Function: isDOMElement
   // Returns true if the given object is a DOM element.
   isDOMElement: function Utils_isDOMElement(object) {
     return object instanceof Ci.nsIDOMElement;
   },
 
   // ----------
+  // Function: isValidXULTab
+  // A xulTab is valid if it has not been closed,
+  // and it has not been removed from the DOM
+  // Returns true if the tab is valid.
+  isValidXULTab: function Utils_isValidXULTab(xulTab) {
+    return !xulTab.closing && xulTab.parentNode;
+  },
+
+  // ----------
   // Function: isNumber
   // Returns true if the argument is a valid number.
   isNumber: function Utils_isNumber(n) {
     return typeof n == 'number' && !isNaN(n);
   },
 
   // ----------
   // Function: isRect
--- a/browser/components/tabview/tabitems.js
+++ b/browser/components/tabview/tabitems.js
@@ -234,17 +234,17 @@ TabItem.prototype = Utils.extend(new Ite
     return data;
   },
 
   // ----------
   // Function: save
   // Store persistent for this object.
   save: function TabItem_save() {
     try {
-      if (!this.tab || this.tab.parentNode == null || !this._reconnected) // too soon/late to save
+      if (!this.tab || !Utils.isValidXULTab(this.tab) || !this._reconnected) // too soon/late to save
         return;
 
       let data = this.getStorageData();
       if (TabItems.storageSanity(data))
         Storage.saveTab(this.tab, data);
     } catch(e) {
       Utils.log("Error in saving tab value: "+e);
     }
@@ -566,17 +566,17 @@ TabItem.prototype = Utils.extend(new Ite
     this._hasBeenDrawn = true;
 
     UI.clearShouldResizeItems();
 
     rect = this.getBounds(); // ensure that it's a <Rect>
 
     Utils.assert(Utils.isRect(this.bounds), 'TabItem.setBounds: this.bounds is not a real rectangle!');
 
-    if (!this.parent && this.tab.parentNode != null)
+    if (!this.parent && Utils.isValidXULTab(this.tab))
       this.setTrenches(rect);
 
     this.save();
   },
 
   // ----------
   // Function: setZ
   // Sets the z-index for this item.
--- a/browser/components/tabview/tabview.js
+++ b/browser/components/tabview/tabview.js
@@ -55,17 +55,17 @@ let AllTabs = {
     move:         "TabMove",
     open:         "TabOpen",
     select:       "TabSelect",
     pinned:       "TabPinned",
     unpinned:     "TabUnpinned"
   },
 
   get tabs() {
-    return Array.filter(gBrowser.tabs, function (tab) !tab.closing);
+    return Array.filter(gBrowser.tabs, function (tab) Utils.isValidXULTab(tab));
   },
 
   register: function AllTabs_register(eventName, callback) {
     gBrowser.tabContainer.addEventListener(this._events[eventName], callback, false);
   },
 
   unregister: function AllTabs_unregister(eventName, callback) {
     gBrowser.tabContainer.removeEventListener(this._events[eventName], callback, false);
--- a/browser/components/tabview/test/browser_tabview_bug600645.js
+++ b/browser/components/tabview/test/browser_tabview_bug600645.js
@@ -1,11 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
+/**
+ * This file tests that, when there is an app tab that references an invalid
+ * favicon, the default favicon appears the group app tab tray, instead of an
+ * empty image that would not be visible.
+ */
+
 const fi = Cc["@mozilla.org/browser/favicon-service;1"].
            getService(Ci.nsIFaviconService);
 
 let newTab;
 
 function test() {
   waitForExplicitFinish();
 
@@ -35,28 +41,30 @@ function onTabPinned() {
   let errorHandler = function(event) {
     newTab.removeEventListener("error", errorHandler, false);
 
     // since the browser code and test code are invoked when an error event is 
     // fired, a delay is used here to avoid the test code run before the browser 
     // code.
     executeSoon(function() {
       let iconSrc = $icon.attr("src");
-      let hasData = true;
-      try {
-        fi.getFaviconDataAsDataURL(iconSrc);
-      } catch(e) {
-        hasData = false;
-      }
-      ok(!hasData, "The icon src doesn't return any data");
+
       // with moz-anno:favicon automatically redirects to the default favIcon 
       // if the given url is invalid
       ok(/^moz-anno:favicon:/.test(iconSrc),
          "The icon url starts with moz-anno:favicon so the default fav icon would be displayed");
 
+      // At this point, as an additional integrity check we could also verify
+      // that the iconSrc URI does not have any associated favicon data.  This
+      // kind of check, however, is not easily supported by the asynchronous
+      // favicon API.  Fortunately, the fact that we received the error event
+      // already indicates that the original favicon was not available.
+      // Morevover, since we are using a "moz-anno:favicon:" URI, we know that
+      // we'll not display an empty icon, but the default favicon.
+
       // clean up
       gBrowser.removeTab(newTab);
       let endGame = function() {
         window.removeEventListener("tabviewhidden", endGame, false);
 
         ok(!TabView.isVisible(), "Tab View is hidden");
         finish();
       }
--- a/browser/components/tabview/ui.js
+++ b/browser/components/tabview/ui.js
@@ -786,17 +786,17 @@ let UI = {
         // we don't want to go to the Tab View UI. 
         if (self._storageBusy)
           return;
 
         // if not closing the last tab
         if (gBrowser.tabs.length > 1) {
           // Don't return to TabView if there are any app tabs
           for (let a = 0; a < gBrowser._numPinnedTabs; a++) {
-            if (!gBrowser.tabs[a].closing)
+            if (Utils.isValidXULTab(gBrowser.tabs[a]))
               return;
           }
 
           let groupItem = GroupItems.getActiveGroupItem();
 
           // 1) Only go back to the TabView tab when there you close the last
           // tab of a groupItem.
           let closingLastOfGroup = (groupItem && 
@@ -1690,36 +1690,44 @@ let UI = {
         Services.prefs.getBoolPref(this.PREF_CHROME_FAVICONS);
 
     return (this._prefFavicons && ("schemeIs" in uri) &&
             (uri.schemeIs("http") || uri.schemeIs("https")));
   },
 
   // ----------
   // Function: getFavIconUrlForTab
-  // Gets fav icon url for the given xul:tab.
+  // Gets the "favicon link URI" for the given xul:tab, or null if unavailable.
   getFavIconUrlForTab: function UI_getFavIconUrlForTab(tab, callback) {
     this._isImageDocument(tab, function(isImageDoc) {
       if (isImageDoc) {
         callback(tab.pinned ? tab.image : null);
       } else {
         let tabImage = tab.image;
         if (tabImage) {
           // if starts with http/https, fetch icon from favicon service via the moz-anno protocal
           if (/^https?:/.test(tabImage))
             tabImage = gFavIconService.getFaviconLinkForIcon(gWindow.makeURI(tab.image)).spec;
 
           callback(tabImage);
         } else {
-          // determine to load the default/cached icon or not and also ensure we don't show the default icon
-          // for about:-style error pages
-          let url = null;
-          if (this._shouldLoadFavIcon(tab))
-            url = gFavIconService.getFaviconImageForPage(tab.linkedBrowser.currentURI).spec;
-          callback(url);
+          // ensure we don't show the default icon for about:-style error pages
+          if (!this._shouldLoadFavIcon(tab)) {
+            callback(null);
+          } else {
+            // determine to load the default/cached icon or not
+            gFavIconService.getFaviconURLForPage(tab.linkedBrowser.currentURI,
+              function (uri) {
+                if (!uri) {
+                  callback(gFavIconService.defaultFavicon.spec);
+                } else {
+                  callback(gFavIconService.getFaviconLinkForIcon(uri).spec);
+                }
+              });
+          }
         }
       }
     }.bind(this));
   },
 
   // ----------
   // Function: notifySessionRestoreEnabled
   // Notify the user that session restore has been automatically enabled
--- a/browser/devtools/debugger/DebuggerUI.jsm
+++ b/browser/devtools/debugger/DebuggerUI.jsm
@@ -40,581 +40,173 @@
  *
  * ***** END LICENSE BLOCK ***** */
 "use strict";
 
 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");
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource:///modules/source-editor.jsm");
 
 let EXPORTED_SYMBOLS = ["DebuggerUI"];
 
 /**
- * Creates a pane that will host the debugger UI.
+ * Provides a simple mechanism of managing debugger instances per tab.
+ *
+ * @param nsIDOMWindow aWindow
+ *        The chrome window for which the DebuggerUI instance is created.
  */
-function DebuggerPane(aTab) {
-  this._tab = aTab;
-  this._close = this.close.bind(this);
-  this._debugTab = this.debugTab.bind(this);
-  this.breakpoints = {};
+function DebuggerUI(aWindow) {
+  this.chromeWindow = aWindow;
 }
 
-DebuggerPane.prototype = {
-  /**
-   * Skip editor breakpoint change events.
-   *
-   * This property tells the source editor event handler to skip handling of
-   * the BREAKPOINT_CHANGE events. This is used when the debugger adds/removes
-   * breakpoints from the editor. Typically, the BREAKPOINT_CHANGE event handler
-   * adds/removes events from the debugger, but when breakpoints are added from
-   * the public debugger API, we need to do things in reverse.
-   *
-   * This implementation relies on the fact that the source editor fires the
-   * BREAKPOINT_CHANGE events synchronously.
-   *
-   * @private
-   * @type boolean
-   */
-  _skipEditorBreakpointChange: false,
-
-  /**
-   * The list of breakpoints in the debugger as tracked by the current
-   * DebuggerPane instance. This an object where the values are BreakpointActor
-   * objects received from the client, while the keys are actor names, for
-   * example "conn0.breakpoint3".
-   *
-   * @type object
-   */
-  breakpoints: null,
+DebuggerUI.prototype = {
 
   /**
-   * Creates and initializes the widgets contained in the debugger UI.
+   * Starts a debugger for the current tab, or stops it if already started.
+   * @return DebuggerPane if the debugger is started, null if it's stopped.
    */
-  create: function DP_create(gBrowser) {
-    this._tab._scriptDebugger = this;
-
-    this._nbox = gBrowser.getNotificationBox(this._tab.linkedBrowser);
-    this._splitter = gBrowser.parentNode.ownerDocument.createElement("splitter");
-    this._splitter.setAttribute("class", "hud-splitter");
-    this.frame = gBrowser.parentNode.ownerDocument.createElement("iframe");
-    this.frame.height = DebuggerUIPreferences.height;
-
-    this._nbox.appendChild(this._splitter);
-    this._nbox.appendChild(this.frame);
-
-    let self = this;
-
-    this.frame.addEventListener("DOMContentLoaded", function initPane(aEvent) {
-      if (aEvent.target != self.frame.contentDocument) {
-        return;
-      }
-      self.frame.removeEventListener("DOMContentLoaded", initPane, true);
-      // Initialize the source editor.
-      self.frame.contentWindow.editor = self.editor = new SourceEditor();
-      self.frame.contentWindow.updateEditorBreakpoints =
-        self._updateEditorBreakpoints.bind(self);
-
-      let config = {
-        mode: SourceEditor.MODES.JAVASCRIPT,
-        showLineNumbers: true,
-        readOnly: true,
-        showAnnotationRuler: true,
-        showOverviewRuler: true,
-      };
+  toggleDebugger: function DUI_toggleDebugger() {
+    let tab = this.chromeWindow.gBrowser.selectedTab;
 
-      let editorPlaceholder = self.frame.contentDocument.getElementById("editor");
-      self.editor.init(editorPlaceholder, config, self._onEditorLoad.bind(self));
-    }, true);
-    this.frame.addEventListener("DebuggerClose", this._close, true);
-
-    this.frame.setAttribute("src", "chrome://browser/content/debugger.xul");
-  },
-
-  /**
-   * The load event handler for the source editor. This method does post-load
-   * editor initialization.
-   */
-  _onEditorLoad: function DP__onEditorLoad() {
-    this.editor.addEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE,
-                                 this._onEditorBreakpointChange.bind(this));
-    // Connect to the debugger server.
-    this.connect();
-  },
-
-  /**
-   * Event handler for breakpoint changes that happen in the editor. This
-   * function syncs the breakpoint changes in the editor to those in the
-   * debugger.
-   *
-   * @private
-   * @param object aEvent
-   *        The SourceEditor.EVENTS.BREAKPOINT_CHANGE event object.
-   */
-  _onEditorBreakpointChange: function DP__onEditorBreakpointChange(aEvent) {
-    if (this._skipEditorBreakpointChange) {
-      return;
+    if (tab._scriptDebugger) {
+      tab._scriptDebugger.close();
+      return null;
     }
-
-    aEvent.added.forEach(this._onEditorBreakpointAdd, this);
-    aEvent.removed.forEach(this._onEditorBreakpointRemove, this);
+    return new DebuggerPane(tab);
   },
 
   /**
-   * Retrieve the URL of the selected script in the debugger view.
-   *
-   * @private
-   * @return string
-   *         The URL of the selected script.
-   */
-  _selectedScript: function DP__selectedScript() {
-    return this.debuggerWindow ?
-           this.debuggerWindow.DebuggerView.Scripts.selected : null;
-  },
-
-  /**
-   * Event handler for new breakpoints that come from the editor.
-   *
-   * @private
-   * @param object aBreakpoint
-   *        The breakpoint object coming from the editor.
-   */
-  _onEditorBreakpointAdd: function DP__onEditorBreakpointAdd(aBreakpoint) {
-    let location = {
-      url: this._selectedScript(),
-      line: aBreakpoint.line + 1,
-    };
-
-    if (location.url) {
-      let callback = function (aClient, aError) {
-        if (aError) {
-          this._skipEditorBreakpointChange = true;
-          let result = this.editor.removeBreakpoint(aBreakpoint.line);
-          this._skipEditorBreakpointChange = false;
-        }
-      }.bind(this);
-      this.addBreakpoint(location, callback, true);
-    }
-  },
-
-  /**
-   * Event handler for breakpoints that are removed from the editor.
-   *
-   * @private
-   * @param object aBreakpoint
-   *        The breakpoint object that was removed from the editor.
+   * Get the debugger for a specified tab.
+   * @return DebuggerPane if a debugger exists for the tab, null otherwise
    */
-  _onEditorBreakpointRemove: function DP__onEditorBreakpointRemove(aBreakpoint) {
-    let url = this._selectedScript();
-    let line = aBreakpoint.line + 1;
-    if (!url) {
-      return;
-    }
-
-    let breakpoint = this.getBreakpoint(url, line);
-    if (breakpoint) {
-      this.removeBreakpoint(breakpoint, null, true);
-    }
-  },
-
-  /**
-   * Update the breakpoints in the editor view. This function takes the list of
-   * breakpoints in the debugger and adds them back into the editor view. This
-   * is invoked when the selected script is changed.
-   *
-   * @private
-   */
-  _updateEditorBreakpoints: function DP__updateEditorBreakpoints()
-  {
-    let url = this._selectedScript();
-    if (!url) {
-      return;
-    }
-
-    this._skipEditorBreakpointChange = true;
-    for each (let breakpoint in this.breakpoints) {
-      if (breakpoint.location.url == url) {
-        this.editor.addBreakpoint(breakpoint.location.line - 1);
-      }
-    }
-    this._skipEditorBreakpointChange = false;
-  },
-
-  /**
-   * Add a breakpoint.
-   *
-   * @param object aLocation
-   *        The location where you want the breakpoint. This object must have
-   *        two properties:
-   *          - url - the URL of the script.
-   *          - line - the line number (starting from 1).
-   * @param function [aCallback]
-   *        Optional function to invoke once the breakpoint is added. The
-   *        callback is invoked with two arguments:
-   *          - aBreakpointClient - the BreakpointActor client object, if the
-   *          breakpoint has been added successfully.
-   *          - aResponseError - if there was any error.
-   * @param boolean [aNoEditorUpdate=false]
-   *        Tells if you want to skip editor updates. Typically the editor is
-   *        updated to visually indicate that a breakpoint has been added.
-   */
-  addBreakpoint:
-  function DP_addBreakpoint(aLocation, aCallback, aNoEditorUpdate) {
-    let breakpoint = this.getBreakpoint(aLocation.url, aLocation.line);
-    if (breakpoint) {
-      aCallback && aCallback(breakpoint);
-      return;
-    }
-
-    this.activeThread.setBreakpoint(aLocation, function(aResponse, aBpClient) {
-      if (!aResponse.error) {
-        this.breakpoints[aBpClient.actor] = aBpClient;
-
-        if (!aNoEditorUpdate) {
-          let url = this._selectedScript();
-          if (url == aLocation.url) {
-            this._skipEditorBreakpointChange = true;
-            this.editor.addBreakpoint(aLocation.line - 1);
-            this._skipEditorBreakpointChange = false;
-          }
-        }
-      }
-
-      aCallback && aCallback(aBpClient, aResponse.error);
-    }.bind(this));
+  getDebugger: function DUI_getDebugger(aTab) {
+    return aTab._scriptDebugger;
   },
 
   /**
-   * Remove a breakpoint.
-   *
-   * @param object aBreakpoint
-   *        The breakpoint you want to remove.
-   * @param function [aCallback]
-   *        Optional function to invoke once the breakpoint is removed. The
-   *        callback is invoked with one argument: the breakpoint location
-   *        object which holds the url and line properties.
-   * @param boolean [aNoEditorUpdate=false]
-   *        Tells if you want to skip editor updates. Typically the editor is
-   *        updated to visually indicate that a breakpoint has been removed.
+   * Get the preferences associated with the debugger frontend.
+   * @return object
    */
-  removeBreakpoint:
-  function DP_removeBreakpoint(aBreakpoint, aCallback, aNoEditorUpdate) {
-    if (!(aBreakpoint.actor in this.breakpoints)) {
-      aCallback && aCallback(aBreakpoint.location);
-      return;
-    }
-
-    aBreakpoint.remove(function() {
-      delete this.breakpoints[aBreakpoint.actor];
+  get preferences() {
+    return DebuggerUIPreferences;
+  }
+};
 
-      if (!aNoEditorUpdate) {
-        let url = this._selectedScript();
-        if (url == aBreakpoint.location.url) {
-          this._skipEditorBreakpointChange = true;
-          this.editor.removeBreakpoint(aBreakpoint.location.line - 1);
-          this._skipEditorBreakpointChange = false;
-        }
-      }
-
-      aCallback && aCallback(aBreakpoint.location);
-    }.bind(this));
-  },
+/**
+ * Creates a pane that will host the debugger.
+ *
+ * @param XULElement aTab
+ *        The tab in which to create the debugger.
+ */
+function DebuggerPane(aTab) {
+  this._tab = aTab;
+  this._create();
+}
 
-  /**
-   * Get the breakpoint object at the given location.
-   *
-   * @param string aUrl
-   *        The URL of where the breakpoint is.
-   * @param number aLine
-   *        The line number where the breakpoint is.
-   * @return object
-   *         The BreakpointActor object.
-   */
-  getBreakpoint: function DP_getBreakpoint(aUrl, aLine) {
-    for each (let breakpoint in this.breakpoints) {
-      if (breakpoint.location.url == aUrl && breakpoint.location.line == aLine) {
-        return breakpoint;
-      }
-    }
-    return null;
-  },
+DebuggerPane.prototype = {
 
   /**
-   * Closes the debugger UI removing child nodes and event listeners.
+   * Creates and initializes the widgets containing the debugger UI.
    */
-  close: function DP_close() {
-    for each (let breakpoint in this.breakpoints) {
-      this.removeBreakpoint(breakpoint);
-    }
-
-    if (this._tab) {
-      this._tab._scriptDebugger = null;
-      this._tab = null;
-    }
-    if (this.frame) {
-      DebuggerUIPreferences.height = this.frame.height;
+  _create: function DP__create() {
+    this._tab._scriptDebugger = this;
 
-      this.frame.removeEventListener("unload", this._close, true);
-      this.frame.removeEventListener("DebuggerClose", this._close, true);
-      if (this.frame.parentNode) {
-        this.frame.parentNode.removeChild(this.frame);
-      }
-      this.frame = null;
-    }
-    if (this._nbox) {
-      this._nbox.removeChild(this._splitter);
-      this._nbox = null;
-    }
+    let gBrowser = this._tab.linkedBrowser.getTabBrowser();
+    let ownerDocument = gBrowser.parentNode.ownerDocument;
 
-    this._splitter = null;
+    this._splitter = ownerDocument.createElement("splitter");
+    this._splitter.setAttribute("class", "hud-splitter");
+
+    this._frame = ownerDocument.createElement("iframe");
+    this._frame.height = DebuggerUIPreferences.height;
 
-    if (this._client) {
-      this._client.removeListener("newScript", this.onNewScript);
-      this._client.removeListener("tabDetached", this._close);
-      this._client.removeListener("tabNavigated", this._debugTab);
-      this._client.close();
-      this._client = null;
-    }
-  },
+    this._nbox = gBrowser.getNotificationBox(this._tab.linkedBrowser);
+    this._nbox.appendChild(this._splitter);
+    this._nbox.appendChild(this._frame);
 
-  /**
-   * Initializes a debugger client and connects it to the debugger server,
-   * wiring event handlers as necessary.
-   */
-  connect: function DP_connect() {
-    this.frame.addEventListener("unload", this._close, true);
+    this.close = this.close.bind(this);
+    let self = this;
 
-    let transport = DebuggerServer.connectPipe();
-    this._client = new DebuggerClient(transport);
-    // Store the new script handler locally, so when it's time to remove it we
-    // don't need to go through the iframe, since it might be cleared.
-    this.onNewScript = this.debuggerWindow.SourceScripts.onNewScript;
-    let self = this;
-    this._client.addListener("tabNavigated", this._debugTab);
-    this._client.addListener("tabDetached", this._close);
-    this._client.addListener("newScript", this.onNewScript);
-    this._client.connect(function(aType, aTraits) {
-      self._client.listTabs(function(aResponse) {
-        let tab = aResponse.tabs[aResponse.selected];
-        self.debuggerWindow.startDebuggingTab(self._client, tab);
-        if (self.onConnected) {
-          self.onConnected(self);
-        }
-      });
-    });
+    this._frame.addEventListener("Debugger:Loaded", function dbgLoaded() {
+      self._frame.removeEventListener("Debugger:Loaded", dbgLoaded, true);
+      self._frame.addEventListener("Debugger:Close", self.close, true);
+      self._frame.addEventListener("unload", self.close, true);
+
+      // Bind shortcuts for accessing the breakpoint methods in the debugger.
+      let bkp = self.debuggerWindow.DebuggerController.Breakpoints;
+      self.addBreakpoint = bkp.addBreakpoint;
+      self.removeBreakpoint = bkp.removeBreakpoint;
+      self.getBreakpoint = bkp.getBreakpoint;
+    }, true);
+
+    this._frame.setAttribute("src", "chrome://browser/content/debugger.xul");
   },
 
   /**
-   * Starts debugging the current tab. This function is called on each location
-   * change in this tab.
+   * Closes the debugger, removing child nodes and event listeners.
    */
-  debugTab: function DP_debugTab(aNotification, aPacket) {
-    let self = this;
-    this._client.activeThread.detach(function() {
-      self._client.activeTab.detach(function() {
-        self._client.listTabs(function(aResponse) {
-          let tab = aResponse.tabs[aResponse.selected];
-          self.debuggerWindow.startDebuggingTab(self._client, tab);
-          if (self.onConnected) {
-            self.onConnected(self);
-          }
-        });
-      });
-    });
-  },
-
-  get debuggerWindow() {
-    return this.frame ? this.frame.contentWindow : null;
-  },
-
-  get debuggerClient() {
-    return this._client;
-  },
-
-  get activeThread() {
-    try {
-      return this.debuggerWindow.ThreadState.activeThread;
-    } catch(ex) {
-      return undefined;
+  close: function DP_close() {
+    if (!this._tab) {
+      return;
     }
-  }
-};
+    this._tab._scriptDebugger = null;
+    this._tab = null;
 
-function DebuggerUI(aWindow) {
-  this.aWindow = aWindow;
-
-  aWindow.addEventListener("Debugger:LoadSource", this._onLoadSource.bind(this));
-}
-
-DebuggerUI.prototype = {
-  /**
-   * Starts the debugger or stops it, if it is already started.
-   */
-  toggleDebugger: function DebuggerUI_toggleDebugger() {
-    if (!DebuggerServer.initialized) {
-      DebuggerServer.init();
-      DebuggerServer.addBrowserActors();
-    }
+    DebuggerUIPreferences.height = this._frame.height;
+    this._frame.removeEventListener("Debugger:Close", this.close, true);
+    this._frame.removeEventListener("unload", this.close, true);
 
-    let gBrowser = this.aWindow.gBrowser;
-    let tab = gBrowser.selectedTab;
-
-    if (tab._scriptDebugger) {
-      // If the debugger is already open, just close it.
-      tab._scriptDebugger.close();
-      return tab._scriptDebugger;
-    }
+    this._nbox.removeChild(this._splitter);
+    this._nbox.removeChild(this._frame);
 
-    let pane = new DebuggerPane(tab);
-    pane.create(gBrowser);
-    return pane;
-  },
-
-  getDebugger: function DebuggerUI_getDebugger(aTab) {
-    return aTab._scriptDebugger;
-  },
-
-  get preferences() {
-    return DebuggerUIPreferences;
+    this._splitter = null;
+    this._frame = null;
+    this._nbox = null;
   },
 
   /**
-   * Handles notifications to load a source script from the cache or from a
-   * local file.
-   * XXX: it may be better to use nsITraceableChannel to get to the sources
-   * without relying on caching when we can (not for eval, etc.):
-   * http://www.softwareishard.com/blog/firebug/nsitraceablechannel-intercept-http-traffic/
+   * Gets the debugger content window.
+   * @return nsIDOMWindow if a debugger window exists, null otherwise
    */
-  _onLoadSource: function DebuggerUI__onLoadSource(aEvent) {
-    let gBrowser = this.aWindow.gBrowser;
-
-    let url = aEvent.detail.url;
-    let showOptions = aEvent.detail.options;
-    let scheme = Services.io.extractScheme(url);
-    switch (scheme) {
-      case "file":
-      case "chrome":
-      case "resource":
-        try {
-          NetUtil.asyncFetch(url, function onFetch(aStream, aStatus) {
-            if (!Components.isSuccessCode(aStatus)) {
-              return this.logError(url, aStatus);
-            }
-            let source = NetUtil.readInputStreamToString(aStream, aStream.available());
-            aStream.close();
-            this._onSourceLoaded(url, source, showOptions);
-          }.bind(this));
-        } catch (ex) {
-          return this.logError(url, ex.name);
-        }
-        break;
-
-      default:
-        let channel = Services.io.newChannel(url, null, null);
-        let chunks = [];
-        let streamListener = { // nsIStreamListener inherits nsIRequestObserver
-          onStartRequest: function (aRequest, aContext, aStatusCode) {
-            if (!Components.isSuccessCode(aStatusCode)) {
-              return this.logError(url, aStatusCode);
-            }
-          }.bind(this),
-          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(""), channel.contentType,
-                                 showOptions);
-          }.bind(this)
-        };
-
-        channel.loadFlags = channel.LOAD_FROM_CACHE;
-        channel.asyncOpen(streamListener, null);
-        break;
-    }
+  get debuggerWindow() {
+    return this._frame ? this._frame.contentWindow : null;
   },
 
   /**
-   * Log an error message in the error console when a script fails to load.
-   *
-   * @param string aUrl
-   *        The URL of the source script.
-   * @param string aStatus
-   *        The failure status code.
+   * Shortcut for accessing the list of breakpoints in the debugger.
+   * @return object if a debugger window exists, null otherwise
    */
-  logError: function DebuggerUI_logError(aUrl, aStatus) {
-    let view = this.getDebugger(gBrowser.selectedTab).DebuggerView;
-    Components.utils.reportError(view.getFormatStr("loadingError", [ aUrl, aStatus ]));
-  },
-
-  /**
-   * Called when source has been loaded.
-   *
-   * @private
-   * @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.
-   * @param object [aOptions]
-   *        Additional options for showing the script (optional). Supported
-   *        options:
-   *        - targetLine: place the editor at the given line number.
-   */
-  _onSourceLoaded: function DebuggerUI__onSourceLoaded(aSourceUrl,
-                                                       aSourceText,
-                                                       aContentType,
-                                                       aOptions) {
-    let dbg = this.getDebugger(this.aWindow.gBrowser.selectedTab);
-    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);
-
-    dbg.debuggerWindow.SourceScripts._onShowScript(script, aOptions);
+  get breakpoints() {
+    let debuggerWindow = this.debuggerWindow;
+    if (debuggerWindow) {
+      return debuggerWindow.DebuggerController.Breakpoints.store;
+    }
+    return null;
   }
 };
 
 /**
  * Various debugger UI preferences (currently just the pane height).
  */
 let DebuggerUIPreferences = {
 
-  _height: -1,
-
   /**
    * Gets the preferred height of the debugger pane.
-   *
    * @return number
-   *         The preferred height.
    */
   get height() {
-    if (this._height < 0) {
+    if (this._height === undefined) {
       this._height = Services.prefs.getIntPref("devtools.debugger.ui.height");
     }
     return this._height;
   },
 
   /**
    * Sets the preferred height of the debugger pane.
-   *
    * @param number value
-   *        The new height.
    */
   set height(value) {
     Services.prefs.setIntPref("devtools.debugger.ui.height", value);
     this._height = value;
   }
 };
rename from browser/devtools/debugger/debugger.js
rename to browser/devtools/debugger/debugger-controller.js
--- a/browser/devtools/debugger/debugger.js
+++ b/browser/devtools/debugger/debugger-controller.js
@@ -34,537 +34,716 @@
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
  * 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";
 
-Components.utils.import("resource:///modules/source-editor.jsm");
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
 
-var gInitialized = false;
-var gClient = null;
-var gTabClient = null;
-
+const DBG_STRINGS_URI = "chrome://browser/locale/devtools/debugger.properties";
 
-function initDebugger()
-{
-  window.removeEventListener("DOMContentLoaded", initDebugger, false);
-  if (gInitialized) {
-    return;
-  }
-  gInitialized = true;
-
-  DebuggerView.Stackframes.initialize();
-  DebuggerView.Properties.initialize();
-  DebuggerView.Scripts.initialize();
-}
+Cu.import("resource:///modules/source-editor.jsm");
+Cu.import("resource://gre/modules/devtools/dbg-server.jsm");
+Cu.import("resource://gre/modules/devtools/dbg-client.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/NetUtil.jsm");
+Cu.import('resource://gre/modules/Services.jsm');
 
 /**
- * Called by chrome to set up a debugging session.
- *
- * @param DebuggerClient aClient
- *        The debugger client.
- * @param object aTabGrip
- *        The remote protocol grip of the tab.
+ * Controls the debugger view by handling the source scripts, the current
+ * thread state and thread stack frame cache.
  */
-function startDebuggingTab(aClient, aTabGrip)
-{
-  gClient = aClient;
+let DebuggerController = {
+
+  /**
+   * Makes a few preliminary changes and bindings to the controller.
+   */
+  init: function() {
+    this._startupDebugger = this._startupDebugger.bind(this);
+    this._shutdownDebugger = this._shutdownDebugger.bind(this);
+    this._onTabNavigated = this._onTabNavigated.bind(this);
+    this._onTabDetached = this._onTabDetached.bind(this);
+
+    window.addEventListener("DOMContentLoaded", this._startupDebugger, true);
+    window.addEventListener("unload", this._shutdownDebugger, true);
+  },
+
+  /**
+   * Initializes the debugger view and connects a debugger client to the server.
+   */
+  _startupDebugger: function DC__startupDebugger() {
+    if (this._isInitialized) {
+      return;
+    }
+    this._isInitialized = true;
+    window.removeEventListener("DOMContentLoaded", this._startupDebugger, true);
+
+    DebuggerView.initializeEditor();
+    DebuggerView.StackFrames.initialize();
+    DebuggerView.Properties.initialize();
+    DebuggerView.Scripts.initialize();
+
+    this.dispatchEvent("Debugger:Loaded");
+    this._connect();
+  },
+
+  /**
+   * Destroys the debugger view, disconnects the debugger client and cleans up
+   * any active listeners.
+   */
+  _shutdownDebugger: function DC__shutdownDebugger() {
+    if (this._isDestroyed) {
+      return;
+    }
+    this._isDestroyed = true;
+    window.removeEventListener("unload", this._shutdownDebugger, true);
+
+    DebuggerView.destroyEditor();
+    DebuggerView.Scripts.destroy();
+    DebuggerView.StackFrames.destroy();
+    DebuggerView.Properties.destroy();
+
+    DebuggerController.SourceScripts.disconnect();
+    DebuggerController.StackFrames.disconnect();
+    DebuggerController.ThreadState.disconnect();
+
+    this.dispatchEvent("Debugger:Unloaded");
+    this._disconnect();
+  },
+
+  /**
+   * Initializes a debugger client and connects it to the debugger server,
+   * wiring event handlers as necessary.
+   */
+  _connect: function DC__connect() {
+    if (!DebuggerServer.initialized) {
+      DebuggerServer.init();
+      DebuggerServer.addBrowserActors();
+    }
+
+    let transport = DebuggerServer.connectPipe();
+    let client = this.client = new DebuggerClient(transport);
 
-  gClient.attachTab(aTabGrip.actor, function(aResponse, aTabClient) {
-    if (aTabClient) {
-      gTabClient = aTabClient;
-      gClient.attachThread(aResponse.threadActor, function(aResponse, aThreadClient) {
+    client.addListener("tabNavigated", this._onTabNavigated);
+    client.addListener("tabDetached", this._onTabDetached);
+
+    client.connect(function(aType, aTraits) {
+      client.listTabs(function(aResponse) {
+        let tab = aResponse.tabs[aResponse.selected];
+        this._startDebuggingTab(client, tab);
+        this.dispatchEvent("Debugger:Connecting");
+      }.bind(this));
+    }.bind(this));
+  },
+
+  /**
+   * Closes the debugger client and removes event handlers as necessary.
+   */
+  _disconnect: function DC__disconnect() {
+    this.client.removeListener("tabNavigated", this._onTabNavigated);
+    this.client.removeListener("tabDetached", this._onTabDetached);
+    this.client.close();
+
+    this.client = null;
+    this.tabClient = null;
+    this.activeThread = null;
+  },
+
+  /**
+   * Starts debugging the current tab. This function is called on each location
+   * change in this tab.
+   */
+  _onTabNavigated: function DC__onTabNavigated(aNotification, aPacket) {
+    let client = this.client;
+
+    client.activeThread.detach(function() {
+      client.activeTab.detach(function() {
+        client.listTabs(function(aResponse) {
+          let tab = aResponse.tabs[aResponse.selected];
+          this._startDebuggingTab(client, tab);
+          this.dispatchEvent("Debugger:Connecting");
+        }.bind(this));
+      }.bind(this));
+    }.bind(this));
+  },
+
+  /**
+   * Stops debugging the current tab.
+   */
+  _onTabDetached: function DC__onTabDetached() {
+    this.dispatchEvent("Debugger:Close");
+  },
+
+  /**
+   * Sets up a debugging session.
+   *
+   * @param DebuggerClient aClient
+   *        The debugger client.
+   * @param object aTabGrip
+   *        The remote protocol grip of the tab.
+   */
+  _startDebuggingTab: function DC__startDebuggingTab(aClient, aTabGrip) {
+    if (!aClient) {
+      Cu.reportError("No client found!");
+      return;
+    }
+    this.client = aClient;
+
+    aClient.attachTab(aTabGrip.actor, function(aResponse, aTabClient) {
+      if (!aTabClient) {
+        Cu.reportError("No tab client found!");
+        return;
+      }
+      this.tabClient = aTabClient;
+
+      aClient.attachThread(aResponse.threadActor, function(aResponse, aThreadClient) {
         if (!aThreadClient) {
-          Components.utils.reportError("Couldn't attach to thread: " +
-                                       aResponse.error);
+          Cu.reportError("Couldn't attach to thread: " + aResponse.error);
           return;
         }
-        ThreadState.connect(aThreadClient, function() {
-          StackFrames.connect(aThreadClient, function() {
-            SourceScripts.connect(aThreadClient, function() {
+        this.activeThread = aThreadClient;
+
+        DebuggerController.ThreadState.connect(function() {
+          DebuggerController.StackFrames.connect(function() {
+            DebuggerController.SourceScripts.connect(function() {
               aThreadClient.resume();
             });
           });
         });
-      });
-    }
-  });
-}
 
-function shutdownDebugger()
-{
-  window.removeEventListener("unload", shutdownDebugger, false);
+      }.bind(this));
+    }.bind(this));
+  },
 
-  SourceScripts.disconnect();
-  StackFrames.disconnect();
-  ThreadState.disconnect();
-  ThreadState.activeThread = false;
-
-  DebuggerView.Stackframes.destroy();
-  DebuggerView.Properties.destroy();
-  DebuggerView.Scripts.destroy();
-}
-
+  /**
+   * Convenience method, dispatching a custom event.
+   *
+   * @param string aType
+   *        The name of the event.
+   * @param string aDetail
+   *        The data passed when initializing the event.
+   */
+  dispatchEvent: function DC_dispatchEvent(aType, aDetail) {
+    let evt = document.createEvent("CustomEvent");
+    evt.initCustomEvent(aType, true, false, aDetail);
+    document.documentElement.dispatchEvent(evt);
+  }
+};
 
 /**
  * ThreadState keeps the UI up to date with the state of the
  * thread (paused/attached/etc.).
  */
-var ThreadState = {
-  activeThread: null,
+function ThreadState() {
+  this._update = this._update.bind(this);
+}
+
+ThreadState.prototype = {
 
   /**
-   * Connect to a thread client.
-   * @param object aThreadClient
-   *        The thread client.
-   * @param function aCallback
-   *        The next function in the initialization sequence.
+   * Gets the current thread the client has connected to.
    */
-  connect: function TS_connect(aThreadClient, aCallback) {
-    this.activeThread = aThreadClient;
-    aThreadClient.addListener("paused", ThreadState.update);
-    aThreadClient.addListener("resumed", ThreadState.update);
-    aThreadClient.addListener("detached", ThreadState.update);
-    this.update();
-    aCallback && aCallback();
-  },
-
-  /**
-   * Update the UI after a thread state change.
-   */
-  update: function TS_update(aEvent) {
-    DebuggerView.Stackframes.updateState(this.activeThread.state);
-    if (aEvent == "detached") {
-      ThreadState.activeThread = false;
-    }
+  get activeThread() {
+    return DebuggerController.activeThread;
   },
 
   /**
-   * Disconnect from the client.
-   */
-  disconnect: function TS_disconnect() {
-    this.activeThread.removeListener("paused", ThreadState.update);
-    this.activeThread.removeListener("resumed", ThreadState.update);
-    this.activeThread.removeListener("detached", ThreadState.update);
-  }
-};
-
-ThreadState.update = ThreadState.update.bind(ThreadState);
-
-/**
- * Keeps the stack frame list up-to-date, using the thread client's
- * stack frame cache.
- */
-var StackFrames = {
-  pageSize: 25,
-  activeThread: null,
-  selectedFrame: null,
-
-  /**
-   * Watch a given thread client.
-   * @param object aThreadClient
-   *        The thread client.
+   * Connect to the current thread client.
+   *
    * @param function aCallback
    *        The next function in the initialization sequence.
    */
-  connect: function SF_connect(aThreadClient, aCallback) {
-    DebuggerView.Stackframes.addClickListener(this.onClick);
+  connect: function TS_connect(aCallback) {
+    this.activeThread.addListener("paused", this._update);
+    this.activeThread.addListener("resumed", this._update);
+    this.activeThread.addListener("detached", this._update);
 
-    this.activeThread = aThreadClient;
-    aThreadClient.addListener("paused", this.onPaused);
-    aThreadClient.addListener("resumed", this.onResume);
-    aThreadClient.addListener("framesadded", this.onFrames);
-    aThreadClient.addListener("framescleared", this.onFramesCleared);
-    this.onFramesCleared();
+    this._update();
+
     aCallback && aCallback();
   },
 
   /**
    * Disconnect from the client.
    */
   disconnect: function TS_disconnect() {
-    this.activeThread.removeListener("paused", this.onPaused);
-    this.activeThread.removeListener("resumed", this.onResume);
-    this.activeThread.removeListener("framesadded", this.onFrames);
-    this.activeThread.removeListener("framescleared", this.onFramesCleared);
+    if (!this.activeThread) {
+      return;
+    }
+    this.activeThread.removeListener("paused", this._update);
+    this.activeThread.removeListener("resumed", this._update);
+    this.activeThread.removeListener("detached", this._update);
+  },
+
+  /**
+   * Update the UI after a thread state change.
+   */
+  _update: function TS__update(aEvent) {
+    DebuggerView.StackFrames.updateState(this.activeThread.state);
+  }
+};
+
+/**
+ * Keeps the stack frame list up-to-date, using the thread client's stack frame
+ * cache.
+ */
+function StackFrames() {
+  this._onPaused = this._onPaused.bind(this);
+  this._onResume = this._onResume.bind(this);
+  this._onFrames = this._onFrames.bind(this);
+  this._onFramesCleared = this._onFramesCleared.bind(this);
+}
+
+StackFrames.prototype = {
+
+  /**
+   * The maximum number of frames allowed to be loaded at a time.
+   */
+  pageSize: 25,
+
+  /**
+   * The currently selected frame depth.
+   */
+  selectedFrame: null,
+
+  /**
+   * Gets the current thread the client has connected to.
+   */
+  get activeThread() {
+    return DebuggerController.activeThread;
+  },
+
+  /**
+   * Watch the given thread client.
+   *
+   * @param function aCallback
+   *        The next function in the initialization sequence.
+   */
+  connect: function SF_connect(aCallback) {
+    this.activeThread.addListener("paused", this._onPaused);
+    this.activeThread.addListener("resumed", this._onResume);
+    this.activeThread.addListener("framesadded", this._onFrames);
+    this.activeThread.addListener("framescleared", this._onFramesCleared);
+
+    this._onFramesCleared();
+
+    aCallback && aCallback();
+  },
+
+  /**
+   * Disconnect from the client.
+   */
+  disconnect: function SF_disconnect() {
+    if (!this.activeThread) {
+      return;
+    }
+    this.activeThread.removeListener("paused", this._onPaused);
+    this.activeThread.removeListener("resumed", this._onResume);
+    this.activeThread.removeListener("framesadded", this._onFrames);
+    this.activeThread.removeListener("framescleared", this._onFramesCleared);
   },
 
   /**
    * Handler for the thread client's paused notification.
    */
-  onPaused: function SF_onPaused() {
+  _onPaused: function SF__onPaused() {
     this.activeThread.fillFrames(this.pageSize);
   },
 
   /**
    * Handler for the thread client's resumed notification.
    */
-  onResume: function SF_onResume() {
-    window.editor.setDebugLocation(-1);
+  _onResume: function SF__onResume() {
+    DebuggerView.editor.setDebugLocation(-1);
   },
 
   /**
    * Handler for the thread client's framesadded notification.
    */
-  onFrames: function SF_onFrames() {
-    DebuggerView.Stackframes.empty();
+  _onFrames: function SF__onFrames() {
+    if (!this.activeThread.cachedFrames.length) {
+      DebuggerView.StackFrames.emptyText();
+      return;
+    }
+    DebuggerView.StackFrames.empty();
 
     for each (let frame in this.activeThread.cachedFrames) {
-      this._addFramePanel(frame);
+      this._addFrame(frame);
     }
-
-    if (this.activeThread.moreFrames) {
-      DebuggerView.Stackframes.dirty = true;
-    }
-
     if (!this.selectedFrame) {
       this.selectFrame(0);
     }
+    if (this.activeThread.moreFrames) {
+      DebuggerView.StackFrames.dirty = true;
+    }
   },
 
   /**
    * Handler for the thread client's framescleared notification.
    */
-  onFramesCleared: function SF_onFramesCleared() {
-    DebuggerView.Stackframes.emptyText();
+  _onFramesCleared: function SF__onFramesCleared() {
+    DebuggerView.StackFrames.emptyText();
     this.selectedFrame = null;
+
     // Clear the properties as well.
     DebuggerView.Properties.localScope.empty();
     DebuggerView.Properties.globalScope.empty();
   },
 
   /**
-   * Event handler for clicks on stack frames.
+   * Update the source editor's current debug location based on the selected
+   * frame and script.
    */
-  onClick: function SF_onClick(aEvent) {
-    let target = aEvent.target;
-    while (target) {
-      if (target.stackFrame) {
-        this.selectFrame(target.stackFrame.depth);
-        return;
-      }
-      target = target.parentNode;
+  updateEditorLocation: function SF_updateEditorLocation() {
+    let frame = this.activeThread.cachedFrames[this.selectedFrame];
+    if (!frame) {
+      return;
+    }
+
+    let url = frame.where.url;
+    let line = frame.where.line;
+    let editor = DebuggerView.editor;
+
+    // Move the editor's caret to the proper line.
+    if (DebuggerView.Scripts.isSelected(url) && line) {
+      editor.setDebugLocation(line - 1);
+    } else {
+      editor.setDebugLocation(-1);
     }
   },
 
   /**
    * Marks the stack frame in the specified depth as selected and updates the
    * properties view with the stack frame's data.
    *
    * @param number aDepth
    *        The depth of the frame in the stack.
    */
   selectFrame: function SF_selectFrame(aDepth) {
+    // Deselect any previously highlighted frame.
     if (this.selectedFrame !== null) {
-      DebuggerView.Stackframes.highlightFrame(this.selectedFrame, false);
+      DebuggerView.StackFrames.unhighlightFrame(this.selectedFrame);
     }
 
+    // Highlight the current frame.
     this.selectedFrame = aDepth;
-    if (aDepth !== null) {
-      DebuggerView.Stackframes.highlightFrame(this.selectedFrame, true);
-    }
+    DebuggerView.StackFrames.highlightFrame(this.selectedFrame);
 
     let frame = this.activeThread.cachedFrames[aDepth];
     if (!frame) {
       return;
     }
 
+    let url = frame.where.url;
+    let line = frame.where.line;
+    let editor = DebuggerView.editor;
+
     // 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);
-      window.editor.setDebugLocation(frame.where.line - 1);
-    } else if (DebuggerView.Scripts.contains(frame.where.url)) {
-      DebuggerView.Scripts.selectScript(frame.where.url);
-      window.editor.setCaretPosition(frame.where.line - 1);
-    } else {
-      window.editor.setDebugLocation(-1);
+    if (DebuggerView.Scripts.isSelected(url) && line) {
+      editor.setCaretPosition(line - 1);
+      editor.setDebugLocation(line - 1);
+    }
+    else if (DebuggerView.Scripts.contains(url)) {
+      DebuggerView.Scripts.selectScript(url);
+      editor.setCaretPosition(line - 1);
+    }
+    else {
+      editor.setDebugLocation(-1);
     }
 
     // Display the local variables.
     let localScope = DebuggerView.Properties.localScope;
     localScope.empty();
+
     // Add "this".
-    if (frame["this"]) {
+    if (frame.this) {
       let thisVar = localScope.addVar("this");
-      thisVar.setGrip({ "type": frame["this"].type,
-                        "class": frame["this"].class });
-      this._addExpander(thisVar, frame["this"]);
+      thisVar.setGrip({
+        type: frame.this.type,
+        class: frame.this.class
+      });
+      this._addExpander(thisVar, frame.this);
     }
 
     if (frame.arguments && frame.arguments.length > 0) {
       // Add "arguments".
       let argsVar = localScope.addVar("arguments");
-      argsVar.setGrip({ "type": "object", "class": "Arguments" });
+      argsVar.setGrip({
+        type: "object",
+        class: "Arguments"
+      });
       this._addExpander(argsVar, frame.arguments);
 
       // Add variables for every argument.
       let objClient = this.activeThread.pauseGrip(frame.callee);
       objClient.getSignature(function SF_getSignature(aResponse) {
-        for (let i = 0; i < aResponse.parameters.length; i++) {
+        for (let i = 0, l = aResponse.parameters.length; i < l; i++) {
           let param = aResponse.parameters[i];
           let paramVar = localScope.addVar(param);
           let paramVal = frame.arguments[i];
+
           paramVar.setGrip(paramVal);
           this._addExpander(paramVar, paramVal);
         }
+
         // Signal that call parameters have been fetched.
-        let evt = document.createEvent("Event");
-        evt.initEvent("Debugger:FetchedParameters", true, false);
-        document.documentElement.dispatchEvent(evt);
+        DebuggerController.dispatchEvent("Debugger:FetchedParameters");
+
       }.bind(this));
     }
   },
 
   /**
-   * Update the source editor current debug location based on the selected frame
-   * and script.
+   * Adds a onexpand callback for a variable, lazily handling the addition of
+   * new properties.
    */
-  updateEditor: function SF_updateEditor() {
-    if (this.selectedFrame === null) {
-      return;
-    }
-
-    let frame = this.activeThread.cachedFrames[this.selectedFrame];
-    if (!frame) {
+  _addExpander: function SF__addExpander(aVar, aObject) {
+    // No need for expansion for null and undefined values, but we do need them
+    // for frame.arguments which is a regular array.
+    if (!aObject || typeof aObject !== "object" ||
+        (aObject.type !== "object" && !Array.isArray(aObject))) {
       return;
     }
 
-    // Move the editor's caret to the proper line.
-    if (DebuggerView.Scripts.isSelected(frame.where.url) && frame.where.line) {
-      window.editor.setDebugLocation(frame.where.line - 1);
-    } else {
-      window.editor.setDebugLocation(-1);
-    }
-  },
-
-  _addExpander: function SF_addExpander(aVar, aObject) {
-    // No need for expansion for null and undefined values, but we do need them
-    // for frame.arguments which is a regular array.
-    if (!aObject || typeof aObject != "object" ||
-        (aObject.type != "object" && !Array.isArray(aObject))) {
-      return;
-    }
-    // Add a dummy property to force the twisty to show up.
-    aVar.addProperties({ " ": { value: " " }});
+    // Force the twisty to show up.
+    aVar.forceShowArrow();
     aVar.onexpand = this._addVarProperties.bind(this, aVar, aObject);
   },
 
-  _addVarProperties: function SF_addVarProperties(aVar, aObject) {
+  /**
+   * Adds properties to a variable in the view. Triggered when a variable is
+   * expanded.
+   */
+  _addVarProperties: function SF__addVarProperties(aVar, aObject) {
     // Retrieve the properties only once.
     if (aVar.fetched) {
       return;
     }
-    // Clear the placeholder property put in place to display the twisty.
-    aVar.empty();
 
-    // For arrays we have to construct a grip-like object to pass into
-    // addProperties.
+    // For arrays we have to construct a grip-like object.
     if (Array.isArray(aObject)) {
-      let properties = { length: { writable: true, value: aObject.length } };
-      for (let i = 0; i < aObject.length; i++) {
-        properties[i + ""] = { value: aObject[i] };
+      let properties = { length: { value: aObject.length } };
+      for (let i = 0, l = aObject.length; i < l; i++) {
+        properties[i] = { value: aObject[i] };
       }
       aVar.addProperties(properties);
+
       // Expansion handlers must be set after the properties are added.
-      for (let i = 0; i < aObject.length; i++) {
-        this._addExpander(aVar[i + ""], aObject[i]);
+      for (let i = 0, l = aObject.length; i < l; i++) {
+        this._addExpander(aVar[i], aObject[i]);
       }
+
       aVar.fetched = true;
       return;
     }
 
     let objClient = this.activeThread.pauseGrip(aObject);
     objClient.getPrototypeAndProperties(function SF_onProtoAndProps(aResponse) {
       // Add __proto__.
-      if (aResponse.prototype.type != "null") {
-        let properties = {};
-        properties["__proto__ "] = { value: aResponse.prototype };
+      if (aResponse.prototype.type !== "null") {
+        let properties = { "__proto__ ": { value: aResponse.prototype } };
         aVar.addProperties(properties);
+
         // Expansion handlers must be set after the properties are added.
         this._addExpander(aVar["__proto__ "], aResponse.prototype);
       }
 
-      // Sort the rest of the properties before adding them, for better UX.
+      // Sort all of the properties before adding them, for better UX.
       let properties = {};
       for each (let prop in Object.keys(aResponse.ownProperties).sort()) {
         properties[prop] = aResponse.ownProperties[prop];
       }
       aVar.addProperties(properties);
+
       // Expansion handlers must be set after the properties are added.
       for (let prop in aResponse.ownProperties) {
         this._addExpander(aVar[prop], aResponse.ownProperties[prop].value);
       }
 
       aVar.fetched = true;
     }.bind(this));
   },
 
   /**
    * Adds the specified stack frame to the list.
    *
    * @param Debugger.Frame aFrame
    *        The new frame to add.
    */
-  _addFramePanel: function SF_addFramePanel(aFrame) {
+  _addFrame: function SF__addFrame(aFrame) {
     let depth = aFrame.depth;
-    let label = SourceScripts._getScriptLabel(aFrame.where.url);
+    let label = DebuggerController.SourceScripts._getScriptLabel(aFrame.where.url);
 
-    let startText = this._frameTitle(aFrame);
+    let startText = this._getFrameTitle(aFrame);
     let endText = label + ":" + aFrame.where.line;
 
-    let panel = DebuggerView.Stackframes.addFrame(depth, startText, endText);
-
-    if (panel) {
-      panel.stackFrame = aFrame;
+    let frame = DebuggerView.StackFrames.addFrame(depth, startText, endText);
+    if (frame) {
+      frame.debuggerFrame = aFrame;
     }
   },
 
   /**
    * Loads more stack frames from the debugger server cache.
    */
-  _addMoreFrames: function SF_addMoreFrames() {
+  addMoreFrames: function SF_addMoreFrames() {
     this.activeThread.fillFrames(
       this.activeThread.cachedFrames.length + this.pageSize);
   },
 
   /**
    * Create a textual representation for the stack frame specified, for
    * displaying in the stack frame list.
    *
    * @param Debugger.Frame aFrame
    *        The stack frame to label.
    */
-  _frameTitle: function SF_frameTitle(aFrame) {
+  _getFrameTitle: function SF__getFrameTitle(aFrame) {
     if (aFrame.type == "call") {
       return aFrame["calleeName"] ? aFrame["calleeName"] : "(anonymous)";
     }
-
     return "(" + aFrame.type + ")";
   }
 };
 
-StackFrames.onPaused = StackFrames.onPaused.bind(StackFrames);
-StackFrames.onResume = StackFrames.onResume.bind(StackFrames);
-StackFrames.onFrames = StackFrames.onFrames.bind(StackFrames);
-StackFrames.onFramesCleared = StackFrames.onFramesCleared.bind(StackFrames);
-StackFrames.onClick = StackFrames.onClick.bind(StackFrames);
-
 /**
  * Keeps the source script list up-to-date, using the thread client's
  * source script cache.
  */
-var SourceScripts = {
-  pageSize: 25,
-  activeThread: null,
-  _labelsCache: null,
+function SourceScripts() {
+  this._onNewScript = this._onNewScript.bind(this);
+  this._onScriptsAdded = this._onScriptsAdded.bind(this);
+  this._onScriptsCleared = this._onScriptsCleared.bind(this);
+  this._onShowScript = this._onShowScript.bind(this);
+  this._onLoadSource = this._onLoadSource.bind(this);
+  this._onLoadSourceFinished = this._onLoadSourceFinished.bind(this);
+}
+
+SourceScripts.prototype = {
+
+  /**
+   * A cache containing simplified labels from script urls.
+   */
+  _labelsCache: {},
 
   /**
-   * Watch a given thread client.
-   * @param object aThreadClient
-   *        The thread client.
+   * Gets the current thread the client has connected to.
+   */
+  get activeThread() {
+    return DebuggerController.activeThread;
+  },
+
+  /**
+   * Gets the current debugger client.
+   */
+  get debuggerClient() {
+    return DebuggerController.client;
+  },
+
+  /**
+   * Watch the given thread client.
+   *
    * @param function aCallback
    *        The next function in the initialization sequence.
    */
-  connect: function SS_connect(aThreadClient, aCallback) {
-    DebuggerView.Scripts.addChangeListener(this.onChange);
+  connect: function SS_connect(aCallback) {
+    window.addEventListener("Debugger:LoadSource", this._onLoadSource, false);
 
-    this.activeThread = aThreadClient;
-    aThreadClient.addListener("scriptsadded", this.onScripts);
-    aThreadClient.addListener("scriptscleared", this.onScriptsCleared);
-    this.clearLabelsCache();
-    this.onScriptsCleared();
+    this.debuggerClient.addListener("newScript", this._onNewScript);
+    this.activeThread.addListener("scriptsadded", this._onScriptsAdded);
+    this.activeThread.addListener("scriptscleared", this._onScriptsCleared);
+
+    this._clearLabelsCache();
+    this._onScriptsCleared();
+
     // Retrieve the list of scripts known to the server from before the client
     // was ready to handle new script notifications.
     this.activeThread.fillScripts();
+
     aCallback && aCallback();
   },
 
   /**
    * Disconnect from the client.
    */
   disconnect: function TS_disconnect() {
-    this.activeThread.removeListener("scriptsadded", this.onScripts);
-    this.activeThread.removeListener("scriptscleared", this.onScriptsCleared);
+    window.removeEventListener("Debugger:LoadSource", this._onLoadSource, false);
+
+    if (!this.activeThread) {
+      return;
+    }
+    this.debuggerClient.removeListener("newScript", this._onNewScript);
+    this.activeThread.removeListener("scriptsadded", this._onScriptsAdded);
+    this.activeThread.removeListener("scriptscleared", this._onScriptsCleared);
   },
 
   /**
    * Handler for the debugger client's unsolicited newScript notification.
    */
-  onNewScript: function SS_onNewScript(aNotification, aPacket) {
+  _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() {
+  _onScriptsAdded: function SS__onScriptsAdded() {
     for each (let script in this.activeThread.cachedScripts) {
       this._addScript(script);
     }
   },
 
   /**
    * Handler for the thread client's scriptscleared notification.
    */
-  onScriptsCleared: function SS_onScriptsCleared() {
+  _onScriptsCleared: function SS__onScriptsCleared() {
     DebuggerView.Scripts.empty();
   },
 
   /**
-   * Handler for changes on the selected source script.
-   */
-  onChange: function SS_onChange(aEvent) {
-    let scripts = aEvent.target;
-    if (!scripts.selectedItem) {
-      return;
-    }
-    let script = scripts.selectedItem.getUserData("sourceScript");
-    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) {
+  _setEditorMode: function SS__setEditorMode(aUrl, aContentType) {
     if (aContentType) {
       if (/javascript/.test(aContentType)) {
-        window.editor.setMode(SourceEditor.MODES.JAVASCRIPT);
+        DebuggerView.editor.setMode(SourceEditor.MODES.JAVASCRIPT);
       } else {
-        window.editor.setMode(SourceEditor.MODES.HTML);
+        DebuggerView.editor.setMode(SourceEditor.MODES.HTML);
       }
       return;
     }
 
     if (this._trimUrlQuery(aUrl).slice(-3) == ".js") {
-      window.editor.setMode(SourceEditor.MODES.JAVASCRIPT);
+      DebuggerView.editor.setMode(SourceEditor.MODES.JAVASCRIPT);
     } else {
-      window.editor.setMode(SourceEditor.MODES.HTML);
+      DebuggerView.editor.setMode(SourceEditor.MODES.HTML);
     }
   },
 
   /**
    * Trims the query part of a url string, if necessary.
    *
    * @param string aUrl
    *        The script url.
    * @return string
    */
-  _trimUrlQuery: function SS_trimUrlQuery(aUrl) {
+  _trimUrlQuery: function SS__trimUrlQuery(aUrl) {
     let q = aUrl.indexOf('?');
     if (q > -1) {
       return aUrl.slice(0, q);
     }
     return aUrl;
   },
 
   /**
@@ -582,17 +761,17 @@ var SourceScripts = {
    * @param string aUrl
    *        The script url.
    * @param string aHref
    *        The content location href to be used. If unspecified, it will
    *        defalult to debugged panrent window location.
    * @return string
    *         The simplified label.
    */
-  _getScriptLabel: function SS_getScriptLabel(aUrl, aHref) {
+  _getScriptLabel: function SS__getScriptLabel(aUrl, aHref) {
     let url = this._trimUrlQuery(aUrl);
 
     if (this._labelsCache[url]) {
       return this._labelsCache[url];
     }
 
     let href = aHref || window.parent.content.location.href;
     let pathElements = url.split("/");
@@ -607,90 +786,497 @@ var SourceScripts = {
         label = url;
       }
     }
 
     return this._labelsCache[url] = label;
   },
 
   /**
-   * Clears the labels cache, populated by SS_getScriptLabel().
+   * Clears the labels cache, populated by SS_getScriptLabel.
    * This should be done every time the content location changes.
    */
-  clearLabelsCache: function SS_clearLabelsCache() {
+  _clearLabelsCache: function SS__clearLabelsCache() {
     this._labelsCache = {};
   },
 
   /**
    * Add the specified script to the list and display it in the editor if the
    * editor is empty.
    */
-  _addScript: function SS_addScript(aScript) {
+  _addScript: function SS__addScript(aScript) {
     DebuggerView.Scripts.addScript(this._getScriptLabel(aScript.url), aScript);
 
-    if (window.editor.getCharCount() == 0) {
+    if (DebuggerView.editor.getCharCount() == 0) {
       this.showScript(aScript);
     }
   },
 
   /**
    * Load the editor with the script text if available, otherwise fire an event
    * to load and display the script text.
    *
    * @param object aScript
    *        The script object coming from the active thread.
    * @param object [aOptions]
-   *        Additional options for showing the script (optional). Supported
-   *        options:
+   *        Additional options for showing the script. Supported options:
    *        - targetLine: place the editor at the given line number.
    */
   showScript: function SS_showScript(aScript, aOptions) {
-    if (!aScript.loaded) {
-      // Notify the chrome code that we need to load a script file.
-      var evt = document.createEvent("CustomEvent");
-      evt.initCustomEvent("Debugger:LoadSource", true, false,
-                          {url: aScript.url, options: aOptions});
-      document.documentElement.dispatchEvent(evt);
-      window.editor.setMode(SourceEditor.MODES.TEXT);
-      window.editor.setText(DebuggerView.getStr("loadingText"));
-      window.editor.resetUndo();
-    } else {
+    if (aScript.loaded) {
       this._onShowScript(aScript, aOptions);
+      return;
     }
+
+    let editor = DebuggerView.editor;
+    editor.setMode(SourceEditor.MODES.TEXT);
+    editor.setText(L10N.getStr("loadingText"));
+    editor.resetUndo();
+
+    // Notify that we need to load a script file.
+    DebuggerController.dispatchEvent("Debugger:LoadSource", {
+      url: aScript.url,
+      options: aOptions
+    });
   },
 
   /**
    * Display the script source once it loads.
    *
    * @private
    * @param object aScript
    *        The script object coming from the active thread.
-   * @param object [aOptions]
-   *        Additional options for showing the script (optional). Supported
-   *        options:
+   * @param object aOptions [optional]
+   *        Additional options for showing the script. Supported options:
    *        - targetLine: place the editor at the given line number.
    */
   _onShowScript: function SS__onShowScript(aScript, aOptions) {
     aOptions = aOptions || {};
-    this.setEditorMode(aScript.url, aScript.contentType);
-    window.editor.setText(aScript.text);
-    window.updateEditorBreakpoints();
-    StackFrames.updateEditor();
+
+    this._setEditorMode(aScript.url, aScript.contentType);
+
+    let editor = DebuggerView.editor;
+    editor.setText(aScript.text);
+    editor.resetUndo();
+
+    DebuggerController.Breakpoints.updateEditorBreakpoints();
+    DebuggerController.StackFrames.updateEditorLocation();
+
+    // Handle any additional options for showing the script.
     if (aOptions.targetLine) {
-      window.editor.setCaretPosition(aOptions.targetLine - 1);
+      editor.setCaretPosition(aOptions.targetLine - 1);
     }
-    window.editor.resetUndo();
+
+    // Notify that we shown script file.
+    DebuggerController.dispatchEvent("Debugger:ScriptShown", {
+      url: aScript.url
+    });
+  },
+
+  /**
+   * Handles notifications to load a source script from the cache or from a
+   * local file.
+   *
+   * XXX: Tt may be better to use nsITraceableChannel to get to the sources
+   * without relying on caching when we can (not for eval, etc.):
+   * http://www.softwareishard.com/blog/firebug/nsitraceablechannel-intercept-http-traffic/
+   */
+  _onLoadSource: function SS__onLoadSource(aEvent) {
+    let url = aEvent.detail.url;
+    let options = aEvent.detail.options;
+    let self = this;
+
+    switch (Services.io.extractScheme(url)) {
+      case "file":
+      case "chrome":
+      case "resource":
+        try {
+          NetUtil.asyncFetch(url, function onFetch(aStream, aStatus) {
+            if (!Components.isSuccessCode(aStatus)) {
+              return self._logError(url, aStatus);
+            }
+            let source = NetUtil.readInputStreamToString(aStream, aStream.available());
+            self._onLoadSourceFinished(url, source, null, options);
+            aStream.close();
+          });
+        } catch (ex) {
+          return self._logError(url, ex.name);
+        }
+        break;
 
-    // Notify the chrome code that we shown script file.
-    let evt = document.createEvent("CustomEvent");
-    evt.initCustomEvent("Debugger:ScriptShown", true, false,
-                        {url: aScript.url});
-    document.documentElement.dispatchEvent(evt);
+      default:
+        let channel = Services.io.newChannel(url, null, null);
+        let chunks = [];
+        let streamListener = {
+          onStartRequest: function(aRequest, aContext, aStatusCode) {
+            if (!Components.isSuccessCode(aStatusCode)) {
+              return self._logError(url, aStatusCode);
+            }
+          },
+          onDataAvailable: function(aRequest, aContext, aStream, aOffset, aCount) {
+            chunks.push(NetUtil.readInputStreamToString(aStream, aCount));
+          },
+          onStopRequest: function(aRequest, aContext, aStatusCode) {
+            if (!Components.isSuccessCode(aStatusCode)) {
+              return self._logError(url, aStatusCode);
+            }
+            self._onLoadSourceFinished(
+              url, chunks.join(""), channel.contentType, options);
+          }
+        };
+
+        channel.loadFlags = channel.LOAD_FROM_CACHE;
+        channel.asyncOpen(streamListener, null);
+        break;
+    }
+  },
+
+  /**
+   * Called when source has been loaded.
+   *
+   * @private
+   * @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.
+   * @param object aOptions [optional]
+   *        Additional options for showing the script. Supported options:
+   *        - targetLine: place the editor at the given line number.
+   */
+  _onLoadSourceFinished:
+  function SS__onLoadSourceFinished(aSourceUrl, aSourceText, aContentType, aOptions) {
+    let scripts = document.getElementById("scripts");
+    let element = scripts.getElementsByAttribute("value", aSourceUrl)[0];
+    let script = element.getUserData("sourceScript");
+
+    script.loaded = true;
+    script.text = aSourceText;
+    script.contentType = aContentType;
+    element.setUserData("sourceScript", script, null);
+
+    this.showScript(script, aOptions);
+  },
+
+  /**
+   * Log an error message in the error console when a script fails to load.
+   *
+   * @param string aUrl
+   *        The URL of the source script.
+   * @param string aStatus
+   *        The failure status code.
+   */
+  _logError: function SS__logError(aUrl, aStatus) {
+    Components.utils.reportError(L10N.getFormatStr("loadingError", [aUrl, aStatus]));
   },
 };
 
-SourceScripts.onScripts = SourceScripts.onScripts.bind(SourceScripts);
-SourceScripts.onNewScript = SourceScripts.onNewScript.bind(SourceScripts);
-SourceScripts.onScriptsCleared = SourceScripts.onScriptsCleared.bind(SourceScripts);
-SourceScripts.onChange = SourceScripts.onChange.bind(SourceScripts);
+/**
+ * Handles all the breakpoints in the current debugger.
+ */
+function Breakpoints() {
+  this._onEditorBreakpointChange = this._onEditorBreakpointChange.bind(this);
+  this._onEditorBreakpointAdd = this._onEditorBreakpointAdd.bind(this);
+  this._onEditorBreakpointRemove = this._onEditorBreakpointRemove.bind(this);
+  this.addBreakpoint = this.addBreakpoint.bind(this);
+  this.removeBreakpoint = this.removeBreakpoint.bind(this);
+  this.getBreakpoint = this.getBreakpoint.bind(this);
+}
+
+Breakpoints.prototype = {
+
+  /**
+   * Skip editor breakpoint change events.
+   *
+   * This property tells the source editor event handler to skip handling of
+   * the BREAKPOINT_CHANGE events. This is used when the debugger adds/removes
+   * breakpoints from the editor. Typically, the BREAKPOINT_CHANGE event handler
+   * adds/removes events from the debugger, but when breakpoints are added from
+   * the public debugger API, we need to do things in reverse.
+   *
+   * This implementation relies on the fact that the source editor fires the
+   * BREAKPOINT_CHANGE events synchronously.
+   *
+   * @private
+   * @type boolean
+   */
+  _skipEditorBreakpointChange: false,
+
+  /**
+   * The list of breakpoints in the debugger as tracked by the current
+   * debugger instance. This an object where the values are BreakpointActor
+   * objects received from the client, while the keys are actor names, for
+   * example "conn0.breakpoint3".
+   *
+   * @type object
+   */
+  store: {},
+
+  /**
+   * Gets the current thread the client has connected to.
+   */
+  get activeThread() {
+    return DebuggerController.ThreadState.activeThread;
+  },
+
+  /**
+   * Gets the source editor in the debugger view.
+   */
+  get editor() {
+    return DebuggerView.editor;
+  },
+
+  /**
+   * Sets up the source editor breakpoint handlers.
+   */
+  initialize: function BP_initialize() {
+    this.editor.addEventListener(
+      SourceEditor.EVENTS.BREAKPOINT_CHANGE, this._onEditorBreakpointChange);
+  },
+
+  /**
+   * Removes all currently added breakpoints.
+   */
+  destroy: function BP_destroy() {
+    for each (let breakpoint in this.store) {
+      this.removeBreakpoint(breakpoint);
+    }
+  },
+
+  /**
+   * Event handler for breakpoint changes that happen in the editor. This
+   * function syncs the breakpoint changes in the editor to those in the
+   * debugger.
+   *
+   * @private
+   * @param object aEvent
+   *        The SourceEditor.EVENTS.BREAKPOINT_CHANGE event object.
+   */
+  _onEditorBreakpointChange: function BP__onEditorBreakpointChange(aEvent) {
+    if (this._skipEditorBreakpointChange) {
+      return;
+    }
+
+    aEvent.added.forEach(this._onEditorBreakpointAdd, this);
+    aEvent.removed.forEach(this._onEditorBreakpointRemove, this);
+  },
+
+  /**
+   * Event handler for new breakpoints that come from the editor.
+   *
+   * @private
+   * @param object aBreakpoint
+   *        The breakpoint object coming from the editor.
+   */
+  _onEditorBreakpointAdd: function BP__onEditorBreakpointAdd(aBreakpoint) {
+    let url = DebuggerView.Scripts.selected;
+    if (!url) {
+      return;
+    }
+
+    let line = aBreakpoint.line + 1;
+
+    let callback = function(aClient, aError) {
+      if (aError) {
+        this._skipEditorBreakpointChange = true;
+        let result = this.editor.removeBreakpoint(aBreakpoint.line);
+        this._skipEditorBreakpointChange = false;
+      }
+    }.bind(this);
+    this.addBreakpoint({ url: url, line: line }, callback, true);
+  },
+
+  /**
+   * Event handler for breakpoints that are removed from the editor.
+   *
+   * @private
+   * @param object aBreakpoint
+   *        The breakpoint object that was removed from the editor.
+   */
+  _onEditorBreakpointRemove: function BP__onEditorBreakpointRemove(aBreakpoint) {
+    let url = DebuggerView.Scripts.selected;
+    if (!url) {
+      return;
+    }
+
+    let line = aBreakpoint.line + 1;
+
+    let breakpoint = this.getBreakpoint(url, line);
+    if (breakpoint) {
+      this.removeBreakpoint(breakpoint, null, true);
+    }
+  },
+
+  /**
+   * Update the breakpoints in the editor view. This function takes the list of
+   * breakpoints in the debugger and adds them back into the editor view. This
+   * is invoked when the selected script is changed.
+   */
+  updateEditorBreakpoints: function BP_updateEditorBreakpoints() {
+    let url = DebuggerView.Scripts.selected;
+    if (!url) {
+      return;
+    }
 
-window.addEventListener("DOMContentLoaded", initDebugger, false);
-window.addEventListener("unload", shutdownDebugger, false);
+    this._skipEditorBreakpointChange = true;
+    for each (let breakpoint in this.store) {
+      if (breakpoint.location.url == url) {
+        this.editor.addBreakpoint(breakpoint.location.line - 1);
+      }
+    }
+    this._skipEditorBreakpointChange = false;
+  },
+
+  /**
+   * Add a breakpoint.
+   *
+   * @param object aLocation
+   *        The location where you want the breakpoint. This object must have
+   *        two properties:
+   *          - url - the URL of the script.
+   *          - line - the line number (starting from 1).
+   * @param function [aCallback]
+   *        Optional function to invoke once the breakpoint is added. The
+   *        callback is invoked with two arguments:
+   *          - aBreakpointClient - the BreakpointActor client object, if the
+   *          breakpoint has been added successfully.
+   *          - aResponseError - if there was any error.
+   * @param boolean [aNoEditorUpdate=false]
+   *        Tells if you want to skip editor updates. Typically the editor is
+   *        updated to visually indicate that a breakpoint has been added.
+   */
+  addBreakpoint:
+  function BP_addBreakpoint(aLocation, aCallback, aNoEditorUpdate) {
+    let breakpoint = this.getBreakpoint(aLocation.url, aLocation.line);
+    if (breakpoint) {
+      aCallback && aCallback(breakpoint);
+      return;
+    }
+
+    this.activeThread.setBreakpoint(aLocation, function(aResponse, aBpClient) {
+      if (!aResponse.error) {
+        this.store[aBpClient.actor] = aBpClient;
+
+        if (!aNoEditorUpdate) {
+          let url = DebuggerView.Scripts.selected;
+          if (url == aLocation.url) {
+            this._skipEditorBreakpointChange = true;
+            this.editor.addBreakpoint(aLocation.line - 1);
+            this._skipEditorBreakpointChange = false;
+          }
+        }
+      }
+
+      aCallback && aCallback(aBpClient, aResponse.error);
+    }.bind(this));
+  },
+
+  /**
+   * Remove a breakpoint.
+   *
+   * @param object aBreakpoint
+   *        The breakpoint you want to remove.
+   * @param function [aCallback]
+   *        Optional function to invoke once the breakpoint is removed. The
+   *        callback is invoked with one argument: the breakpoint location
+   *        object which holds the url and line properties.
+   * @param boolean [aNoEditorUpdate=false]
+   *        Tells if you want to skip editor updates. Typically the editor is
+   *        updated to visually indicate that a breakpoint has been removed.
+   */
+  removeBreakpoint:
+  function BP_removeBreakpoint(aBreakpoint, aCallback, aNoEditorUpdate) {
+    if (!(aBreakpoint.actor in this.store)) {
+      aCallback && aCallback(aBreakpoint.location);
+      return;
+    }
+
+    aBreakpoint.remove(function() {
+      delete this.store[aBreakpoint.actor];
+
+      if (!aNoEditorUpdate) {
+        let url = DebuggerView.Scripts.selected;
+        if (url == aBreakpoint.location.url) {
+          this._skipEditorBreakpointChange = true;
+          this.editor.removeBreakpoint(aBreakpoint.location.line - 1);
+          this._skipEditorBreakpointChange = false;
+        }
+      }
+
+      aCallback && aCallback(aBreakpoint.location);
+    }.bind(this));
+  },
+
+  /**
+   * Get the breakpoint object at the given location.
+   *
+   * @param string aUrl
+   *        The URL of where the breakpoint is.
+   * @param number aLine
+   *        The line number where the breakpoint is.
+   * @return object
+   *         The BreakpointActor object.
+   */
+  getBreakpoint: function BP_getBreakpoint(aUrl, aLine) {
+    for each (let breakpoint in this.store) {
+      if (breakpoint.location.url == aUrl && breakpoint.location.line == aLine) {
+        return breakpoint;
+      }
+    }
+    return null;
+  }
+};
+
+/**
+ * Localization convenience methods.
+ */
+let L10N = {
+
+  /**
+   * L10N shortcut function.
+   *
+   * @param string aName
+   * @return string
+   */
+  getStr: function L10N_getStr(aName) {
+    return this.stringBundle.GetStringFromName(aName);
+  },
+
+  /**
+   * L10N shortcut function.
+   *
+   * @param string aName
+   * @param array aArray
+   * @return string
+   */
+  getFormatStr: function L10N_getFormatStr(aName, aArray) {
+    return this.stringBundle.formatStringFromName(aName, aArray, aArray.length);
+  }
+};
+
+XPCOMUtils.defineLazyGetter(L10N, "stringBundle", function() {
+  return Services.strings.createBundle(DBG_STRINGS_URI);
+});
+
+/**
+ * Preliminary setup for the DebuggerController object.
+ */
+DebuggerController.init();
+DebuggerController.ThreadState = new ThreadState();
+DebuggerController.StackFrames = new StackFrames();
+DebuggerController.SourceScripts = new SourceScripts();
+DebuggerController.Breakpoints = new Breakpoints();
+
+/**
+ * Export some properties to the global scope for easier access in tests.
+ */
+Object.defineProperty(window, "gClient", {
+  get: function() { return DebuggerController.client; }
+});
+
+Object.defineProperty(window, "gTabClient", {
+  get: function() { return DebuggerController.tabClient; }
+});
+
+Object.defineProperty(window, "gThreadClient", {
+  get: function() { return DebuggerController.activeThread; }
+});
--- a/browser/devtools/debugger/debugger-view.js
+++ b/browser/devtools/debugger/debugger-view.js
@@ -1,11 +1,11 @@
 /* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=2 et sw=2 tw=80: */
-/***** BEGIN LICENSE BLOCK *****
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* ***** BEGIN LICENSE BLOCK *****
  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  *
  * The contents of this file are subject to the Mozilla Public License Version
  * 1.1 (the "License"); you may not use this file except in compliance with
  * the License. You may obtain a copy of the License at
  * http://www.mozilla.org/MPL/
  *
  * Software distributed under the License is distributed on an "AS IS" basis,
@@ -27,124 +27,285 @@
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
  * 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
+ * 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 *****/
+ * ***** END LICENSE BLOCK ***** */
 "use strict";
 
-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.
  */
 let DebuggerView = {
 
   /**
-   * L10N shortcut function
+   * An instance of SourceEditor.
+   */
+  editor: null,
+
+  /**
+   * Initializes the SourceEditor instance.
+   */
+  initializeEditor: function DV_initializeEditor() {
+    let placeholder = document.getElementById("editor");
+
+    let config = {
+      mode: SourceEditor.MODES.JAVASCRIPT,
+      showLineNumbers: true,
+      readOnly: true,
+      showAnnotationRuler: true,
+      showOverviewRuler: true,
+    };
+
+    this.editor = new SourceEditor();
+    this.editor.init(placeholder, config, this._onEditorLoad.bind(this));
+  },
+
+  /**
+   * Removes the SourceEditor instance and added breakpoints.
+   */
+  destroyEditor: function DV_destroyEditor() {
+    DebuggerController.Breakpoints.destroy();
+    this.editor = null;
+  },
+
+  /**
+   * The load event handler for the source editor. This method does post-load
+   * editor initialization.
+   */
+  _onEditorLoad: function DV__onEditorLoad() {
+    DebuggerController.Breakpoints.initialize();
+  }
+};
+
+/**
+ * Functions handling the scripts UI.
+ */
+function ScriptsView() {
+  this._onScriptsChange = this._onScriptsChange.bind(this);
+}
+
+ScriptsView.prototype = {
+
+  /**
+   * Removes all elements from the scripts container, leaving it empty.
+   */
+  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 aName
-   * @return string
+   * @param string aUrl
+   *        The script URL.
+   * @return boolean
    */
-  getStr: function DV_getStr(aName) {
-    return this.stringBundle.GetStringFromName(aName);
+  contains: function DVS_contains(aUrl) {
+    if (this._scripts.getElementsByAttribute("value", aUrl).length > 0) {
+      return true;
+    }
+    return false;
+  },
+
+  /**
+   * Checks whether the script with the specified label is among the scripts
+   * known to the debugger and shown in the list.
+   *
+   * @param string aLabel
+   *        The script label.
+   * @return boolean
+   */
+  containsLabel: function DVS_containsLabel(aLabel) {
+    if (this._scripts.getElementsByAttribute("label", aLabel).length > 0) {
+      return true;
+    }
+    return false;
   },
 
   /**
-   * L10N shortcut function
+   * 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, l = this._scripts.itemCount; i < l; i++) {
+      if (this._scripts.getItemAtIndex(i).value == aUrl) {
+        this._scripts.selectedIndex = i;
+        return;
+      }
+    }
+  },
+
+  /**
+   * Checks whether the script with the specified URL is selected in the list.
    *
-   * @param string aName
-   * @param array aArray
-   * @return string
+   * @param string aUrl
+   *        The script URL.
+   */
+  isSelected: function DVS_isSelected(aUrl) {
+    if (this._scripts.selectedItem &&
+        this._scripts.selectedItem.value == aUrl) {
+      return true;
+    }
+    return false;
+  },
+
+  /**
+   * Retrieve the URL of the selected script.
+   * @return string | null
+   */
+  get selected() {
+    return this._scripts.selectedItem ?
+           this._scripts.selectedItem.value : null;
+  },
+
+  /**
+   * Returns the list of URIs for scripts in the page.
+   * @return array
    */
-  getFormatStr: function DV_getFormatStr(aName, aArray) {
-    return this.stringBundle.formatStringFromName(aName, aArray, aArray.length);
+  get scriptLocations() {
+    let locations = [];
+    for (let i = 0, l = this._scripts.itemCount; i < l; i++) {
+      locations.push(this._scripts.getItemAtIndex(i).value);
+    }
+    return locations;
+  },
+
+  /**
+   * 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 aLabel
+   *        The simplified script location to be shown.
+   * @param string aScript
+   *        The source script.
+   * @return object
+   *         The newly created html node representing the added script.
+   */
+  addScript: function DVS_addScript(aLabel, aScript) {
+    // Make sure we don't duplicate anything.
+    if (this.containsLabel(aLabel)) {
+      return null;
+    }
+
+    let script = this._scripts.appendItem(aLabel, aScript.url);
+    script.setAttribute("tooltiptext", aScript.url);
+    script.setUserData("sourceScript", aScript, null);
+
+    this._scripts.selectedItem = script;
+    return script;
+  },
+
+  /**
+   * The cached click listener for the scripts container.
+   */
+  _onScriptsChange: function DVS__onScriptsChange() {
+    let script = this._scripts.selectedItem.getUserData("sourceScript");
+    DebuggerController.SourceScripts.showScript(script);
+  },
+
+  /**
+   * The cached scripts container.
+   */
+  _scripts: null,
+
+  /**
+   * Initialization function, called when the debugger is initialized.
+   */
+  initialize: function DVS_initialize() {
+    this._scripts = document.getElementById("scripts");
+    this._scripts.addEventListener("select", this._onScriptsChange, false);
+  },
+
+  /**
+   * Destruction function, called when the debugger is shut down.
+   */
+  destroy: function DVS_destroy() {
+    this._scripts.removeEventListener("select", this._onScriptsChange, false);
+    this._scripts = null;
   }
 };
 
-XPCOMUtils.defineLazyGetter(DebuggerView, "stringBundle", function() {
-  return Services.strings.createBundle(DBG_STRINGS_URI);
-});
-
 /**
  * Functions handling the html stackframes UI.
  */
-DebuggerView.Stackframes = {
+function StackFramesView() {
+  this._onFramesScroll = this._onFramesScroll.bind(this);
+  this._onCloseButtonClick = this._onCloseButtonClick.bind(this);
+  this._onResumeButtonClick = this._onResumeButtonClick.bind(this);
+  this._onStepOverClick = this._onStepOverClick.bind(this);
+  this._onStepInClick = this._onStepInClick.bind(this);
+  this._onStepOutClick = this._onStepOutClick.bind(this);
+}
+
+StackFramesView.prototype = {
 
   /**
    * Sets the current frames state based on the debugger active thread state.
    *
    * @param string aState
    *        Either "paused" or "attached".
    */
   updateState: function DVF_updateState(aState) {
     let resume = document.getElementById("resume");
     let status = document.getElementById("status");
 
     // If we're paused, show a pause label and a resume label on the button.
-    if (aState === "paused") {
-      status.textContent = DebuggerView.getStr("pausedState");
-      resume.label = DebuggerView.getStr("resumeLabel");
-    } else if (aState === "attached") {
-      // If we're attached, do the opposite.
-      status.textContent = DebuggerView.getStr("runningState");
-      resume.label = DebuggerView.getStr("pauseLabel");
-    } else {
-      // No valid state parameter.
+    if (aState == "paused") {
+      status.textContent = L10N.getStr("pausedState");
+      resume.label = L10N.getStr("resumeLabel");
+    }
+    // If we're attached, do the opposite.
+    else if (aState == "attached") {
+      status.textContent = L10N.getStr("runningState");
+      resume.label = L10N.getStr("pauseLabel");
+    }
+    // No valid state parameter.
+    else {
       status.textContent = "";
     }
   },
 
   /**
-   * Sets the onClick listener for the stackframes container.
-   *
-   * @param function aHandler
-   *        The delegate used as the event listener.
-   */
-  addClickListener: function DVF_addClickListener(aHandler) {
-    // save the handler so it can be removed on shutdown
-    this._onFramesClick = aHandler;
-    this._frames.addEventListener("click", aHandler, false);
-  },
-
-  /**
    * Removes all elements from the stackframes container, leaving it empty.
    */
   empty: function DVF_empty() {
     while (this._frames.firstChild) {
       this._frames.removeChild(this._frames.firstChild);
     }
   },
 
   /**
    * Removes all elements from the stackframes container, and adds a child node
    * with an empty text note attached.
    */
   emptyText: function DVF_emptyText() {
-    // make sure the container is empty first
+    // Make sure the container is empty first.
     this.empty();
 
     let item = document.createElement("div");
 
-    // the empty node should look grayed out to avoid confusion
+    // The empty node should look grayed out to avoid confusion.
     item.className = "empty list-item";
-    item.appendChild(document.createTextNode(DebuggerView.getStr("emptyText")));
+    item.appendChild(document.createTextNode(L10N.getStr("emptyText")));
 
     this._frames.appendChild(item);
   },
 
   /**
    * Adds a frame to the stackframes container.
    * If the frame already exists (was previously added), null is returned.
    * Otherwise, the newly created element is returned.
@@ -154,71 +315,81 @@ DebuggerView.Stackframes = {
    * @param string aFrameNameText
    *        The name to be displayed in the list.
    * @param string aFrameDetailsText
    *        The details to be displayed in the list.
    * @return object
    *         The newly created html node representing the added frame.
    */
   addFrame: function DVF_addFrame(aDepth, aFrameNameText, aFrameDetailsText) {
-    // make sure we don't duplicate anything
+    // Make sure we don't duplicate anything.
     if (document.getElementById("stackframe-" + aDepth)) {
       return null;
     }
 
     let frame = document.createElement("div");
     let frameName = document.createElement("span");
     let frameDetails = document.createElement("span");
 
-    // create a list item to be added to the stackframes container
+    // Create a list item to be added to the stackframes container.
     frame.id = "stackframe-" + aDepth;
     frame.className = "dbg-stackframe list-item";
 
-    // this list should display the name and details for the frame
+    // This list should display the name and details for the frame.
     frameName.className = "dbg-stackframe-name";
     frameDetails.className = "dbg-stackframe-details";
     frameName.appendChild(document.createTextNode(aFrameNameText));
     frameDetails.appendChild(document.createTextNode(aFrameDetailsText));
 
     frame.appendChild(frameName);
     frame.appendChild(frameDetails);
 
     this._frames.appendChild(frame);
 
-    // return the element for later use if necessary
+    // Return the element for later use if necessary.
     return frame;
   },
 
   /**
    * Highlights a frame from the stackframe container as selected/deselected.
    *
    * @param number aDepth
    *        The frame depth specified by the debugger.
-   * @param boolean aSelect
-   *        True if the frame should be selected, false otherwise.
+   * @param boolean aFlag
+   *        True if the frame should be deselected, false otherwise.
    */
-  highlightFrame: function DVF_highlightFrame(aDepth, aSelect) {
+  highlightFrame: function DVF_highlightFrame(aDepth, aFlag) {
     let frame = document.getElementById("stackframe-" + aDepth);
 
-    // the list item wasn't found in the stackframe container
+    // The list item wasn't found in the stackframe container.
     if (!frame) {
       return;
     }
 
-    // add the 'selected' css class if the frame isn't already selected
-    if (aSelect && !frame.classList.contains("selected")) {
+    // Add the 'selected' css class if the frame isn't already selected.
+    if (!aFlag && !frame.classList.contains("selected")) {
       frame.classList.add("selected");
-
-    // remove the 'selected' css class if the frame is already selected
-    } else if (!aSelect && frame.classList.contains("selected")) {
+    }
+    // Remove the 'selected' css class if the frame is already selected.
+    else if (aFlag && frame.classList.contains("selected")) {
       frame.classList.remove("selected");
     }
   },
 
   /**
+   * Deselects a frame from the stackframe container.
+   *
+   * @param number aDepth
+   *        The frame depth specified by the debugger.
+   */
+  unhighlightFrame: function DVF_unhighlightFrame(aDepth) {
+    this.highlightFrame(aDepth, true)
+  },
+
+  /**
    * Gets the current dirty state.
    *
    * @return boolean value
    *         True if should load more frames.
    */
   get dirty() {
     return this._dirty;
   },
@@ -229,81 +400,87 @@ DebuggerView.Stackframes = {
    * @param boolean aValue
    *        True if should load more frames.
    */
   set dirty(aValue) {
     this._dirty = aValue;
   },
 
   /**
-   * The cached click listener for the stackframes container.
+   * Listener handling the stackframes container click event.
    */
-  _onFramesClick: null,
+  _onFramesClick: function DVF__onFramesClick(aEvent) {
+    let target = aEvent.target;
+
+    while (target) {
+      if (target.debuggerFrame) {
+        DebuggerController.StackFrames.selectFrame(target.debuggerFrame.depth);
+        return;
+      }
+      target = target.parentNode;
+    }
+  },
 
   /**
    * Listener handling the stackframes container scroll event.
    */
   _onFramesScroll: function DVF__onFramesScroll(aEvent) {
-    // update the stackframes container only if we have to
+    // Update the stackframes container only if we have to.
     if (this._dirty) {
       let clientHeight = this._frames.clientHeight;
       let scrollTop = this._frames.scrollTop;
       let scrollHeight = this._frames.scrollHeight;
 
-      // if the stackframes container was scrolled past 95% of the height,
-      // load more content
+      // If the stackframes container was scrolled past 95% of the height,
+      // load more content.
       if (scrollTop >= (scrollHeight - clientHeight) * 0.95) {
         this._dirty = false;
 
-        StackFrames._addMoreFrames();
+        DebuggerController.StackFrames.addMoreFrames();
       }
     }
   },
 
   /**
    * Listener handling the close button click event.
    */
   _onCloseButtonClick: function DVF__onCloseButtonClick() {
-    let root = document.documentElement;
-    let debuggerClose = document.createEvent("Events");
-
-    debuggerClose.initEvent("DebuggerClose", true, false);
-    root.dispatchEvent(debuggerClose);
+    DebuggerController.dispatchEvent("Debugger:Close");
   },
 
   /**
    * Listener handling the pause/resume button click event.
    */
   _onResumeButtonClick: function DVF__onResumeButtonClick() {
-    if (ThreadState.activeThread.paused) {
-      ThreadState.activeThread.resume();
+    if (DebuggerController.activeThread.paused) {
+      DebuggerController.activeThread.resume();
     } else {
-      ThreadState.activeThread.interrupt();
+      DebuggerController.activeThread.interrupt();
     }
   },
 
   /**
    * Listener handling the step over button click event.
    */
   _onStepOverClick: function DVF__onStepOverClick() {
-    ThreadState.activeThread.stepOver();
+    DebuggerController.activeThread.stepOver();
   },
 
   /**
    * Listener handling the step in button click event.
    */
   _onStepInClick: function DVF__onStepInClick() {
-    ThreadState.activeThread.stepIn();
+    DebuggerController.activeThread.stepIn();
   },
 
   /**
    * Listener handling the step out button click event.
    */
   _onStepOutClick: function DVF__onStepOutClick() {
-    ThreadState.activeThread.stepOut();
+    DebuggerController.activeThread.stepOut();
   },
 
   /**
    * Specifies if the active thread has more frames which need to be loaded.
    */
   _dirty: false,
 
   /**
@@ -322,16 +499,17 @@ DebuggerView.Stackframes = {
     let stepOut = document.getElementById("step-out");
     let frames = document.getElementById("stackframes");
 
     close.addEventListener("click", this._onCloseButtonClick, false);
     resume.addEventListener("click", this._onResumeButtonClick, false);
     stepOver.addEventListener("click", this._onStepOverClick, false);
     stepIn.addEventListener("click", this._onStepInClick, false);
     stepOut.addEventListener("click", this._onStepOutClick, false);
+    frames.addEventListener("click", this._onFramesClick, false);
     frames.addEventListener("scroll", this._onFramesScroll, false);
     window.addEventListener("resize", this._onFramesScroll, false);
 
     this._frames = frames;
   },
 
   /**
    * Destruction function, called when the debugger is shut down.
@@ -355,54 +533,60 @@ DebuggerView.Stackframes = {
 
     this._frames = null;
   }
 };
 
 /**
  * Functions handling the properties view.
  */
-DebuggerView.Properties = {
+function PropertiesView() {
+  this._addScope = this._addScope.bind(this);
+  this._addVar = this._addVar.bind(this);
+  this._addProperties = this._addProperties.bind(this);
+}
+
+PropertiesView.prototype = {
 
   /**
    * Adds a scope to contain any inspected variables.
    * If the optional id is not specified, the scope html node will have a
    * default id set as aName-scope.
    *
    * @param string aName
    *        The scope name (e.g. "Local", "Global" or "With block").
    * @param string aId
    *        Optional, an id for the scope html node.
    * @return object
    *         The newly created html node representing the added scope or null
    *         if a node was not created.
    */
   _addScope: function DVP__addScope(aName, aId) {
-    // make sure the parent container exists
+    // Make sure the parent container exists.
     if (!this._vars) {
       return null;
     }
 
-    // compute the id of the element if not specified
+    // Compute the id of the element if not specified.
     aId = aId || (aName.toLowerCase().trim().replace(" ", "-") + "-scope");
 
-    // contains generic nodes and functionality
+    // Contains generic nodes and functionality.
     let element = this._createPropertyElement(aName, aId, "scope", this._vars);
 
-    // make sure the element was created successfully
+    // Make sure the element was created successfully.
     if (!element) {
       return null;
     }
 
     /**
      * @see DebuggerView.Properties._addVar
      */
     element.addVar = this._addVar.bind(this, element);
 
-    // return the element for later use if necessary
+    // Return the element for later use if necessary.
     return element;
   },
 
   /**
    * Adds a variable to a specified scope.
    * If the optional id is not specified, the variable html node will have a
    * default id set as aScope.id->aName-variable.
    *
@@ -411,63 +595,63 @@ DebuggerView.Properties = {
    * @param string aName
    *        The variable name.
    * @param string aId
    *        Optional, an id for the variable html node.
    * @return object
    *         The newly created html node representing the added var.
    */
   _addVar: function DVP__addVar(aScope, aName, aId) {
-    // make sure the scope container exists
+    // Make sure the scope container exists.
     if (!aScope) {
       return null;
     }
 
-    // compute the id of the element if not specified
+    // Compute the id of the element if not specified.
     aId = aId || (aScope.id + "->" + aName + "-variable");
 
-    // contains generic nodes and functionality
+    // Contains generic nodes and functionality.
     let element = this._createPropertyElement(aName, aId, "variable",
                                               aScope.querySelector(".details"));
 
-    // make sure the element was created successfully
+    // Make sure the element was created successfully.
     if (!element) {
       return null;
     }
 
     /**
      * @see DebuggerView.Properties._setGrip
      */
     element.setGrip = this._setGrip.bind(this, element);
 
     /**
      * @see DebuggerView.Properties._addProperties
      */
     element.addProperties = this._addProperties.bind(this, element);
 
-    // setup the additional elements specific for a variable node
+    // Setup the additional elements specific for a variable node.
     element.refresh(function() {
       let separator = document.createElement("span");
       let info = document.createElement("span");
       let title = element.querySelector(".title");
       let arrow = element.querySelector(".arrow");
 
-      // separator shouldn't be selectable
+      // Separator shouldn't be selectable.
       separator.className = "unselectable";
       separator.appendChild(document.createTextNode(": "));
 
-      // the variable information (type, class and/or value)
+      // The variable information (type, class and/or value).
       info.className = "info";
 
       title.appendChild(separator);
       title.appendChild(info);
 
     }.bind(this));
 
-    // return the element for later use if necessary
+    // Return the element for later use if necessary.
     return element;
   },
 
   /**
    * Sets the specific grip for a variable.
    * The grip should contain the value or the type & class, as defined in the
    * remote debugger protocol. For convenience, undefined and null are
    * both considered types.
@@ -476,31 +660,37 @@ DebuggerView.Properties = {
    *        The parent variable element.
    * @param object aGrip
    *        The primitive or object defining the grip, specifying
    *        the value and/or type & class of the variable (if the type
    *        is not specified, it will be inferred from the value).
    *        e.g. 42
    *             true
    *             "nasu"
-   *             { type: "undefined" } }
-   *             { type: "null" } }
-   *             { type: "object", class: "Object" } }
+   *             { type: "undefined" }
+   *             { type: "null" }
+   *             { type: "object", class: "Object" }
    * @return object
    *         The same variable.
    */
   _setGrip: function DVP__setGrip(aVar, aGrip) {
-    // make sure the variable container exists
+    // Make sure the variable container exists.
     if (!aVar) {
       return null;
     }
+    if (aGrip === undefined) {
+      aGrip = { type: "undefined" };
+    }
+    if (aGrip === null) {
+      aGrip = { type: "null" };
+    }
 
     let info = aVar.querySelector(".info") || aVar.target.info;
 
-    // make sure the info node exists
+    // Make sure the info node exists.
     if (!info) {
       return null;
     }
 
     info.textContent = this._propertyString(aGrip);
     info.classList.add(this._propertyColor(aGrip));
 
     return aVar;
@@ -518,40 +708,40 @@ DebuggerView.Properties = {
    *        specifying the value and/or type & class of the variable,
    *        or 'get' & 'set' accessor properties.
    *        e.g. { "someProp0": { value: 42 },
    *               "someProp1": { value: true },
    *               "someProp2": { value: "nasu" },
    *               "someProp3": { value: { type: "undefined" } },
    *               "someProp4": { value: { type: "null" } },
    *               "someProp5": { value: { type: "object", class: "Object" } },
-   *               "someProp6": { get: { "type": "object", "class": "Function" },
-   *                              set: { "type": "undefined" } } }
+   *               "someProp6": { get: { type: "object", class: "Function" },
+   *                              set: { type: "undefined" } }
    * @return object
    *         The same variable.
    */
   _addProperties: function DVP__addProperties(aVar, aProperties) {
-    // for each property, add it using the passed object key/grip
+    // For each property, add it using the passed object key/grip.
     for (let i in aProperties) {
       // Can't use aProperties.hasOwnProperty(i), because it may be overridden.
       if (Object.getOwnPropertyDescriptor(aProperties, i)) {
 
-        // get the specified descriptor for current property
+        // Get the specified descriptor for current property.
         let desc = aProperties[i];
 
-        // as described in the remote debugger protocol, the value grip must be
-        // contained in a 'value' property
+        // As described in the remote debugger protocol, the value grip must be
+        // contained in a 'value' property.
         let value = desc["value"];
 
-        // for accessor property descriptors, the two grips need to be
-        // contained in 'get' and 'set' properties
+        // For accessor property descriptors, the two grips need to be
+        // contained in 'get' and 'set' properties.
         let getter = desc["get"];
         let setter = desc["set"];
 
-        // handle data property and accessor property descriptors
+        // Handle data property and accessor property descriptors.
         if (value !== undefined) {
           this._addProperty(aVar, [i, value]);
         }
         if (getter !== undefined || setter !== undefined) {
           let prop = this._addProperty(aVar, [i]).expand();
           prop.getter = this._addProperty(prop, ["get", getter]);
           prop.setter = this._addProperty(prop, ["set", setter]);
         }
@@ -580,108 +770,108 @@ DebuggerView.Properties = {
    * @param string aName
    *        Optional, the property name.
    * @paarm string aId
    *        Optional, an id for the property html node.
    * @return object
    *         The newly created html node representing the added prop.
    */
   _addProperty: function DVP__addProperty(aVar, aProperty, aName, aId) {
-    // make sure the variable container exists
+    // Make sure the variable container exists.
     if (!aVar) {
       return null;
     }
 
-    // compute the id of the element if not specified
+    // Compute the id of the element if not specified.
     aId = aId || (aVar.id + "->" + aProperty[0] + "-property");
 
-    // contains generic nodes and functionality
+    // Contains generic nodes and functionality.
     let element = this._createPropertyElement(aName, aId, "property",
                                               aVar.querySelector(".details"));
 
-    // make sure the element was created successfully
+    // Make sure the element was created successfully.
     if (!element) {
       return null;
     }
 
     /**
      * @see DebuggerView.Properties._setGrip
      */
     element.setGrip = this._setGrip.bind(this, element);
 
     /**
      * @see DebuggerView.Properties._addProperties
      */
     element.addProperties = this._addProperties.bind(this, element);
 
-    // setup the additional elements specific for a variable node
+    // Setup the additional elements specific for a variable node.
     element.refresh(function(pKey, pGrip) {
       let propertyString = this._propertyString(pGrip);
       let propertyColor = this._propertyColor(pGrip);
       let key = document.createElement("div");
       let value = document.createElement("div");
       let separator = document.createElement("span");
       let title = element.querySelector(".title");
       let arrow = element.querySelector(".arrow");
 
-      // use a key element to specify the property name
+      // Use a key element to specify the property name.
       key.className = "key";
       key.appendChild(document.createTextNode(pKey));
 
-      // use a value element to specify the property value
+      // Use a value element to specify the property value.
       value.className = "value";
       value.appendChild(document.createTextNode(propertyString));
       value.classList.add(propertyColor);
 
-      // separator shouldn't be selected
+      // Separator shouldn't be selected.
       separator.className = "unselectable";
       separator.appendChild(document.createTextNode(": "));
 
-      if (pKey) {
+      if ("undefined" !== typeof pKey) {
         title.appendChild(key);
       }
-      if (pGrip) {
+      if ("undefined" !== typeof pGrip) {
         title.appendChild(separator);
         title.appendChild(value);
       }
 
-      // make the property also behave as a variable, to allow
-      // recursively adding properties to properties
+      // Make the property also behave as a variable, to allow
+      // recursively adding properties to properties.
       element.target = {
         info: value
       };
 
-      // save the property to the variable for easier access
+      // Save the property to the variable for easier access.
       Object.defineProperty(aVar, pKey, { value: element,
                                           writable: false,
                                           enumerable: true,
                                           configurable: true });
     }.bind(this), aProperty);
 
-    // return the element for later use if necessary
+    // Return the element for later use if necessary.
     return element;
   },
 
   /**
    * Returns a custom formatted property string for a type and a value.
    *
    * @param string | object aGrip
    *        The variable grip.
    * @return string
    *         The formatted property string.
    */
   _propertyString: function DVP__propertyString(aGrip) {
     if (aGrip && "object" === typeof aGrip) {
-      switch (aGrip["type"]) {
+      switch (aGrip.type) {
         case "undefined":
           return "undefined";
         case "null":
           return "null";
         default:
-          return "[" + aGrip["type"] + " " + aGrip["class"] + "]";
+          return "[" + aGrip.type + " " + aGrip.class + "]";
       }
     } else {
       switch (typeof aGrip) {
         case "string":
           return "\"" + aGrip + "\"";
         case "boolean":
           return aGrip ? "true" : "false";
         default:
@@ -697,17 +887,17 @@ DebuggerView.Properties = {
    * @param string | object aGrip
    *        The variable grip.
    *
    * @return string
    *         The css class style.
    */
   _propertyColor: function DVP__propertyColor(aGrip) {
     if (aGrip && "object" === typeof aGrip) {
-      switch (aGrip["type"]) {
+      switch (aGrip.type) {
         case "undefined":
           return "token-undefined";
         case "null":
           return "token-null";
       }
     } else {
       switch (typeof aGrip) {
         case "string":
@@ -732,47 +922,47 @@ DebuggerView.Properties = {
    * @param string aClass
    *        Recommended style class used by the created element node.
    * @param object aParent
    *        The parent node which will contain the element.
    * @return object
    *         The newly created html node representing the generic elem.
    */
   _createPropertyElement: function DVP__createPropertyElement(aName, aId, aClass, aParent) {
-    // make sure we don't duplicate anything and the parent exists
+    // Make sure we don't duplicate anything and the parent exists.
     if (document.getElementById(aId)) {
       return null;
     }
     if (!aParent) {
       return null;
     }
 
     let element = document.createElement("div");
     let arrow = document.createElement("span");
     let name = document.createElement("span");
     let title = document.createElement("div");
     let details = document.createElement("div");
 
-    // create a scope node to contain all the elements
+    // Create a scope node to contain all the elements.
     element.id = aId;
     element.className = aClass;
 
-    // the expand/collapse arrow
+    // The expand/collapse arrow.
     arrow.className = "arrow";
     arrow.style.visibility = "hidden";
 
-    // the name element
+    // The name element.
     name.className = "name unselectable";
     name.appendChild(document.createTextNode(aName || ""));
 
-    // the title element, containing the arrow and the name
+    // The title element, containing the arrow and the name.
     title.className = "title";
     title.addEventListener("click", function() { element.toggle(); }, true);
 
-    // the node element which will contain any added scope variables
+    // The node element which will contain any added scope variables.
     details.className = "details";
 
     title.appendChild(arrow);
     title.appendChild(name);
 
     element.appendChild(title);
     element.appendChild(details);
 
@@ -846,16 +1036,55 @@ DebuggerView.Properties = {
 
       if ("function" === typeof element.ontoggle) {
         element.ontoggle(element);
       }
       return element;
     };
 
     /**
+     * Shows the element expand/collapse arrow (only if necessary!).
+     * @return object
+     *         The same element.
+     */
+    element.showArrow = function DVP_element_showArrow() {
+      if (details.childNodes.length) {
+        arrow.style.visibility = "visible";
+      }
+      return element;
+    };
+
+    /**
+     * Forces the element expand/collapse arrow to be visible, even if there
+     * are no child elements.
+     *
+     * @param boolean aPreventHideFlag
+     *        Prevents the arrow to be hidden when requested.
+     * @return object
+     *         The same element.
+     */
+    element.forceShowArrow = function DVP_element_forceShowArrow(aPreventHideFlag) {
+      element._preventHide = aPreventHideFlag;
+      arrow.style.visibility = "visible";
+      return element;
+    };
+
+    /**
+     * Hides the element expand/collapse arrow.
+     * @return object
+     *         The same element.
+     */
+    element.hideArrow = function DVP_element_hideArrow() {
+      if (!element._preventHide) {
+        arrow.style.visibility = "hidden";
+      }
+      return element;
+    };
+
+    /**
      * Returns if the element is visible.
      * @return boolean
      *         True if the element is visible.
      */
     Object.defineProperty(element, "visible", {
       get: function DVP_element_getVisible() {
         return element.style.display !== "none";
       },
@@ -931,107 +1160,107 @@ DebuggerView.Properties = {
       if ("function" === typeof aFunction) {
         aFunction.apply(this, aArguments);
       }
 
       let node = aParent.parentNode;
       let arrow = node.querySelector(".arrow");
       let children = node.querySelector(".details").childNodes.length;
 
-      // if the parent details node has at least one element, set the
-      // expand/collapse arrow visible
+      // If the parent details node has at least one element, set the
+      // expand/collapse arrow visible.
       if (children) {
         arrow.style.visibility = "visible";
       } else {
         arrow.style.visibility = "hidden";
       }
     }.bind(this);
 
-    // return the element for later use and customization
+    // Return the element for later use and customization.
     return element;
   },
 
   /**
    * Returns the global scope container.
    */
   get globalScope() {
     return this._globalScope;
   },
 
   /**
    * Sets the display mode for the global scope container.
    *
-   * @param boolean value
+   * @param boolean aFlag
    *        False to hide the container, true to show.
    */
-  set globalScope(value) {
-    if (value) {
+  set globalScope(aFlag) {
+    if (aFlag) {
       this._globalScope.show();
     } else {
       this._globalScope.hide();
     }
   },
 
   /**
    * Returns the local scope container.
    */
   get localScope() {
     return this._localScope;
   },
 
   /**
    * Sets the display mode for the local scope container.
    *
-   * @param boolean value
+   * @param boolean aFlag
    *        False to hide the container, true to show.
    */
-  set localScope(value) {
-    if (value) {
+  set localScope(aFlag) {
+    if (aFlag) {
       this._localScope.show();
     } else {
       this._localScope.hide();
     }
   },
 
   /**
    * Returns the with block scope container.
    */
   get withScope() {
     return this._withScope;
   },
 
   /**
    * Sets the display mode for the with block scope container.
    *
-   * @param boolean value
+   * @param boolean aFlag
    *        False to hide the container, true to show.
    */
-  set withScope(value) {
-    if (value) {
+  set withScope(aFlag) {
+    if (aFlag) {
       this._withScope.show();
     } else {
       this._withScope.hide();
     }
   },
 
   /**
    * Returns the closure scope container.
    */
   get closureScope() {
     return this._closureScope;
   },
 
   /**
    * Sets the display mode for the with block scope container.
    *
-   * @param boolean value
+   * @param boolean aFlag
    *        False to hide the container, true to show.
    */
-  set closureScope(value) {
-    if (value) {
+  set closureScope(aFlag) {
+    if (aFlag) {
       this._closureScope.show();
     } else {
       this._closureScope.hide();
     }
   },
 
   /**
    * The cached variable properties container.
@@ -1046,191 +1275,39 @@ DebuggerView.Properties = {
   _withScope: null,
   _closureScope: null,
 
   /**
    * Initialization function, called when the debugger is initialized.
    */
   initialize: function DVP_initialize() {
     this._vars = document.getElementById("variables");
-    this._localScope = this._addScope(DebuggerView.getStr("localScope")).expand();
-    this._withScope = this._addScope(DebuggerView.getStr("withScope")).hide();
-    this._closureScope = this._addScope(DebuggerView.getStr("closureScope")).hide();
-    this._globalScope = this._addScope(DebuggerView.getStr("globalScope"));
+    this._localScope = this._addScope(L10N.getStr("localScope")).expand();
+    this._withScope = this._addScope(L10N.getStr("withScope")).hide();
+    this._closureScope = this._addScope(L10N.getStr("closureScope")).hide();
+    this._globalScope = this._addScope(L10N.getStr("globalScope"));
   },
 
   /**
    * Destruction function, called when the debugger is shut down.
    */
   destroy: function DVP_destroy() {
     this._vars = null;
     this._globalScope = null;
     this._localScope = null;
     this._withScope = null;
     this._closureScope = null;
   }
 };
 
 /**
- * Functions handling the html scripts UI.
+ * Preliminary setup for the DebuggerView object.
  */
-DebuggerView.Scripts = {
-
-  /**
-   * Sets the change event listener for the source scripts container.
-   *
-   * @param function aHandler
-   *        The delegate used as the event listener.
-   */
-  addChangeListener: function DVS_addChangeListener(aHandler) {
-    // Save the handler so it can be removed on shutdown.
-    this._onScriptsChange = aHandler;
-    this._scripts.addEventListener("select", aHandler, false);
-  },
-
-  /**
-   * Removes all elements from the scripts container, leaving it empty.
-   */
-  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.
-   * @return boolean
-   */
-  contains: function DVS_contains(aUrl) {
-    if (this._scripts.getElementsByAttribute("value", aUrl).length > 0) {
-      return true;
-    }
-    return false;
-  },
-
-  /**
-   * Checks whether the script with the specified label is among the scripts
-   * known to the debugger and shown in the list.
-   *
-   * @param string aLabel
-   *        The script label.
-   * @return boolean
-   */
-  containsLabel: function DVS_containsLabel(aLabel) {
-    if (this._scripts.getElementsByAttribute("label", aLabel).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;
-  },
+DebuggerView.Scripts = new ScriptsView();
+DebuggerView.StackFrames = new StackFramesView();
+DebuggerView.Properties = new PropertiesView();
 
-  /**
-   * 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;
-      }
-    }
-  },
-
-   /**
-   	* Retrieve the URL of the selected script.
-   	* @return string|null
-   	*/
-   get selected() {
-    return this._scripts.selectedItem ?
-           this._scripts.selectedItem.value : null;
-   },
-
-  /**
-   * 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 aLabel
-   *        The simplified script location to be shown.
-   * @param string aScript
-   *        The source script.
-   * @return object
-   *         The newly created html node representing the added script.
-   */
-  addScript: function DVS_addScript(aLabel, aScript) {
-    // make sure we don't duplicate anything
-    if (this.containsLabel(aLabel)) {
-      return null;
-    }
-
-    let script = this._scripts.appendItem(aLabel, aScript.url);
-    script.setAttribute("tooltiptext", aScript.url);
-    script.setUserData("sourceScript", aScript, null);
-
-    this._scripts.selectedItem = script;
-    return script;
-  },
-
-  /**
-   * Returns the list of URIs for scripts in the page.
-   */
-  scriptLocations: function DVS_scriptLocations() {
-    let locations = [];
-    for (let i = 0; i < this._scripts.itemCount; i++) {
-      locations.push(this._scripts.getItemAtIndex(i).value);
-    }
-    return locations;
-  },
-
-  /**
-   * The cached click listener for the scripts container.
-   */
-  _onScriptsChange: null,
-
-  /**
-   * The cached scripts container.
-   */
-  _scripts: null,
-
-  /**
-   * Initialization function, called when the debugger is initialized.
-   */
-  initialize: function DVS_initialize() {
-    this._scripts = document.getElementById("scripts");
-  },
-
-  /**
-   * Destruction function, called when the debugger is shut down.
-   */
-  destroy: function DVS_destroy() {
-    this._scripts.removeEventListener("select", this._onScriptsChange, false);
-    this._scripts = null;
-  }
-};
-
-
-let DVF = DebuggerView.Stackframes;
-DVF._onFramesScroll = DVF._onFramesScroll.bind(DVF);
-DVF._onCloseButtonClick = DVF._onCloseButtonClick.bind(DVF);
-DVF._onResumeButtonClick = DVF._onResumeButtonClick.bind(DVF);
-DVF._onStepOverClick = DVF._onStepOverClick.bind(DVF);
-DVF._onStepInClick = DVF._onStepInClick.bind(DVF);
-DVF._onStepOutClick = DVF._onStepOutClick.bind(DVF);
+/**
+ * Export the source editor to the global scope for easier access in tests.
+ */
+Object.defineProperty(window, "editor", {
+  get: function() { return DebuggerView.editor; }
+});
--- a/browser/devtools/debugger/debugger.xul
+++ b/browser/devtools/debugger/debugger.xul
@@ -42,59 +42,63 @@
 <?xml-stylesheet href="chrome://browser/content/debugger.css" type="text/css"?>
 <?xml-stylesheet href="chrome://browser/skin/devtools/debugger.css" type="text/css"?>
 <!DOCTYPE window [
 <!ENTITY % debuggerDTD SYSTEM "chrome://browser/locale/devtools/debugger.dtd" >
  %debuggerDTD;
 ]>
 <?xul-overlay href="chrome://global/content/editMenuOverlay.xul"?>
 <?xul-overlay href="chrome://browser/content/source-editor-overlay.xul"?>
+
 <xul:window xmlns="http://www.w3.org/1999/xhtml"
             xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
-    <xul:script type="text/javascript" src="chrome://global/content/globalOverlay.js"/>
-    <xul:script type="text/javascript" src="debugger.js"/>
-    <xul:script type="text/javascript" src="debugger-view.js"/>
-    <xul:popupset id="debugger-popups">
-      <xul:menupopup id="sourceEditorContextMenu"
-                     onpopupshowing="goUpdateSourceEditorMenuItems()">
-        <xul:menuitem id="se-cMenu-copy"/>
-        <xul:menuseparator/>
-        <xul:menuitem id="se-cMenu-selectAll"/>
-        <xul:menuseparator/>
-        <xul:menuitem id="se-cMenu-find"/>
-        <xul:menuitem id="se-cMenu-findAgain"/>
-        <xul:menuseparator/>
-        <xul:menuitem id="se-cMenu-gotoLine"/>
-      </xul:menupopup>
-    </xul:popupset>
-    <xul:commandset id="editMenuCommands"/>
-    <xul:commandset id="sourceEditorCommands"/>
-    <xul:keyset id="sourceEditorKeys"/>
+
+  <xul:script type="text/javascript" src="chrome://global/content/globalOverlay.js"/>
+  <xul:script type="text/javascript" src="debugger-controller.js"/>
+  <xul:script type="text/javascript" src="debugger-view.js"/>
+
+  <xul:popupset id="debugger-popups">
+    <xul:menupopup id="sourceEditorContextMenu"
+                   onpopupshowing="goUpdateSourceEditorMenuItems()">
+      <xul:menuitem id="se-cMenu-copy"/>
+      <xul:menuseparator/>
+      <xul:menuitem id="se-cMenu-selectAll"/>
+      <xul:menuseparator/>
+      <xul:menuitem id="se-cMenu-find"/>
+      <xul:menuitem id="se-cMenu-findAgain"/>
+      <xul:menuseparator/>
+      <xul:menuitem id="se-cMenu-gotoLine"/>
+    </xul:menupopup>
+  </xul:popupset>
+
+  <xul:commandset id="editMenuCommands"/>
+  <xul:commandset id="sourceEditorCommands"/>
+  <xul:keyset id="sourceEditorKeys"/>
 
-    <div id="body" class="vbox flex">
-        <xul:toolbar id="dbg-toolbar">
-            <xul:button id="close">&debuggerUI.closeButton;</xul:button>
-            <xul:button id="resume"/>
-            <xul:button id="step-over">&debuggerUI.stepOverButton;</xul:button>
-            <xul:button id="step-in">&debuggerUI.stepInButton;</xul:button>
-            <xul:button id="step-out">&debuggerUI.stepOutButton;</xul:button>
-            <xul:menulist id="scripts"/>
-        </xul:toolbar>
-        <div id="dbg-content" class="hbox flex">
-            <div id="stack" class="vbox">
-                <div class="title unselectable">&debuggerUI.stackTitle;</div>
-                <div id="stackframes" class="vbox flex"></div>
-            </div>
-            <div id="script" class="vbox flex">
-                <div class="title unselectable">&debuggerUI.scriptTitle;</div>
-                <div id="editor" class="vbox flex"></div>
-            </div>
-            <div id="properties" class="vbox">
-                <div class="title unselectable">&debuggerUI.propertiesTitle;</div>
-                <div id="variables" class="vbox flex"></div>
-            </div>
-        </div>
-        <div id="dbg-statusbar">
-            <span id="status"></span>
-        </div>
+  <div id="body" class="vbox flex">
+    <xul:toolbar id="dbg-toolbar">
+      <xul:button id="close">&debuggerUI.closeButton;</xul:button>
+      <xul:button id="resume"/>
+      <xul:button id="step-over">&debuggerUI.stepOverButton;</xul:button>
+      <xul:button id="step-in">&debuggerUI.stepInButton;</xul:button>
+      <xul:button id="step-out">&debuggerUI.stepOutButton;</xul:button>
+      <xul:menulist id="scripts"/>
+    </xul:toolbar>
+    <div id="dbg-content" class="hbox flex">
+      <div id="stack" class="vbox">
+        <div class="title unselectable">&debuggerUI.stackTitle;</div>
+        <div id="stackframes" class="vbox flex"></div>
+      </div>
+      <div id="script" class="vbox flex">
+        <div class="title unselectable">&debuggerUI.scriptTitle;</div>
+        <div id="editor" class="vbox flex"></div>
+      </div>
+      <div id="properties" class="vbox">
+        <div class="title unselectable">&debuggerUI.propertiesTitle;</div>
+        <div id="variables" class="vbox flex"></div>
+      </div>
     </div>
+    <div id="dbg-statusbar">
+      <span id="status"></span>
+    </div>
+  </div>
 
 </xul:window>
--- a/browser/devtools/debugger/test/browser_dbg_bug723069_editor-breakpoints.js
+++ b/browser/devtools/debugger/test/browser_dbg_bug723069_editor-breakpoints.js
@@ -24,17 +24,17 @@ function test()
   let scriptShown = false;
   let framesAdded = false;
 
   debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
     gTab = aTab;
     gDebuggee = aDebuggee;
     gPane = aPane;
     gDebugger = gPane.debuggerWindow;
-    gPane.activeThread.addOneTimeListener("framesadded", function() {
+    gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
       framesAdded = true;
       runTest();
     });
 
     gDebuggee.firstCall();
   });
 
   window.addEventListener("Debugger:ScriptShown", function _onEvent(aEvent) {
@@ -52,26 +52,26 @@ function test()
       Services.tm.currentThread.dispatch({ run: onScriptShown }, 0);
     }
   }
 
   function onScriptShown()
   {
     gScripts = gDebugger.DebuggerView.Scripts;
 
-    is(gDebugger.StackFrames.activeThread.state, "paused",
+    is(gDebugger.DebuggerController.activeThread.state, "paused",
       "Should only be getting stack frames while paused.");
 
     is(gScripts._scripts.itemCount, 2, "Found the expected number of scripts.");
 
     gEditor = gDebugger.editor;
 
     isnot(gEditor.getText().indexOf("debugger"), -1,
           "The correct script was loaded initially.");
-    isnot(gScripts.selected, gScripts.scriptLocations()[0],
+    isnot(gScripts.selected, gScripts.scriptLocations[0],
           "the correct script is selected");
 
     gBreakpoints = gPane.breakpoints;
     is(Object.keys(gBreakpoints), 0, "no breakpoints");
     ok(!gPane.getBreakpoint("foo", 3), "getBreakpoint('foo', 3) returns falsey");
 
     is(gEditor.getBreakpoints().length, 0, "no breakpoints in the editor");
 
@@ -156,17 +156,17 @@ function test()
   function testBreakpointAddBackground()
   {
     info("add a breakpoint to the second script which is not selected");
 
     is(Object.keys(gBreakpoints).length, 0, "no breakpoints in the debugger");
     ok(!gPane.getBreakpoint(gScripts.selected, 6),
        "getBreakpoint(selectedScript, 6) returns no breakpoint");
 
-    let script0 = gScripts.scriptLocations()[0];
+    let script0 = gScripts.scriptLocations[0];
     isnot(script0, gScripts.selected,
           "first script location is not the currently selected script");
 
     let location = {url: script0, line: 5};
     gEditor.addEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE,
                              onEditorBreakpointAddBackgroundTrap);
     gPane.addBreakpoint(location, onBreakpointAddBackground);
   }
@@ -182,41 +182,41 @@ function test()
   }
 
   function onBreakpointAddBackground(aBreakpointClient, aResponseError)
   {
     breakpointsAdded++;
 
     ok(aBreakpointClient, "breakpoint2 added, client received");
     ok(!aResponseError, "breakpoint2 added without errors");
-    is(aBreakpointClient.location.url, gScripts.scriptLocations()[0],
+    is(aBreakpointClient.location.url, gScripts.scriptLocations[0],
        "breakpoint2 client url is correct");
     is(aBreakpointClient.location.line, 5,
        "breakpoint2 client line is correct");
 
     executeSoon(function() {
       ok(aBreakpointClient.actor in gBreakpoints,
          "breakpoint2 client found in the list of debugger breakpoints");
       is(Object.keys(gBreakpoints).length, 1, "one breakpoint in the debugger");
-      is(gPane.getBreakpoint(gScripts.scriptLocations()[0], 5), aBreakpointClient,
+      is(gPane.getBreakpoint(gScripts.scriptLocations[0], 5), aBreakpointClient,
          "getBreakpoint(scriptLocations[0], 5) returns the correct breakpoint");
 
       // remove the trap listener
       gEditor.removeEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE,
                                   onEditorBreakpointAddBackgroundTrap);
 
       gEditor.addEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE,
                                onEditorBreakpointAddSwitch);
       gEditor.addEventListener(SourceEditor.EVENTS.TEXT_CHANGED,
                                onEditorTextChanged);
 
       info("switch to the second script");
 
       gScripts._scripts.selectedIndex = 0;
-      gDebugger.SourceScripts.onChange({ target: gScripts._scripts });
+      gDebugger.DebuggerController.SourceScripts.onChange({ target: gScripts._scripts });
     });
   }
 
   function onEditorBreakpointAddSwitch(aEvent)
   {
     gEditor.removeEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE,
                                 onEditorBreakpointAddSwitch);
     editorBreakpointChanges++;
@@ -262,23 +262,23 @@ function test()
     ok(aEvent, "breakpoint2 removed from the editor");
     is(aEvent.added.length, 0, "no breakpoint was added to the editor");
     is(aEvent.removed.length, 1, "one breakpoint was removed from the editor");
     is(aEvent.removed[0].line, 4, "editor breakpoint line is correct");
 
     is(gEditor.getBreakpoints().length, 0, "editor.getBreakpoints().length is correct");
 
     executeSoon(function() {
-      gDebugger.StackFrames.activeThread.resume(finish);
+      gDebugger.DebuggerController.activeThread.resume(finish);
     });
   }
 
   registerCleanupFunction(function() {
     is(Object.keys(gBreakpoints).length, 0, "no breakpoint in the debugger");
-    ok(!gPane.getBreakpoint(gScripts.scriptLocations()[0], 5),
+    ok(!gPane.getBreakpoint(gScripts.scriptLocations[0], 5),
        "getBreakpoint(scriptLocations[0], 5) returns no breakpoint");
 
     removeTab(gTab);
     is(breakpointsAdded, 2, "correct number of breakpoints have been added");
     is(breakpointsRemoved, 1, "correct number of breakpoints have been removed");
     is(editorBreakpointChanges, 4, "correct number of editor breakpoint changes");
     gPane = null;
     gTab = null;
--- a/browser/devtools/debugger/test/browser_dbg_bug731394_editor-contextmenu.js
+++ b/browser/devtools/debugger/test/browser_dbg_bug731394_editor-contextmenu.js
@@ -23,17 +23,17 @@ function test()
   let framesAdded = false;
 
   debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
     gTab = aTab;
     gDebuggee = aDebuggee;
     gPane = aPane;
     gDebugger = gPane.debuggerWindow;
 
-    gPane.activeThread.addOneTimeListener("framesadded", function() {
+    gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
       framesAdded = true;
       runTest();
     });
     gDebuggee.firstCall();
   });
 
   window.addEventListener("Debugger:ScriptShown", function _onEvent(aEvent) {
     let url = aEvent.detail.url;
@@ -50,17 +50,17 @@ function test()
       Services.tm.currentThread.dispatch({ run: onScriptShown }, 0);
     }
   }
 
   function onScriptShown()
   {
     let scripts = gDebugger.DebuggerView.Scripts._scripts;
 
-    is(gDebugger.StackFrames.activeThread.state, "paused",
+    is(gDebugger.DebuggerController.activeThread.state, "paused",
       "Should only be getting stack frames while paused.");
 
     is(scripts.itemCount, 2, "Found the expected number of scripts.");
 
     let editor = gDebugger.editor;
 
     isnot(editor.getText().indexOf("debugger"), -1,
           "The correct script was loaded initially.");
@@ -102,17 +102,17 @@ function test()
     for (let id in commands) {
       let element = document.getElementById(id);
       is(element.hasAttribute("disabled"), commands[id],
          id + " hasAttribute('disabled') check");
     }
 
     executeSoon(function() {
       contextMenu.hidePopup();
-      gDebugger.StackFrames.activeThread.resume(finish);
+      gDebugger.DebuggerController.activeThread.resume(finish);
     });
   }
 
   registerCleanupFunction(function() {
     removeTab(gTab);
     gPane = null;
     gTab = null;
     gDebuggee = null;
--- a/browser/devtools/debugger/test/browser_dbg_clean-exit.js
+++ b/browser/devtools/debugger/test/browser_dbg_clean-exit.js
@@ -17,19 +17,19 @@ function test() {
     gPane = aPane;
     gDebugger = gPane.debuggerWindow;
 
     testCleanExit();
   });
 }
 
 function testCleanExit() {
-  gPane.activeThread.addOneTimeListener("framesadded", function() {
+  gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
     Services.tm.currentThread.dispatch({ run: function() {
-      is(gDebugger.StackFrames.activeThread.paused, true,
+      is(gDebugger.DebuggerController.activeThread.paused, true,
         "Should be paused after the debugger statement.");
 
       closeDebuggerAndFinish(gTab);
     }}, 0);
   });
 
   gTab.linkedBrowser.contentWindow.wrappedJSObject.runDebuggerStatement();
 }
--- a/browser/devtools/debugger/test/browser_dbg_debuggerstatement.html
+++ b/browser/devtools/debugger/test/browser_dbg_debuggerstatement.html
@@ -1,11 +1,11 @@
 <!DOCTYPE HTML>
 <html>
-<head><title>Browser Debugger Test Tab</title>
+<head><meta charset='utf-8'/><title>Browser Debugger Test Tab</title>
 <!-- Any copyright is dedicated to the Public Domain.
      http://creativecommons.org/publicdomain/zero/1.0/ -->
 <script type="text/javascript">
 
 function runDebuggerStatement()
 {
   debugger;
 }
--- a/browser/devtools/debugger/test/browser_dbg_displayName.html
+++ b/browser/devtools/debugger/test/browser_dbg_displayName.html
@@ -1,11 +1,11 @@
 <!DOCTYPE HTML>
 <html>
-<head><title>Browser Debugger Test Tab</title>
+<head><meta charset='utf-8'/><title>Browser Debugger Test Tab</title>
 <!-- Any copyright is dedicated to the Public Domain.
      http://creativecommons.org/publicdomain/zero/1.0/ -->
 <script type="text/javascript">
 
 var a = function() {
   return function() {
     debugger;
   }
--- a/browser/devtools/debugger/test/browser_dbg_displayName.js
+++ b/browser/devtools/debugger/test/browser_dbg_displayName.js
@@ -17,37 +17,37 @@ function test() {
     gPane = aPane;
     gDebugger = gPane.debuggerWindow;
 
     testAnonCall();
   });
 }
 
 function testAnonCall() {
-  gPane.activeThread.addOneTimeListener("framesadded", function() {
+  gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
     Services.tm.currentThread.dispatch({ run: function() {
 
-      let frames = gDebugger.DebuggerView.Stackframes._frames;
+      let frames = gDebugger.DebuggerView.StackFrames._frames;
 
-      is(gDebugger.StackFrames.activeThread.state, "paused",
+      is(gDebugger.DebuggerController.activeThread.state, "paused",
         "Should only be getting stack frames while paused.");
 
       is(frames.querySelectorAll(".dbg-stackframe").length, 3,
         "Should have three frames.");
 
       is(frames.querySelector("#stackframe-0 .dbg-stackframe-name").textContent,
         "anonFunc", "Frame name should be anonFunc");
 
       resumeAndFinish();
     }}, 0);
   });
 
   gDebuggee.evalCall();
 }
 
 function resumeAndFinish() {
-  gDebugger.StackFrames.activeThread.resume(function() {
+  gDebugger.DebuggerController.activeThread.resume(function() {
     removeTab(gTab);
     gPane = null;
     gDebuggee = null;
     finish();
   });
 }
--- a/browser/devtools/debugger/test/browser_dbg_frame-parameters.html
+++ b/browser/devtools/debugger/test/browser_dbg_frame-parameters.html
@@ -1,11 +1,12 @@
 <!DOCTYPE HTML>
 <html>
   <head>
+    <meta charset='utf-8'/>
     <title>Debugger Function Call Parameter Test</title>
     <!-- Any copyright is dedicated to the Public Domain.
          http://creativecommons.org/publicdomain/zero/1.0/ -->
     <script type="text/javascript">
       window.addEventListener("load", function() {
         function test(aArg, bArg, cArg, dArg, eArg, fArg) {
           var a = 1;
           var b = { a: a };
--- a/browser/devtools/debugger/test/browser_dbg_iframes.html
+++ b/browser/devtools/debugger/test/browser_dbg_iframes.html
@@ -1,11 +1,11 @@
 <!DOCTYPE HTML>
 <html>
-<head><title>Browser Debugger IFrame Test Tab</title>
+<head><meta charset='utf-8'/><title>Browser Debugger IFrame Test Tab</title>
 <!-- Any copyright is dedicated to the Public Domain.
      http://creativecommons.org/publicdomain/zero/1.0/ -->
 </head>
 
 <body>
   <iframe src="browser_dbg_debuggerstatement.html"></iframe>
 </body>
 
--- a/browser/devtools/debugger/test/browser_dbg_iframes.js
+++ b/browser/devtools/debugger/test/browser_dbg_iframes.js
@@ -11,32 +11,32 @@ var gTab = null;
 const TEST_URL = EXAMPLE_URL + "browser_dbg_iframes.html";
 
 function test() {
   debug_tab_pane(TEST_URL, function(aTab, aDebuggee, aPane) {
     gTab = aTab;
     gPane = aPane;
     let gDebugger = gPane.debuggerWindow;
 
-    is(gDebugger.StackFrames.activeThread.paused, false,
+    is(gDebugger.DebuggerController.activeThread.paused, false,
       "Should be running after debug_tab_pane.");
 
-    gPane.activeThread.addOneTimeListener("framesadded", function() {
+    gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
       Services.tm.currentThread.dispatch({ run: function() {
 
-        let frames = gDebugger.DebuggerView.Stackframes._frames;
+        let frames = gDebugger.DebuggerView.StackFrames._frames;
         let childNodes = frames.childNodes;
 
-        is(gDebugger.StackFrames.activeThread.paused, true,
+        is(gDebugger.DebuggerController.activeThread.paused, true,
           "Should be paused after an interrupt request.");
 
         is(frames.querySelectorAll(".dbg-stackframe").length, 1,
           "Should have one frame in the stack.");
 
-        gPane.activeThread.addOneTimeListener("resumed", function() {
+        gDebugger.DebuggerController.activeThread.addOneTimeListener("resumed", function() {
           Services.tm.currentThread.dispatch({ run: function() {
             closeDebuggerAndFinish(gTab);
           }}, 0);
         });
 
         EventUtils.sendMouseEvent({ type: "click" },
           gDebugger.document.getElementById("resume"),
           gDebugger);
--- a/browser/devtools/debugger/test/browser_dbg_location-changes.js
+++ b/browser/devtools/debugger/test/browser_dbg_location-changes.js
@@ -19,23 +19,23 @@ function test()
     gPane = aPane;
     gDebugger = gPane.debuggerWindow;
 
     testSimpleCall();
   });
 }
 
 function testSimpleCall() {
-  gPane.activeThread.addOneTimeListener("framesadded", function() {
+  gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
     Services.tm.currentThread.dispatch({
       run: function() {
-        var frames = gDebugger.DebuggerView.Stackframes._frames,
+        var frames = gDebugger.DebuggerView.StackFrames._frames,
             childNodes = frames.childNodes;
 
-        is(gDebugger.StackFrames.activeThread.state, "paused",
+        is(gDebugger.DebuggerController.activeThread.state, "paused",
           "Should only be getting stack frames while paused.");
 
         is(frames.querySelectorAll(".dbg-stackframe").length, 1,
           "Should have only one frame.");
 
         is(childNodes.length, frames.querySelectorAll(".dbg-stackframe").length,
           "All children should be frames.");
 
@@ -44,20 +44,20 @@ function testSimpleCall() {
     }, 0);
   });
 
   gDebuggee.simpleCall();
 }
 
 function testLocationChange()
 {
-  gDebugger.StackFrames.activeThread.resume(function() {
-    gPane._client.addOneTimeListener("tabNavigated", function(aEvent, aPacket) {
+  gDebugger.DebuggerController.activeThread.resume(function() {
+    gDebugger.DebuggerController.client.addOneTimeListener("tabNavigated", function(aEvent, aPacket) {
       ok(true, "tabNavigated event was fired.");
-      gPane._client.addOneTimeListener("tabAttached", function(aEvent, aPacket) {
+      gDebugger.DebuggerController.client.addOneTimeListener("tabAttached", function(aEvent, aPacket) {
         ok(true, "Successfully reattached to the tab again.");
 
         closeDebuggerAndFinish(gTab);
       });
     });
     content.location = TAB1_URL;
   });
 }
--- a/browser/devtools/debugger/test/browser_dbg_panesize.js
+++ b/browser/devtools/debugger/test/browser_dbg_panesize.js
@@ -17,24 +17,27 @@ function test() {
     ok(pane, "toggleDebugger() should return a pane.");
 
     is(DebuggerUI.getDebugger(gBrowser.selectedTab), pane,
       "getDebugger() should return the same pane as toggleDebugger().");
 
     ok(DebuggerUI.preferences.height,
       "The debugger preferences should have a saved height value.");
 
-    is(DebuggerUI.preferences.height, pane.frame.height,
+    is(DebuggerUI.preferences.height, pane._frame.height,
       "The debugger pane height should be the same as the preferred value.");
 
-    pane.frame.height = someHeight;
+    pane._frame.height = someHeight;
     ok(DebuggerUI.preferences.height !== someHeight,
       "Height preferences shouldn't have been updated yet.");
 
-    pane.onConnected = function() {
+    pane._frame.addEventListener("Debugger:Connecting", function dbgConnected() {
+      pane._frame.removeEventListener("Debugger:Connecting", dbgConnected, true);
+
       removeTab(tab1);
       finish();
 
       is(DebuggerUI.preferences.height, someHeight,
         "Height preferences should have been updated by now.");
-    };
+
+    }, true);
   });
 }
--- a/browser/devtools/debugger/test/browser_dbg_pause-resume.js
+++ b/browser/devtools/debugger/test/browser_dbg_pause-resume.js
@@ -14,56 +14,56 @@ function test() {
     gPane = aPane;
     gDebugger = gPane.debuggerWindow;
 
     testPause();
   });
 }
 
 function testPause() {
-  is(gDebugger.StackFrames.activeThread.paused, false,
+  is(gDebugger.DebuggerController.activeThread.paused, false,
     "Should be running after debug_tab_pane.");
 
   let button = gDebugger.document.getElementById("resume");
-  is(button.label, gDebugger.DebuggerView.getStr("pauseLabel"),
+  is(button.label, gDebugger.L10N.getStr("pauseLabel"),
     "Button label should be pause when running.");
 
-  gPane.activeThread.addOneTimeListener("paused", function() {
+  gDebugger.DebuggerController.activeThread.addOneTimeListener("paused", function() {
     Services.tm.currentThread.dispatch({ run: function() {
 
-      let frames = gDebugger.DebuggerView.Stackframes._frames;
+      let frames = gDebugger.DebuggerView.StackFrames._frames;
       let childNodes = frames.childNodes;
 
-      is(gDebugger.StackFrames.activeThread.paused, true,
+      is(gDebugger.DebuggerController.activeThread.paused, true,
         "Should be paused after an interrupt request.");
 
-      is(button.label, gDebugger.DebuggerView.getStr("resumeLabel"),
+      is(button.label, gDebugger.L10N.getStr("resumeLabel"),
         "Button label should be resume when paused.");
 
       is(frames.querySelectorAll(".dbg-stackframe").length, 0,
         "Should have no frames when paused in the main loop.");
 
       testResume();
     }}, 0);
   });
 
   EventUtils.sendMouseEvent({ type: "click" },
     gDebugger.document.getElementById("resume"),
     gDebugger);
 }
 
 function testResume() {
-  gPane.activeThread.addOneTimeListener("resumed", function() {
+  gDebugger.DebuggerController.activeThread.addOneTimeListener("resumed", function() {
     Services.tm.currentThread.dispatch({ run: function() {
 
-      is(gDebugger.StackFrames.activeThread.paused, false,
+      is(gDebugger.DebuggerController.activeThread.paused, false,
         "Should be paused after an interrupt request.");
 
       let button = gDebugger.document.getElementById("resume");
-      is(button.label, gDebugger.DebuggerView.getStr("pauseLabel"),
+      is(button.label, gDebugger.L10N.getStr("pauseLabel"),
         "Button label should be pause when running.");
 
       closeDebuggerAndFinish(gTab);
     }}, 0);
   });
 
   EventUtils.sendMouseEvent({ type: "click" },
     gDebugger.document.getElementById("resume"),
--- a/browser/devtools/debugger/test/browser_dbg_propertyview-01.js
+++ b/browser/devtools/debugger/test/browser_dbg_propertyview-01.js
@@ -15,17 +15,17 @@ function test() {
     gPane = aPane;
     gDebugger = gPane.debuggerWindow;
 
     testSimpleCall();
   });
 }
 
 function testSimpleCall() {
-  gPane.activeThread.addOneTimeListener("framesadded", function() {
+  gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
     Services.tm.currentThread.dispatch({ run: function() {
 
       let globalScope = gDebugger.DebuggerView.Properties._globalScope;
       let localScope = gDebugger.DebuggerView.Properties._localScope;
       let withScope = gDebugger.DebuggerView.Properties._withScope;
       let closureScope = gDebugger.DebuggerView.Properties._closureScope;
 
       ok(globalScope,
@@ -77,20 +77,20 @@ function testSimpleCall() {
       resumeAndFinish();
     }}, 0);
   });
 
   gDebuggee.simpleCall();
 }
 
 function resumeAndFinish() {
-  gDebugger.StackFrames.activeThread.resume(function() {
+  gDebugger.DebuggerController.activeThread.resume(function() {
     let vs = gDebugger.DebuggerView.Scripts;
-    let ss = gDebugger.SourceScripts;
-    ss.onScriptsCleared();
+    let ss = gDebugger.DebuggerController.SourceScripts;
+    ss._onScriptsCleared();
 
     is(ss._trimUrlQuery("a/b/c.d?test=1&random=4"), "a/b/c.d",
       "Trimming the url query isn't done properly.");
 
     let urls = [
       { href: "ici://some.address.com/random/", leaf: "subrandom/" },
       { href: "ni://another.address.org/random/subrandom/", leaf: "page.html" },
       { href: "san://interesting.address.gro/random/", leaf: "script.js" },
--- a/browser/devtools/debugger/test/browser_dbg_propertyview-02.js
+++ b/browser/devtools/debugger/test/browser_dbg_propertyview-02.js
@@ -15,17 +15,17 @@ function test() {
     gPane = aPane;
     gDebugger = gPane.debuggerWindow;
 
     testSimpleCall();
   });
 }
 
 function testSimpleCall() {
-  gPane.activeThread.addOneTimeListener("framesadded", function() {
+  gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
     Services.tm.currentThread.dispatch({ run: function() {
 
       let testScope = gDebugger.DebuggerView.Properties._addScope("test");
 
       ok(testScope,
         "Should have created a scope.");
 
       is(testScope.id, "test-scope",
@@ -111,17 +111,17 @@ function testSimpleCall() {
 
       EventUtils.sendMouseEvent({ type: "click" },
         testScope.querySelector(".title"),
         gDebugger);
 
       ok(!testScope.expanded,
         "Clicking again the testScope tilte should collapse it.");
 
-      gDebugger.StackFrames.activeThread.resume(function() {
+      gDebugger.DebuggerController.activeThread.resume(function() {
         closeDebuggerAndFinish(gTab);
       });
     }}, 0);
   });
 
   gDebuggee.simpleCall();
 }
 
--- a/browser/devtools/debugger/test/browser_dbg_propertyview-03.js
+++ b/browser/devtools/debugger/test/browser_dbg_propertyview-03.js
@@ -15,17 +15,17 @@ function test() {
     gPane = aPane;
     gDebugger = gPane.debuggerWindow;
 
     testSimpleCall();
   });
 }
 
 function testSimpleCall() {
-  gPane.activeThread.addOneTimeListener("framesadded", function() {
+  gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
     Services.tm.currentThread.dispatch({ run: function() {
 
       let testScope = gDebugger.DebuggerView.Properties._addScope("test");
       let testVar = testScope.addVar("something");
       let duplVar = testScope.addVar("something");
 
       ok(testVar,
         "Should have created a variable.");
@@ -113,17 +113,17 @@ function testSimpleCall() {
 
       testScope.remove();
       is(removeCallbackSender, testScope,
         "The removeCallback wasn't called as it should.");
 
       is(gDebugger.DebuggerView.Properties._vars.childNodes.length, 4,
         "The scope should have been removed from the parent container tree.");
 
-      gDebugger.StackFrames.activeThread.resume(function() {
+      gDebugger.DebuggerController.activeThread.resume(function() {
         closeDebuggerAndFinish(gTab);
       });
     }}, 0);
   });
 
   gDebuggee.simpleCall();
 }
 
--- a/browser/devtools/debugger/test/browser_dbg_propertyview-04.js
+++ b/browser/devtools/debugger/test/browser_dbg_propertyview-04.js
@@ -15,17 +15,17 @@ function test() {
     gPane = aPane;
     gDebugger = gPane.debuggerWindow;
 
     testSimpleCall();
   });
 }
 
 function testSimpleCall() {
-  gPane.activeThread.addOneTimeListener("framesadded", function() {
+  gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
     Services.tm.currentThread.dispatch({ run: function() {
 
       let testScope = gDebugger.DebuggerView.Properties._addScope("test");
       let testVar = testScope.addVar("something");
 
       let properties = testVar.addProperties({ "child": { "value": { "type": "object",
                                                                      "class": "Object" } } });
 
@@ -68,17 +68,17 @@ function testSimpleCall() {
       is(testVar.querySelector(".details").childNodes.length, 0,
         "The var should remove all it's details container tree children.");
 
       testVar.remove();
 
       is(testScope.querySelector(".details").childNodes.length, 0,
         "The var should have been removed from the parent container tree.");
 
-      gDebugger.StackFrames.activeThread.resume(function() {
+      gDebugger.DebuggerController.activeThread.resume(function() {
         closeDebuggerAndFinish(gTab);
       });
     }}, 0);
   });
 
   gDebuggee.simpleCall();
 }
 
--- a/browser/devtools/debugger/test/browser_dbg_propertyview-05.js
+++ b/browser/devtools/debugger/test/browser_dbg_propertyview-05.js
@@ -15,17 +15,17 @@ function test() {
     gPane = aPane;
     gDebugger = gPane.debuggerWindow;
 
     testSimpleCall();
   });
 }
 
 function testSimpleCall() {
-  gPane.activeThread.addOneTimeListener("framesadded", function() {
+  gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
     Services.tm.currentThread.dispatch({ run: function() {
 
       let testScope = gDebugger.DebuggerView.Properties._addScope("test");
       let testVar = testScope.addVar("something");
 
       testVar.setGrip(1.618);
 
       is(testVar.querySelector(".info").textContent, "1.618",
@@ -76,17 +76,17 @@ function testSimpleCall() {
       is(testVar.querySelector(".details").childNodes.length, 0,
         "The var should remove all it's details container tree children.");
 
       testVar.remove();
 
       is(testScope.querySelector(".details").childNodes.length, 0,
         "The var should have been removed from the parent container tree.");
 
-      gDebugger.StackFrames.activeThread.resume(function() {
+      gDebugger.DebuggerController.activeThread.resume(function() {
         closeDebuggerAndFinish(gTab);
       });
     }}, 0);
   });
 
   gDebuggee.simpleCall();
 }
 
--- a/browser/devtools/debugger/test/browser_dbg_propertyview-06.js
+++ b/browser/devtools/debugger/test/browser_dbg_propertyview-06.js
@@ -15,17 +15,17 @@ function test() {
     gPane = aPane;
     gDebugger = gPane.debuggerWindow;
 
     testSimpleCall();
   });
 }
 
 function testSimpleCall() {
-  gPane.activeThread.addOneTimeListener("framesadded", function() {
+  gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
     Services.tm.currentThread.dispatch({ run: function() {
 
       let globalScope = gDebugger.DebuggerView.Properties.globalScope;
       let localScope = gDebugger.DebuggerView.Properties.localScope;
       let windowVar = globalScope.addVar("window");
       let documentVar = globalScope.addVar("document");
       let localVar0 = localScope.addVar("localVariable");
       let localVar1 = localScope.addVar("localVar1");
@@ -112,17 +112,17 @@ function testSimpleCall() {
         "The grip information for the localVar3 wasn't set correctly.");
 
       is(localVar4.querySelector(".info").textContent, "null",
         "The grip information for the localVar4 wasn't set correctly.");
 
       is(localVar5.querySelector(".info").textContent, "[object Object]",
         "The grip information for the localVar5 wasn't set correctly.");
 
-      gDebugger.StackFrames.activeThread.resume(function() {
+      gDebugger.DebuggerController.activeThread.resume(function() {
         closeDebuggerAndFinish(gTab);
       });
     }}, 0);
   });
 
   gDebuggee.simpleCall();
 }
 
--- a/browser/devtools/debugger/test/browser_dbg_propertyview-07.js
+++ b/browser/devtools/debugger/test/browser_dbg_propertyview-07.js
@@ -30,28 +30,28 @@ function testFrameParameters()
   gDebugger.addEventListener("Debugger:FetchedParameters", function test() {
     dump("Entered Debugger:FetchedParameters!\n");
 
     gDebugger.removeEventListener("Debugger:FetchedParameters", test, false);
     Services.tm.currentThread.dispatch({ run: function() {
 
       dump("After currentThread.dispatch!\n");
 
-      var frames = gDebugger.DebuggerView.Stackframes._frames,
+      var frames = gDebugger.DebuggerView.StackFrames._frames,
           childNodes = frames.childNodes,
           localScope = gDebugger.DebuggerView.Properties.localScope,
           localNodes = localScope.querySelector(".details").childNodes;
 
       dump("Got our variables:\n");
       dump("frames     - " + frames.constructor + "\n");
       dump("childNodes - " + childNodes.constructor + "\n");
       dump("localScope - " + localScope.constructor + "\n");
       dump("localNodes - " + localNodes.constructor + "\n");
 
-      is(gDebugger.StackFrames.activeThread.state, "paused",
+      is(gDebugger.DebuggerController.activeThread.state, "paused",
         "Should only be getting stack frames while paused.");
 
       is(frames.querySelectorAll(".dbg-stackframe").length, 3,
         "Should have three frames.");
 
       is(localNodes.length, 8,
         "The localScope should contain all the created variable elements.");
 
@@ -84,28 +84,28 @@ function testFrameParameters()
   }, false);
 
   EventUtils.sendMouseEvent({ type: "click" },
     content.document.querySelector("button"),
     content.window);
 }
 
 function resumeAndFinish() {
-  gPane.activeThread.addOneTimeListener("framescleared", function() {
+  gDebugger.DebuggerController.activeThread.addOneTimeListener("framescleared", function() {
     Services.tm.currentThread.dispatch({ run: function() {
-      var frames = gDebugger.DebuggerView.Stackframes._frames;
+      var frames = gDebugger.DebuggerView.StackFrames._frames;
 
       is(frames.querySelectorAll(".dbg-stackframe").length, 0,
         "Should have no frames.");
 
       closeDebuggerAndFinish(gTab);
     }}, 0);
   });
 
-  gDebugger.StackFrames.activeThread.resume();
+  gDebugger.DebuggerController.activeThread.resume();
 }
 
 registerCleanupFunction(function() {
   removeTab(gTab);
   gPane = null;
   gTab = null;
   gDebugger = null;
 });
--- a/browser/devtools/debugger/test/browser_dbg_propertyview-08.js
+++ b/browser/devtools/debugger/test/browser_dbg_propertyview-08.js
@@ -30,26 +30,26 @@ function testFrameParameters()
   gDebugger.addEventListener("Debugger:FetchedParameters", function test() {
     dump("Entered Debugger:FetchedParameters!\n");
 
     gDebugger.removeEventListener("Debugger:FetchedParameters", test, false);
     Services.tm.currentThread.dispatch({ run: function() {
 
       dump("After currentThread.dispatch!\n");
 
-      var frames = gDebugger.DebuggerView.Stackframes._frames,
+      var frames = gDebugger.DebuggerView.StackFrames._frames,
           localScope = gDebugger.DebuggerView.Properties.localScope,
           localNodes = localScope.querySelector(".details").childNodes;
 
       dump("Got our variables:\n");
       dump("frames     - " + frames.constructor + "\n");
       dump("localScope - " + localScope.constructor + "\n");
       dump("localNodes - " + localNodes.constructor + "\n");
 
-      is(gDebugger.StackFrames.activeThread.state, "paused",
+      is(gDebugger.DebuggerController.activeThread.state, "paused",
         "Should only be getting stack frames while paused.");
 
       is(frames.querySelectorAll(".dbg-stackframe").length, 3,
         "Should have three frames.");
 
       is(localNodes.length, 8,
         "The localScope should contain all the created variable elements.");
 
@@ -99,28 +99,28 @@ function testFrameParameters()
   }, false);
 
   EventUtils.sendMouseEvent({ type: "click" },
     content.document.querySelector("button"),
     content.window);
 }
 
 function resumeAndFinish() {
-  gPane.activeThread.addOneTimeListener("framescleared", function() {
+  gDebugger.DebuggerController.activeThread.addOneTimeListener("framescleared", function() {
     Services.tm.currentThread.dispatch({ run: function() {
-      var frames = gDebugger.DebuggerView.Stackframes._frames;
+      var frames = gDebugger.DebuggerView.StackFrames._frames;
 
       is(frames.querySelectorAll(".dbg-stackframe").length, 0,
         "Should have no frames.");
 
       closeDebuggerAndFinish(gTab);
     }}, 0);
   });
 
-  gDebugger.StackFrames.activeThread.resume();
+  gDebugger.DebuggerController.activeThread.resume();
 }
 
 registerCleanupFunction(function() {
   removeTab(gTab);
   gPane = null;
   gTab = null;
   gDebugger = null;
 });
--- a/browser/devtools/debugger/test/browser_dbg_script-switching.html
+++ b/browser/devtools/debugger/test/browser_dbg_script-switching.html
@@ -1,12 +1,13 @@
 <!DOCTYPE HTML>
 <html>
-	<head>
-		<title>Browser Debugger Script Switching Test</title>
+  <head>
+    <meta charset='utf-8'/>
+    <title>Browser Debugger Script Switching 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"></script>
     <script type="text/javascript" src="test-script-switching-02.js"></script>
-	</head>
-	<body>
-	</body>
+  </head>
+  <body>
+  </body>
 </html>
--- a/browser/devtools/debugger/test/browser_dbg_script-switching.js
+++ b/browser/devtools/debugger/test/browser_dbg_script-switching.js
@@ -24,17 +24,17 @@ function test()
   let framesAdded = false;
 
   debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
     gTab = aTab;
     gDebuggee = aDebuggee;
     gPane = aPane;
     gDebugger = gPane.debuggerWindow;
 
-    gPane.activeThread.addOneTimeListener("framesadded", function() {
+    gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
       framesAdded = true;
       runTest();
     });
 
     gDebuggee.firstCall();
   });
 
   window.addEventListener("Debugger:ScriptShown", function _onEvent(aEvent) {
@@ -52,17 +52,17 @@ function test()
       Services.tm.currentThread.dispatch({ run: testScriptsDisplay }, 0);
     }
   }
 }
 
 function testScriptsDisplay() {
   gScripts = gDebugger.DebuggerView.Scripts._scripts;
 
-  is(gDebugger.StackFrames.activeThread.state, "paused",
+  is(gDebugger.DebuggerController.activeThread.state, "paused",
     "Should only be getting stack frames while paused.");
 
   is(gScripts.itemCount, 2, "Found the expected number of scripts.");
 
   for (let i = 0; i < gScripts.itemCount; i++) {
     info("label: " + i + " " + gScripts.getItemAtIndex(i).getAttribute("label"));
   }
 
@@ -74,16 +74,18 @@ function testScriptsDisplay() {
   ok(gDebugger.DebuggerView.Scripts.contains(EXAMPLE_URL +
     label2), "Second script url is incorrect.");
 
   ok(gDebugger.DebuggerView.Scripts.containsLabel(
     label1), "First script label is incorrect.");
   ok(gDebugger.DebuggerView.Scripts.containsLabel(
     label2), "Second script label is incorrect.");
 
+  dump("Debugger editor text:\n" + gDebugger.editor.getText() + "\n");
+
   ok(gDebugger.editor.getText().search(/debugger/) != -1,
     "The correct script was loaded initially.");
 
   is(gDebugger.editor.getDebugLocation(), 5,
      "editor debugger location is correct.");
 
   window.addEventListener("Debugger:ScriptShown", function _onEvent(aEvent) {
     let url = aEvent.detail.url;
@@ -93,26 +95,28 @@ function testScriptsDisplay() {
     }
   });
 
   gDebugger.DebuggerView.Scripts.selectScript(EXAMPLE_URL + label1);
 }
 
 function testSwitchPaused()
 {
+  dump("Debugger editor text:\n" + gDebugger.editor.getText() + "\n");
+
   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.getDebugLocation(), -1,
      "editor debugger location has been cleared.");
 
-  gDebugger.StackFrames.activeThread.resume(function() {
+  gDebugger.DebuggerController.activeThread.resume(function() {
     window.addEventListener("Debugger:ScriptShown", function _onEvent(aEvent) {
       let url = aEvent.detail.url;
       if (url.indexOf("-02.js") != -1) {
         window.removeEventListener(aEvent.type, _onEvent);
         testSwitchRunning();
       }
     });
 
--- a/browser/devtools/debugger/test/browser_dbg_select-line.js
+++ b/browser/devtools/debugger/test/browser_dbg_select-line.js
@@ -26,21 +26,21 @@ function test()
     gPane = aPane;
     gDebugger = gPane.debuggerWindow;
 
     testSelectLine();
   });
 }
 
 function testSelectLine() {
-  gPane.activeThread.addOneTimeListener("scriptsadded", function() {
+  gDebugger.DebuggerController.activeThread.addOneTimeListener("scriptsadded", function() {
     Services.tm.currentThread.dispatch({ run: function() {
       gScripts = gDebugger.DebuggerView.Scripts._scripts;
 
-      is(gDebugger.StackFrames.activeThread.state, "paused",
+      is(gDebugger.DebuggerController.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.");
 
       // Yield control back to the event loop so that the debugger has a
@@ -62,17 +62,17 @@ function testSelectLine() {
 
           // 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() {
+            gDebugger.DebuggerController.activeThread.resume(function() {
               closeDebuggerAndFinish(gTab);
             });
           });
         });
 
         // Scroll all the way down to ensure stackframe-3 is visible.
         let stackframes = gDebugger.document.getElementById("stackframes");
         stackframes.scrollTop = stackframes.scrollHeight;
--- a/browser/devtools/debugger/test/browser_dbg_stack-01.js
+++ b/browser/devtools/debugger/test/browser_dbg_stack-01.js
@@ -16,32 +16,32 @@ function test() {
     gPane = aPane;
     gDebugger = gPane.debuggerWindow;
 
     testSimpleCall();
   });
 }
 
 function testSimpleCall() {
-  gPane.activeThread.addOneTimeListener("framesadded", function() {
+  gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
     Services.tm.currentThread.dispatch({ run: function() {
 
-      let frames = gDebugger.DebuggerView.Stackframes._frames;
+      let frames = gDebugger.DebuggerView.StackFrames._frames;
       let childNodes = frames.childNodes;
 
-      is(gDebugger.StackFrames.activeThread.state, "paused",
+      is(gDebugger.DebuggerController.activeThread.state, "paused",
         "Should only be getting stack frames while paused.");
 
       is(frames.querySelectorAll(".dbg-stackframe").length, 1,
         "Should have only one frame.");
 
       is(childNodes.length, frames.querySelectorAll(".dbg-stackframe").length,
         "All children should be frames.");
 
-      gDebugger.StackFrames.activeThread.resume(function() {
+      gDebugger.DebuggerController.activeThread.resume(function() {
         closeDebuggerAndFinish(gTab);
       });
     }}, 0);
   });
 
   gDebuggee.simpleCall();
 }
 
--- a/browser/devtools/debugger/test/browser_dbg_stack-02.js
+++ b/browser/devtools/debugger/test/browser_dbg_stack-02.js
@@ -16,23 +16,23 @@ function test() {
     gPane = aPane;
     gDebugger = gPane.debuggerWindow;
 
     testEvalCall();
   });
 }
 
 function testEvalCall() {
-  gPane.activeThread.addOneTimeListener("framesadded", function() {
+  gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
     Services.tm.currentThread.dispatch({ run: function() {
 
-      let frames = gDebugger.DebuggerView.Stackframes._frames;
+      let frames = gDebugger.DebuggerView.StackFrames._frames;
       let childNodes = frames.childNodes;
 
-      is(gDebugger.StackFrames.activeThread.state, "paused",
+      is(gDebugger.DebuggerController.activeThread.state, "paused",
         "Should only be getting stack frames while paused.");
 
       is(frames.querySelectorAll(".dbg-stackframe").length, 2,
         "Should have two frames.");
 
       is(childNodes.length, frames.querySelectorAll(".dbg-stackframe").length,
         "All children should be frames.");
 
@@ -62,17 +62,17 @@ function testEvalCall() {
         gDebugger);
 
       ok(frames.querySelector("#stackframe-0").classList.contains("selected"),
          "First frame should be selected after click inside the first frame.");
 
       ok(!frames.querySelector("#stackframe-1").classList.contains("selected"),
          "Second frame should not be selected after click inside the first frame.");
 
-      gDebugger.StackFrames.activeThread.resume(function() {
+      gDebugger.DebuggerController.activeThread.resume(function() {
         closeDebuggerAndFinish(gTab);
       });
     }}, 0);
   });
 
   gDebuggee.evalCall();
 }
 
--- a/browser/devtools/debugger/test/browser_dbg_stack-03.js
+++ b/browser/devtools/debugger/test/browser_dbg_stack-03.js
@@ -16,43 +16,43 @@ function test() {
     gPane = aPane;
     gDebugger = gPane.debuggerWindow;
 
     testRecurse();
   });
 }
 
 function testRecurse() {
-  gDebuggee.gRecurseLimit = (gDebugger.StackFrames.pageSize * 2) + 1;
+  gDebuggee.gRecurseLimit = (gDebugger.DebuggerController.StackFrames.pageSize * 2) + 1;
 
-  gPane.activeThread.addOneTimeListener("framesadded", function() {
+  gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
     Services.tm.currentThread.dispatch({ run: function() {
 
-      let frames = gDebugger.DebuggerView.Stackframes._frames;
-      let pageSize = gDebugger.StackFrames.pageSize;
+      let frames = gDebugger.DebuggerView.StackFrames._frames;
+      let pageSize = gDebugger.DebuggerController.StackFrames.pageSize;
       let recurseLimit = gDebuggee.gRecurseLimit;
       let childNodes = frames.childNodes;
 
       is(frames.querySelectorAll(".dbg-stackframe").length, pageSize,
         "Should have the max limit of frames.");
 
       is(childNodes.length, frames.querySelectorAll(".dbg-stackframe").length,
         "All children should be frames.");
 
 
-      gPane.activeThread.addOneTimeListener("framesadded", function() {
+      gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
 
         is(frames.querySelectorAll(".dbg-stackframe").length, pageSize * 2,
           "Should now have twice the max limit of frames.");
 
-        gPane.activeThread.addOneTimeListener("framesadded", function() {
+        gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
           is(frames.querySelectorAll(".dbg-stackframe").length, recurseLimit,
             "Should have reached the recurse limit.");
 
-          gDebugger.StackFrames.activeThread.resume(function() {
+          gDebugger.DebuggerController.activeThread.resume(function() {
             closeDebuggerAndFinish(gTab);
           });
         });
 
         frames.scrollTop = frames.scrollHeight;
       });
 
       frames.scrollTop = frames.scrollHeight;
--- a/browser/devtools/debugger/test/browser_dbg_stack-04.js
+++ b/browser/devtools/debugger/test/browser_dbg_stack-04.js
@@ -16,47 +16,47 @@ function test() {
     gPane = aPane;
     gDebugger = gPane.debuggerWindow;
 
     testEvalCallResume();
   });
 }
 
 function testEvalCallResume() {
-  gPane.activeThread.addOneTimeListener("framesadded", function() {
+  gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
     Services.tm.currentThread.dispatch({ run: function() {
 
-      let frames = gDebugger.DebuggerView.Stackframes._frames;
+      let frames = gDebugger.DebuggerView.StackFrames._frames;
       let childNodes = frames.childNodes;
 
-      is(gDebugger.StackFrames.activeThread.state, "paused",
+      is(gDebugger.DebuggerController.activeThread.state, "paused",
         "Should only be getting stack frames while paused.");
 
       is(frames.querySelectorAll(".dbg-stackframe").length, 2,
         "Should have two frames.");
 
       is(childNodes.length, frames.querySelectorAll(".dbg-stackframe").length,
         "All children should be frames.");
 
 
-      gPane.activeThread.addOneTimeListener("framescleared", function() {
+      gDebugger.DebuggerController.activeThread.addOneTimeListener("framescleared", function() {
 
         is(frames.querySelectorAll(".dbg-stackframe").length, 0,
           "Should have no frames after resume");
 
         is(childNodes.length, 1,
           "Should only have one child.");
 
         is(frames.querySelectorAll(".empty").length, 1,
            "Should have the empty list explanation.");
 
         closeDebuggerAndFinish(gTab);
       });
 
-      gPane.activeThread.resume();
+      gDebugger.DebuggerController.activeThread.resume();
     }}, 0);
   });
 
   gDebuggee.evalCall();
 }
 
 registerCleanupFunction(function() {
   removeTab(gTab);
--- a/browser/devtools/debugger/test/browser_dbg_stack-05.js
+++ b/browser/devtools/debugger/test/browser_dbg_stack-05.js
@@ -19,17 +19,17 @@ function test() {
   let framesAdded = false;
 
   debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
     gTab = aTab;
     gDebuggee = aDebuggee;
     gPane = aPane;
     gDebugger = gPane.debuggerWindow;
 
-    gPane.activeThread.addOneTimeListener("framesadded", function() {
+    gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
       framesAdded = true;
       runTest();
     });
 
     gDebuggee.firstCall();
   });
 
   window.addEventListener("Debugger:ScriptShown", function _onEvent(aEvent) {
@@ -46,17 +46,17 @@ function test() {
     if (scriptShown && framesAdded) {
       Services.tm.currentThread.dispatch({ run: testRecurse }, 0);
     }
   }
 }
 
 function testRecurse()
 {
-  let frames = gDebugger.DebuggerView.Stackframes._frames;
+  let frames = gDebugger.DebuggerView.StackFrames._frames;
   let childNodes = frames.childNodes;
 
   is(frames.querySelectorAll(".dbg-stackframe").length, 4,
     "Correct number of frames.");
 
   is(childNodes.length, frames.querySelectorAll(".dbg-stackframe").length,
     "All children should be frames.");
 
@@ -90,17 +90,17 @@ function testRecurse()
      "First frame should be selected after click inside the first frame.");
 
   ok(!frames.querySelector("#stackframe-2").classList.contains("selected"),
      "Third frame should not be selected after click inside the first frame.");
 
   is(gDebugger.editor.getDebugLocation(), 5,
      "editor debugger location is correct (frame 0 again).");
 
-  gDebugger.StackFrames.activeThread.resume(function() {
+  gDebugger.DebuggerController.activeThread.resume(function() {
     is(gDebugger.editor.getDebugLocation(), -1,
        "editor debugger location is correct after resume.");
     closeDebuggerAndFinish(gTab);
   });
 }
 
 registerCleanupFunction(function() {
   removeTab(gTab);
--- a/browser/devtools/debugger/test/browser_dbg_stack.html
+++ b/browser/devtools/debugger/test/browser_dbg_stack.html
@@ -1,11 +1,11 @@
 <!DOCTYPE HTML>
 <html>
-<head><title>Browser Debugger Test Tab</title>
+<head><meta charset='utf-8'/><title>Browser Debugger Test Tab</title>
 <!-- Any copyright is dedicated to the Public Domain.
      http://creativecommons.org/publicdomain/zero/1.0/ -->
 <script type="text/javascript">
 
 function simpleCall() {
   debugger;
 }
 
--- a/browser/devtools/debugger/test/browser_dbg_tab1.html
+++ b/browser/devtools/debugger/test/browser_dbg_tab1.html
@@ -1,10 +1,11 @@
 <!DOCTYPE HTML>
 <html>
-	<head>
-		<title>Browser Debugger Test Tab</title>
+  <head>
+    <meta charset='utf-8'/>
+    <title>Browser Debugger Test Tab</title>
     <!-- Any copyright is dedicated to the Public Domain.
          http://creativecommons.org/publicdomain/zero/1.0/ -->
-	</head>
-	<body>
-	</body>
+  </head>
+  <body>
+  </body>
 </html>
--- a/browser/devtools/debugger/test/browser_dbg_tab2.html
+++ b/browser/devtools/debugger/test/browser_dbg_tab2.html
@@ -1,10 +1,11 @@
 <!DOCTYPE HTML>
 <html>
-	<head>
-		<title>Browser Debugger Test Tab 2</title>
+  <head>
+    <meta charset='utf-8'/>
+    <title>Browser Debugger Test Tab 2</title>
     <!-- Any copyright is dedicated to the Public Domain.
          http://creativecommons.org/publicdomain/zero/1.0/ -->
-	</head>
-	<body>
-	</body>
+  </head>
+  <body>
+  </body>
 </html>
--- a/browser/devtools/debugger/test/browser_dbg_update-editor-mode.html
+++ b/browser/devtools/debugger/test/browser_dbg_update-editor-mode.html
@@ -1,12 +1,13 @@
 <!DOCTYPE HTML>
 <html>
-	<head>
-		<title>Browser Debugger Update Editor Mode Test</title>
+  <head>
+    <meta charset='utf-8'/>
+    <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>
+  </head>
+  <body>
+  </body>
 </html>
--- a/browser/devtools/debugger/test/browser_dbg_update-editor-mode.js
+++ b/browser/devtools/debugger/test/browser_dbg_update-editor-mode.js
@@ -24,17 +24,17 @@ function test()
   let framesAdded = false;
 
   debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
     gTab = aTab;
     gDebuggee = aDebuggee;
     gPane = aPane;
     gDebugger = gPane.debuggerWindow;
 
-    gPane.activeThread.addOneTimeListener("framesadded", function() {
+    gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
       framesAdded = true;
       runTest();
     });
     gDebuggee.firstCall();
   });
 
   window.addEventListener("Debugger:ScriptShown", function _onEvent(aEvent) {
     let url = aEvent.detail.url;
@@ -51,17 +51,17 @@ function test()
       Services.tm.currentThread.dispatch({ run: testScriptsDisplay }, 0);
     }
   }
 }
 
 function testScriptsDisplay() {
   gScripts = gDebugger.DebuggerView.Scripts._scripts;
 
-  is(gDebugger.StackFrames.activeThread.state, "paused",
+  is(gDebugger.DebuggerController.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,
@@ -85,17 +85,17 @@ function testSwitchPaused()
     "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() {
+  gDebugger.DebuggerController.activeThread.resume(function() {
     closeDebuggerAndFinish(gTab);
   });
 }
 
 registerCleanupFunction(function() {
   removeTab(gTab);
   gPane = null;
   gTab = null;
--- a/browser/devtools/debugger/test/head.js
+++ b/browser/devtools/debugger/test/head.js
@@ -45,18 +45,18 @@ function addTab(aURL, aOnload)
   return tab;
 }
 
 function removeTab(aTab) {
   gBrowser.removeTab(aTab);
 }
 
 function closeDebuggerAndFinish(aTab) {
-  DebuggerUI.aWindow.addEventListener("Debugger:Shutdown", function cleanup() {
-    DebuggerUI.aWindow.removeEventListener("Debugger:Shutdown", cleanup, false);
+  DebuggerUI.chromeWindow.addEventListener("Debugger:Shutdown", function cleanup() {
+    DebuggerUI.chromeWindow.removeEventListener("Debugger:Shutdown", cleanup, false);
     finish();
   }, false);
   DebuggerUI.getDebugger(aTab).close();
 }
 
 function get_tab_actor_for_url(aClient, aURL, aCallback) {
   aClient.listTabs(function(aResponse) {
     for each (let tab in aResponse.tabs) {
@@ -91,17 +91,18 @@ function attach_thread_actor_for_url(aCl
 function debug_tab_pane(aURL, aOnDebugging)
 {
   let tab = addTab(aURL, function() {
     gBrowser.selectedTab = gTab;
 
     let debuggee = tab.linkedBrowser.contentWindow.wrappedJSObject;
 
     let pane = DebuggerUI.toggleDebugger();
-    pane.onConnected = function() {
+    pane._frame.addEventListener("Debugger:Connecting", function dbgConnected() {
+      pane._frame.removeEventListener("Debugger:Connecting", dbgConnected, true);
+
       // Wait for the initial resume...
       pane.debuggerWindow.gClient.addOneTimeListener("resumed", function() {
-        delete pane.onConnected;
         aOnDebugging(tab, debuggee, pane);
       });
-    };
+    }, true);
   });
 }
--- a/browser/devtools/highlighter/highlighter.jsm
+++ b/browser/devtools/highlighter/highlighter.jsm
@@ -41,16 +41,17 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 const Cu = Components.utils;
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 
+Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource:///modules/devtools/LayoutHelpers.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 var EXPORTED_SYMBOLS = ["Highlighter"];
 
 const INSPECTOR_INVISIBLE_ELEMENTS = {
   "head": true,
   "base": true,
@@ -72,17 +73,17 @@ const PSEUDO_CLASSES = [":hover", ":acti
  * The highlighter is built dynamically into the browser element.
  * The caller is in charge of destroying the highlighter (ie, the highlighter
  * won't be destroyed if a new tab is selected for example).
  *
  * API:
  *
  *   // Constructor and destructor.
  *   // @param aWindow - browser.xul window.
- *   Highlighter(aWindow); 
+ *   Highlighter(aWindow);
  *   void destroy();
  *
  *   // Highlight a node.
  *   // @param aNode - node to highlight
  *   // @param aScroll - scroll to ensure the node is visible
  *   void highlight(aNode, aScroll);
  *
  *   // Get the selected node.
@@ -248,17 +249,17 @@ Highlighter.prototype = {
   },
 
   /**
    * Notify that a pseudo-class lock was toggled on the highlighted element
    *
    * @param aPseudo - The pseudo-class to toggle, e.g. ":hover".
    */
   pseudoClassLockToggled: function Highlighter_pseudoClassLockToggled(aPseudo)
-  {  
+  {
     this.emitEvent("pseudoclasstoggled", [aPseudo]);
     this.updateInfobar();
   },
 
   /**
    * Update the highlighter size and position.
    */
   invalidateSize: function Highlighter_invalidateSize(aScroll)
@@ -425,19 +426,24 @@ Highlighter.prototype = {
   },
 
   /**
    * Build the node Infobar.
    *
    * <box id="highlighter-nodeinfobar-container">
    *   <box id="Highlighter-nodeinfobar-arrow-top"/>
    *   <hbox id="highlighter-nodeinfobar">
-   *     <xhtml:span id="highlighter-nodeinfobar-tagname"/>
-   *     <xhtml:span id="highlighter-nodeinfobar-id"/>
-   *     <xhtml:span id="highlighter-nodeinfobar-classes"/>
+   *     <toolbarbutton class="highlighter-nodeinfobar-button" id="highlighter-nodeinfobar-inspectbutton"/>
+   *     <hbox id="highlighter-nodeinfobar-text">
+   *       <xhtml:span id="highlighter-nodeinfobar-tagname"/>
+   *       <xhtml:span id="highlighter-nodeinfobar-id"/>
+   *       <xhtml:span id="highlighter-nodeinfobar-classes"/>
+   *       <xhtml:span id="highlighter-nodeinfobar-pseudo-classes"/>
+   *     </hbox>
+   *     <toolbarbutton class="highlighter-nodeinfobar-button" id="highlighter-nodeinfobar-menu"/>
    *   </hbox>
    *   <box id="Highlighter-nodeinfobar-arrow-bottom"/>
    * </box>
    *
    * @param nsIDOMElement aParent
    *        The container of the infobar.
    */
   buildInfobar: function Highlighter_buildInfobar(aParent)
@@ -461,27 +467,62 @@ Highlighter.prototype = {
     let tagNameLabel = this.chromeDoc.createElementNS("http://www.w3.org/1999/xhtml", "span");
     tagNameLabel.id = "highlighter-nodeinfobar-tagname";
 
     let idLabel = this.chromeDoc.createElementNS("http://www.w3.org/1999/xhtml", "span");
     idLabel.id = "highlighter-nodeinfobar-id";
 
     let classesBox = this.chromeDoc.createElementNS("http://www.w3.org/1999/xhtml", "span");
     classesBox.id = "highlighter-nodeinfobar-classes";
-    
+
     let pseudoClassesBox = this.chromeDoc.createElementNS("http://www.w3.org/1999/xhtml", "span");
     pseudoClassesBox.id = "highlighter-nodeinfobar-pseudo-classes";
-    
+
     // Add some content to force a better boundingClientRect down below.
     pseudoClassesBox.textContent = "&nbsp;";
 
-    nodeInfobar.appendChild(tagNameLabel);
-    nodeInfobar.appendChild(idLabel);
-    nodeInfobar.appendChild(classesBox);
-    nodeInfobar.appendChild(pseudoClassesBox);
+    // Create buttons
+
+    let inspect = this.chromeDoc.createElement("toolbarbutton");
+    inspect.id = "highlighter-nodeinfobar-inspectbutton";
+    inspect.className = "highlighter-nodeinfobar-button"
+    let toolbarInspectButton =
+      this.chromeDoc.getElementById("inspector-inspect-toolbutton");
+    inspect.setAttribute("tooltiptext",
+                         toolbarInspectButton.getAttribute("tooltiptext"));
+    inspect.setAttribute("command", "Inspector:Inspect");
+
+    let nodemenu = this.chromeDoc.createElement("toolbarbutton");
+    nodemenu.setAttribute("type", "menu");
+    nodemenu.id = "highlighter-nodeinfobar-menu";
+    nodemenu.className = "highlighter-nodeinfobar-button"
+    nodemenu.setAttribute("tooltiptext",
+                          this.strings.GetStringFromName("nodeMenu.tooltiptext"));
+
+    let menu = this.chromeDoc.getElementById("inspector-node-popup");
+    menu = menu.cloneNode(true);
+    menu.id = "highlighter-node-menu";
+
+    nodemenu.appendChild(menu);
+
+    // <hbox id="highlighter-nodeinfobar-text"/>
+    let texthbox = this.chromeDoc.createElement("hbox");
+    texthbox.id = "highlighter-nodeinfobar-text";
+    texthbox.setAttribute("align", "center");
+    texthbox.setAttribute("flex", "1");
+
+    texthbox.appendChild(tagNameLabel);
+    texthbox.appendChild(idLabel);
+    texthbox.appendChild(classesBox);
+    texthbox.appendChild(pseudoClassesBox);
+
+    nodeInfobar.appendChild(inspect);
+    nodeInfobar.appendChild(texthbox);
+    nodeInfobar.appendChild(nodemenu);
+
     container.appendChild(arrowBoxTop);
     container.appendChild(nodeInfobar);
     container.appendChild(arrowBoxBottom);
 
     aParent.appendChild(container);
 
     nodeInfobar.onclick = (function _onInfobarRightClick(aEvent) {
       if (aEvent.button == 2) {
@@ -506,23 +547,23 @@ Highlighter.prototype = {
    */
   openPseudoClassMenu: function Highlighter_openPseudoClassMenu()
   {
     let menu = this.chromeDoc.createElement("menupopup");
     menu.id = "infobar-context-menu";
 
     let popupSet = this.chromeDoc.getElementById("mainPopupSet");
     popupSet.appendChild(menu);
-    
+
     let fragment = this.buildPseudoClassMenu();
     menu.appendChild(fragment);
 
     menu.openPopup(this.nodeInfo.pseudoClassesBox, "end_before", 0, 0, true, false);
-  },  
-  
+  },
+
   /**
    * Create the menuitems for toggling the selection's pseudo-class state
    *
    * @returns DocumentFragment. The menuitems for toggling pseudo-classes.
    */
   buildPseudoClassMenu: function IUI_buildPseudoClassesMenu()
   {
     let fragment = this.chromeDoc.createDocumentFragment();
@@ -882,8 +923,14 @@ Highlighter.prototype = {
   },
 };
 
 ///////////////////////////////////////////////////////////////////////////
 
 XPCOMUtils.defineLazyGetter(this, "DOMUtils", function () {
   return Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils)
 });
+
+XPCOMUtils.defineLazyGetter(Highlighter.prototype, "strings",
+  function () {
+    return Services.strings.createBundle(
+            "chrome://browser/locale/devtools/inspector.properties");
+  });
--- a/browser/devtools/highlighter/inspector.jsm
+++ b/browser/devtools/highlighter/inspector.jsm
@@ -23,16 +23,17 @@
  * Contributor(s):
  *   Rob Campbell <rcampbell@mozilla.com> (original author)
  *   Mihai Șucan <mihai.sucan@gmail.com>
  *   Julian Viereck <jviereck@mozilla.com>
  *   Paul Rouget <paul@mozilla.com>
  *   Kyle Simpson <ksimpson@mozilla.com>
  *   Johan Charlez <johan.charlez@gmail.com>
  *   Mike Ratcliffe <mratcliffe@mozilla.com>
+ *   Murali S R <murali.sr92@yahoo.com>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
@@ -1921,39 +1922,39 @@ HTMLBreadcrumbs.prototype = {
           }
         })(nodes[i]);
 
         fragment.appendChild(item);
       }
     }
     this.menu.appendChild(fragment);
     this.menu.openPopup(aButton, "before_start", 0, 0, true, false);
+    aButton.setAttribute("siblings-menu-open", "true");
   },
 
   /**
    * Generic event handler.
    *
    * @param nsIDOMEvent aEvent
    *        The DOM event object.
    */
   handleEvent: function BC_handleEvent(aEvent)
   {
-    if (aEvent.type == "mousedown") {
+    if (aEvent.type == "mousedown" && aEvent.button == 0) {
       // on Click and Hold, open the Siblings menu
 
       let timer;
       let container = this.container;
       let window = this.IUI.win;
 
       function openMenu(aEvent) {
         cancelHold();
         let target = aEvent.originalTarget;
         if (target.tagName == "button") {
           target.onBreadcrumbsHold();
-          target.setAttribute("siblings-menu-open", "true");
         }
       }
 
       function handleClick(aEvent) {
         cancelHold();
         let target = aEvent.originalTarget;
         if (target.tagName == "button") {
           target.onBreadcrumbsClick();
--- a/browser/devtools/jar.mn
+++ b/browser/devtools/jar.mn
@@ -8,11 +8,10 @@ browser.jar:
     content/browser/styleeditor.css               (styleeditor/styleeditor.css)
     content/browser/devtools/csshtmltree.xul      (styleinspector/csshtmltree.xul)
     content/browser/devtools/cssruleview.xul      (styleinspector/cssruleview.xul)
     content/browser/devtools/styleinspector.css   (styleinspector/styleinspector.css)
     content/browser/orion.js                      (sourceeditor/orion/orion.js)
 *   content/browser/source-editor-overlay.xul     (sourceeditor/source-editor-overlay.xul)
 *   content/browser/debugger.xul                  (debugger/debugger.xul)
     content/browser/debugger.css                  (debugger/debugger.css)
-    content/browser/debugger.js                   (debugger/debugger.js)
+    content/browser/debugger-controller.js        (debugger/debugger-controller.js)
     content/browser/debugger-view.js              (debugger/debugger-view.js)
-
--- a/browser/devtools/webconsole/GcliCommands.jsm
+++ b/browser/devtools/webconsole/GcliCommands.jsm
@@ -224,18 +224,18 @@ gcli.addCommand({
       name: "file",
       type: {
         name: "selection",
         data: function() {
           let win = HUDService.currentContext();
           let dbg = win.DebuggerUI.getDebugger(win.gBrowser.selectedTab);
           let files = [];
           if (dbg) {
-            let scriptsView = dbg.frame.contentWindow.DebuggerView.Scripts;
-            for each (let script in scriptsView.scriptLocations()) {
+            let scriptsView = dbg.debuggerWindow.DebuggerView.Scripts;
+            for each (let script in scriptsView.scriptLocations) {
               files.push(script);
             }
           }
           return files;
         }
       },
       description: gcli.lookup("breakaddlineFileDesc")
     },
--- a/browser/devtools/webconsole/test/browser_gcli_break.js
+++ b/browser/devtools/webconsole/test/browser_gcli_break.js
@@ -64,41 +64,42 @@ function testCreateCommands() {
 
   type("break add");
   is(requisition.getStatus().toString(), "ERROR", "break add is ERROR");
 
   type("break add line");
   is(requisition.getStatus().toString(), "ERROR", "break add line is ERROR");
 
   let pane = DebuggerUI.toggleDebugger();
-  pane.onConnected = function test_onConnected(aPane) {
+  pane._frame.addEventListener("Debugger:Connecting", function dbgConnected() {
+    pane._frame.removeEventListener("Debugger:Connecting", dbgConnected, true);
+
     // Wait for the initial resume.
-    aPane.debuggerWindow.gClient.addOneTimeListener("resumed", function() {
-      delete aPane.onConnected;
-      aPane.debuggerWindow.gClient.activeThread.addOneTimeListener("framesadded", function() {
+    pane.debuggerWindow.gClient.addOneTimeListener("resumed", function() {
+      pane.debuggerWindow.gClient.activeThread.addOneTimeListener("framesadded", function() {
         type("break add line " + TEST_URI + " " + content.wrappedJSObject.line0);
         is(requisition.getStatus().toString(), "VALID", "break add line is VALID");
         requisition.exec();
 
         type("break list");
         is(requisition.getStatus().toString(), "VALID", "break list is VALID");
         requisition.exec();
 
-        aPane.debuggerWindow.gClient.activeThread.resume(function() {
+        pane.debuggerWindow.gClient.activeThread.resume(function() {
           type("break del 0");
           is(requisition.getStatus().toString(), "VALID", "break del 0 is VALID");
           requisition.exec();
 
           closeConsole();
           finishTest();
         });
       });
       // Trigger newScript notifications using eval.
       content.wrappedJSObject.firstCall();
     });
-  }
+  }, true);
 }
 
 function type(command) {
   gcliterm.inputNode.value = command.slice(0, -1);
   gcliterm.inputNode.focus();
   EventUtils.synthesizeKey(command.slice(-1), {});
 }
--- a/browser/locales/en-US/chrome/browser/devtools/inspector.properties
+++ b/browser/locales/en-US/chrome/browser/devtools/inspector.properties
@@ -27,8 +27,14 @@ ruleView.accesskey=R
 ruleView.tooltiptext=View and Edit CSS
 
 # LOCALIZATION NOTE (inspectButton.tooltiptext):
 # This button appears in the Inspector Toolbar. inspectButton is stateful,
 # if it's pressed users can select an element with the mouse. Pressing the
 # "Return" key # changes that state. %S is the keyboard shortcut (VK_RETURN in
 # chrome://global/locale/keys.properties).
 inspectButton.tooltiptext=Select element with mouse (%S)
+
+
+# LOCALIZATION NOTE (nodeMenu.tooltiptext)
+# This menu appears in the Infobar (on top of the highlighted node) once
+# the node is selected.
+nodeMenu.tooltiptext=Node operations
--- a/browser/modules/webappsUI.jsm
+++ b/browser/modules/webappsUI.jsm
@@ -121,13 +121,13 @@ let webappsUI = {
       host = requestingURI.host;
     } catch(e) {
       host = requestingURI.spec;
     }
 
     let message = bundle.getFormattedString("webapps.requestInstall",
                                             [manifest.name, host], 2);
 
-    aWindow.PopupNotifications.show(aBrowser, "webapps-install", message, "webapps-notification-icon",
-                                                       mainAction, null, { popupIconURL: manifest.iconURLForSize(64) });
+    aWindow.PopupNotifications.show(aBrowser, "webapps-install", message,
+                                    "webapps-notification-icon", mainAction);
 
   }
 }
--- a/browser/themes/gnomestripe/browser.css
+++ b/browser/themes/gnomestripe/browser.css
@@ -2022,39 +2022,88 @@ panel[dimmed="true"] {
 }
 
 #devtools-sidebar-box {
   background-color: -moz-Field;
 }
 
 /* Highlighter - Node Infobar */
 
+#highlighter-nodeinfobar {
+  color: hsl(200, 100%, 65%);
+  border: 1px solid hsla(210, 19%, 63%, .5);
+  border-radius: 3px;
+  background: -moz-linear-gradient(hsl(209, 18%, 30%), hsl(210, 24%, 16%)) no-repeat padding-box;
+}
+
 /* Highlighter - Node Infobar - text */
 
+#highlighter-nodeinfobar-text {
+  /* 100% - size of the buttons and margins */
+  max-width: -moz-calc(100% - 2 * (26px + 6px));
+  padding-bottom: 1px;
+}
+
 html|*#highlighter-nodeinfobar-tagname {
   color: white;
 }
 
 html|*#highlighter-nodeinfobar-id {
   color: hsl(90, 79%, 52%);
 }
 
 html|*#highlighter-nodeinfobar-pseudo-classes {
   color: hsl(20, 100%, 70%);
 }
 
-/* Highlighter - Node Infobar - box & arrow */
+/* Highlighter - Node Infobar - buttons */
+
+.highlighter-nodeinfobar-button {
+  -moz-appearance: none;
+  border: 0 solid hsla(210,8%,5%,.45);
+  padding: 0;
+  width: 26px;
+  min-height: 26px;
+}
+
+#highlighter-nodeinfobar-inspectbutton {
+  -moz-border-end-width: 1px;
+  box-shadow: 1px 0 0 hsla(210,16%,76%,.15), -1px 0 0 hsla(210,16%,76%,.15) inset;
+  -moz-margin-end: 6px;
+  list-style-image: url("chrome://browser/skin/devtools/inspect-button.png");
+  -moz-image-region: rect(0px 16px 16px 0px);
+}
+
+#highlighter-nodeinfobar-inspectbutton:-moz-locale-dir(rtl) {
+  box-shadow: -1px 0 0 hsla(210,16%,76%,.15), 1px 0 0 hsla(210,16%,76%,.15) inset;
+}
 
-#highlighter-nodeinfobar {
-  color: hsl(200, 100%, 65%);
-  border: 1px solid hsla(210, 19%, 63%, .5);
-  border-radius: 3px;
-  padding: 8px 16px;
-  background: -moz-linear-gradient(hsl(209, 18%, 30%), hsl(210, 24%, 16%)) no-repeat padding-box;
-}
+#highlighter-nodeinfobar-inspectbutton:active:hover,
+#highlighter-nodeinfobar-container:not([locked]) >  #highlighter-nodeinfobar >  #highlighter-nodeinfobar-inspectbutton {
+  -moz-image-region: rect(0px 32px 16px 16px);
+}
+
+#highlighter-nodeinfobar-menu {
+  -moz-border-start-width: 1px;
+  box-shadow: -1px 0 0 hsla(210,16%,76%,.15), 1px 0 0 hsla(210,16%,76%,.15) inset;
+  -moz-margin-start: 6px;
+}
+
+#highlighter-nodeinfobar-menu:-moz-locale-dir(rtl) {
+  box-shadow: 1px 0 0 hsla(210,16%,76%,.15), -1px 0 0 hsla(210,16%,76%,.15) inset;
+}
+
+#highlighter-nodeinfobar-menu > .toolbarbutton-menu-dropmarker {
+  -moz-appearance: none !important;
+  list-style-image: url("chrome://browser/skin/devtools/dropmarker.png");
+  -moz-box-align: center;
+  -moz-margin-start: -1px;
+}
+
+/* Highlighter - Node Infobar - box & arrow */
 
 .highlighter-nodeinfobar-arrow {
   width: 14px;
   height: 14px;
   -moz-margin-start: -moz-calc(50% - 7px);
   -moz-transform: rotate(-45deg);
   border: 1px solid transparent;
   background-clip: padding-box;
new file mode 100644
index 0000000000000000000000000000000000000000..7e91860fb5bccc230d94b2ebc5b549b9e2637087
GIT binary patch
literal 218
zc%17D@N?(olHy`uVBq!ia0vp^>_E)I!3HFqj;YoHDaPU;cPEB*=VV?2Ic!PZ?k)`f
zL2$v|<&%LToCO|{#S9GG!XV7ZFl&wk$dD3GUsv`!OiaAI7H_8Rm=6?^%?ybsan8@p
zP0cG|00HNs)Wnk16ovB4k_-iRPv3wPy;P9phMq2tAsp9}@9eGq?!?B{X2IsOp<`lW
uV<TgClk2*5>pu1v$W4N<B%V2l9$;kPuNGKg&%W<B$W%{PKbLh*2~7Y420teN
--- a/browser/themes/gnomestripe/jar.mn
+++ b/browser/themes/gnomestripe/jar.mn
@@ -126,16 +126,17 @@ browser.jar:
   skin/classic/browser/devtools/splitview.css         (devtools/splitview.css)
   skin/classic/browser/devtools/styleeditor.css       (devtools/styleeditor.css)
   skin/classic/browser/devtools/debugger.css          (devtools/debugger.css)
   skin/classic/browser/devtools/magnifying-glass.png  (devtools/magnifying-glass.png)
   skin/classic/browser/devtools/itemToggle.png        (devtools/itemToggle.png)
   skin/classic/browser/devtools/itemArrow-rtl.png     (devtools/itemArrow-rtl.png)
   skin/classic/browser/devtools/itemArrow-ltr.png     (devtools/itemArrow-ltr.png)
   skin/classic/browser/devtools/inspect-button.png    (devtools/inspect-button.png)
+  skin/classic/browser/devtools/dropmarker.png        (devtools/dropmarker.png)
 #ifdef MOZ_SERVICES_SYNC
   skin/classic/browser/sync-16-throbber.png
   skin/classic/browser/sync-16.png
   skin/classic/browser/sync-24-throbber.png
   skin/classic/browser/sync-32.png
   skin/classic/browser/sync-bg.png
   skin/classic/browser/sync-128.png
   skin/classic/browser/sync-desktopIcon.png
index cf59fa3ca1e903968f793d940b2dc651f6ac4e1f..df3978c4c6fe2623b011ce3c90f77316d158190e
GIT binary patch
literal 475
zc$@*-0VMv3P)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV0004^Nkl<Zc-p0q
zO(;ZB9K|1HqwL-ro|!S;%zO9Ad|{<2WkE#BXZc97x3;jhu|bkznlUpA$zm3R4HjZC
zMMC4F6lQ#^)DR2HKj$hTH&br?>b!GKr~9r%ME~O3$JevuQ_ZMfxd6~#qTB{T9yz<y
zX(tf!%GvY?%Y7i`DV^GEwNYlYngb2*pv9Bhz@l3@JMP!$_3n^!)TdF{ZAsx4e-5k%
z)T)@za-HgR((U9h2Ri!htp(J&+W{sXSn-z>MD&u_Zlj%Uh8SgrIb82H*a;ar!hxVm
zc?~*B>AcmT%XUUrA3$?2qFD(Y;lR8m)2dge!+L{KO^i;OIb0`e4W6MR99VG4v{hr!
zvgV?_I+ON4;5zQqd4`Vo7j&7D0Y+=4PBGm|3Acl@%xn}lTs%WZI1n;qO8CoYMpd%Y
zT5)LFrfdUfc*xKZzkx$}nzbvZLu!%lalCTwV#4$SXn4rb5e_(|JZrg>C#O4D;r&>}
z#`C&2383L2Lq|05NxF+knL)GdAzb+apyB=Oi3fU^l<sEvnSRy=pyB=2eFN#WI?#vV
RJ%<1Q002ovPDHLkV1oFW;bZ^+
index 442132d987b04e37e7cd24b45785e7b605be2e32..7c5e48cb8bea63eb814b30fd96cb55762bb1753c
GIT binary patch
literal 2942
zc$@)#3xV{BP)<h;3K|Lk000e1NJLTq002M$002M;1^@s6s%dfF0000PbVXQnQ*UN;
zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU-AW1|)RCwCeTWf3_R~5eZ&SSmy+TN{W
zJ5Q;kg{0u5^o0~a3Z*!u5Ksc76;caA6(A^oDjI(93lNoB+De5#6@O5XKqVkm<sk(L
zq5i1EQ-BIeqQ+{IN~s@-li1$f+1=Thxtwq2&hE_YdgIu$liJeB-aYr6?>z1~ckaw=
z1k3)96A2=n$WWv!9?#ktaRY+ap5Hfq%gF;f&j0bi4jY6qc$mvN2_oy@pS*3$t$PaQ
z>k~W79eeK{Vjzsc!(7%$5LpL*XEFEhiJfMCY`eiYaX}b^hq<hikW~W2!@EX`cZ}x#
zj6k<+3xfBpiy#T$VGc5o&B`~CkqzVX+X_!nklPU`IFf>vOp!_k<{$%EwK<B-o&dk}
zpPL%Ly;#s5o!n*m92vb9sJ%brK?btW$)XiR1_?IbKWv^Xj_d7|utZo19%P{d-G2Kh
z`n>x3OLuR5tTb+vN)twRi7x94S?EAloe}tEOn~-<?S;{zp?$9uu0B&uAAUwDI)@~X
z4s?N`T47Tqz(46dv}5@A*o2YK<wY{S?g@wtp#xoDL{5sECjm#c6-EoXcx=n05erlj
zaT(OKv>QJ8ouKBTvd{$vur^Zy{N5+0504i0yrywKxag`QP<MHmjePi+PZ-NYH9!Zt
zzyMai_5#tb@}JCU&y>dWR^ZVjAmGWbKFvx;kNbo%c+ud)F))DDuQm{USbgo;4~*ZH
z9}uO%A+6oiUJ#g9tFoIu`*k+B|31cR3qD~C9_IYuy>_yI0W4tl<6O{>1n9R68IPB4
zGCDUo$76bagcYYBWvn_+-|GR<7(C2DCb>xh3z+?AgR~PM4sRVA8PNElkx{*4r}r8b
z8$0?9#u^J0sKr=Q5yr&B9AuJuzyc<))Am`Uz4~;HKQcO^=LJs$Pb;ep71`jO_cCTJ
zQQ*}$5f5{afoxP4Sil5!+EpM@PNp_yYF{iCJ6E4OO*V4ie#UB-f)#hE^raOL#^BK$
zWFVW=2PUx9J|Nz$2`~)7@5$%1jy{%+CL2EZX>#9Ta!_N9^QW2p_KZ&$gNHfDKsLF5
z02A10-zAC^%U`J7KPZ1M5&*6Mxjei4^XJ%G-+F|dfBb7~?!}+7*`K}O6UN|S4l<BU
z>H`zl>S~bM^TRX~pzX}-pBf(0+t=~v!A5AznmNn#;Zf@IVJ6IcjKISjWFVWYAYcL;
zHqyKtrka2ujZY5`YVCthcuiNU*)6bciR#c(^T39UcUuDd-T#SOOYa%b#lE4Oh^<;j
zBD_R<^q7z769F4GU@NVwfk<QJf3SCK_kH7oe=8UUlMZ%#B9~^|_-#Lr-VP=MX6<8e
z>@k5GU;z`@umM}JnMNgujRudWxc8I>{^hP1`GRiHHJk-xW9KJ{8c|h!(sy20WG6`R
zSwaUGzzT6;1GZo@jVH)rqru}*HmjL=r(&=sUA;V7P0v+CW7m!F5JWQ|X~!w#`!d~h
z)fhHl3pUeuj4n1Byw))Dyk_`J+YTIcy?{c;T@_47p4XOX$tGkv^woFR@LPmz!uCd!
zAT|=b;h1^RG+IsDY|2(JJ;_~#uA%38fsN1}Y{Q3*Btfh<_yK9=4cBN{!$HNS9`<D1
zVRw~4+*PWen_P=00~><39fA+=WxYud>j{34n|V{VS3Z2pQ>P6}b_r%pRdEd+d<Qmc
zgm05V`|t%mttScBlK_KWTm7M+n{*;I8IL~I=E15s)Spc!v3j!is`so08UGG!*brgj
zBR;?<_y!-<{X(h<@E>!zQd*|aTS2p^BRsIzs0S<Mxn25wtrd{eaRW?XGi9rr5AY2>
zUeDvR>uLe1DlZ;*n{dvlSFv)O91qC#+GEdMBX!dyOkm4|&7?nSi%h)*VlT6jf%_za
z(zGm90bY+)m#fff)u8$x*h1Oru)m70>7N54*^ZSU_5muqT1~dD^>A8tYk_NB2Gw`z
zwv!1=V8cdq1MKjn?+2l9*GC^FRzc7|aJp{E_=@&g;a(LI`3_8Ai!OFUUouGmU#$hg
zBvAKNE4Zma65@%FYovH0q>lwmV6SF(o$;?D0qS-wEIZ$@i0lI*SS`m11EG~u<RvLg
zQi30sXFe7%fgQCK^&zNCnOzGcH@{W|&=<I2Rf5}Gxt7b_D)Um^Dgz6cz;3r&=Vq8)
z3$%>#cp}sa=|omlb_65W(*|CErHi<Yu0CYb0;`Y+D}X+=Ac$&322X_7atTaqd{R9X
z0tUvw6e4b`8)ei2vUMKkBGLw766me2hD)NQj>ar_BP`__R$s|M2fDxj7BFKi7pa86
z7kMLEAYOt(w270Kdck20!(vU*yzYY#bf60iV8yVzm4B8k5Z?TB>;G_7w1UlKp|SAd
z?DEmNXe>B7ednG}On6k%aRM)#hb(lU3k+bzsx7e(i0EBuR(k-RUJ6x?BH{>mEN?R>
zXU~88)Z!;kyi@+)6Q>sMzsei0u9(&v%Z7DkNwdye6>$O&bC7{7bf60iU;$HzgsspX
z`I6lpz~Vu-N>QzfYud~<?D^xT77v}RkmHOky-~5}9{lIrmyY~>_JO1SnEgyIf(&G#
z16^PM3z)!eS6Td$DAxH{bmMzKr1YB_O4pwM&Z))eGi96JfwMBvWul8j7l_Ugy?G5f
zr(`cG-7>I%32fNliFV6$4`8v&o@}i^gW1etLGY)`_C@tzyYZ2t$sd@=i^G$7?Zly-
z!@qiX=irHN?i$!8n1~X1{v63b7CO*X56Y{+1U78IR=Waa`)ZKo9?&`h6IHzit9bF?
zhv|0yj$+RH<@C+`g@e2Jk3YI|<iPHsse`u<@Bi3c!|$E*m=`7RFb5gPLI=9Q09G^!
zumzi~Dqz{110-dAXtB~@b1OBrP^qy?R*hY$TdeGKuKezioc+?rcS&W#)CAuy@_fHG
z$fvX%-<kvm4|Bc@$ujjbSzrJwSqZQS+wcLtz$f?yA6eojiD@JNfX-j9)W33azVf->
z&X$k8GFyJ|_jBcAf4W$G=)0#c-+Maf`Kof%eSNv%4QL|#uQ54VrYUZ+eLTmfIKAZU
zs4U|7b0h;<Ux##o0W9^RElk1!Y{E8tfG_X~zQITMy4EDDH39y??8&oN{_~@mtFJx(
z=F)3Fd27l4d}`*(tN&eTV7IS?!Nd6ZWA6*QGEBfWZm_*P$M%xdsV<&OVGc5o4HEzi
zg(V|NsK6#{M}2{B@DaYo-#DgJ0eIx60$m|mR-a|H@^%<}@>!xEjARqkq`N7=-TJtg
zp8Mf#9}0yq1`l(P@nwSqB%ketNwAg8a>AF2@-g)a2n{#Y3e+RiP<*$CQQRZHBOO_w
zGjD;yDh+&4|Nh&L?AZ#!7(C4RG9=q$Pv5978@dYg_-R8=c{nsf53D&hZ)r=eP4cPH
zFPpyrVGN!>XZeB8M1qhh`qS4RNnLBTE_gM0&Z|%oEPBS>priB%-vh!JJb#X4)IsRF
zR`N#wM5g2K=HHx+u70q8)aA?GlH{^1xm%G!);tdK$Hc=NWFmd+CqQ9st^{=1v}#_B
z{m#8CFF9A}-fxKlUzR>$3?AknqkbY`Z|Vw2UP9FdWqHfq)6TpU&Lv91qVx%4@Gu9N
z$Y$pC+qu3bAkhXv0xIk;?nUoe=Yl7iv(hJw!NXi60evt3tSTV<4Va@!yF$JGvNPvB
z(>(9hKp2CkuH!W&>-D?*Gf9A2!7u@~THzI0W54#6<tIQGtAmWCWc&p5d-b!bfMgOH
o>J*35+f_x&k^6|ug2&(g0r?4Ut=mXi?EnA(07*qoM6N<$g398TZ~y=R
--- a/browser/themes/pinstripe/browser.css
+++ b/browser/themes/pinstripe/browser.css
@@ -2767,40 +2767,89 @@ panel[dimmed="true"] {
 }
 
 #devtools-sidebar-box {
   background-color: -moz-Field;
 }
 
 /* Highlighter - Node Infobar */
 
+#highlighter-nodeinfobar {
+  color: hsl(200, 100%, 65%);
+  border: 1px solid hsla(210, 19%, 63%, .5);
+  border-radius: 3px;
+  background: -moz-linear-gradient(hsl(209, 18%, 30%), hsl(210, 24%, 16%)) no-repeat padding-box;
+}
+
 /* Highlighter - Node Infobar - text */
 
+#highlighter-nodeinfobar-text {
+  /* 100% - size of the buttons + margins */
+  max-width: -moz-calc(100% - 2 * (26px + 6px));
+  padding-bottom: 1px;
+}
+
 html|*#highlighter-nodeinfobar-tagname {
   color: white;
 }
 
 html|*#highlighter-nodeinfobar-id {
   color: hsl(90, 79%, 52%);
 }
 
 html|*#highlighter-nodeinfobar-pseudo-classes {
   color: hsl(20, 100%, 70%);
 }
 
+/* Highlighter - Node Infobar - buttons */
+
+.highlighter-nodeinfobar-button {
+  -moz-appearance: none;
+  border: 0 solid hsla(210,8%,5%,.45);
+  padding: 0;
+  width: 26px;
+  min-height: 26px;
+}
+
+#highlighter-nodeinfobar-inspectbutton {
+  -moz-border-end-width: 1px;
+  box-shadow: 1px 0 0 hsla(210,16%,76%,.15), -1px 0 0 hsla(210,16%,76%,.15) inset;
+  -moz-margin-end: 6px;
+  list-style-image: url("chrome://browser/skin/devtools/inspect-button.png");
+  -moz-image-region: rect(0px 16px 16px 0px);
+}
+
+#highlighter-nodeinfobar-inspectbutton:-moz-locale-dir(rtl) {
+  box-shadow: -1px 0 0 hsla(210,16%,76%,.15), 1px 0 0 hsla(210,16%,76%,.15) inset;
+}
+
+#highlighter-nodeinfobar-inspectbutton:active:hover,
+#highlighter-nodeinfobar-container:not([locked]) >  #highlighter-nodeinfobar >  #highlighter-nodeinfobar-inspectbutton {
+  -moz-image-region: rect(0px 32px 16px 16px);
+}
+
+#highlighter-nodeinfobar-menu {
+  -moz-border-start-width: 1px;
+  box-shadow: -1px 0 0 hsla(210,16%,76%,.15), 1px 0 0 hsla(210,16%,76%,.15) inset;
+  -moz-margin-start: 6px;
+}
+
+#highlighter-nodeinfobar-menu:-moz-locale-dir(rtl) {
+  box-shadow: 1px 0 0 hsla(210,16%,76%,.15), -1px 0 0 hsla(210,16%,76%,.15) inset;
+}
+
+#highlighter-nodeinfobar-menu > .toolbarbutton-menu-dropmarker {
+  -moz-appearance: none !important;
+  list-style-image: url("chrome://browser/skin/devtools/dropmarker.png");
+  -moz-box-align: center;
+  -moz-margin-start: -1px;
+}
+
 /* Highlighter - Node Infobar - box & arrow */
 
-#highlighter-nodeinfobar {
-  color: hsl(200, 100%, 65%);
-  border: 1px solid hsla(210, 19%, 63%, .5);
-  border-radius: 3px;
-  padding: 8px 16px;
-  background: -moz-linear-gradient(hsl(209, 18%, 30%), hsl(210, 24%, 16%)) no-repeat padding-box;
-}
-
 .highlighter-nodeinfobar-arrow {
   width: 14px;
   height: 14px;
   -moz-margin-start: -moz-calc(50% - 7px);
   -moz-transform: rotate(-45deg);
   border: 1px solid transparent;
   background-clip: padding-box;
   background-repeat: no-repeat;
new file mode 100644
index 0000000000000000000000000000000000000000..7e91860fb5bccc230d94b2ebc5b549b9e2637087
GIT binary patch
literal 218
zc%17D@N?(olHy`uVBq!ia0vp^>_E)I!3HFqj;YoHDaPU;cPEB*=VV?2Ic!PZ?k)`f
zL2$v|<&%LToCO|{#S9GG!XV7ZFl&wk$dD3GUsv`!OiaAI7H_8Rm=6?^%?ybsan8@p
zP0cG|00HNs)Wnk16ovB4k_-iRPv3wPy;P9phMq2tAsp9}@9eGq?!?B{X2IsOp<`lW
uV<TgClk2*5>pu1v$W4N<B%V2l9$;kPuNGKg&%W<B$W%{PKbLh*2~7Y420teN
--- a/browser/themes/pinstripe/jar.mn
+++ b/browser/themes/pinstripe/jar.mn
@@ -167,16 +167,17 @@ browser.jar:
   skin/classic/browser/devtools/styleeditor.css             (devtools/styleeditor.css)
   skin/classic/browser/devtools/debugger.css                (devtools/debugger.css)
   skin/classic/browser/devtools/magnifying-glass.png        (devtools/magnifying-glass.png)
   skin/classic/browser/devtools/itemToggle.png              (devtools/itemToggle.png)
   skin/classic/browser/devtools/itemArrow-rtl.png           (devtools/itemArrow-rtl.png)
   skin/classic/browser/devtools/itemArrow-ltr.png           (devtools/itemArrow-ltr.png)
   skin/classic/browser/devtools/background-noise-toolbar.png (devtools/background-noise-toolbar.png)
   skin/classic/browser/devtools/inspect-button.png          (devtools/inspect-button.png)
+  skin/classic/browser/devtools/dropmarker.png              (devtools/dropmarker.png)
 #ifdef MOZ_SERVICES_SYNC
   skin/classic/browser/sync-throbber.png
   skin/classic/browser/sync-16.png
   skin/classic/browser/sync-32.png
   skin/classic/browser/sync-bg.png
   skin/classic/browser/sync-128.png
   skin/classic/browser/sync-desktopIcon.png
   skin/classic/browser/sync-mobileIcon.png
index cf59fa3ca1e903968f793d940b2dc651f6ac4e1f..40c73f707903d93780a109d624245c5c345206fb
GIT binary patch
literal 348
zc%17D@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Ea{HEjtmSN`?>!lvI6;x#X;^)
z4C~IxyaaMM3p^r=85sBugD~Uq{1qucL8%hgh?3y^w370~qEv=}#LT=BJwMkFg)(D3
zQ$0figD*u3fvQe>x;TbdoL+j_kn50xKx?A3V9u5gY+>^pbdG3Oh#$P>%h4zh<`j_>
z#Cc8WMl{=-w`=z{DOv10Dtf8MujFB~<fcEN+Z<hb>o>@EEqdYf@=F%`C!70Bmb;@G
zbxyZ^U|1#5$$V@EufUo!uWwZ9@-4}%YP9BAz;ABR&Ez>rL*dw)WQXj%48M}ju|2X-
z<1`a+74+QDXuz}b(Wx^v3LH#Dd<JXZl-=83Zua1}-9b*PJo&>%&gN>omA$zBO=TQ+
npWo(h(Ho`&d0(*K>$XyT=AvW&^UoxLf{Ve^)z4*}Q$iB}=y`&0
index 442132d987b04e37e7cd24b45785e7b605be2e32..7c5e48cb8bea63eb814b30fd96cb55762bb1753c
GIT binary patch
literal 2942
zc$@)#3xV{BP)<h;3K|Lk000e1NJLTq002M$002M;1^@s6s%dfF0000PbVXQnQ*UN;
zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU-AW1|)RCwCeTWf3_R~5eZ&SSmy+TN{W
zJ5Q;kg{0u5^o0~a3Z*!u5Ksc76;caA6(A^oDjI(93lNoB+De5#6@O5XKqVkm<sk(L
zq5i1EQ-BIeqQ+{IN~s@-li1$f+1=Thxtwq2&hE_YdgIu$liJeB-aYr6?>z1~ckaw=
z1k3)96A2=n$WWv!9?#ktaRY+ap5Hfq%gF;f&j0bi4jY6qc$mvN2_oy@pS*3$t$PaQ
z>k~W79eeK{Vjzsc!(7%$5LpL*XEFEhiJfMCY`eiYaX}b^hq<hikW~W2!@EX`cZ}x#
zj6k<+3xfBpiy#T$VGc5o&B`~CkqzVX+X_!nklPU`IFf>vOp!_k<{$%EwK<B-o&dk}
zpPL%Ly;#s5o!n*m92vb9sJ%brK?btW$)XiR1_?IbKWv^Xj_d7|utZo19%P{d-G2Kh
z`n>x3OLuR5tTb+vN)twRi7x94S?EAloe}tEOn~-<?S;{zp?$9uu0B&uAAUwDI)@~X
z4s?N`T47Tqz(46dv}5@A*o2YK<wY{S?g@wtp#xoDL{5sECjm#c6-EoXcx=n05erlj
zaT(OKv>QJ8ouKBTvd{$vur^Zy{N5+0504i0yrywKxag`QP<MHmjePi+PZ-NYH9!Zt
zzyMai_5#tb@}JCU&y>dWR^ZVjAmGWbKFvx;kNbo%c+ud)F))DDuQm{USbgo;4~*ZH
z9}uO%A+6oiUJ#g9tFoIu`*k+B|31cR3qD~C9_IYuy>_yI0W4tl<6O{>1n9R68IPB4
zGCDUo$76bagcYYBWvn_+-|GR<7(C2DCb>xh3z+?AgR~PM4sRVA8PNElkx{*4r}r8b
z8$0?9#u^J0sKr=Q5yr&B9AuJuzyc<))Am`Uz4~;HKQcO^=LJs$Pb;ep71`jO_cCTJ
zQQ*}$5f5{afoxP4Sil5!+EpM@PNp_yYF{iCJ6E4OO*V4ie#UB-f)#hE^raOL#^BK$
zWFVW=2PUx9J|Nz$2`~)7@5$%1jy{%+CL2EZX>#9Ta!_N9^QW2p_KZ&$gNHfDKsLF5
z02A10-zAC^%U`J7KPZ1M5&*6Mxjei4^XJ%G-+F|dfBb7~?!}+7*`K}O6UN|S4l<BU
z>H`zl>S~bM^TRX~pzX}-pBf(0+t=~v!A5AznmNn#;Zf@IVJ6IcjKISjWFVWYAYcL;
zHqyKtrka2ujZY5`YVCthcuiNU*)6bciR#c(^T39UcUuDd-T#SOOYa%b#lE4Oh^<;j
zBD_R<^q7z769F4GU@NVwfk<QJf3SCK_kH7oe=8UUlMZ%#B9~^|_-#Lr-VP=MX6<8e
z>@k5GU;z`@umM}JnMNgujRudWxc8I>{^hP1`GRiHHJk-xW9KJ{8c|h!(sy20WG6`R
zSwaUGzzT6;1GZo@jVH)rqru}*HmjL=r(&=sUA;V7P0v+CW7m!F5JWQ|X~!w#`!d~h
z)fhHl3pUeuj4n1Byw))Dyk_`J+YTIcy?{c;T@_47p4XOX$tGkv^woFR@LPmz!uCd!
zAT|=b;h1^RG+IsDY|2(JJ;_~#uA%38fsN1}Y{Q3*Btfh<_yK9=4cBN{!$HNS9`<D1
zVRw~4+*PWen_P=00~><39fA+=WxYud>j{34n|V{VS3Z2pQ>P6}b_r%pRdEd+d<Qmc
zgm05V`|t%mttScBlK_KWTm7M+n{*;I8IL~I=E15s)Spc!v3j!is`so08UGG!*brgj
zBR;?<_y!-<{X(h<@E>!zQd*|aTS2p^BRsIzs0S<Mxn25wtrd{eaRW?XGi9rr5AY2>
zUeDvR>uLe1DlZ;*n{dvlSFv)O91qC#+GEdMBX!dyOkm4|&7?nSi%h)*VlT6jf%_za
z(zGm90bY+)m#fff)u8$x*h1Oru)m70>7N54*^ZSU_5muqT1~dD^>A8tYk_NB2Gw`z
zwv!1=V8cdq1MKjn?+2l9*GC^FRzc7|aJp{E_=@&g;a(LI`3_8Ai!OFUUouGmU#$hg
zBvAKNE4Zma65@%FYovH0q>lwmV6SF(o$;?D0qS-wEIZ$@i0lI*SS`m11EG~u<RvLg
zQi30sXFe7%fgQCK^&zNCnOzGcH@{W|&=<I2Rf5}Gxt7b_D)Um^Dgz6cz;3r&=Vq8)
z3$%>#cp}sa=|omlb_65W(*|CErHi<Yu0CYb0;`Y+D}X+=Ac$&322X_7atTaqd{R9X
z0tUvw6e4b`8)ei2vUMKkBGLw766me2hD)NQj>ar_BP`__R$s|M2fDxj7BFKi7pa86
z7kMLEAYOt(w270Kdck20!(vU*yzYY#bf60iV8yVzm4B8k5Z?TB>;G_7w1UlKp|SAd
z?DEmNXe>B7ednG}On6k%aRM)#hb(lU3k+bzsx7e(i0EBuR(k-RUJ6x?BH{>mEN?R>
zXU~88)Z!;kyi@+)6Q>sMzsei0u9(&v%Z7DkNwdye6>$O&bC7{7bf60iU;$HzgsspX
z`I6lpz~Vu-N>QzfYud~<?D^xT77v}RkmHOky-~5}9{lIrmyY~>_JO1SnEgyIf(&G#
z16^PM3z)!eS6Td$DAxH{bmMzKr1YB_O4pwM&Z))eGi96JfwMBvWul8j7l_Ugy?G5f
zr(`cG-7>I%32fNliFV6$4`8v&o@}i^gW1etLGY)`_C@tzyYZ2t$sd@=i^G$7?Zly-
z!@qiX=irHN?i$!8n1~X1{v63b7CO*X56Y{+1U78IR=Waa`)ZKo9?&`h6IHzit9bF?
zhv|0yj$+RH<@C+`g@e2Jk3YI|<iPHsse`u<@Bi3c!|$E*m=`7RFb5gPLI=9Q09G^!
zumzi~Dqz{110-dAXtB~@b1OBrP^qy?R*hY$TdeGKuKezioc+?rcS&W#)CAuy@_fHG
z$fvX%-<kvm4|Bc@$ujjbSzrJwSqZQS+wcLtz$f?yA6eojiD@JNfX-j9)W33azVf->
z&X$k8GFyJ|_jBcAf4W$G=)0#c-+Maf`Kof%eSNv%4QL|#uQ54VrYUZ+eLTmfIKAZU
zs4U|7b0h;<Ux##o0W9^RElk1!Y{E8tfG_X~zQITMy4EDDH39y??8&oN{_~@mtFJx(
z=F)3Fd27l4d}`*(tN&eTV7IS?!Nd6ZWA6*QGEBfWZm_*P$M%xdsV<&OVGc5o4HEzi
zg(V|NsK6#{M}2{B@DaYo-#DgJ0eIx60$m|mR-a|H@^%<}@>!xEjARqkq`N7=-TJtg
zp8Mf#9}0yq1`l(P@nwSqB%ketNwAg8a>AF2@-g)a2n{#Y3e+RiP<*$CQQRZHBOO_w
zGjD;yDh+&4|Nh&L?AZ#!7(C4RG9=q$Pv5978@dYg_-R8=c{nsf53D&hZ)r=eP4cPH
zFPpyrVGN!>XZeB8M1qhh`qS4RNnLBTE_gM0&Z|%oEPBS>priB%-vh!JJb#X4)IsRF
zR`N#wM5g2K=HHx+u70q8)aA?GlH{^1xm%G!);tdK$Hc=NWFmd+CqQ9st^{=1v}#_B
z{m#8CFF9A}-fxKlUzR>$3?AknqkbY`Z|Vw2UP9FdWqHfq)6TpU&Lv91qVx%4@Gu9N
z$Y$pC+qu3bAkhXv0xIk;?nUoe=Yl7iv(hJw!NXi60evt3tSTV<4Va@!yF$JGvNPvB
z(>(9hKp2CkuH!W&>-D?*Gf9A2!7u@~THzI0W54#6<tIQGtAmWCWc&p5d-b!bfMgOH
o>J*35+f_x&k^6|ug2&(g0r?4Ut=mXi?EnA(07*qoM6N<$g398TZ~y=R
--- a/browser/themes/winstripe/browser.css
+++ b/browser/themes/winstripe/browser.css
@@ -2689,40 +2689,89 @@ panel[dimmed="true"] {
 }
 
 #devtools-sidebar-box {
   background-color: -moz-Field;
 }
 
 /* Highlighter - Node Infobar */
 
+#highlighter-nodeinfobar {
+  color: hsl(200, 100%, 65%);
+  border: 1px solid hsla(210, 19%, 63%, .5);
+  border-radius: 3px;
+  background: -moz-linear-gradient(hsl(209, 18%, 30%), hsl(210, 24%, 16%)) no-repeat padding-box;
+}
+
 /* Highlighter - Node Infobar - text */
 
+#highlighter-nodeinfobar-text {
+  /* 100% - size of the buttons and margins */
+  max-width: -moz-calc(100% - 2 * (26px + 6px));
+  padding-bottom: 1px;
+}
+
 html|*#highlighter-nodeinfobar-tagname {
   color: white;
 }
 
 html|*#highlighter-nodeinfobar-id {
   color: hsl(90, 79%, 52%);
 }
 
 html|*#highlighter-nodeinfobar-pseudo-classes {
   color: hsl(20, 100%, 70%);
 }
 
+/* Highlighter - Node Infobar - buttons */
+
+.highlighter-nodeinfobar-button {
+  -moz-appearance: none;
+  border: 0 solid hsla(210,8%,5%,.45);
+  padding: 0;
+  width: 26px;
+  min-height: 26px;
+}
+
+#highlighter-nodeinfobar-inspectbutton {
+  -moz-border-end-width: 1px;
+  box-shadow: 1px 0 0 hsla(210,16%,76%,.15), -1px 0 0 hsla(210,16%,76%,.15) inset;
+  -moz-margin-end: 6px;
+  list-style-image: url("chrome://browser/skin/devtools/inspect-button.png");
+  -moz-image-region: rect(0px 16px 16px 0px);
+}
+
+#highlighter-nodeinfobar-inspectbutton:-moz-locale-dir(rtl) {
+  box-shadow: -1px 0 0 hsla(210,16%,76%,.15), 1px 0 0 hsla(210,16%,76%,.15) inset;
+}
+
+#highlighter-nodeinfobar-inspectbutton:active:hover,
+#highlighter-nodeinfobar-container:not([locked]) >  #highlighter-nodeinfobar >  #highlighter-nodeinfobar-inspectbutton {
+  -moz-image-region: rect(0px 32px 16px 16px);
+}
+
+#highlighter-nodeinfobar-menu {
+  -moz-border-start-width: 1px;
+  box-shadow: -1px 0 0 hsla(210,16%,76%,.15), 1px 0 0 hsla(210,16%,76%,.15) inset;
+  -moz-margin-start: 6px;
+}
+
+#highlighter-nodeinfobar-menu:-moz-locale-dir(rtl) {
+  box-shadow: 1px 0 0 hsla(210,16%,76%,.15), -1px 0 0 hsla(210,16%,76%,.15) inset;
+}
+
+#highlighter-nodeinfobar-menu > .toolbarbutton-menu-dropmarker {
+  -moz-appearance: none !important;
+  list-style-image: url("chrome://browser/skin/devtools/dropmarker.png");
+  -moz-box-align: center;
+  -moz-margin-start: -1px;
+}
+
 /* Highlighter - Node Infobar - box & arrow */
 
-#highlighter-nodeinfobar {
-  color: hsl(200, 100%, 65%);
-  border: 1px solid hsla(210, 19%, 63%, .5);
-  border-radius: 3px;
-  padding: 8px 16px;
-  background: -moz-linear-gradient(hsl(209, 18%, 30%), hsl(210, 24%, 16%)) no-repeat padding-box;
-}
-
 .highlighter-nodeinfobar-arrow {
   width: 14px;
   height: 14px;
   -moz-margin-start: -moz-calc(50% - 7px);
   -moz-transform: rotate(-45deg);
   border: 1px solid transparent;
   background-clip: padding-box;
   background-repeat: no-repeat;
new file mode 100644
index 0000000000000000000000000000000000000000..7e91860fb5bccc230d94b2ebc5b549b9e2637087
GIT binary patch
literal 218
zc%17D@N?(olHy`uVBq!ia0vp^>_E)I!3HFqj;YoHDaPU;cPEB*=VV?2Ic!PZ?k)`f
zL2$v|<&%LToCO|{#S9GG!XV7ZFl&wk$dD3GUsv`!OiaAI7H_8Rm=6?^%?ybsan8@p
zP0cG|00HNs)Wnk16ovB4k_-iRPv3wPy;P9phMq2tAsp9}@9eGq?!?B{X2IsOp<`lW
uV<TgClk2*5>pu1v$W4N<B%V2l9$;kPuNGKg&%W<B$W%{PKbLh*2~7Y420teN
--- a/browser/themes/winstripe/jar.mn
+++ b/browser/themes/winstripe/jar.mn
@@ -153,16 +153,17 @@ browser.jar:
         skin/classic/browser/devtools/splitview.css                 (devtools/splitview.css)
         skin/classic/browser/devtools/styleeditor.css               (devtools/styleeditor.css)
         skin/classic/browser/devtools/debugger.css                  (devtools/debugger.css)
         skin/classic/browser/devtools/magnifying-glass.png          (devtools/magnifying-glass.png)
         skin/classic/browser/devtools/itemToggle.png                (devtools/itemToggle.png)
         skin/classic/browser/devtools/itemArrow-rtl.png             (devtools/itemArrow-rtl.png)
         skin/classic/browser/devtools/itemArrow-ltr.png             (devtools/itemArrow-ltr.png)
         skin/classic/browser/devtools/inspect-button.png            (devtools/inspect-button.png)
+        skin/classic/browser/devtools/dropmarker.png                (devtools/dropmarker.png)
 #ifdef MOZ_SERVICES_SYNC
         skin/classic/browser/sync-throbber.png
         skin/classic/browser/sync-16.png
         skin/classic/browser/sync-32.png
         skin/classic/browser/sync-128.png
         skin/classic/browser/sync-bg.png
         skin/classic/browser/sync-desktopIcon.png
         skin/classic/browser/sync-mobileIcon.png
@@ -326,16 +327,17 @@ browser.jar:
         skin/classic/aero/browser/devtools/splitview.css             (devtools/splitview.css)
         skin/classic/aero/browser/devtools/styleeditor.css           (devtools/styleeditor.css)
         skin/classic/aero/browser/devtools/debugger.css              (devtools/debugger.css)
         skin/classic/aero/browser/devtools/magnifying-glass.png      (devtools/magnifying-glass.png)
         skin/classic/aero/browser/devtools/itemToggle.png            (devtools/itemToggle.png)
         skin/classic/aero/browser/devtools/itemArrow-rtl.png         (devtools/itemArrow-rtl.png)
         skin/classic/aero/browser/devtools/itemArrow-ltr.png         (devtools/itemArrow-ltr.png)
         skin/classic/aero/browser/devtools/inspect-button.png        (devtools/inspect-button.png)
+        skin/classic/aero/browser/devtools/dropmarker.png            (devtools/dropmarker.png)
 #ifdef MOZ_SERVICES_SYNC
         skin/classic/aero/browser/sync-throbber.png
         skin/classic/aero/browser/sync-16.png
         skin/classic/aero/browser/sync-32.png
         skin/classic/aero/browser/sync-128.png
         skin/classic/aero/browser/sync-bg.png
         skin/classic/aero/browser/sync-desktopIcon.png
         skin/classic/aero/browser/sync-mobileIcon.png
index cf59fa3ca1e903968f793d940b2dc651f6ac4e1f..7112ab4e42d3165ba3c0a107936d11f9ddb0d270
GIT binary patch
literal 355
zc%17D@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Ea{HEjtmSN`?>!lvI6;x#X;^)
z4C~IxyaaMM3p^r=85sBugD~Uq{1qucL8%hgh?3y^w370~qEv=}#LT=BJwMkFg)(D3
zQ$0figD*u3fvPTfx;TbdoL+kQAQz*f%<+%5fqF7~8YinyX#T`;&vmEJq$9VsrCx7K
zy}4xDSu5+j!m{#jTuc7^-RAZ-b;i>VEC27Gxo^2-XUCHI0)C$K`Hc-HkMX_8zR#Mp
zC!+CUR(OMYfJ>mo0$p2aj*<&EayZ#TPVYU${Aoh7bxaG>shrykk5<(2oOrg3?~7}%
z^b2Xe&IfE&$+KMwm}7Gt1<lW$;V-#jX~@39<y*nG`_|?c!j`e5du-mzl4`bGf#W@g
w?bB-)d=3fvP3ZV5?&MpkbojyY`x>uU72>Yh9XiB%ALu&<Pgg&ebxsLQ08tK#u>b%7
index 442132d987b04e37e7cd24b45785e7b605be2e32..7c5e48cb8bea63eb814b30fd96cb55762bb1753c
GIT binary patch
literal 2942
zc$@)#3xV{BP)<h;3K|Lk000e1NJLTq002M$002M;1^@s6s%dfF0000PbVXQnQ*UN;
zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU-AW1|)RCwCeTWf3_R~5eZ&SSmy+TN{W
zJ5Q;kg{0u5^o0~a3Z*!u5Ksc76;caA6(A^oDjI(93lNoB+De5#6@O5XKqVkm<sk(L
zq5i1EQ-BIeqQ+{IN~s@-li1$f+1=Thxtwq2&hE_YdgIu$liJeB-aYr6?>z1~ckaw=
z1k3)96A2=n$WWv!9?#ktaRY+ap5Hfq%gF;f&j0bi4jY6qc$mvN2_oy@pS*3$t$PaQ
z>k~W79eeK{Vjzsc!(7%$5LpL*XEFEhiJfMCY`eiYaX}b^hq<hikW~W2!@EX`cZ}x#
zj6k<+3xfBpiy#T$VGc5o&B`~CkqzVX+X_!nklPU`IFf>vOp!_k<{$%EwK<B-o&dk}
zpPL%Ly;#s5o!n*m92vb9sJ%brK?btW$)XiR1_?IbKWv^Xj_d7|utZo19%P{d-G2Kh
z`n>x3OLuR5tTb+vN)twRi7x94S?EAloe}tEOn~-<?S;{zp?$9uu0B&uAAUwDI)@~X
z4s?N`T47Tqz(46dv}5@A*o2YK<wY{S?g@wtp#xoDL{5sECjm#c6-EoXcx=n05erlj
zaT(OKv>QJ8ouKBTvd{$vur^Zy{N5+0504i0yrywKxag`QP<MHmjePi+PZ-NYH9!Zt
zzyMai_5#tb@}JCU&y>dWR^ZVjAmGWbKFvx;kNbo%c+ud)F))DDuQm{USbgo;4~*ZH
z9}uO%A+6oiUJ#g9tFoIu`*k+B|31cR3qD~C9_IYuy>_yI0W4tl<6O{>1n9R68IPB4
zGCDUo$76bagcYYBWvn_+-|GR<7(C2DCb>xh3z+?AgR~PM4sRVA8PNElkx{*4r}r8b
z8$0?9#u^J0sKr=Q5yr&B9AuJuzyc<))Am`Uz4~;HKQcO^=LJs$Pb;ep71`jO_cCTJ
zQQ*}$5f5{afoxP4Sil5!+EpM@PNp_yYF{iCJ6E4OO*V4ie#UB-f)#hE^raOL#^BK$
zWFVW=2PUx9J|Nz$2`~)7@5$%1jy{%+CL2EZX>#9Ta!_N9^QW2p_KZ&$gNHfDKsLF5
z02A10-zAC^%U`J7KPZ1M5&*6Mxjei4^XJ%G-+F|dfBb7~?!}+7*`K}O6UN|S4l<BU
z>H`zl>S~bM^TRX~pzX}-pBf(0+t=~v!A5AznmNn#;Zf@IVJ6IcjKISjWFVWYAYcL;
zHqyKtrka2ujZY5`YVCthcuiNU*)6bciR#c(^T39UcUuDd-T#SOOYa%b#lE4Oh^<;j
zBD_R<^q7z769F4GU@NVwfk<QJf3SCK_kH7oe=8UUlMZ%#B9~^|_-#Lr-VP=MX6<8e
z>@k5GU;z`@umM}JnMNgujRudWxc8I>{^hP1`GRiHHJk-xW9KJ{8c|h!(sy20WG6`R
zSwaUGzzT6;1GZo@jVH)rqru}*HmjL=r(&=sUA;V7P0v+CW7m!F5JWQ|X~!w#`!d~h
z)fhHl3pUeuj4n1Byw))Dyk_`J+YTIcy?{c;T@_47p4XOX$tGkv^woFR@LPmz!uCd!
zAT|=b;h1^RG+IsDY|2(JJ;_~#uA%38fsN1}Y{Q3*Btfh<_yK9=4cBN{!$HNS9`<D1
zVRw~4+*PWen_P=00~><39fA+=WxYud>j{34n|V{VS3Z2pQ>P6}b_r%pRdEd+d<Qmc
zgm05V`|t%mttScBlK_KWTm7M+n{*;I8IL~I=E15s)Spc!v3j!is`so08UGG!*brgj
zBR;?<_y!-<{X(h<@E>!zQd*|aTS2p^BRsIzs0S<Mxn25wtrd{eaRW?XGi9rr5AY2>
zUeDvR>uLe1DlZ;*n{dvlSFv)O91qC#+GEdMBX!dyOkm4|&7?nSi%h)*VlT6jf%_za
z(zGm90bY+)m#fff)u8$x*h1Oru)m70>7N54*^ZSU_5muqT1~dD^>A8tYk_NB2Gw`z
zwv!1=V8cdq1MKjn?+2l9*GC^FRzc7|aJp{E_=@&g;a(LI`3_8Ai!OFUUouGmU#$hg
zBvAKNE4Zma65@%FYovH0q>lwmV6SF(o$;?D0qS-wEIZ$@i0lI*SS`m11EG~u<RvLg
zQi30sXFe7%fgQCK^&zNCnOzGcH@{W|&=<I2Rf5}Gxt7b_D)Um^Dgz6cz;3r&=Vq8)
z3$%>#cp}sa=|omlb_65W(*|CErHi<Yu0CYb0;`Y+D}X+=Ac$&322X_7atTaqd{R9X
z0tUvw6e4b`8)ei2vUMKkBGLw766me2hD)NQj>ar_BP`__R$s|M2fDxj7BFKi7pa86
z7kMLEAYOt(w270Kdck20!(vU*yzYY#bf60iV8yVzm4B8k5Z?TB>;G_7w1UlKp|SAd
z?DEmNXe>B7ednG}On6k%aRM)#hb(lU3k+bzsx7e(i0EBuR(k-RUJ6x?BH{>mEN?R>
zXU~88)Z!;kyi@+)6Q>sMzsei0u9(&v%Z7DkNwdye6>$O&bC7{7bf60iU;$HzgsspX
z`I6lpz~Vu-N>QzfYud~<?D^xT77v}RkmHOky-~5}9{lIrmyY~>_JO1SnEgyIf(&G#
z16^PM3z)!eS6Td$DAxH{bmMzKr1YB_O4pwM&Z))eGi96JfwMBvWul8j7l_Ugy?G5f
zr(`cG-7>I%32fNliFV6$4`8v&o@}i^gW1etLGY)`_C@tzyYZ2t$sd@=i^G$7?Zly-
z!@qiX=irHN?i$!8n1~X1{v63b7CO*X56Y{+1U78IR=Waa`)ZKo9?&`h6IHzit9bF?
zhv|0yj$+RH<@C+`g@e2Jk3YI|<iPHsse`u<@Bi3c!|$E*m=`7RFb5gPLI=9Q09G^!
zumzi~Dqz{110-dAXtB~@b1OBrP^qy?R*hY$TdeGKuKezioc+?rcS&W#)CAuy@_fHG
z$fvX%-<kvm4|Bc@$ujjbSzrJwSqZQS+wcLtz$f?yA6eojiD@JNfX-j9)W33azVf->
z&X$k8GFyJ|_jBcAf4W$G=)0#c-+Maf`Kof%eSNv%4QL|#uQ54VrYUZ+eLTmfIKAZU
zs4U|7b0h;<Ux##o0W9^RElk1!Y{E8tfG_X~zQITMy4EDDH39y??8&oN{_~@mtFJx(
z=F)3Fd27l4d}`*(tN&eTV7IS?!Nd6ZWA6*QGEBfWZm_*P$M%xdsV<&OVGc5o4HEzi
zg(V|NsK6#{M}2{B@DaYo-#DgJ0eIx60$m|mR-a|H@^%<}@>!xEjARqkq`N7=-TJtg
zp8Mf#9}0yq1`l(P@nwSqB%ketNwAg8a>AF2@-g)a2n{#Y3e+RiP<*$CQQRZHBOO_w
zGjD;yDh+&4|Nh&L?AZ#!7(C4RG9=q$Pv5978@dYg_-R8=c{nsf53D&hZ)r=eP4cPH
zFPpyrVGN!>XZeB8M1qhh`qS4RNnLBTE_gM0&Z|%oEPBS>priB%-vh!JJb#X4)IsRF
zR`N#wM5g2K=HHx+u70q8)aA?GlH{^1xm%G!);tdK$Hc=NWFmd+CqQ9st^{=1v}#_B
z{m#8CFF9A}-fxKlUzR>$3?AknqkbY`Z|Vw2UP9FdWqHfq)6TpU&Lv91qVx%4@Gu9N
z$Y$pC+qu3bAkhXv0xIk;?nUoe=Yl7iv(hJw!NXi60evt3tSTV<4Va@!yF$JGvNPvB
z(>(9hKp2CkuH!W&>-D?*Gf9A2!7u@~THzI0W54#6<tIQGtAmWCWc&p5d-b!bfMgOH
o>J*35+f_x&k^6|ug2&(g0r?4Ut=mXi?EnA(07*qoM6N<$g398TZ~y=R
--- a/client.py
+++ b/client.py
@@ -2,49 +2,71 @@
 
 NSPR_DIRS = (('nsprpub', 'mozilla/nsprpub'),)
 NSS_DIRS  = (('dbm', 'mozilla/dbm'),
              ('security/nss', 'mozilla/security/nss'),
              ('security/coreconf', 'mozilla/security/coreconf'),
              ('security/dbm', 'mozilla/security/dbm'))
 NSSCKBI_DIRS = (('security/nss/lib/ckfw/builtins', 'mozilla/security/nss/lib/ckfw/builtins'),)
 LIBFFI_DIRS = (('js/ctypes/libffi', 'libffi'),)
+WEBIDLPARSER_DIR = 'dom/bindings/parser'
+WEBIDLPARSER_REPO = 'https://hg.mozilla.org/users/khuey_mozilla.com/webidl-parser'
+WEBIDLPARSER_EXCLUSIONS = ['.hgignore', '.gitignore', '.hg', 'ply']
 
 CVSROOT_MOZILLA = ':pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot'
 CVSROOT_LIBFFI = ':pserver:anoncvs@sources.redhat.com:/cvs/libffi'
 
 import os
 import sys
 import datetime
 import shutil
+import glob
 from optparse import OptionParser
 from subprocess import check_call
 
 topsrcdir = os.path.dirname(__file__)
 if topsrcdir == '':
     topsrcdir = '.'
 
 def check_call_noisy(cmd, *args, **kwargs):
     print "Executing command:", cmd
     check_call(cmd, *args, **kwargs)
 
 def do_hg_pull(dir, repository, hg):
     fulldir = os.path.join(topsrcdir, dir)
     # clone if the dir doesn't exist, pull if it does
     if not os.path.exists(fulldir):
-        fulldir = os.path.join(topsrcdir, dir)
         check_call_noisy([hg, 'clone', repository, fulldir])
     else:
         cmd = [hg, 'pull', '-u', '-R', fulldir]
         if repository is not None:
             cmd.append(repository)
         check_call_noisy(cmd)
     check_call([hg, 'parent', '-R', fulldir,
                 '--template=Updated to revision {node}.\n'])
 
+def do_hg_replace(dir, repository, tag, exclusions, hg):
+    """
+        Replace the contents of dir with the contents of repository, except for
+        files matching exclusions.
+    """
+    fulldir = os.path.join(topsrcdir, dir)
+    if os.path.exists(fulldir):
+        shutil.rmtree(fulldir)
+
+    assert not os.path.exists(fulldir)
+    check_call_noisy([hg, 'clone', '-u', tag, repository, fulldir])
+
+    for thing in exclusions:
+        for excluded in glob.iglob(os.path.join(fulldir, thing)):
+            if os.path.isdir(excluded):
+                shutil.rmtree(excluded)
+            else:
+                os.remove(excluded)
+
 def do_cvs_export(modules, tag, cvsroot, cvs):
     """Check out a CVS directory without CVS metadata, using "export"
     modules is a list of directories to check out and the corresponding
     cvs module, e.g. (('nsprpub', 'mozilla/nsprpub'))
     """
     for module_tuple in modules:
         module = module_tuple[0]
         cvs_module = module_tuple[1]
@@ -55,25 +77,27 @@ def do_cvs_export(modules, tag, cvsroot,
 
         (parent, leaf) = os.path.split(module)
         print "CVS export begin: " + datetime.datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S UTC")
         check_call_noisy([cvs, '-d', cvsroot,
                           'export', '-r', tag, '-d', leaf, cvs_module],
                          cwd=os.path.join(topsrcdir, parent))
         print "CVS export end: " + datetime.datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S UTC")
 
-o = OptionParser(usage="client.py [options] update_nspr tagname | update_nss tagname | update_libffi tagname")
+o = OptionParser(usage="client.py [options] update_nspr tagname | update_nss tagname | update_libffi tagname | update_webidlparser tagname")
 o.add_option("--skip-mozilla", dest="skip_mozilla",
              action="store_true", default=False,
              help="Obsolete")
 
 o.add_option("--cvs", dest="cvs", default=os.environ.get('CVS', 'cvs'),
              help="The location of the cvs binary")
 o.add_option("--cvsroot", dest="cvsroot",
              help="The CVSROOT (default for mozilla checkouts: %s)" % CVSROOT_MOZILLA)
+o.add_option("--hg", dest="hg", default=os.environ.get('HG', 'hg'),
+             help="The location of the hg binary")
 
 try:
     options, args = o.parse_args()
     action = args[0]
 except IndexError:
     o.print_help()
     sys.exit(2)
 
@@ -99,11 +123,14 @@ elif action in ('update_nssckbi'):
         options.cvsroot = os.environ.get('CVSROOT', CVSROOT_MOZILLA)
     do_cvs_export(NSSCKBI_DIRS, tag, options.cvsroot, options.cvs)
     print >>file("security/nss/TAG-INFO-CKBI", "w"), tag
 elif action in ('update_libffi'):
     tag, = args[1:]
     if not options.cvsroot:
         options.cvsroot = CVSROOT_LIBFFI
     do_cvs_export(LIBFFI_DIRS, tag, options.cvsroot, options.cvs)
+elif action in ('update_webidlparser'):
+    tag, = args[1:]
+    do_hg_replace(WEBIDLPARSER_DIR, WEBIDLPARSER_REPO, tag, WEBIDLPARSER_EXCLUSIONS, options.hg)
 else:
     o.print_help()
     sys.exit(2)
--- a/configure.in
+++ b/configure.in
@@ -6362,17 +6362,17 @@ fi
 dnl ========================================================
 dnl Enable building the signmar program.
 dnl This option is much different than the --enable-verify-mar option.
 dnl --enable-verify-mar is for enabling the verification check on MAR
 dnl files in the updater.  The --enable-signmar option is for building
 dnl the signmar program.
 dnl ========================================================
 
-MOZ_ARG_ENABLE_BOOL(sign-mar,
+MOZ_ARG_ENABLE_BOOL(signmar,
 [  --enable-signmar     Enable building the signmar program],
     MOZ_ENABLE_SIGNMAR=1,
     MOZ_ENABLE_SIGNMAR= )
 
 if test -n "$MOZ_ENABLE_SIGNMAR"; then
   AC_DEFINE(MOZ_ENABLE_SIGNMAR)
 fi
 
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -7889,32 +7889,36 @@ nsDocShell::CheckLoadingPermissions()
 
 //*****************************************************************************
 // nsDocShell: Site Loading
 //*****************************************************************************   
 namespace
 {
 
 // Callback used by CopyFavicon to inform the favicon service that one URI
-// (mNewURI) has the same favicon URI (OnFaviconDataAvailable's aFaviconURI) as
-// another.
+// (mNewURI) has the same favicon URI (OnComplete's aFaviconURI) as another.
 class nsCopyFaviconCallback : public nsIFaviconDataCallback
 {
 public:
     NS_DECL_ISUPPORTS
 
     nsCopyFaviconCallback(nsIURI *aNewURI)
       : mNewURI(aNewURI)
     {
     }
 
     NS_IMETHODIMP
-    OnFaviconDataAvailable(nsIURI *aFaviconURI, PRUint32 aDataLen,
-                           const PRUint8 *aData, const nsACString &aMimeType)
+    OnComplete(nsIURI *aFaviconURI, PRUint32 aDataLen,
+               const PRUint8 *aData, const nsACString &aMimeType)
     {
+        // Continue only if there is an associated favicon.
+        if (!aFaviconURI) {
+          return NS_OK;
+        }
+
         NS_ASSERTION(aDataLen == 0,
                      "We weren't expecting the callback to deliver data.");
         nsCOMPtr<mozIAsyncFavicons> favSvc =
             do_GetService("@mozilla.org/browser/favicon-service;1");
         NS_ENSURE_STATE(favSvc);
 
         return favSvc->SetAndFetchFaviconForPage(mNewURI, aFaviconURI,
                                                  false, nsnull);
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -1600,16 +1600,20 @@ nsGlobalWindow::SetScriptContext(nsIScri
   NS_ASSERTION(IsOuterWindow(), "Uh, SetScriptContext() called on inner window!");
 
   NS_ASSERTION(!aScriptContext || !mContext, "Bad call to SetContext()!");
 
   if (aScriptContext) {
     // should probably assert the context is clean???
     aScriptContext->WillInitializeContext();
 
+    // We need point the context to the global window before initializing it
+    // so that it can make various decisions properly.
+    aScriptContext->SetGlobalObject(this);
+
     nsresult rv = aScriptContext->InitContext();
     NS_ENSURE_SUCCESS(rv, rv);
 
     if (IsFrame()) {
       // This window is a [i]frame, don't bother GC'ing when the
       // frame's context is destroyed since a GC will happen when the
       // frameset or host document is destroyed anyway.
 
@@ -1869,18 +1873,16 @@ WindowStateHolder::~WindowStateHolder()
   }
 }
 
 NS_IMPL_ISUPPORTS1(WindowStateHolder, WindowStateHolder)
 
 nsresult
 nsGlobalWindow::CreateOuterObject(nsGlobalWindow* aNewInner)
 {
-  mContext->SetGlobalObject(this);
-
   JSContext* cx = mContext->GetNativeContext();
 
   if (IsChromeWindow()) {
     // Always enable E4X for XUL and other chrome content -- there is no
     // need to preserve the <!-- script hiding hack from JS-in-HTML daze
     // (introduced in 1995 for graceful script degradation in Netscape 1,
     // Mosaic, and other pre-JS browsers).
     JS_SetOptions(cx, JS_GetOptions(cx) | JSOPTION_XML);
@@ -7168,16 +7170,19 @@ nsGlobalWindow::ShowModalDialog(const ns
                                 const nsAString& aOptions,
                                 nsIVariant **aRetVal)
 {
   FORWARD_TO_OUTER(ShowModalDialog, (aURI, aArgs, aOptions, aRetVal),
                    NS_ERROR_NOT_INITIALIZED);
 
   *aRetVal = nsnull;
 
+  if (Preferences::GetBool("dom.disable_window_showModalDialog", false))
+    return NS_ERROR_NOT_AVAILABLE;
+
   // Before bringing up the window/dialog, unsuppress painting and flush
   // pending reflows.
   EnsureReflowFlushAndPaint();
 
   if (AreDialogsBlocked() || !ConfirmDialogAllowed())
     return NS_ERROR_NOT_AVAILABLE;
 
   nsCOMPtr<nsIDOMWindow> dlgWin;
@@ -8923,16 +8928,17 @@ nsGlobalWindow::OpenInternal(const nsASt
       }
     }
   }
 
   NS_ENSURE_SUCCESS(rv, rv);
 
   // success!
 
+  NS_ENSURE_TRUE(domReturn, NS_OK);
   domReturn.swap(*aReturn);
 
   if (aDoJSFixups) {      
     nsCOMPtr<nsIDOMChromeWindow> chrome_win(do_QueryInterface(*aReturn));
     if (!chrome_win) {
       // A new non-chrome window was created from a call to
       // window.open() from JavaScript, make sure there's a document in
       // the new window. We do this by simply asking the new window for
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -940,17 +940,21 @@ nsJSContext::JSOptionChangedCallback(con
   sPostGCEventsToConsole = Preferences::GetBool(js_memlog_option_str);
 
   bool strict = Preferences::GetBool(js_strict_option_str);
   if (strict)
     newDefaultJSOptions |= JSOPTION_STRICT;
   else
     newDefaultJSOptions &= ~JSOPTION_STRICT;
 
-  nsIScriptGlobalObject *global = context->GetGlobalObject();
+  // The vanilla GetGlobalObject returns null if a global isn't set up on
+  // the context yet. We can sometimes be call midway through context init,
+  // So ask for the member directly instead.
+  nsIScriptGlobalObject *global = context->GetGlobalObjectRef();
+
   // XXX should we check for sysprin instead of a chrome window, to make
   // XXX components be covered by the chrome pref instead of the content one?
   nsCOMPtr<nsIDOMWindow> contentWindow(do_QueryInterface(global));
   nsCOMPtr<nsIDOMChromeWindow> chromeWindow(do_QueryInterface(global));
 
   bool useMethodJIT = Preferences::GetBool(chromeWindow || !contentWindow ?
                                                js_methodjit_chrome_str :
                                                js_methodjit_content_str);
--- a/dom/base/nsJSEnvironment.h
+++ b/dom/base/nsJSEnvironment.h
@@ -130,16 +130,18 @@ public:
                                    const nsAString& aBody,
                                    const char* aURL,
                                    PRUint32 aLineNo,
                                    PRUint32 aVersion,
                                    bool aShared,
                                    JSObject** aFunctionObject);
 
   virtual nsIScriptGlobalObject *GetGlobalObject();
+  inline nsIScriptGlobalObject *GetGlobalObjectRef() { return mGlobalObjectRef; };
+
   virtual JSContext* GetNativeContext();
   virtual JSObject* GetNativeGlobal();
   virtual nsresult CreateNativeGlobalForInner(
                                       nsIScriptGlobalObject *aGlobal,
                                       bool aIsChrome,
                                       nsIPrincipal *aPrincipal,
                                       JSObject** aNativeGlobal,
                                       nsISupports **aHolder);
--- a/dom/bindings/Makefile.in
+++ b/dom/bindings/Makefile.in
@@ -70,26 +70,26 @@ bindinggen_dependencies := \
   Codegen.py \
   ParserResults.pkl \
   $(GLOBAL_DEPS) \
   $(NULL)
 
 $(binding_header_files): %Binding.h: $(bindinggen_dependencies) \
                                      $(webidl_base)/%.webidl \
                                      $(NULL)
-	$(PYTHON) $(topsrcdir)/config/pythonpath.py \
+	PYTHONDONTWRITEBYTECODE=1 $(PYTHON) $(topsrcdir)/config/pythonpath.py \
 	  $(PLY_INCLUDE) -I$(srcdir)/parser \
     	  $(srcdir)/BindingGen.py $(ACCESSOR_OPT) header \
 	  $(srcdir)/Bindings.conf $*Binding \
 	  $(webidl_base)/$*.webidl
 
 $(binding_cpp_files): %Binding.cpp: $(bindinggen_dependencies) \
                                     $(webidl_base)/%.webidl \
                                     $(NULL)
-	$(PYTHON) $(topsrcdir)/config/pythonpath.py \
+	PYTHONDONTWRITEBYTECODE=1 $(PYTHON) $(topsrcdir)/config/pythonpath.py \
 	  $(PLY_INCLUDE) -I$(srcdir)/parser \
 	  $(srcdir)/BindingGen.py $(ACCESSOR_OPT) cpp \
 	  $(srcdir)/Bindings.conf $*Binding \
 	  $(webidl_base)/$*.webidl
 
 $(globalgen_targets): ParserResults.pkl
 
 CACHE_DIR = _cache
@@ -104,17 +104,17 @@ globalgen_dependencies := \
   $(NULL)
 
 $(CACHE_DIR)/.done:
 	$(MKDIR) -p $(CACHE_DIR)
 	@$(TOUCH) $@
 
 ParserResults.pkl: $(globalgen_dependencies) \
                    $(addprefix $(webidl_base)/, $(webidl_files))
-	$(PYTHON) $(topsrcdir)/config/pythonpath.py \
+	PYTHONDONTWRITEBYTECODE=1 $(PYTHON) $(topsrcdir)/config/pythonpath.py \
     $(PLY_INCLUDE) -I$(srcdir)/parser \
     $(srcdir)/GlobalGen.py $(ACCESSOR_OPT) $(srcdir)/Bindings.conf $(webidl_base) \
     --cachedir=$(CACHE_DIR) \
     $(webidl_files)
 
 GARBAGE += \
   $(binding_header_files) \
   $(binding_cpp_files) \
--- a/dom/bindings/parser/WebIDL.py
+++ b/dom/bindings/parser/WebIDL.py
@@ -64,98 +64,110 @@ def parseInt(literal):
         base = 10
 
     value = int(string, base)
     return value * sign
 
 # Magic for creating enums
 def M_add_class_attribs(attribs):
     def foo(name, bases, dict_):
-        for v, k in attribs:
+        for v, k in enumerate(attribs):
             dict_[k] = v
+        assert 'length' not in dict_
+        dict_['length'] = len(attribs)
         return type(name, bases, dict_)
     return foo
 
 def enum(*names):
     class Foo(object):
-        __metaclass__ = M_add_class_attribs(enumerate(names))
+        __metaclass__ = M_add_class_attribs(names)
         def __setattr__(self, name, value):  # this makes it read-only
             raise NotImplementedError
     return Foo()
 
 class WebIDLError(Exception):
     def __init__(self, message, location, warning=False):
         self.message = message
         self.location = location
         self.warning = warning
 
     def __str__(self):
         return "%s: %s%s%s" % (self.warning and 'warning' or 'error',
                                self.message, ", " if self.location else "",
                                self.location)
 
 class Location(object):
-    _line = None
-
     def __init__(self, lexer, lineno, lexpos, filename):
+        self._line = None
         self._lineno = lineno
         self._lexpos = lexpos
         self._lexdata = lexer.lexdata
         self._file = filename if filename else "<unknown>"
 
     def __eq__(self, other):
         return self._lexpos == other._lexpos and \
                self._file == other._file
 
+    def filename(self):
+        return self._file
+
     def resolve(self):
         if self._line:
             return
 
         startofline = self._lexdata.rfind('\n', 0, self._lexpos) + 1
         endofline = self._lexdata.find('\n', self._lexpos, self._lexpos + 80)
-        self._line = self._lexdata[startofline:endofline]
+        if endofline != -1:
+            self._line = self._lexdata[startofline:endofline]
+        else:
+            self._line = self._lexdata[startofline:]
         self._colno = self._lexpos - startofline
 
-    def pointerline(self):
-        def i():
-            for i in xrange(0, self._colno):
-                yield " "
-            yield "^"
-
-        return "".join(i())
-
     def get(self):
         self.resolve()
         return "%s line %s:%s" % (self._file, self._lineno, self._colno)
 
+    def _pointerline(self):
+        return " " * self._colno + "^"
+
     def __str__(self):
         self.resolve()
         return "%s line %s:%s\n%s\n%s" % (self._file, self._lineno, self._colno,
-                                          self._line, self.pointerline())
+                                          self._line, self._pointerline())
 
 class BuiltinLocation(object):
     def __init__(self, text):
         self.msg = text
 
+    def __eq__(self, other):
+        return isinstance(other, BuiltinLocation) and \
+               self.msg == other.msg
+
+    def filename(self):
+        return '<builtin>'
+
+    def resolve(self):
+        pass
+
     def get(self):
         return self.msg
 
     def __str__(self):
         return self.get()
 
 
 # Data Model
 
 class IDLObject(object):
     def __init__(self, location):
         self.location = location
         self.userData = dict()
 
     def filename(self):
-        return self.location._file
+        return self.location.filename()
 
     def isInterface(self):
         return False
 
     def isEnum(self):
         return False
 
     def isCallback(self):
@@ -193,16 +205,21 @@ class IDLScope(IDLObject):
         return self.QName()
 
     def QName(self):
         if self._name:
             return self._name.QName() + "::"
         return "::"
 
     def ensureUnique(self, identifier, object):
+        """
+            Ensure that there is at most one 'identifier' in scope ('self').
+            Note that object can be None.  This occurs if we end up here for an
+            interface type we haven't seen yet.
+        """
         assert isinstance(identifier, IDLUnresolvedIdentifier)
         assert not object or isinstance(object, IDLObjectWithIdentifier)
         assert not object or object.identifier == identifier
 
         if identifier.name in self._dict:
             if not object:
                 return
 
@@ -295,16 +312,19 @@ class IDLUnresolvedIdentifier(IDLObject)
 
         scope.ensureUnique(self, object)
 
         identifier = IDLIdentifier(self.location, scope, self.name)
         if object:
             object.identifier = identifier
         return identifier
 
+    def finish(self):
+        assert False # Should replace with a resolved identifier first.
+
 class IDLObjectWithIdentifier(IDLObject):
     def __init__(self, location, parentScope, identifier):
         IDLObject.__init__(self, location)
 
         assert isinstance(identifier, IDLUnresolvedIdentifier)
 
         self.identifier = identifier
 
@@ -363,19 +383,18 @@ class IDLExternalInterface(IDLObjectWith
 class IDLInterface(IDLObjectWithScope):
     def __init__(self, location, parentScope, name, parent, members):
         assert isinstance(parentScope, IDLScope)
         assert isinstance(name, IDLUnresolvedIdentifier)
         assert not parent or isinstance(parent, IDLParentPlaceholder)
 
         self.parent = parent
         self._callback = False
-
+        self._finished = False
         self.members = list(members) # clone the list
-        assert iter(self.members) # Assert it's iterable
 
         IDLObjectWithScope.__init__(self, location, parentScope, name)
 
     def __str__(self):
         return "Interface '%s'" % self.identifier.name
 
     def ctor(self):
         identifier = IDLUnresolvedIdentifier(self.location, "constructor",
@@ -399,116 +418,82 @@ class IDLInterface(IDLObjectWithScope):
 
         retval = originalObject.addOverload(newObject)
         # Might be a ctor, which isn't in self.members
         if newObject in self.members:
             self.members.remove(newObject)
         return retval
 
     def finish(self, scope):
-        if hasattr(self, "_finished"):
+        if self._finished:
             return
 
         self._finished = True
 
         assert not self.parent or isinstance(self.parent, IDLParentPlaceholder)
         parent = self.parent.finish(scope) if self.parent else None
         assert not parent or isinstance(parent, IDLInterface)
 
         self.parent = parent
 
         assert iter(self.members)
-        members = None
 
         if self.parent:
             self.parent.finish(scope)
             assert iter(self.parent.members)
 
             members = list(self.parent.members)
             members.extend(self.members)
         else:
             members = list(self.members)
 
-        SpecialType = enum(
-            'NamedGetter',
-            'NamedSetter',
-            'NamedCreator',
-            'NamedDeleter',
-            'IndexedGetter',
-            'IndexedSetter',
-            'IndexedCreator',
-            'IndexedDeleter'
-        )
-
-        specialMembersSeen = [False for i in range(8)]
-
         def memberNotOnParentChain(member, iface):
             assert iface
 
             if not iface.parent:
                 return True
 
             assert isinstance(iface.parent, IDLInterface)
             if member in iface.parent.members:
                 return False
             return memberNotOnParentChain(member, iface.parent)
 
+        # Ensure that there's at most one of each {named,indexed}
+        # {getter,setter,creator,deleter}.
+        specialMembersSeen = set()
         for member in members:
             if memberNotOnParentChain(member, self):
                 member.resolve(self)
-                
-            if member.tag == IDLInterfaceMember.Tags.Method:
-                if member.isGetter():
-                    if member.isNamed():
-                        if specialMembersSeen[SpecialType.NamedGetter]:
-                            raise WebIDLError("Multiple named getters on %s" % (self),
-                                              self.location)
-                        specialMembersSeen[SpecialType.NamedGetter] = True
-                    else:
-                        assert member.isIndexed()
-                        if specialMembersSeen[SpecialType.IndexedGetter]:
-                            raise WebIDLError("Multiple indexed getters on %s" % (self),
-                                              self.location)
-                        specialMembersSeen[SpecialType.IndexedGetter] = True
-                if member.isSetter():
-                    if member.isNamed():
-                        if specialMembersSeen[SpecialType.NamedSetter]:
-                            raise WebIDLError("Multiple named setters on %s" % (self),
-                                              self.location)
-                        specialMembersSeen[SpecialType.NamedSetter] = True
-                    else:
-                        assert member.isIndexed()
-                        if specialMembersSeen[SpecialType.IndexedSetter]:
-                            raise WebIDLError("Multiple indexed setters on %s" % (self),
-                                              self.location)
-                        specialMembersSeen[SpecialType.IndexedSetter] = True
-                if member.isCreator():
-                    if member.isNamed():
-                        if specialMembersSeen[SpecialType.NamedCreator]:
-                            raise WebIDLError("Multiple named creators on %s" % (self),
-                                              self.location)
-                        specialMembersSeen[SpecialType.NamedCreator] = True
-                    else:
-                        assert member.isIndexed()
-                        if specialMembersSeen[SpecialType.IndexedCreator]:
-                            raise WebIDLError("Multiple indexed creators on %s" % (self),
-                                              self.location)
-                        specialMembersSeen[SpecialType.IndexedCreator] = True
-                if member.isDeleter():
-                    if member.isNamed():
-                        if specialMembersSeen[SpecialType.NamedDeleter]:
-                            raise WebIDLError("Multiple named deleters on %s" % (self),
-                                              self.location)
-                        specialMembersSeen[SpecialType.NamedDeleter] = True
-                    else:
-                        assert member.isIndexed()
-                        if specialMembersSeen[SpecialType.IndexedDeleter]:
-                            raise WebIDLError("Multiple indexed Deleters on %s" % (self),
-                                              self.location)
-                        specialMembersSeen[SpecialType.IndexedDeleter] = True
+
+            if member.tag != IDLInterfaceMember.Tags.Method:
+                continue
+
+            if member.isGetter():
+                memberType = "getters"
+            elif member.isSetter():
+                memberType = "setters"
+            elif member.isCreator():
+                memberType = "creators"
+            elif member.isDeleter():
+                memberType = "deleters"
+            else:
+                continue
+
+            if member.isNamed():
+                memberType = "named " + memberType
+            elif member.isIndexed():
+                memberType = "indexed " + memberType
+            else:
+                continue
+
+            if memberType in specialMembersSeen:
+                raise WebIDLError("Multiple " + memberType + " on %s" % (self),
+                                   self.location)
+
+            specialMembersSeen.add(memberType)
 
         for member in self.members:
             member.finish(scope)
 
     def isInterface(self):
         return True
 
     def isExternal(self):
@@ -524,17 +509,17 @@ class IDLInterface(IDLObjectWithScope):
         depth = 0
         parent = self.parent
         while parent:
             depth = depth + 1
             parent = parent.parent
         return depth
 
     def hasConstants(self):
-        return reduce(lambda b, m: b or m.isConst(), self.members, False)
+        return any(m.isConst() for m in self.members)
 
     def hasInterfaceObject(self):
         if self.isCallback():
             return self.hasConstants()
         return not hasattr(self, "_noInterfaceObject")
 
     def hasInterfacePrototypeObject(self):
         return not self.isCallback()
@@ -562,20 +547,17 @@ class IDLInterface(IDLObjectWithScope):
 
                 args = attrlist[0] if len(attrlist) else []
 
                 retType = IDLWrapperType(self.location, self)
                 
                 identifier = IDLUnresolvedIdentifier(self.location, "constructor",
                                                      allowForbidden=True)
 
-                method = IDLMethod(self.location, identifier, retType, args,
-                                   False, False, False, False, False, False,
-                                   False, False)
-
+                method = IDLMethod(self.location, identifier, retType, args)
                 method.resolve(self)
 
             self._extendedAttrDict[identifier] = attrlist if len(attrlist) else True
 
 class IDLEnum(IDLObjectWithIdentifier):
     def __init__(self, location, parentScope, name, values):
         assert isinstance(parentScope, IDLScope)
         assert isinstance(name, IDLUnresolvedIdentifier)
@@ -758,25 +740,34 @@ class IDLNullableType(IDLType):
         return self.inner.isCallback()
 
     def isPrimitive(self):
         return self.inner.isPrimitive()
 
     def isString(self):
         return self.inner.isString()
 
+    def isFloat(self):
+        return self.inner.isFloat()
+
+    def isInteger(self):
+        return self.inner.isInteger()
+
     def isVoid(self):
         return False
 
     def isSequence(self):
         return self.inner.isSequence()
 
     def isArray(self):
         return self.inner.isArray()
 
+    def isArrayBuffer(self):
+        return self.inner.isArrayBuffer()
+
     def isDictionary(self):
         return self.inner.isDictionary()
 
     def isInterface(self):
         return self.inner.isInterface()
 
     def isEnum(self):
         return self.inner.isEnum()
@@ -792,17 +783,17 @@ class IDLNullableType(IDLType):
         return self.inner.isComplete()
 
     def complete(self, scope):
         self.inner = self.inner.complete(scope)
         self.name = self.inner.name
         return self
 
     def unroll(self):
-        return self.inner
+        return self.inner.unroll()
 
     def isDistinguishableFrom(self, other):
         if other.nullable():
             # Can't tell which type null should become
             return False
         return self.inner.isDistinguishableFrom(other)
 
 class IDLSequenceType(IDLType):
@@ -830,43 +821,45 @@ class IDLSequenceType(IDLType):
 
     def isVoid(self):
         return False
 
     def isSequence(self):
         return True
 
     def isArray(self):
-        return self.inner.isArray()
+        return False
 
     def isDictionary(self):
-        return self.inner.isDictionary()
+        return False
 
     def isInterface(self):
-        return self.inner.isInterface()
+        return False
 
     def isEnum(self):
-        return self.inner.isEnum();
+        return False
 
     def tag(self):
+        # XXXkhuey this is probably wrong.
         return self.inner.tag()
 
     def resolveType(self, parentScope):
         assert isinstance(parentScope, IDLScope)
         self.inner.resolveType(parentScope)
 
     def isComplete(self):
         return self.inner.isComplete()
 
     def complete(self, scope):
         self.inner = self.inner.complete(scope)
+        self.name = self.inner.name
         return self
 
     def unroll(self):
-        return self.inner
+        return self.inner.unroll()
 
     def isDistinguishableFrom(self, other):
         return (other.isPrimitive() or other.isString() or other.isEnum() or
                 other.isDictionary() or other.isDate() or
                 # XXXbz we should also be checking for indexed
                 # properties on interfaces
                 (other.isInterface() and not other.isCallback() and
                  not other.isArrayBuffer()))
@@ -890,57 +883,59 @@ class IDLArrayType(IDLType):
 
     def __str__(self):
         return self.inner.__str__() + "Array"
 
     def nullable(self):
         return False
 
     def isPrimitive(self):
-        return self.inner.isPrimitive()
+        return False
 
     def isString(self):
-        return self.inner.isString()
+        return False
 
     def isVoid(self):
         return False
 
     def isSequence(self):
         assert not self.inner.isSequence()
-        return self.inner.isSequence()
+        return False
 
     def isArray(self):
         return True
 
     def isDictionary(self):
         assert not self.inner.isDictionary()
-        return self.inner.isDictionary()
+        return False
 
     def isInterface(self):
-        return self.inner.isInterface()
+        return False
 
     def isEnum(self):
-        return self.inner.isEnum()
+        return False
 
     def tag(self):
+        # XXXkhuey this is probably wrong.
         return self.inner.tag()
 
     def resolveType(self, parentScope):
         assert isinstance(parentScope, IDLScope)
         self.inner.resolveType(parentScope)
 
     def isComplete(self):
         return self.inner.isComplete()
 
     def complete(self, scope):
         self.inner = self.inner.complete(scope)
+        self.name = self.inner.name
         return self
 
     def unroll(self):
-        return self.inner
+        return self.inner.unroll()
 
     def isDistinguishableFrom(self, other):
         return (other.isPrimitive() or other.isString() or other.isEnum() or
                 other.isDictionary() or other.isDate() or
                 # XXXbz we should also be checking for indexed
                 # properties on interfaces
                 (other.isInterface() and not other.isCallback() and
                  not other.isArrayBuffer()))
@@ -990,17 +985,17 @@ class IDLTypedefType(IDLType, IDLObjectW
     def resolve(self, parentScope):
         assert isinstance(parentScope, IDLScope)
         IDLObjectWithIdentifier.resolve(self, parentScope)
 
     def tag(self):
         return self.inner.tag()
 
     def unroll(self):
-        return self.inner
+        return self.inner.unroll()
 
     def isDistinguishableFrom(self, other):
         return self.inner.isDistinguishableFrom(other)
 
 class IDLWrapperType(IDLType):
     def __init__(self, location, inner):
         IDLType.__init__(self, location, inner.identifier.name)
         self.inner = inner
@@ -1036,16 +1031,20 @@ class IDLWrapperType(IDLType):
 
     def isInterface(self):
         return isinstance(self.inner, IDLInterface) or \
                isinstance(self.inner, IDLExternalInterface)
 
     def isEnum(self):
         return isinstance(self.inner, IDLEnum)
 
+    def resolveType(self, parentScope):
+        assert isinstance(parentScope, IDLScope)
+        self.inner.resolve(parentScope)
+
     def isComplete(self):
         return True
 
     def tag(self):
         if self.isInterface():
             return IDLType.Tags.interface
         elif self.isEnum():
             return IDLType.Tags.enum
@@ -1117,42 +1116,42 @@ class IDLBuiltinType(IDLType):
             Types.date: IDLType.Tags.date,
             Types.void: IDLType.Tags.void,
             Types.ArrayBuffer: IDLType.Tags.interface
         }
 
     def __init__(self, location, name, type):
         IDLType.__init__(self, location, name)
         self.builtin = True
-        self.type = type
+        self._typeTag = type
 
     def isPrimitive(self):
-        return self.type <= IDLBuiltinType.Types.double
+        return self._typeTag <= IDLBuiltinType.Types.double
 
     def isString(self):
-        return self.type == IDLBuiltinType.Types.domstring
+        return self._typeTag == IDLBuiltinType.Types.domstring
 
     def isInteger(self):
-        return self.type <= IDLBuiltinType.Types.unsigned_long_long
+        return self._typeTag <= IDLBuiltinType.Types.unsigned_long_long
 
     def isArrayBuffer(self):
-        return self.type == IDLBuiltinType.Types.ArrayBuffer
+        return self._typeTag == IDLBuiltinType.Types.ArrayBuffer
 
     def isInterface(self):
         # ArrayBuffers are interface types per the TypedArray spec,
         # but we handle them as builtins because SpiderMonkey implements
         # ArrayBuffers.
-        return self.type == IDLBuiltinType.Types.ArrayBuffer
+        return self._typeTag == IDLBuiltinType.Types.ArrayBuffer
 
     def isFloat(self):
-        return self.type == IDLBuiltinType.Types.float or \
-               self.type == IDLBuiltinType.Types.double
+        return self._typeTag == IDLBuiltinType.Types.float or \
+               self._typeTag == IDLBuiltinType.Types.double
 
     def tag(self):
-        return IDLBuiltinType.TagLookup[self.type]
+        return IDLBuiltinType.TagLookup[self._typeTag]
 
     def isDistinguishableFrom(self, other):
         if self.isPrimitive() or self.isString():
             return (other.isInterface() or other.isObject() or
                     other.isCallback() or other.isDictionary() or
                     other.isSequence() or other.isArray() or
                     other.isDate())
         if self.isAny():
@@ -1275,17 +1274,17 @@ class IDLValue(IDLObject):
         # Else, see if we can coerce to 'type'.
         if self.type.isInteger():
             if not self.type.isInteger():
                 raise WebIDLError("Cannot coerce type %s to type %s." %
                                   (self.type, type), location)
 
             # We're both integer types.  See if we fit.
 
-            (min, max) = integerTypeSizes[type.type]
+            (min, max) = integerTypeSizes[type._typeTag]
             if self.value <= max and self.value >= min:
                 # Promote
                 return IDLValue(self.location, type, self.value)
             else:
                 raise WebIDLError("Value %s is out of range for type %s." %
                                   (self.value, type), location)
         else:
             pass
@@ -1487,18 +1486,20 @@ class IDLMethod(IDLInterfaceMember, IDLS
 
     NamedOrIndexed = enum(
         'Neither',
         'Named',
         'Indexed'
     )
 
     def __init__(self, location, identifier, returnType, arguments,
-                 static, getter, setter, creator, deleter, specialType, legacycaller,
-                 stringifier):
+                 static=False, getter=False, setter=False, creator=False,
+                 deleter=False, specialType=NamedOrIndexed.Neither,
+                 legacycaller=False, stringifier=False):
+        # REVIEW: specialType is NamedOrIndexed -- wow, this is messed up.
         IDLInterfaceMember.__init__(self, location, identifier,
                                     IDLInterfaceMember.Tags.Method)
 
         self._hasOverloads = False
 
         assert isinstance(returnType, IDLType)
         self._returnType = [returnType]
 
@@ -1673,16 +1674,17 @@ class Tokenizer(object):
         "STRING",
         "WHITESPACE",
         "OTHER"
         ]
 
     def t_INTEGER(self, t):
         r'-?(0([0-7]+|[Xx][0-9A-Fa-f]+)?|[1-9][0-9]*)'
         try:
+            # Can't use int(), because that doesn't handle octal properly.
             t.value = parseInt(t.value)
         except:
             raise WebIDLError("Invalid integer literal",
                               Location(lexer=self.lexer,
                                        lineno=self.lexer.lineno,
                                        lexpos=self.lexer.lexpos,
                                        filename=self._filename))
         return t
@@ -2256,18 +2258,19 @@ class Parser(Tokenizer):
                  "indexed" if specialType == IDLMethod.NamedOrIndexed.Indexed else "",
                  "getter" if getter else "",
                  "setter" if setter else "",
                  "deleter" if deleter else "",
                  "creator" if creator else "",
                  "legacycaller" if legacycaller else ""), allowDoubleUnderscore=True)
 
         method = IDLMethod(self.getLocation(p, 2), identifier, returnType, arguments,
-                           static, getter, setter, creator, deleter, specialType,
-                           legacycaller, False)
+                           static=static, getter=getter, setter=setter, creator=creator,
+                           deleter=deleter, specialType=specialType,
+                           legacycaller=legacycaller, stringifier=False)
         p[0] = method
 
     def p_QualifiersStatic(self, p):
         """
             Qualifiers : STATIC
         """
         p[0] = [IDLMethod.Special.Static]
 
@@ -2856,17 +2859,24 @@ class Parser(Tokenizer):
         self._filename = filename
         self._productions.extend(self.parser.parse(lexer=self.lexer))
         self._filename = None
 
     def finish(self):
         for production in self._productions:
             production.finish(self.globalScope())
 
-        return set(self._productions)
+        # De-duplicate self._productions, without modifying its order.
+        seen = set()
+        result = []
+        for p in self._productions:
+            if p not in seen:
+                seen.add(p)
+                result.append(p)
+        return result
 
     def reset(self):
         return Parser()
 
     # Builtin IDL defined by WebIDL
     _builtins = """
         typedef unsigned long long DOMTimeStamp;
     """
deleted file mode 100644
--- a/dom/bindings/parser/__init__.py
+++ /dev/null
@@ -1,1 +0,0 @@
-__all__ = ['WebIDL']
--- a/dom/bindings/parser/runtests.py
+++ b/dom/bindings/parser/runtests.py
@@ -32,41 +32,81 @@
 # 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 *****
 
 import os, sys
 import glob
+import optparse
+import traceback
 import WebIDL
 
 class TestHarness(object):
+    def __init__(self, test, verbose):
+        self.test = test
+        self.verbose = verbose
+        self.printed_intro = False
+
+    def start(self):
+        if self.verbose:
+            self.maybe_print_intro()
+
+    def finish(self):
+        if self.verbose or self.printed_intro:
+            print "Finished test %s" % self.test
+
+    def maybe_print_intro(self):
+        if not self.printed_intro:
+            print "Starting test %s" % self.test
+            self.printed_intro = True
+
+    def test_pass(self, msg):
+        if self.verbose:
+            print "TEST-PASS | %s" % msg
+
+    def test_fail(self, msg):
+        self.maybe_print_intro()
+        print "TEST-UNEXPECTED-FAIL | %s" % msg
+
     def ok(self, condition, msg):
         if condition:
-            print "TEST-PASS | %s" % msg
+            self.test_pass(msg)
         else:
-            print "TEST-UNEXPECTED-FAIL | %s" % msg
+            self.test_fail(msg)
 
     def check(self, a, b, msg):
         if a == b:
-            print "TEST-PASS | %s" % msg
+            self.test_pass(msg)
         else:
-            print "TEST-UNEXPECTED-FAIL | %s" % msg
+            self.test_fail(msg)
             print "\tGot %s expected %s" % (a, b)
 
-def run_tests():
-    harness = TestHarness()
+def run_tests(tests, verbose):
+    testdir = os.path.join(os.path.dirname(__file__), 'tests')
+    if not tests:
+        tests = glob.iglob(os.path.join(testdir, "*.py"))
+    sys.path.append(testdir)
 
-    tests = glob.iglob("tests/*.py")
-    sys.path.append("./tests")
     for test in tests:
         (testpath, ext) = os.path.splitext(os.path.basename(test))
         _test = __import__(testpath, globals(), locals(), ['WebIDLTest'])
-        #try:
-        _test.WebIDLTest.__call__(WebIDL.Parser(), harness)
-        #except:
-        #    print "TEST-UNEXPECTED-FAIL | Unhandled exception in Test %s" % testpath
-        #    print sys.exc_info()[0]
-        print "Test %s Complete\n" % testpath
+
+        harness = TestHarness(test, verbose)
+        harness.start()
+        try:
+            _test.WebIDLTest.__call__(WebIDL.Parser(), harness)
+        except:
+            print "TEST-UNEXPECTED-FAIL | Unhandled exception in test %s" % testpath
+            traceback.print_exc()
+        finally:
+            harness.finish()
 
 if __name__ == '__main__':
-    run_tests()
+    usage = """%prog [OPTIONS] [TESTS]
+               Where TESTS are relative to the tests directory."""
+    parser = optparse.OptionParser(usage=usage)
+    parser.add_option('-q', '--quiet', action='store_false', dest='verbose', default=True,
+                      help="Don't print passing tests.")
+    options, tests = parser.parse_args()
+
+    run_tests(tests, verbose=options.verbose)
new file mode 100644
--- /dev/null
+++ b/dom/bindings/parser/tests/test_array_of_interface.py
@@ -0,0 +1,13 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+    parser.parse("""
+      interface A {
+        attribute long a;
+      };
+
+      interface B {
+        attribute A[] b;
+      };
+    """);
+    parser.finish()
new file mode 100644
--- /dev/null
+++ b/dom/bindings/parser/tests/test_builtin_filename.py
@@ -0,0 +1,11 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+    parser.parse("""
+        interface Test {
+          attribute long b;
+        };
+    """);
+
+    attr = parser.finish()[0].members[0]
+    harness.check(attr.type.filename(), '<builtin>', 'Filename on builtin type')
--- a/dom/bindings/parser/tests/test_constructor.py
+++ b/dom/bindings/parser/tests/test_constructor.py
@@ -57,19 +57,19 @@ def WebIDLTest(parser, harness):
     results = parser.finish()
     harness.check(len(results), 3, "Should be two productions")
     harness.ok(isinstance(results[0], WebIDL.IDLInterface),
                "Should be an IDLInterface")
     harness.ok(isinstance(results[1], WebIDL.IDLInterface),
                "Should be an IDLInterface")
 
     checkMethod(results[0].ctor(), "::TestConstructorNoArgs::constructor",
-                "constructor", [("TestConstructorNoArgs", [])])
+                "constructor", [("TestConstructorNoArgs (Wrapper)", [])])
     checkMethod(results[1].ctor(), "::TestConstructorWithArgs::constructor",
                 "constructor",
-                [("TestConstructorWithArgs",
+                [("TestConstructorWithArgs (Wrapper)",
                  [("::TestConstructorWithArgs::constructor::name", "name", "String", False, False)])])
     checkMethod(results[2].ctor(), "::TestConstructorOverloads::constructor",
                 "constructor",
-                [("TestConstructorOverloads",
+                [("TestConstructorOverloads (Wrapper)",
                  [("::TestConstructorOverloads::constructor::foo", "foo", "Object", False, False)]),
-                 ("TestConstructorOverloads",
+                 ("TestConstructorOverloads (Wrapper)",
                  [("::TestConstructorOverloads::constructor::bar", "bar", "Boolean", False, False)])])
new file mode 100644
--- /dev/null
+++ b/dom/bindings/parser/tests/test_deduplicate.py
@@ -0,0 +1,15 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+    parser.parse("""
+        interface Foo;
+        interface Bar;
+        interface Foo;
+        """);
+
+    results = parser.finish()
+
+    # There should be no duplicate interfaces in the result.
+    expectedNames = sorted(['Foo', 'Bar'])
+    actualNames = sorted(map(lambda iface: iface.identifier.name, results))
+    harness.check(actualNames, expectedNames, "Parser shouldn't output duplicate names.")
--- a/dom/bindings/parser/tests/test_enum.py
+++ b/dom/bindings/parser/tests/test_enum.py
@@ -42,20 +42,20 @@ def WebIDLTest(parser, harness):
     harness.check(method.identifier.QName(), "::TestEnumInterface::doFoo",
                   "Method has correct QName")
     harness.check(method.identifier.name, "doFoo", "Method has correct name")
 
     signatures = method.signatures()
     harness.check(len(signatures), 1, "Expect one signature")
 
     (returnType, arguments) = signatures[0]
-    harness.check(str(returnType), "TestEnum", "Method type is the correct name")
+    harness.check(str(returnType), "TestEnum (Wrapper)", "Method type is the correct name")
     harness.check(len(arguments), 1, "Method has the right number of arguments")
     arg = arguments[0]
     harness.ok(isinstance(arg, WebIDL.IDLArgument), "Should be an IDLArgument")
     harness.check(str(arg.type), "Boolean", "Argument has the right type")
 
     attr = members[1]
     harness.check(attr.identifier.QName(), "::TestEnumInterface::foo",
                   "Attr has correct QName")
     harness.check(attr.identifier.name, "foo", "Attr has correct name")
 
-    harness.check(str(attr.type), "TestEnum", "Attr type is the correct name")
+    harness.check(str(attr.type), "TestEnum (Wrapper)", "Attr type is the correct name")
new file mode 100644
--- /dev/null
+++ b/dom/bindings/parser/tests/test_error_colno.py
@@ -0,0 +1,20 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+    # Check that error messages put the '^' in the right place.
+
+    threw = False
+    input = 'interface ?'
+    try:
+        parser.parse(input)
+        results = parser.finish()
+    except WebIDL.WebIDLError as e:
+        threw = True
+        lines = str(e).split('\n')
+
+        harness.check(len(lines), 3, 'Expected number of lines in error message')
+        harness.check(lines[1], input, 'Second line shows error')
+        harness.check(lines[2], ' ' * (len(input) - 1) + '^',
+                      'Correct column pointer in error message')
+
+    harness.ok(threw, "Should have thrown.")
new file mode 100644
--- /dev/null
+++ b/dom/bindings/parser/tests/test_nullable_equivalency.py
@@ -0,0 +1,134 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+    parser.parse("""
+        interface TestNullableEquivalency1 {
+          attribute long  a;
+          attribute long? b;
+        };
+
+        interface TestNullableEquivalency2 {
+          attribute ArrayBuffer  a;
+          attribute ArrayBuffer? b;
+        };
+
+        /* Not implemented */
+        /*dictionary TestNullableEquivalency3Dict {
+          long foo = 42;
+        };
+
+        interface TestNullableEquivalency3 {
+          attribute Test3Dict  a;
+          attribute Test3Dict? b;
+        };*/
+
+        enum TestNullableEquivalency4Enum {
+          "Foo",
+          "Bar"
+        };
+
+        interface TestNullableEquivalency4 {
+          attribute TestNullableEquivalency4Enum  a;
+          attribute TestNullableEquivalency4Enum? b;
+        };
+
+        interface TestNullableEquivalency5 {
+          attribute TestNullableEquivalency4  a;
+          attribute TestNullableEquivalency4? b;
+        };
+
+        interface TestNullableEquivalency6 {
+          attribute boolean  a;
+          attribute boolean? b;
+        };
+
+        interface TestNullableEquivalency7 {
+          attribute DOMString  a;
+          attribute DOMString? b;
+        };
+
+        /* Not implemented. */
+        /*interface TestNullableEquivalency8 {
+          attribute float  a;
+          attribute float? b;
+        };*/
+
+        interface TestNullableEquivalency8 {
+          attribute double  a;
+          attribute double? b;
+        };
+
+        interface TestNullableEquivalency9 {
+          attribute object  a;
+          attribute object? b;
+        };
+
+        interface TestNullableEquivalency10 {
+          attribute double[]  a;
+          attribute double[]? b;
+        };
+
+        interface TestNullableEquivalency11 {
+          attribute TestNullableEquivalency9[]  a;
+          attribute TestNullableEquivalency9[]? b;
+        };
+    """)
+
+    for decl in parser.finish():
+        if decl.isInterface():
+            checkEquivalent(decl, harness)
+
+def checkEquivalent(iface, harness):
+    type1 = iface.members[0].type
+    type2 = iface.members[1].type
+
+    harness.check(type1.nullable(), False, 'attr1 should not be nullable')
+    harness.check(type2.nullable(), True, 'attr2 should be nullable')
+
+    # We don't know about type1, but type2, the nullable type, definitely
+    # shouldn't be builtin.
+    harness.check(type2.builtin, False, 'attr2 should not be builtin')
+
+    # Ensure that all attributes of type2 match those in type1, except for:
+    #  - names on an ignore list,
+    #  - names beginning with '_',
+    #  - functions which throw when called with no args, and
+    #  - class-level non-callables ("static variables").
+    #
+    # Yes, this is an ugly, fragile hack.  But it finds bugs...
+    for attr in dir(type1):
+        if attr.startswith('_') or \
+           attr in ['nullable', 'builtin', 'filename', 'location',
+                    'inner', 'QName'] or \
+           (hasattr(type(type1), attr) and not callable(getattr(type1, attr))):
+            continue
+
+        a1 = getattr(type1, attr)
+
+        if callable(a1):
+            try:
+                v1 = a1()
+            except:
+                # Can't call a1 with no args, so skip this attriute.
+                continue
+
+            try:
+                a2 = getattr(type2, attr)
+            except:
+                harness.ok(False, 'Missing %s attribute on type %s in %s' % (attr, type2, iface))
+                continue
+
+            if not callable(a2):
+                harness.ok(False, "%s attribute on type %s in %s wasn't callable" % (attr, type2, iface))
+                continue
+
+            v2 = a2()
+            harness.check(v2, v1, '%s method return value' % attr)
+        else:
+            try:
+                a2 = getattr(type2, attr)
+            except:
+                harness.ok(False, 'Missing %s attribute on type %s in %s' % (attr, type2, iface))
+                continue
+
+            harness.check(a2, a1, '%s attribute should match' % attr)
--- a/dom/plugins/ipc/PluginModuleParent.cpp
+++ b/dom/plugins/ipc/PluginModuleParent.cpp
@@ -553,19 +553,21 @@ PluginModuleParent::NPP_SetValue(NPP ins
 }
 
 bool
 PluginModuleParent::RecvBackUpXResources(const FileDescriptor& aXSocketFd)
 {
 #ifndef MOZ_X11
     NS_RUNTIMEABORT("This message only makes sense on X11 platforms");
 #else
-    NS_ABORT_IF_FALSE(0 > mPluginXSocketFdDup.mFd,
+    NS_ABORT_IF_FALSE(0 > mPluginXSocketFdDup.get(),
                       "Already backed up X resources??");
-    mPluginXSocketFdDup.mFd = aXSocketFd.fd;
+    int fd = aXSocketFd.fd; // Copy to discard |const| qualifier
+    mPluginXSocketFdDup.forget();
+    mPluginXSocketFdDup.reset(fd);
 #endif
     return true;
 }
 
 void
 PluginModuleParent::NPP_URLRedirectNotify(NPP instance, const char* url,
                                           int32_t status, void* notifyData)
 {
--- a/dom/system/gonk/ril_worker.js
+++ b/dom/system/gonk/ril_worker.js
@@ -653,17 +653,24 @@ let RIL = {
     }
     if (model_id == "I9023" || model_id == "I9020") {
       if (DEBUG) {
         debug("Detected I9020/I9023, enabling " +
               "RILQUIRKS_DATACALLSTATE_DOWN_IS_UP");
       }
       RILQUIRKS_DATACALLSTATE_DOWN_IS_UP = true;
     }
-
+    let ril_impl = libcutils.property_get("gsm.version.ril-impl");
+    if (ril_impl == "Qualcomm RIL 1.0") {
+      if (DEBUG) {
+        debug("Detected Qualcomm RIL 1.0, " +
+              "disabling RILQUIRKS_V5_LEGACY to false");
+      }
+      RILQUIRKS_V5_LEGACY = false;
+    }
     this.rilQuirksInitialized = true;
   },
 
   /**
    * Parse an integer from a string, falling back to a default value
    * if the the provided value is not a string or does not contain a valid
    * number.
    *
@@ -2349,22 +2356,16 @@ RIL[UNSOLICITED_RESPONSE_RADIO_STATE_CHA
   });
 
   // If the radio is up and on, so let's query the card state.
   // On older RILs only if the card is actually ready, though.
   if (radioState == RADIO_STATE_UNAVAILABLE ||
       radioState == RADIO_STATE_OFF) {
     return;
   }
-  if (RILQUIRKS_V5_LEGACY &&
-      (radioState == RADIO_STATE_SIM_NOT_READY ||
-       radioState == RADIO_STATE_RUIM_NOT_READY ||
-       radioState == RADIO_STATE_NV_NOT_READY)) {
-    return;
-  }
   this.getICCStatus();
 };
 RIL[UNSOLICITED_RESPONSE_CALL_STATE_CHANGED] = function UNSOLICITED_RESPONSE_CALL_STATE_CHANGED() {
   this.getCurrentCalls();
 };
 RIL[UNSOLICITED_RESPONSE_VOICE_NETWORK_STATE_CHANGED] = function UNSOLICITED_RESPONSE_VOICE_NETWORK_STATE_CHANGED() {
   if (DEBUG) debug("Network state changed, re-requesting phone state.");
   this.requestNetworkInfo();
@@ -2464,25 +2465,31 @@ RIL[UNSOLICITED_RESTRICTED_STATE_CHANGED
 RIL[UNSOLICITED_ENTER_EMERGENCY_CALLBACK_MODE] = null;
 RIL[UNSOLICITED_CDMA_CALL_WAITING] = null;
 RIL[UNSOLICITED_CDMA_OTA_PROVISION_STATUS] = null;
 RIL[UNSOLICITED_CDMA_INFO_REC] = null;
 RIL[UNSOLICITED_OEM_HOOK_RAW] = null;
 RIL[UNSOLICITED_RINGBACK_TONE] = null;
 RIL[UNSOLICITED_RESEND_INCALL_MUTE] = null;
 RIL[UNSOLICITED_RIL_CONNECTED] = function UNSOLICITED_RIL_CONNECTED(length) {
+  // Prevent response id collision between UNSOLICITED_RIL_CONNECTED and
+  // UNSOLICITED_VOICE_RADIO_TECH_CHANGED for Akami on gingerbread branch.
+  if (!length) {
+    this.initRILQuirks();
+    return;
+  }
+
   let version = Buf.readUint32List()[0];
   RILQUIRKS_V5_LEGACY = (version < 5);
   if (DEBUG) {
     debug("Detected RIL version " + version);
     debug("RILQUIRKS_V5_LEGACY is " + RILQUIRKS_V5_LEGACY);
   }
 };
 
-
 /**
  * This object exposes the functionality to parse and serialize PDU strings
  *
  * A PDU is a string containing a series of hexadecimally encoded octets
  * or nibble-swapped binary-coded decimals (BCDs). It contains not only the
  * message text but information about the sender, the SMS service center,
  * timestamp, etc.
  */
--- a/dom/system/gonk/systemlibs.js
+++ b/dom/system/gonk/systemlibs.js
@@ -1,16 +1,19 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 const SYSTEM_PROPERTY_KEY_MAX = 32;
 const SYSTEM_PROPERTY_VALUE_MAX = 92;
 
-const DEBUG=false;
+// We leave this as 'undefined' instead of setting it to 'false'. That
+// way a file that includes us can have it defined already without us
+// overriding the value here.
+let DEBUG;
 
 /**
  * Expose some system-level functions.
  */
 let libcutils = (function() {
   let lib;
   try {
     lib = ctypes.open("libcutils.so");
--- a/dom/wifi/DOMWifiManager.js
+++ b/dom/wifi/DOMWifiManager.js
@@ -121,22 +121,22 @@ DOMWifiManager.prototype = {
         break;
 
       case "WifiManager:associate:Return:NO":
         request = this.takeRequest(msg.rid);
         Services.DOMRequest.fireError(request, "Unable to add the network");
         break;
 
       case "WifiManager:forget:Return:OK":
-        request = this._takeRequest(msg.rid);
+        request = this.takeRequest(msg.rid);
         Services.DOMRequest.fireSuccess(request, true);
         break;
 
       case "WifiManager:forget:Return:NO":
-        request = this._takeRequest(msg.rid);
+        request = this.takeRequest(msg.rid);
         Services.DOMRequest.fireError(request, msg.data);
         break;
 
       case "WifiManager:onconnecting":
         this._currentNetwork = msg.network;
         this._fireOnConnecting(msg.network);
         break;
 
@@ -216,17 +216,17 @@ DOMWifiManager.prototype = {
     var request = this.createRequest();
     this._sendMessageForRequest("WifiManager:associate", network, request);
     return request;
   },
 
   forget: function nsIDOMWifiManager_forget(network) {
     if (!this._hasPrivileges)
       throw new Components.Exception("Denied", Cr.NS_ERROR_FAILURE);
-    var request = Services.DOMRequest.createRequest(this._window);
+    var request = this.createRequest();
     this._sendMessageForRequest("WifiManager:forget", network, request);
     return request;
   },
 
   get enabled() {
     if (!this._hasPrivileges)
       throw new Components.Exception("Denied", Cr.NS_ERROR_FAILURE);
     return this._enabled;
--- a/dom/wifi/WifiWorker.js
+++ b/dom/wifi/WifiWorker.js
@@ -5,17 +5,17 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
-const DEBUG = false; // set to false to suppress debug messages
+const DEBUG = false; // set to true to show debug messages
 
 const WIFIWORKER_CONTRACTID = "@mozilla.org/wifi/worker;1";
 const WIFIWORKER_CID        = Components.ID("{a14e8977-d259-433a-a88d-58dd44657e5b}");
 
 const WIFIWORKER_WORKER     = "resource://gre/modules/wifi_worker.js";
 
 // A note about errors and error handling in this file:
 // The libraries that we use in this file are intended for C code. For
@@ -438,27 +438,25 @@ var WifiManager = (function() {
       dhcpInfo = data.status ? null : data;
       notify("dhcpconnected", { info: dhcpInfo });
       callback(data.status ? null : data);
     });
   }
 
   function stopDhcp(ifname, callback) {
     controlMessage({ cmd: "dhcp_stop", ifname: ifname }, function(data) {
-      if (!data.status)
-        dhcpInfo = null;
+      dhcpInfo = null;
       notify("dhcplost");
       callback(!data.status);
     });
   }
 
   function releaseDhcpLease(ifname, callback) {
     controlMessage({ cmd: "dhcp_release_lease", ifname: ifname }, function(data) {
-      if (!data.status)
-        dhcpInfo = null;
+      dhcpInfo = null;
       notify("dhcplost");
       callback(!data.status);
     });
   }
 
   function getDhcpError(callback) {
     controlMessage({ cmd: "dhcp_get_errmsg" }, function(data) {
       callback(data.error);
@@ -494,21 +492,16 @@ var WifiManager = (function() {
       handler.call(eventObject);
     }
   }
 
   function notifyStateChange(fields) {
     fields.prevState = manager.state;
     manager.state = fields.state;
 
-    // If we got disconnected, kill the DHCP client in preparation for
-    // reconnection.
-    if (fields.state === "DISCONNECTED" && dhcpInfo)
-      stopDhcp(manager.ifname, function() {});
-
     notify("statechange", fields);
   }
 
   function parseStatus(status, reconnected) {
     if (status === null) {
       debug("Unable to get wpa supplicant's status");
       return;
     }
@@ -575,16 +568,26 @@ var WifiManager = (function() {
         }, 5000, Ci.nsITimer.TYPE_ONE_SHOT);
       return;
     }
 
     retryTimer = null;
     notify("supplicantlost");
   }
 
+  manager.connectionDropped = function(callback) {
+    // If we got disconnected, kill the DHCP client in preparation for
+    // reconnection.
+    resetConnections(manager.ifname, function() {
+      stopDhcp(manager.ifname, function() {
+        callback();
+      });
+    });
+  }
+
   manager.start = function() {
     debug("detected SDK version " + sdkVersion);
 
     // If we reconnected to an already-running supplicant, then manager.state
     // will have already been updated to the supplicant's state. Otherwise, we
     // started the supplicant ourselves and need to connect.
     if (manager.state === "UNINITIALIZED")
       connectToSupplicant(connectCallback);
@@ -717,19 +720,19 @@ var WifiManager = (function() {
       // will keep going for a bit longer
       if (eventData.indexOf("recv error") !== -1 && ++recvErrors < 10)
         return true;
 
       notify("supplicantlost");
       return false;
     }
     if (eventData.indexOf("CTRL-EVENT-DISCONNECTED") === 0) {
-      notifyStateChange({ state: "DISCONNECTED" });
       manager.connectionInfo.bssid = null;
       manager.connectionInfo.ssid = null;
+      manager.connectionInfo.id = -1;
       return true;
     }
     if (eventData.indexOf("CTRL-EVENT-CONNECTED") === 0) {
       // Format: CTRL-EVENT-CONNECTED - Connection to 00:1e:58:ec:d5:6d completed (reauth) [id=1 id_str=]
       var bssid = eventData.split(" ")[4];
       var id = eventData.substr(eventData.indexOf("id=")).split(" ")[0];
       notifyStateChange({ state: "CONNECTED", BSSID: bssid, id: id });
       onconnected(false);
@@ -864,21 +867,23 @@ var WifiManager = (function() {
           });
         });
       });
     } else {
       // Note these following calls ignore errors. If we fail to kill the
       // supplicant gracefully, then we need to continue telling it to die
       // until it does.
       terminateSupplicant(function (ok) {
-        stopSupplicant(function (status) {
-          manager.state = "UNINITIALIZED";
-          closeSupplicantConnection(function () {
-            disableInterface(manager.ifname, function (ok) {
-              unloadDriver(callback);
+        manager.connectionDropped(function () {
+          stopSupplicant(function (status) {
+            manager.state = "UNINITIALIZED";
+            closeSupplicantConnection(function () {
+              disableInterface(manager.ifname, function (ok) {
+                unloadDriver(callback);
+              });
             });
           });
         });
       });
     }
   }
 
   manager.disconnect = disconnectCommand;
@@ -1088,17 +1093,18 @@ function isWepHexKey(s) {
 let netToDOM;
 let netFromDOM;
 
 function WifiWorker() {
   var self = this;
 
   this._mm = Cc["@mozilla.org/parentprocessmessagemanager;1"].getService(Ci.nsIFrameMessageManager);
   const messages = ["WifiManager:setEnabled", "WifiManager:getNetworks",
-                    "WifiManager:associate", "WifiManager:getState"];
+                    "WifiManager:associate", "WifiManager:forget",
+                    "WifiManager:getState"];
 
   messages.forEach((function(msgName) {
     this._mm.addMessageListener(msgName, this);
   }).bind(this));
 
   this.wantScanResults = [];
 
   this._needToEnableNetworks = false;
@@ -1224,16 +1230,22 @@ function WifiWorker() {
         // id has not yet been filled in, so we can only report the ssid and
         // bssid.
         self.currentNetwork =
           { bssid: WifiManager.connectionInfo.bssid,
             ssid: quote(WifiManager.connectionInfo.ssid) };
         self._fireEvent("onconnecting", { network: netToDOM(self.currentNetwork) });
         break;
       case "ASSOCIATED":
+        if (!self.currentNetwork) {
+          self.currentNetwork =
+            { bssid: WifiManager.connectionInfo.bssid,
+              ssid: quote(WifiManager.connectionInfo.ssid) };
+        }
+
         self.currentNetwork.netId = this.id;
         WifiManager.getNetworkConfiguration(self.currentNetwork, function (){});
         break;
       case "COMPLETED":
         // Now that we've successfully completed the connection, re-enable the
         // rest of our networks.
         // XXX Need to do this eventually if the user entered an incorrect
         // password. For now, we require user interaction to break the loop and
@@ -1245,35 +1257,37 @@ function WifiWorker() {
 
         // We get the ASSOCIATED event when we've associated but not connected, so
         // wait until the handshake is complete.
         if (this.fromStatus) {
           // In this case, we connected to an already-connected wpa_supplicant,
           // because of that we need to gather information about the current
           // network here.
           self.currentNetwork = { ssid: quote(WifiManager.connectionInfo.ssid),
-                                  known: true }
+                                  netId: WifiManager.connectionInfo.id };
           WifiManager.getNetworkConfiguration(self.currentNetwork, function(){});
         }
 
         self._startConnectionInfoTimer();
         self._fireEvent("onassociate", { network: netToDOM(self.currentNetwork) });
         break;
       case "CONNECTED":
         break;
       case "DISCONNECTED":
         self._fireEvent("ondisconnect", {});
         self.currentNetwork = null;
 
-        // We've disconnected from a network because of a call to forgetNetwork.
-        // Reconnect to the next available network (if any).
-        if (self._reconnectOnDisconnect) {
-          self._reconnectOnDisconnect = false;
-          WifiManager.reconnect(function(){});
-        }
+        WifiManager.connectionDropped(function() {
+          // We've disconnected from a network because of a call to forgetNetwork.
+          // Reconnect to the next available network (if any).
+          if (self._reconnectOnDisconnect) {
+            self._reconnectOnDisconnect = false;
+            WifiManager.reconnect(function(){});
+          }
+        });
 
         break;
     }
   };
 
   WifiManager.ondhcpconnected = function() {
     if (this.info)
       self._fireEvent("onconnect", { network: netToDOM(self.currentNetwork) });
@@ -1284,16 +1298,23 @@ function WifiWorker() {
   WifiManager.onscanresultsavailable = function() {
     if (self.wantScanResults.length === 0) {
       debug("Scan results available, but we don't need them");
       return;
     }
 
     debug("Scan results are available! Asking for them.");
     WifiManager.getScanResults(function(r) {
+      // Failure.
+      if (!r) {
+        self.wantScanResults.forEach(function(callback) { callback(null) });
+        self.wantScanResults = [];
+        return;
+      }
+
       // Now that we have scan results, there's no more need to continue
       // scanning. Ignore any errors from this command.
       WifiManager.setScanMode("inactive", function() {});
       let lines = r.split("\n");
       // NB: Skip the header line.
       self.networks = Object.create(null);
       for (let i = 1; i < lines.length; ++i) {
         // bssid / frequency / signal level / flags / ssid
@@ -1563,35 +1584,45 @@ WifiWorker.prototype = {
         return { network: net,
                  connectionInfo: this._lastConnectionInfo,
                  enabled: WifiManager.state !== "UNINITIALIZED", };
       }
     }
   },
 
   getNetworks: function(rid, mid) {
+    const message = "WifiManager:getNetworks:Return";
+    if (WifiManager.state === "UNINITIALIZED") {
+      this._sendMessage(message, false, "Wifi is disabled", rid, mid);
+      return;
+    }
+
     this.waitForScan((function (networks) {
-      this._sendMessage("WifiManager:getNetworks:Return",
-                        networks !== null, networks, rid, mid);
+      this._sendMessage(message, networks !== null, networks, rid, mid);
     }).bind(this));
     WifiManager.scan(true, function() {});
   },
 
   setWifiEnabled: function(enable, rid, mid) {
     WifiManager.setWifiEnabled(enable, (function (status) {
       if (enable && status === 0)
         WifiManager.start();
       this._sendMessage("WifiManager:setEnabled:Return",
                         (status === 0), enable, rid, mid);
     }).bind(this));
   },
 
   associate: function(network, rid, mid) {
     const MAX_PRIORITY = 9999;
     const message = "WifiManager:associate:Return";
+    if (WifiManager.state === "UNINITIALIZED") {
+      this._sendMessage(message, false, "Wifi is disabled", rid, mid);
+      return;
+    }
+
     let privnet = network;
     let self = this;
     function networkReady() {
       // saveConfig now before we disable most of the other networks.
       function selectAndConnect() {
         WifiManager.enableNetwork(privnet.netId, true, function (ok) {
           if (ok)
             self._needToEnableNetworks = true;
@@ -1646,25 +1677,31 @@ WifiWorker.prototype = {
         this.configuredNetworks[ssid] = privnet;
         networkReady();
       }).bind(this));
     }
   },
 
   forget: function(network, rid, mid) {
     const message = "WifiManager:forget:Return";
+    if (WifiManager.state === "UNINITIALIZED") {
+      this._sendMessage(message, false, "Wifi is disabled", rid, mid);
+      return;
+    }
+
     let ssid = network.ssid;
     if (!(ssid in this.configuredNetworks)) {
       this._sendMessage(message, false, "Trying to forget an unknown network", rid, mid);
       return;
     }
 
     let self = this;
     let configured = this.configuredNetworks[ssid];
-    this._reconnectOnDisconnect = (this._currentNetwork.ssid === ssid);
+    this._reconnectOnDisconnect = (this.currentNetwork &&
+                                   (this.currentNetwork.ssid === ssid));
     WifiManager.removeNetwork(configured.netId, function(ok) {
       if (!ok) {
         self._sendMessage(message, false, "Unable to remove the network", rid, mid);
         self._reconnectOnDisconnect = false;
         return;
       }
 
       WifiManager.saveConfig(function() {
--- a/dom/wifi/wifi_worker.js
+++ b/dom/wifi/wifi_worker.js
@@ -8,17 +8,17 @@
 
 importScripts("libhardware_legacy.js", "systemlibs.js");
 
 var cbuf = ctypes.char.array(4096)();
 var hwaddr = ctypes.uint8_t.array(6)();
 var len = ctypes.size_t();
 var ints = ctypes.int.array(8)();
 
-const DEBUG = false;
+let DEBUG = false;
 
 let debug;
 if (DEBUG) {
   debug = function (s) {
     dump("-*- WifiWorker component: " + s + "\n");
   };
 } else {
   debug = function (s) {};
--- a/gfx/2d/Factory.cpp
+++ b/gfx/2d/Factory.cpp
@@ -94,40 +94,38 @@ Factory::CreateDrawTarget(BackendType aB
     {
       RefPtr<DrawTargetD2D> newTarget;
       newTarget = new DrawTargetD2D();
       if (newTarget->Init(aSize, aFormat)) {
         return newTarget;
       }
       break;
     }
-#elif defined XP_MACOSX || defined ANDROID || defined LINUX
+#elif defined XP_MACOSX
+  case BACKEND_COREGRAPHICS:
+    {
+      RefPtr<DrawTargetCG> newTarget;
+      newTarget = new DrawTargetCG();
+      if (newTarget->Init(aSize, aFormat)) {
+        return newTarget;
+      }
+      break;
+    }
+#endif
 #ifdef USE_SKIA
   case BACKEND_SKIA:
     {
       RefPtr<DrawTargetSkia> newTarget;
       newTarget = new DrawTargetSkia();
       if (newTarget->Init(aSize, aFormat)) {
         return newTarget;
       }
       break;
     }
 #endif
-#ifdef XP_MACOSX
-  case BACKEND_COREGRAPHICS:
-    {
-      RefPtr<DrawTargetCG> newTarget;
-      newTarget = new DrawTargetCG();
-      if (newTarget->Init(aSize, aFormat)) {
-        return newTarget;
-      }
-      break;
-    }
-#endif
-#endif
   default:
     gfxDebug() << "Invalid draw target type specified.";
     return NULL;
   }
 
   gfxDebug() << "Failed to create DrawTarget, Type: " << aBackend << " Size: " << aSize;
   // Failed
   return NULL;
--- a/gfx/2d/ScaledFontWin.cpp
+++ b/gfx/2d/ScaledFontWin.cpp
@@ -31,17 +31,17 @@
  * 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 ***** */
 
 #include "ScaledFontWin.h"
-#include "ScaeldFontBase.h"
+#include "ScaledFontBase.h"
 
 #ifdef USE_SKIA
 #include "skia/SkTypeface_win.h"
 #endif
 
 namespace mozilla {
 namespace gfx {
 
@@ -50,17 +50,17 @@ ScaledFontWin::ScaledFontWin(LOGFONT* aF
   , mLogFont(*aFont)
 {
 }
 
 #ifdef USE_SKIA
 SkTypeface* ScaledFontWin::GetSkTypeface()
 {
   if (!mTypeface) {
-    mTypeface = SkCreateTypefaceFromLOGFONT(lf);
+    mTypeface = SkCreateTypefaceFromLOGFONT(mLogFont);
   }
   return mTypeface;
 }
 #endif
 
 
 }
 }
--- a/gfx/src/X11Util.h
+++ b/gfx/src/X11Util.h
@@ -47,16 +47,18 @@
 #elif defined(MOZ_WIDGET_QT)
 #include "gfxQtPlatform.h"
 #undef CursorShape
 #  include <X11/Xlib.h>
 #else
 #  error Unknown toolkit
 #endif 
 
+#include "mozilla/Scoped.h"
+
 #include "gfxCore.h"
 #include "nsDebug.h"
 
 namespace mozilla {
 
 /**
  * Return the default X Display created and used by the UI toolkit.
  */
@@ -80,48 +82,24 @@ DefaultXDisplay()
 bool
 XVisualIDToInfo(Display* aDisplay, VisualID aVisualID,
                 Visual** aVisual, unsigned int* aDepth);
 
 /**
  * Invoke XFree() on a pointer to memory allocated by Xlib (if the
  * pointer is nonnull) when this class goes out of scope.
  */
-template<typename T>
-struct ScopedXFree
+template <typename T>
+struct ScopedXFreePtrTraits
 {
-  ScopedXFree() : mPtr(NULL) {}
-  ScopedXFree(T* aPtr) : mPtr(aPtr) {}
-
-  ~ScopedXFree() { Assign(NULL); }
-
-  ScopedXFree& operator=(T* aPtr) { Assign(aPtr); return *this; }
-
-  operator T*() const { return get(); }
-  T* operator->() const { return get(); }
-  T* get() const { return mPtr; }
-
-private:
-  void Assign(T* aPtr)
-  {
-    NS_ASSERTION(!mPtr || mPtr != aPtr, "double-XFree() imminent");
-
-    if (mPtr)
-      XFree(mPtr);
-    mPtr = aPtr;
-  }
-
-  T* mPtr;
-
-  // disable these
-  ScopedXFree(const ScopedXFree&);
-  ScopedXFree& operator=(const ScopedXFree&);
-  static void* operator new (size_t);
-  static void operator delete (void*);
+  typedef T *type;
+  static T *empty() { return NULL; }
+  static void release(T *ptr) { if (ptr!=NULL) XFree(ptr); }
 };
+SCOPED_TEMPLATE(ScopedXFree, ScopedXFreePtrTraits);
 
 /**
  * On construction, set a graceful X error handler that doesn't crash the application and records X errors.
  * On destruction, restore the X error handler to what it was before construction.
  * 
  * The SyncAndGetError() method allows to know whether a X error occurred, optionally allows to get the full XErrorEvent,
  * and resets the recorded X error state so that a single X error will be reported only once.
  *
--- a/gfx/thebes/gfxDrawable.cpp
+++ b/gfx/thebes/gfxDrawable.cpp
@@ -79,31 +79,16 @@ PreparePatternForUntiledDrawing(gfxPatte
         return;
     }
 
     // In theory we can handle this using cairo's EXTEND_PAD,
     // but implementation limitations mean we have to consult
     // the surface type.
     switch (currentTarget->GetType()) {
 
-        // The printing surfaces don't natively support or need
-        // EXTEND_PAD for padding the edges. Using EXTEND_PAD this way
-        // is suboptimal as it will result in the printing surface
-        // creating a new image for each fill operation. The pattern
-        // will be painted to the image to pad out the pattern, then
-        // the new image will be used as the source. This increases
-        // printing time and memory use, and prevents the use of mime
-        // data from cairo_surface_set_mime_data(). Bug 691061.
-        case gfxASurface::SurfaceTypePDF:
-        case gfxASurface::SurfaceTypePS:
-        case gfxASurface::SurfaceTypeWin32Printing:
-            aPattern->SetExtend(gfxPattern::EXTEND_NONE);
-            aPattern->SetFilter(aDefaultFilter);
-            break;
-
 #ifdef MOZ_X11
         case gfxASurface::SurfaceTypeXlib:
         {
             // See bugs 324698, 422179, and 468496.  This is a workaround for
             // XRender's RepeatPad not being implemented correctly on old X
             // servers.
             //
             // In this situation, cairo avoids XRender and instead reads back
--- a/ipc/ril/Ril.cpp
+++ b/ipc/ril/Ril.cpp
@@ -169,17 +169,17 @@ void RilReconnectTask::Run() {
     Enqueue(1000);
 }
 
 class RilWriteTask : public Task {
     virtual void Run();
 };
 
 void RilWriteTask::Run() {
-    sClient->OnFileCanWriteWithoutBlocking(sClient->mSocket.mFd);
+    sClient->OnFileCanWriteWithoutBlocking(sClient->mSocket.rwget());
 }
 
 static void
 ConnectToRil(Monitor* aMonitor, bool* aSuccess)
 {
     MOZ_ASSERT(!sClient);
 
     sClient = new RilClient();
@@ -200,63 +200,63 @@ RilClient::OpenSocket()
     // before we see how this works on the phone.
     struct sockaddr_un addr;
     socklen_t alen;
     size_t namelen;
     int err;
     memset(&addr, 0, sizeof(addr));
     strcpy(addr.sun_path, RIL_SOCKET_NAME);
     addr.sun_family = AF_LOCAL;
-    mSocket.mFd = socket(AF_LOCAL, SOCK_STREAM, 0);
+    mSocket.reset(socket(AF_LOCAL, SOCK_STREAM, 0));
     alen = strlen(RIL_SOCKET_NAME) + offsetof(struct sockaddr_un, sun_path) + 1;
 #else
     struct hostent *hp;
     struct sockaddr_in addr;
     socklen_t alen;
 
     hp = gethostbyname("localhost");
     if (hp == 0) return false;
 
     memset(&addr, 0, sizeof(addr));
     addr.sin_family = hp->h_addrtype;
     addr.sin_port = htons(RIL_TEST_PORT);
     memcpy(&addr.sin_addr, hp->h_addr, hp->h_length);
-    mSocket.mFd = socket(hp->h_addrtype, SOCK_STREAM, 0);
+    mSocket.reset(socket(hp->h_addrtype, SOCK_STREAM, 0));
     alen = sizeof(addr);
 #endif
 
-    if (mSocket.mFd < 0) {
+    if (mSocket.get() < 0) {
         LOG("Cannot create socket for RIL!\n");
         return false;
     }
 
-    if (connect(mSocket.mFd, (struct sockaddr *) &addr, alen) < 0) {
+    if (connect(mSocket.get(), (struct sockaddr *) &addr, alen) < 0) {
 #if defined(MOZ_WIDGET_GONK)
         LOG("Cannot open socket for RIL!\n");
 #endif
-        close(mSocket.mFd);
+        mSocket.dispose();
         return false;
     }
 
     // Set close-on-exec bit.
-    int flags = fcntl(mSocket.mFd, F_GETFD);
+    int flags = fcntl(mSocket.get(), F_GETFD);
     if (-1 == flags) {
         return false;
     }
 
     flags |= FD_CLOEXEC;
-    if (-1 == fcntl(mSocket.mFd, F_SETFD, flags)) {
+    if (-1 == fcntl(mSocket.get(), F_SETFD, flags)) {
         return false;
     }
 
     // Select non-blocking IO.
-    if (-1 == fcntl(mSocket.mFd, F_SETFL, O_NONBLOCK)) {
+    if (-1 == fcntl(mSocket.get(), F_SETFL, O_NONBLOCK)) {
         return false;
     }
-    if (!mIOLoop->WatchFileDescriptor(mSocket.mFd,
+    if (!mIOLoop->WatchFileDescriptor(mSocket.get(),
                                       true,
                                       MessageLoopForIO::WATCH_READ,
                                       &mReadWatcher,
                                       this)) {
         return false;
     }
     LOG("Socket open for RIL\n");
     return true;
@@ -269,17 +269,17 @@ RilClient::OnFileCanReadWithoutBlocking(
     //
     //   - mIncoming is completely read
     //     If so, sConsumer->MessageReceived(mIncoming.forget())
     //
     //   - mIncoming isn't completely read, but there's no more
     //     data available on the socket
     //     If so, break;
 
-    MOZ_ASSERT(fd == mSocket.mFd);
+    MOZ_ASSERT(fd == mSocket.get());
     while (true) {
         if (!mIncoming) {
             mIncoming = new RilRawData();
             ssize_t ret = read(fd, mIncoming->mData, RilRawData::MAX_DATA_SIZE);
             if (ret <= 0) {
                 if (ret == -1) {
                     if (errno == EINTR) {
                         continue; // retry system call when interrupted
@@ -290,17 +290,17 @@ RilClient::OnFileCanReadWithoutBlocking(
                     // else fall through to error handling on other errno's
                 }
                 LOG("Cannot read from network, error %d\n", ret);
                 // At this point, assume that we can't actually access
                 // the socket anymore, and start a reconnect loop.
                 mIncoming.forget();
                 mReadWatcher.StopWatchingFileDescriptor();
                 mWriteWatcher.StopWatchingFileDescriptor();
-                close(mSocket.mFd);
+                close(mSocket.get());
                 RilReconnectTask::Enqueue();
                 return;
             }
             mIncoming->mSize = ret;
             sConsumer->MessageReceived(mIncoming.forget());
             if (ret < ssize_t(RilRawData::MAX_DATA_SIZE)) {
                 return;
             }
@@ -313,17 +313,17 @@ RilClient::OnFileCanWriteWithoutBlocking
 {
     // Try to write the bytes of mCurrentRilRawData.  If all were written, continue.
     //
     // Otherwise, save the byte position of the next byte to write
     // within mCurrentRilRawData, and request another write when the
     // system won't block.
     //
 
-    MOZ_ASSERT(fd == mSocket.mFd);
+    MOZ_ASSERT(fd == mSocket.get());
 
     while (!mOutgoingQ.empty() || mCurrentRilRawData != NULL) {
         if(!mCurrentRilRawData) {
             mCurrentRilRawData = mOutgoingQ.front();
             mOutgoingQ.pop();
             mCurrentWriteOffset = 0;
         }
         const uint8_t *toWrite;
--- a/js/src/Makefile.in
+++ b/js/src/Makefile.in
@@ -251,18 +251,19 @@ EXPORTS_js = \
 		TemplateLib.h \
 		Utility.h \
 		Vector.h \
 		$(NULL)
 
 ###############################################
 # BEGIN include sources for low-level code shared with mfbt
 #
-VPATH += $(srcdir)/../../mfbt
-include $(srcdir)/../../mfbt/exported_headers.mk
+MFBT_ROOT = $(srcdir)/../../mfbt
+VPATH += $(MFBT_ROOT)
+include $(MFBT_ROOT)/exported_headers.mk
 
 ifdef ENABLE_METHODJIT
 
 ###############################################
 # BEGIN include sources for the method JIT
 #
 VPATH += 	$(srcdir)/methodjit
 
@@ -314,35 +315,16 @@ ifeq (mips, $(findstring mips,$(TARGET_C
 CPPSRCS +=	TrampolineMIPS.cpp
 endif
 #
 # END enclude sources for the method JIT
 #############################################
 
 endif
 
-###############################################
-# BEGIN include sources for V8 dtoa
-#
-VPATH += 	$(srcdir)/v8-dtoa \
-		$(NONE)
-
-CPPSRCS += 	checks.cc \
-		conversions.cc \
-		diy-fp.cc \
-		v8-dtoa.cc \
-		fast-dtoa.cc \
-		platform.cc \
-		utils.cc \
-		$(NONE)
-
-#
-# END enclude sources for V8 dtoa
-#############################################
-
 # For architectures without YARR JIT, PCRE is faster than the YARR
 # interpreter (bug 684559).
 
 ifeq (,$(filter arm% sparc %86 x86_64 mips%,$(TARGET_CPU)))
 
 VPATH +=        $(srcdir)/yarr/pcre \
 		$(NULL)
 
@@ -446,16 +428,18 @@ else
 SHARED_LIBRARY_LIBS += \
     ctypes/libffi/.libs/libffi.$(LIB_SUFFIX) \
     $(NULL)
 endif
 endif
 
 endif # JS_HAS_CTYPES
 
+LOCAL_INCLUDES += -I$(MFBT_ROOT)/double-conversion
+
 ifdef HAVE_DTRACE
 INSTALLED_HEADERS += \
 		$(CURDIR)/javascript-trace.h \
 		$(NULL)
 endif
 
 # PerfMeasurement is available regardless of low-level support for it;
 # it just doesn't necessarily do anything useful.  There is one
@@ -489,17 +473,17 @@ endif
 endif
 endif
 
 include $(topsrcdir)/config/config.mk
 
 ifeq (,$(MOZ_GLUE_PROGRAM_LDFLAGS))
 # When building standalone, we need to include mfbt sources, and to declare
 # "exported" mfbt symbols on its behalf when we use its headers.
-include $(srcdir)/../../mfbt/sources.mk
+include $(MFBT_ROOT)/sources.mk
 DEFINES += -DIMPL_MFBT
 endif
 
 EXTRA_DSO_LDOPTS += $(NSPR_LIBS)
 
 ifndef BUILD_OPT
 MOCHAFILE	= 1
 endif
--- a/js/src/ctypes/CTypes.cpp
+++ b/js/src/ctypes/CTypes.cpp
@@ -16,17 +16,16 @@
  *
  * The Initial Developer of the Original Code is
  * The Mozilla Foundation <http://www.mozilla.org/>.
  * Portions created by the Initial Developer are Copyright (C) 2009
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *  Dan Witte <dwitte@mozilla.com>
- *  David Rajchenbach-Teller <dteller@mozilla.com>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
@@ -58,19 +57,16 @@
 #endif
 
 #if defined(XP_UNIX)
 #include <errno.h>
 #elif defined(XP_WIN)
 #include <windows.h>
 #endif
 
-#include "mozilla/StandardInteger.h"
-#include "mozilla/Scoped.h"
-
 using namespace std;
 
 namespace js {
 namespace ctypes {
 
 /*******************************************************************************
 ** JSAPI function prototypes
 *******************************************************************************/
@@ -93,17 +89,17 @@ namespace CType {
     jsval* vp);
   static JSBool PtrGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp);
   static JSBool CreateArray(JSContext* cx, unsigned argc, jsval* vp);
   static JSBool ToString(JSContext* cx, unsigned argc, jsval* vp);
   static JSBool ToSource(JSContext* cx, unsigned argc, jsval* vp);
   static JSBool HasInstance(JSContext* cx, JSObject* obj, const jsval* v, JSBool* bp);
 
 
-  /*
+  /**
    * Get the global "ctypes" object.
    *
    * |obj| must be a CType object.
    *
    * This function never returns NULL.
    */
   static JSObject* GetGlobalCTypes(JSContext* cx, JSObject* obj);
 
@@ -184,139 +180,26 @@ namespace CData {
 
   static JSBool ValueGetter(JSContext* cx, JSObject* obj, jsid idval,
                             jsval* vp);
   static JSBool ValueSetter(JSContext* cx, JSObject* obj, jsid idval,
                             JSBool strict, jsval* vp);
   static JSBool Address(JSContext* cx, unsigned argc, jsval* vp);
   static JSBool ReadString(JSContext* cx, unsigned argc, jsval* vp);
   static JSBool ToSource(JSContext* cx, unsigned argc, jsval* vp);
-  static JSString *GetSourceString(JSContext *cx, JSObject *typeObj,
-                                   void *data);
+
   static JSBool ErrnoGetter(JSContext* cx, JSObject *obj, jsid idval,
                             jsval* vp);
 
 #if defined(XP_WIN)
   static JSBool LastErrorGetter(JSContext* cx, JSObject *obj, jsid idval,
                                 jsval* vp);
 #endif // defined(XP_WIN)
 }
 
-namespace CDataFinalizer {
-  /*
-   * Attach a C function as a finalizer to a JS object.
-   *
-   * This function is available from JS as |ctypes.withFinalizer|.
-   *
-   * JavaScript signature:
-   * function(CData, CData):   CDataFinalizer
-   *          value  finalizer finalizable
-   *
-   * Where |finalizer| is a one-argument function taking a value
-   * with the same type as |value|.
-   */
-  static JSBool Construct(JSContext* cx, unsigned argc, jsval *vp);
-
-  /*
-   * Private data held by |CDataFinalizer|.
-   *
-   * See also |enum CDataFinalizerSlot| for the slots of
-   * |CDataFinalizer|.
-   *
-   * Note: the private data may be NULL, if |dispose|, |forget| or the
-   * finalizer has already been called.
-   */
-  struct Private {
-    /*
-     * The C data to pass to the code.
-     * Finalization/|dispose|/|forget| release this memory.
-     */
-    void *cargs;
-
-    /*
-     * The total size of the buffer pointed by |cargs|
-     */
-    size_t cargs_size;
-
-    /*
-     * Low-level signature information.
-     * Finalization/|dispose|/|forget| release this memory.
-     */
-    ffi_cif CIF;
-
-    /*
-     * The C function to invoke during finalization.
-     * Do not deallocate this.
-     */
-    uintptr_t code;
-
-    /*
-     * A buffer for holding the return value.
-     * Finalization/|dispose|/|forget| release this memory.
-     */
-    void *rvalue;
-  };
-
-  /*
-   * Methods of instances of |CDataFinalizer|
-   */
-  namespace Methods {
-    static JSBool Dispose(JSContext* cx, unsigned argc, jsval *vp);
-    static JSBool Forget(JSContext* cx, unsigned argc, jsval *vp);
-    static JSBool ToSource(JSContext* cx, unsigned argc, jsval *vp);
-    static JSBool ToString(JSContext* cx, unsigned argc, jsval *vp);
-  }
-
-  /*
-   * Utility functions
-   *
-   * @return true if |obj| is a CDataFinalizer, false otherwise.
-   */
-  static bool IsCDataFinalizer(JSObject *obj);
-
-  /*
-   * Clean up the finalization information of a CDataFinalizer.
-   *
-   * Used by |Finalize|, |Dispose| and |Forget|.
-   *
-   * @param p The private information of the CDataFinalizer. If NULL,
-   * this function does nothing.
-   * @param obj Either NULL, if the object should not be cleaned up (i.e.
-   * during finalization) or a CDataFinalizer JSObject. Always use NULL
-   * if you are calling from a finalizer.
-   */
-  static void Cleanup(Private *p, JSObject *obj);
-
-  /*
-   * Perform the actual call to the finalizer code.
-   */
-  static void CallFinalizer(CDataFinalizer::Private *p,
-                            int* errnoStatus,
-                            int32_t* lastErrorStatus);
-
-  /*
-   * Return the CType of a CDataFinalizer object, or NULL if the object
-   * has been cleaned-up already.
-   */
-  static JSObject *GetCType(JSContext *cx, JSObject *obj);
-
-  /*
-   * Perform finalization of a |CDataFinalizer|
-   */
-  static void Finalize(JSFreeOp *fop, JSObject *obj);
-
-  /*
-   * Return the jsval contained by this finalizer.
-   *
-   * Note that the jsval is actually not recorded, but converted back from C.
-   */
-  static bool GetValue(JSContext *cx, JSObject *obj, jsval *result);
- }
-
-
 // Int64Base provides functions common to Int64 and UInt64.
 namespace Int64Base {
   JSObject* Construct(JSContext* cx, JSObject* proto, uint64_t data,
     bool isUnsigned);
 
   uint64_t GetInt(JSObject* obj);
 
   JSBool ToString(JSContext* cx, JSObject* obj, unsigned argc, jsval* vp,
@@ -412,55 +295,28 @@ static JSClass sCDataClass = {
 static JSClass sCClosureClass = {
   "CClosure",
   JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_HAS_RESERVED_SLOTS(CCLOSURE_SLOTS),
   JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
   JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, CClosure::Finalize,
   NULL, NULL, NULL, NULL, CClosure::Trace
 };
 
-/*
- * Class representing the prototype of CDataFinalizer.
- */
-static JSClass sCDataFinalizerProtoClass = {
-  "CDataFinalizer",
-  0,
-  JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
-  JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub
-};
-
-/*
- * Class representing instances of CDataFinalizer.
- *
- * Instances of CDataFinalizer have both private data (with type
- * |CDataFinalizer::Private|) and slots (see |CDataFinalizerSlots|).
- */
-static JSClass sCDataFinalizerClass = {
-  "CDataFinalizer",
-  JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(CDATAFINALIZER_SLOTS),
-  JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
-  JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, CDataFinalizer::Finalize,
-};
-
-
 #define CTYPESFN_FLAGS \
   (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)
 
 #define CTYPESCTOR_FLAGS \
   (CTYPESFN_FLAGS | JSFUN_CONSTRUCTOR)
 
 #define CTYPESPROP_FLAGS \
   (JSPROP_SHARED | JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)
 
 #define CDATAFN_FLAGS \
   (JSPROP_READONLY | JSPROP_PERMANENT)
 
-#define CDATAFINALIZERFN_FLAGS \
-  (JSPROP_READONLY | JSPROP_PERMANENT)
-
 static JSPropertySpec sCTypeProps[] = {
   { "name", 0, CTYPESPROP_FLAGS, CType::NameGetter, NULL },
   { "size", 0, CTYPESPROP_FLAGS, CType::SizeGetter, NULL },
   { "ptr", 0, CTYPESPROP_FLAGS, CType::PtrGetter, NULL },
   { "prototype", 0, CTYPESPROP_FLAGS, CType::PrototypeGetter, NULL },
   { 0, 0, 0, NULL, NULL }
 };
 
@@ -480,28 +336,16 @@ static JSPropertySpec sCDataProps[] = {
 static JSFunctionSpec sCDataFunctions[] = {
   JS_FN("address", CData::Address, 0, CDATAFN_FLAGS),
   JS_FN("readString", CData::ReadString, 0, CDATAFN_FLAGS),
   JS_FN("toSource", CData::ToSource, 0, CDATAFN_FLAGS),
   JS_FN("toString", CData::ToSource, 0, CDATAFN_FLAGS),
   JS_FS_END
 };
 
-static JSPropertySpec sCDataFinalizerProps[] = {
-  { 0, 0, 0, NULL, NULL }
-};
-
-static JSFunctionSpec sCDataFinalizerFunctions[] = {
-  JS_FN("dispose",  CDataFinalizer::Methods::Dispose,  0, CDATAFINALIZERFN_FLAGS),
-  JS_FN("forget",   CDataFinalizer::Methods::Forget,   0, CDATAFINALIZERFN_FLAGS),
-  JS_FN("toString", CDataFinalizer::Methods::ToString, 0, CDATAFINALIZERFN_FLAGS),
-  JS_FN("toSource", CDataFinalizer::Methods::ToSource, 0, CDATAFINALIZERFN_FLAGS),
-  JS_FS_END
-};
-
 static JSFunctionSpec sPointerFunction =
   JS_FN("PointerType", PointerType::Create, 1, CTYPESCTOR_FLAGS);
 
 static JSPropertySpec sPointerProps[] = {
   { "targetType", 0, CTYPESPROP_FLAGS, PointerType::TargetTypeGetter, NULL },
   { 0, 0, 0, NULL, NULL }
 };
 
@@ -635,17 +479,16 @@ static JSPropertySpec sModuleProps[] = {
 #if defined(XP_WIN)
   { "winLastError", 0, JSPROP_SHARED | JSPROP_PERMANENT,
     CData::LastErrorGetter, NULL },
 #endif // defined(XP_WIN)
   { 0, 0, 0, NULL, NULL }
 };
 
 static JSFunctionSpec sModuleFunctions[] = {
-  JS_FN("CDataFinalizer", CDataFinalizer::Construct, 2, CTYPESFN_FLAGS),
   JS_FN("open", Library::Open, 1, CTYPESFN_FLAGS),
   JS_FN("cast", CData::Cast, 2, CTYPESFN_FLAGS),
   JS_FN("getRuntime", CData::GetRuntime, 1, CTYPESFN_FLAGS),
   JS_FN("libraryName", Library::Name, 1, CTYPESFN_FLAGS),
   JS_FS_END
 };
 
 static inline bool FloatIsFinite(double f) {
@@ -657,26 +500,19 @@ static inline bool FloatIsFinite(double 
 }
 
 JS_ALWAYS_INLINE JSString*
 NewUCString(JSContext* cx, const AutoString& from)
 {
   return JS_NewUCStringCopyN(cx, from.begin(), from.length());
 }
 
-/*
- * Return a size rounded up to a multiple of a power of two.
- *
- * Note: |align| must be a power of 2.
- */
 JS_ALWAYS_INLINE size_t
 Align(size_t val, size_t align)
 {
-  // Ensure that align is a power of two.
-  MOZ_ASSERT(align != 0 && (align & (align - 1)) == 0);
   return ((val - 1) | (align - 1)) + 1;
 }
 
 static ABICode
 GetABICode(JSObject* obj)
 {
   // make sure we have an object representing a CABI class,
   // and extract the enumerated class type from the reserved slot.
@@ -1128,36 +964,16 @@ GetCallbacks(JSObject* obj)
 
   jsval result = JS_GetReservedSlot(obj, SLOT_CALLBACKS);
   if (JSVAL_IS_VOID(result))
     return NULL;
 
   return static_cast<JSCTypesCallbacks*>(JSVAL_TO_PRIVATE(result));
 }
 
-// Utility function to access a property of an object as an object
-// returns false and sets the error if the property does not exist
-// or is not an object
-bool GetObjectProperty(JSContext *cx, JSObject *obj,
-                       const char *property, JSObject **result)
-{
-  jsval val;
-  if (!JS_GetProperty(cx, obj, property, &val)) {
-    return false;
-  }
-
-  if (JSVAL_IS_PRIMITIVE(val)) {
-    JS_ReportError(cx, "missing or non-object field");
-    return false;
-  }
-
-  *result = JSVAL_TO_OBJECT(val);
-  return true;
-}
-
 JS_BEGIN_EXTERN_C
 
 JS_PUBLIC_API(JSBool)
 JS_InitCTypesClass(JSContext* cx, JSObject* global)
 {
   // attach ctypes property to global object
   JSObject* ctypes = JS_NewObject(cx, &sCTypesGlobalClass, NULL, NULL);
   if (!ctypes)
@@ -1171,40 +987,16 @@ JS_InitCTypesClass(JSContext* cx, JSObje
   if (!InitTypeClasses(cx, ctypes))
     return false;
 
   // attach API functions and properties
   if (!JS_DefineFunctions(cx, ctypes, sModuleFunctions) ||
       !JS_DefineProperties(cx, ctypes, sModuleProps))
     return false;
 
-  // Set up ctypes.CDataFinalizer.prototype.
-  JSObject* ctor;
-  if (!GetObjectProperty(cx, ctypes, "CDataFinalizer", &ctor)) {
-    return NULL;
-  }
-
-  JSObject* prototype = JS_NewObject(cx, &sCDataFinalizerProtoClass,
-                                     NULL, ctypes);
-  if (!prototype)
-    return NULL;
-
-  if (!JS_DefineProperties(cx, prototype, sCDataFinalizerProps) ||
-      !JS_DefineFunctions(cx, prototype, sCDataFinalizerFunctions))
-    return NULL;
-
-  if (!JS_DefineProperty(cx, ctor, "prototype", OBJECT_TO_JSVAL(prototype),
-                         NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
-    return NULL;
-
-  if (!JS_DefineProperty(cx, prototype, "constructor", OBJECT_TO_JSVAL(ctor),
-                         NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
-    return NULL;
-
-
   // Seal the ctypes object, to prevent modification.
   return JS_FreezeObject(cx, ctypes);
 }
 
 JS_PUBLIC_API(void)
 JS_SetCTypesCallbacks(JSObject* ctypesObj,
                       JSCTypesCallbacks* callbacks)
 {
@@ -1476,25 +1268,17 @@ jsvalToInteger(JSContext* cx, jsval val,
     }
 
     if (UInt64::IsUInt64(obj)) {
       // Make sure the integer fits in IntegerType.
       uint64_t i = Int64Base::GetInt(obj);
       return ConvertExact(i, result);
     }
 
-    if (CDataFinalizer::IsCDataFinalizer(obj)) {
-      jsval innerData;
-      if (!CDataFinalizer::GetValue(cx, obj, &innerData)) {
-        return false; // Nothing to convert
-      }
-      return jsvalToInteger(cx, innerData, result);
-    }
-
-    return false;
+    return false; 
   }
   if (JSVAL_IS_BOOLEAN(val)) {
     // Implicitly promote boolean values to 0 or 1, like C.
     *result = JSVAL_TO_BOOLEAN(val);
     JS_ASSERT(*result == 0 || *result == 1);
     return true;
   }
   // Don't silently convert null to an integer. It's probably a mistake.
@@ -1655,25 +1439,16 @@ jsvalToBigInteger(JSContext* cx,
       return ConvertExact(i, result);
     }
 
     if (Int64::IsInt64(obj)) {
       // Make sure the integer fits in IntegerType.
       int64_t i = Int64Base::GetInt(obj);
       return ConvertExact(i, result);
     }
-
-    if (CDataFinalizer::IsCDataFinalizer(obj)) {
-      jsval innerData;
-      if (!CDataFinalizer::GetValue(cx, obj, &innerData)) {
-        return false; // Nothing to convert
-      }
-      return jsvalToBigInteger(cx, innerData, allowString, result);
-    }
-
   }
   return false;
 }
 
 // Implicitly convert val to a size value, where the size value is represented
 // by size_t but must also fit in a double.
 static bool
 jsvalToSize(JSContext* cx, jsval val, bool allowString, size_t* result)
@@ -2004,50 +1779,30 @@ ImplicitConvert(JSContext* cx,
                 jsval val,
                 JSObject* targetType,
                 void* buffer,
                 bool isArgument,
                 bool* freePointer)
 {
   JS_ASSERT(CType::IsSizeDefined(targetType));
 
-  // First, check if val is either a CData object or a CDataFinalizer
-  // of type targetType.
+  // First, check if val is a CData object of type targetType.
   JSObject* sourceData = NULL;
   JSObject* sourceType = NULL;
-  if (!JSVAL_IS_PRIMITIVE(val)) {
-    if (CData::IsCData(JSVAL_TO_OBJECT(val))) {
-      sourceData = JSVAL_TO_OBJECT(val);
-      sourceType = CData::GetCType(sourceData);
-
-      // If the types are equal, copy the buffer contained within the CData.
-      // (Note that the buffers may overlap partially or completely.)
-      if (CType::TypesEqual(sourceType, targetType)) {
-        size_t size = CType::GetSize(sourceType);
-        memmove(buffer, CData::GetData(sourceData), size);
-        return true;
-      }
-    } else if (CDataFinalizer::IsCDataFinalizer(JSVAL_TO_OBJECT(val))) {
-      sourceData = JSVAL_TO_OBJECT(val);
-      sourceType = CDataFinalizer::GetCType(cx, sourceData);
-
-      CDataFinalizer::Private *p = (CDataFinalizer::Private *)
-        JS_GetPrivate(sourceData);
-
-      if (!p) {
-        // We have called |dispose| or |forget| already.
-        JS_ReportError(cx, "Attempting to convert an empty CDataFinalizer");
-        return JS_FALSE;
-      }
-
-      // If the types are equal, copy the buffer contained within the CData.
-      if (CType::TypesEqual(sourceType, targetType)) {
-        memmove(buffer, p->cargs, p->cargs_size);
-        return true;
-      }
+  if (!JSVAL_IS_PRIMITIVE(val) &&
+      CData::IsCData(JSVAL_TO_OBJECT(val))) {
+    sourceData = JSVAL_TO_OBJECT(val);
+    sourceType = CData::GetCType(sourceData);
+
+    // If the types are equal, copy the buffer contained within the CData.
+    // (Note that the buffers may overlap partially or completely.)
+    if (CType::TypesEqual(sourceType, targetType)) {
+      size_t size = CType::GetSize(sourceType);
+      memmove(buffer, CData::GetData(sourceData), size);
+      return true;
     }
   }
 
   TypeCode targetCode = CType::GetTypeCode(targetType);
 
   switch (targetCode) {
   case TYPE_bool: {
     // Do not implicitly lose bits, but allow the values 0, 1, and -0.
@@ -6266,36 +6021,16 @@ CData::ReadString(JSContext* cx, unsigne
 
   if (!result)
     return JS_FALSE;
 
   JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(result));
   return JS_TRUE;
 }
 
-JSString *
-CData::GetSourceString(JSContext *cx, JSObject *typeObj, void *data)
-{
-  // Walk the types, building up the toSource() string.
-  // First, we build up the type expression:
-  // 't.ptr' for pointers;
-  // 't.array([n])' for arrays;
-  // 'n' for structs, where n = t.name, the struct's name. (We assume this is
-  // bound to a variable in the current scope.)
-  AutoString source;
-  BuildTypeSource(cx, typeObj, true, source);
-  AppendString(source, "(");
-  if (!BuildDataSource(cx, typeObj, data, false, source))
-    return NULL;
-
-  AppendString(source, ")");
-
-  return NewUCString(cx, source);
-}
-
 JSBool
 CData::ToSource(JSContext* cx, unsigned argc, jsval* vp)
 {
   if (argc != 0) {
     JS_ReportError(cx, "toSource takes zero arguments");
     return JS_FALSE;
   }
 
@@ -6306,21 +6041,34 @@ CData::ToSource(JSContext* cx, unsigned 
     return JS_FALSE;
   }
 
   JSString* result;
   if (CData::IsCData(obj)) {
     JSObject* typeObj = CData::GetCType(obj);
     void* data = CData::GetData(obj);
 
-    result = CData::GetSourceString(cx, typeObj, data);
-  } else {
+    // Walk the types, building up the toSource() string.
+    // First, we build up the type expression:
+    // 't.ptr' for pointers;
+    // 't.array([n])' for arrays;
+    // 'n' for structs, where n = t.name, the struct's name. (We assume this is
+    // bound to a variable in the current scope.)
+    AutoString source;
+    BuildTypeSource(cx, typeObj, true, source);
+    AppendString(source, "(");
+    if (!BuildDataSource(cx, typeObj, data, false, source))
+      return JS_FALSE;
+
+    AppendString(source, ")");
+
+    result = NewUCString(cx, source);
+  }
+  else
     result = JS_NewStringCopyZ(cx, "[CData proto object]");
-  }
-
   if (!result)
     return JS_FALSE;
 
   JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(result));
   return JS_TRUE;
 }
 
 JSBool
@@ -6345,521 +6093,16 @@ CData::LastErrorGetter(JSContext* cx, JS
   }
 
 
   *vp = JS_GetReservedSlot(obj, SLOT_LASTERROR);
   return JS_TRUE;
 }
 #endif // defined(XP_WIN)
 
-JSBool
-CDataFinalizer::Methods::ToSource(JSContext *cx, unsigned argc, jsval *vp)
-{
-  JSObject* objThis = JS_THIS_OBJECT(cx, vp);
-  if (!objThis || !CDataFinalizer::IsCDataFinalizer(objThis)) {
-    JS_ReportError(cx, "not a CDataFinalizer");
-    return JS_FALSE;
-  }
-
-  CDataFinalizer::Private *p = (CDataFinalizer::Private *)
-    JS_GetPrivate(objThis);
-
-  JSString *strMessage;
-  if (!p) {
-    strMessage = JS_NewStringCopyZ(cx, "ctypes.CDataFinalizer()");
-  } else {
-    JSObject *objType = CDataFinalizer::GetCType(cx, objThis);
-    if (!objType) {
-      JS_ReportError(cx, "CDataFinalizer has no type");
-      return JS_FALSE;
-    }
-
-    AutoString source;
-    AppendString(source, "ctypes.CDataFinalizer(");
-    JSString *srcValue = CData::GetSourceString(cx, objType, p->cargs);
-    if (!srcValue) {
-      return JS_FALSE;
-    }
-    AppendString(source, srcValue);
-    AppendString(source, ", ");
-    jsval valCodePtrType = JS_GetReservedSlot(objThis,
-                                              SLOT_DATAFINALIZER_CODETYPE);
-    if (JSVAL_IS_PRIMITIVE(valCodePtrType)) {
-      return JS_FALSE;
-    }