Merge from mozilla-central.
authorDavid Anderson <danderson@mozilla.com>
Fri, 09 Mar 2012 13:37:58 -0800
changeset 106008 57680b93b9c2cf4dea49c120c5a6321966bce3c3
parent 106007 8a9d061f5008a186b51bc115832c9bcce4d733f7 (current diff)
parent 88636 bfb1b7520ce9714dd7d089fb266fc40f004db923 (diff)
child 106009 3ab9cb07980d077b9c0e4c4f0425314dec431d1d
push id14706
push usereakhgari@mozilla.com
push dateTue, 11 Sep 2012 20:39:52 +0000
treeherdermozilla-inbound@d50bf1edaabe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone13.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 from mozilla-central.
accessible/src/atk/nsApplicationAccessibleWrap.cpp
accessible/src/atk/nsApplicationAccessibleWrap.h
accessible/src/base/NotificationController.cpp
accessible/src/base/nsAccessible.h
accessible/src/base/nsDocAccessible.cpp
accessible/src/base/nsDocAccessible.h
accessible/src/html/nsHTMLFormControlAccessible.cpp
accessible/src/html/nsHTMLFormControlAccessible.h
accessible/src/html/nsHyperTextAccessible.cpp
accessible/src/html/nsHyperTextAccessible.h
accessible/src/xforms/nsXFormsAccessible.cpp
accessible/src/xforms/nsXFormsAccessible.h
accessible/src/xul/nsXULFormControlAccessible.cpp
accessible/src/xul/nsXULFormControlAccessible.h
accessible/tests/mochitest/common.js
accessible/tests/mochitest/events.js
aclocal.m4
browser/app/profile/firefox.js
browser/base/content/browser.js
browser/base/content/nsContextMenu.js
browser/base/jar.mn
browser/components/dirprovider/DirectoryProvider.cpp
browser/components/migration/src/nsIEProfileMigrator.cpp
browser/components/nsBrowserContentHandler.js
browser/components/nsBrowserGlue.js
browser/devtools/styleinspector/CssLogic.jsm
browser/themes/gnomestripe/fullscreen-video.css
browser/themes/gnomestripe/section_collapsed-rtl.png
browser/themes/gnomestripe/section_collapsed.png
browser/themes/gnomestripe/section_expanded.png
browser/themes/pinstripe/fullscreen-video.css
browser/themes/pinstripe/section_collapsed-rtl.png
browser/themes/pinstripe/section_collapsed.png
browser/themes/pinstripe/section_expanded.png
browser/themes/winstripe/fullscreen-video.css
browser/themes/winstripe/section_collapsed-rtl.png
browser/themes/winstripe/section_collapsed.png
browser/themes/winstripe/section_expanded.png
build/mobile/robocop/Actions.java.in
build/unix/check_debug_ranges.py
caps/include/nsPrincipal.h
caps/include/nsScriptSecurityManager.h
caps/src/nsJSPrincipals.cpp
caps/src/nsNullPrincipal.cpp
caps/src/nsPrincipal.cpp
caps/src/nsScriptSecurityManager.cpp
caps/src/nsSystemPrincipal.cpp
config/autoconf.mk.in
configure.in
content/base/public/nsContentUtils.h
content/base/public/nsIHTMLToTextSink.h
content/base/src/nsContentUtils.cpp
content/base/src/nsFrameMessageManager.cpp
content/base/src/nsGenericElement.cpp
content/base/src/nsGkAtomList.h
content/base/src/nsNodeUtils.cpp
content/base/src/nsPlainTextSerializer.cpp
content/base/src/nsPlainTextSerializer.h
content/base/src/nsWebSocket.cpp
content/canvas/src/CanvasUtils.cpp
content/canvas/src/CanvasUtils.h
content/canvas/src/WebGLContext.h
content/canvas/test/webgl/test_webgl_conformance_test_suite.html
content/html/content/src/nsFormSubmission.cpp
content/html/content/src/nsHTMLInputElement.cpp
content/html/content/src/nsHTMLTextAreaElement.cpp
content/html/document/src/ImageDocument.cpp
content/media/nsMediaCache.cpp
content/svg/content/src/nsSVGElement.cpp
content/svg/content/src/nsSVGUseElement.cpp
content/xbl/src/nsBindingManager.cpp
content/xslt/src/xml/txDOM.h
content/xslt/src/xslt/txKey.h
content/xslt/src/xslt/txKeyFunctionCall.cpp
dom/base/nsDOMClassInfo.cpp
dom/base/nsDOMClassInfoClasses.h
dom/base/nsFocusManager.cpp
dom/base/nsGlobalWindow.cpp
dom/base/nsJSEnvironment.cpp
dom/indexedDB/AsyncConnectionHelper.cpp
dom/indexedDB/IDBDatabase.cpp
dom/indexedDB/IDBIndex.cpp
dom/indexedDB/IDBObjectStore.cpp
dom/interfaces/base/domstubs.idl
dom/src/storage/nsDOMStorage.cpp
dom/src/storage/nsDOMStorage.h
dom/workers/RuntimeService.cpp
dom/workers/WorkerPrivate.cpp
dom/workers/WorkerScope.cpp
editor/txtsvc/src/nsTextServicesDocument.cpp
embedding/android/GeckoAppShell.java
embedding/components/windowwatcher/src/nsWindowWatcher.cpp
gfx/gl/GLContext.cpp
gfx/gl/GLContextProviderEGL.cpp
gfx/thebes/gfxContext.cpp
gfx/thebes/gfxDWriteFontList.cpp
gfx/thebes/gfxDWriteFontList.h
gfx/thebes/gfxFT2Fonts.cpp
gfx/thebes/gfxFT2Fonts.h
gfx/thebes/gfxFont.cpp
gfx/thebes/gfxFont.h
gfx/thebes/gfxFontUtils.cpp
gfx/thebes/gfxFontUtils.h
gfx/thebes/gfxGDIFontList.cpp
gfx/thebes/gfxMacPlatformFontList.h
gfx/thebes/gfxMacPlatformFontList.mm
gfx/thebes/gfxPlatform.cpp
gfx/thebes/gfxPlatform.h
gfx/thebes/gfxPlatformFontList.cpp
gfx/thebes/gfxPlatformFontList.h
gfx/thebes/gfxPlatformGtk.cpp
gfx/thebes/gfxPlatformMac.cpp
gfx/thebes/gfxPlatformMac.h
gfx/thebes/gfxQtPlatform.cpp
gfx/thebes/gfxUserFontSet.cpp
gfx/thebes/gfxUserFontSet.h
gfx/thebes/gfxWindowsNativeDrawing.cpp
gfx/thebes/gfxWindowsNativeDrawing.h
gfx/thebes/gfxWindowsPlatform.cpp
gfx/thebes/gfxWindowsPlatform.h
image/src/RasterImage.cpp
ipc/testshell/XPCShellEnvironment.cpp
js/src/Makefile.in
js/src/MemoryMetrics.cpp
js/src/configure.in
js/src/frontend/BytecodeEmitter.cpp
js/src/frontend/Parser.cpp
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jsarray.cpp
js/src/jsarray.h
js/src/jsatom.cpp
js/src/jsbool.cpp
js/src/jsbool.h
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jscompartment.cpp
js/src/jscompartment.h
js/src/jsexn.cpp
js/src/jsfriendapi.cpp
js/src/jsfriendapi.h
js/src/jsfun.cpp
js/src/jsgc.cpp
js/src/jsgc.h
js/src/jsinfer.cpp
js/src/jsinterp.cpp
js/src/jsinterpinlines.h
js/src/jsnum.cpp
js/src/jsobj.cpp
js/src/jsobj.h
js/src/jsobjinlines.h
js/src/json.cpp
js/src/jsopcode.cpp
js/src/jsproxy.cpp
js/src/jsprvtd.h
js/src/jspubtd.h
js/src/jsscope.cpp
js/src/jsscript.cpp
js/src/jsstr.cpp
js/src/jstypedarray.cpp
js/src/jstypedarray.h
js/src/jsxml.cpp
js/src/methodjit/Compiler.cpp
js/src/methodjit/Compiler.h
js/src/methodjit/MonoIC.cpp
js/src/methodjit/PolyIC.cpp
js/src/methodjit/StubCalls.cpp
js/src/shell/js.cpp
js/src/shell/jsworkers.cpp
js/src/vm/Debugger.cpp
js/src/vm/GlobalObject.cpp
js/src/vm/RegExpObject-inl.h
js/src/vm/RegExpObject.cpp
js/src/vm/RegExpObject.h
js/src/vm/String.cpp
js/xpconnect/loader/mozJSComponentLoader.cpp
js/xpconnect/loader/mozJSSubScriptLoader.cpp
js/xpconnect/shell/xpcshell.cpp
js/xpconnect/src/XPCComponents.cpp
js/xpconnect/src/XPCJSRuntime.cpp
js/xpconnect/src/XPCVariant.cpp
js/xpconnect/src/XPCWrappedJS.cpp
js/xpconnect/src/XPCWrappedJSClass.cpp
js/xpconnect/src/XPCWrappedNative.cpp
js/xpconnect/src/nsXPConnect.cpp
js/xpconnect/src/xpcprivate.h
js/xpconnect/src/xpcpublic.h
js/xpconnect/wrappers/AccessCheck.cpp
layout/base/FramePropertyTable.cpp
layout/base/FramePropertyTable.h
layout/base/nsBidiPresUtils.cpp
layout/base/nsCSSFrameConstructor.cpp
layout/base/nsLayoutUtils.cpp
layout/base/nsPresContext.cpp
layout/base/nsPresContext.h
layout/base/tests/Makefile.in
layout/build/nsLayoutModule.cpp
layout/forms/nsComboboxControlFrame.cpp
layout/forms/nsComboboxControlFrame.h
layout/generic/nsBlockFrame.cpp
layout/generic/nsBlockFrame.h
layout/generic/nsBlockReflowContext.cpp
layout/generic/nsContainerFrame.cpp
layout/generic/nsContainerFrame.h
layout/generic/nsFrame.cpp
layout/generic/nsFrame.h
layout/generic/nsHTMLParts.h
layout/generic/nsIFrame.h
layout/generic/nsLineBox.cpp
layout/generic/nsLineBox.h
layout/generic/nsObjectFrame.cpp
layout/generic/nsTextFrameThebes.cpp
layout/inspector/src/inDOMView.cpp
layout/mathml/nsMathMLContainerFrame.cpp
layout/reftests/bugs/reftest.list
layout/reftests/svg/smil/reftest.list
layout/style/nsCSSAnonBoxes.cpp
layout/style/nsCSSParser.cpp
layout/style/nsCSSPseudoClasses.cpp
layout/style/nsCSSPseudoElements.cpp
layout/style/nsCSSScanner.cpp
layout/style/nsCSSScanner.h
layout/svg/base/src/nsSVGGradientFrame.h
layout/svg/base/src/nsSVGUtils.cpp
layout/tables/nsTableFrame.cpp
layout/tables/nsTableFrame.h
layout/tables/nsTableOuterFrame.cpp
layout/tables/nsTableOuterFrame.h
layout/xul/base/src/nsBoxFrame.h
layout/xul/base/src/nsMenuFrame.cpp
layout/xul/base/src/nsMenuFrame.h
mobile/android/base/AndroidManifest.xml.in
mobile/android/base/AutoCompletePopup.java
mobile/android/base/GeckoApp.java
mobile/android/base/GeckoAppShell.java
mobile/android/base/Makefile.in
mobile/xul/app/mobile.js
mobile/xul/themes/core/browser.css
mobile/xul/themes/core/gingerbread/browser.css
mobile/xul/themes/core/honeycomb/browser.css
modules/libpref/src/init/all.js
mozglue/android/APKOpen.cpp
mozglue/android/APKOpen.h
mozglue/android/Makefile.in
netwerk/base/src/nsIOService.cpp
netwerk/protocol/http/HttpChannelChild.cpp
netwerk/protocol/http/HttpChannelChild.h
parser/html/nsHtml5TreeOperation.cpp
rdf/base/src/nsRDFContentSink.cpp
security/manager/ssl/src/nsCrypto.cpp
security/manager/ssl/src/nsNSSCertHelper.cpp
services/sync/modules/policies.js
services/sync/tests/unit/head_http_server.js
services/sync/tests/unit/test_syncengine_sync.js
services/sync/tests/unit/test_syncscheduler.js
startupcache/test/TestStartupCache.cpp
testing/testsuite-targets.mk
testing/xpcshell/xpcshell.ini
toolkit/components/aboutmemory/content/aboutMemory.js
toolkit/components/alerts/nsAlertsService.cpp
toolkit/components/downloads/nsDownloadManager.cpp
toolkit/components/places/History.cpp
toolkit/components/telemetry/Telemetry.cpp
toolkit/components/telemetry/TelemetryHistograms.h
toolkit/components/telemetry/TelemetryPing.js
toolkit/components/telemetry/tests/unit/test_TelemetryPing.js
toolkit/content/license.html
toolkit/mozapps/extensions/XPIProvider.jsm
toolkit/mozapps/extensions/content/extensions.js
toolkit/mozapps/extensions/content/setting.xml
toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1/options.xul
toolkit/mozapps/extensions/test/browser/browser_inlinesettings.js
toolkit/themes/winstripe/mozapps/extensions/extensions.css
toolkit/xre/nsAppRunner.cpp
widget/android/AndroidBridge.cpp
widget/android/AndroidBridge.h
widget/gtk2/nsGtkIMModule.cpp
widget/gtk2/nsGtkIMModule.h
widget/gtk2/nsNativeThemeGTK.cpp
widget/gtk2/nsNativeThemeGTK.h
widget/windows/JumpListBuilder.cpp
widget/windows/nsNativeThemeWin.cpp
widget/windows/nsNativeThemeWin.h
widget/windows/nsUXThemeData.cpp
widget/windows/nsUXThemeData.h
widget/windows/nsWindow.cpp
widget/windows/nsWindow.h
widget/windows/nsWindowDefs.h
widget/xpwidgets/GfxInfoX11.cpp
widget/xpwidgets/nsBaseWidget.h
widget/xpwidgets/nsIdleService.cpp
widget/xpwidgets/nsNativeTheme.cpp
widget/xpwidgets/nsNativeTheme.h
xpcom/base/nsStackWalk.cpp
xpcom/ds/nsAtomTable.cpp
xpcom/ds/nsDoubleHashtable.h
xpcom/glue/nsTHashtable.h
xpcom/io/nsDirectoryService.cpp
xpcom/tests/TestAtoms.cpp
--- a/accessible/public/nsIAccessibleEditableText.idl
+++ b/accessible/public/nsIAccessibleEditableText.idl
@@ -38,17 +38,17 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsISupports.idl"
 
 interface nsIEditor;
 
-[scriptable, uuid(52837507-202d-4e72-a482-5f068a1fd720)]
+[scriptable, uuid(e242d495-5cde-4b1c-8c84-2525b14939f5)]
 interface nsIAccessibleEditableText : nsISupports
 {
   /**
    * Sets the attributes for the text between the two given indices. The old
    * attributes are replaced by the new list of attributes. For example,
    * sets font styles, such as italic, bold...
    *
    * @param startPos - start index of the text whose attributes are modified.
@@ -98,24 +98,9 @@ interface nsIAccessibleEditableText : ns
 
   /**
    * Pastes text from the clipboard.
    *
    * @param position - index at which to insert the text from the system
    *                   clipboard into the text represented by this object.
    */
   void pasteText (in long position);
-
-  /**
-   * Returns an editor associated with the accessible.
-   */
-  [noscript] readonly attribute nsIEditor associatedEditor;
 };
-
-/*
- Assumptions:
-
- selectAttributes method takes an nsISupports parameter.
-        'set' methods throw exception on failure.
- 'wstring' inputs are potentially multibyte (UTF-16 for
-        instance); 'string' and UTF-8 may be a better choice.
-
-*/
--- a/accessible/src/atk/nsApplicationAccessibleWrap.cpp
+++ b/accessible/src/atk/nsApplicationAccessibleWrap.cpp
@@ -692,16 +692,26 @@ nsApplicationAccessibleWrap::Unload()
     }
     // if (sATKLib) {
     //     PR_UnloadLibrary(sATKLib);
     //     sATKLib = nsnull;
     // }
 }
 
 NS_IMETHODIMP
+nsApplicationAccessibleWrap::GetName(nsAString& aName)
+{
+  // ATK doesn't provide a way to obtain an application name (for example,
+  // Firefox or Thunderbird) like IA2 does. Thus let's return an application
+  // name as accessible name that was used to get a branding name (for example,
+  // Minefield aka nightly Firefox or Daily aka nightly Thunderbird).
+  return GetAppName(aName);
+}
+
+NS_IMETHODIMP
 nsApplicationAccessibleWrap::GetNativeInterface(void **aOutAccessible)
 {
     *aOutAccessible = nsnull;
 
     if (!mAtkObject) {
         mAtkObject =
             reinterpret_cast<AtkObject *>
                             (g_object_new(MAI_TYPE_ATK_OBJECT, NULL));
--- a/accessible/src/atk/nsApplicationAccessibleWrap.h
+++ b/accessible/src/atk/nsApplicationAccessibleWrap.h
@@ -52,16 +52,18 @@ public:
 public:
     nsApplicationAccessibleWrap();
     virtual ~nsApplicationAccessibleWrap();
 
     // nsAccessNode
     virtual bool Init();
 
     // nsAccessible
+    NS_IMETHOD GetName(nsAString &aName);
+
     virtual bool AppendChild(nsAccessible* aChild);
     virtual bool RemoveChild(nsAccessible* aChild);
 
     // return the atk object for app root accessible
     NS_IMETHOD GetNativeInterface(void **aOutAccessible);
 };
 
 #endif   /* __NS_APP_ROOT_ACCESSIBLE_H__ */
--- a/accessible/src/base/NotificationController.cpp
+++ b/accessible/src/base/NotificationController.cpp
@@ -675,18 +675,17 @@ NotificationController::CreateTextChange
     return;
 
   nsHyperTextAccessible* textAccessible = container->AsHyperText();
   if (!textAccessible)
     return;
 
   // Don't fire event for the first html:br in an editor.
   if (aEvent->mAccessible->Role() == roles::WHITESPACE) {
-    nsCOMPtr<nsIEditor> editor;
-    textAccessible->GetAssociatedEditor(getter_AddRefs(editor));
+    nsCOMPtr<nsIEditor> editor = textAccessible->GetEditor();
     if (editor) {
       bool isEmpty = false;
       editor->GetDocumentIsEmpty(&isEmpty);
       if (isEmpty)
         return;
     }
   }
 
--- a/accessible/src/base/nsAccessible.h
+++ b/accessible/src/base/nsAccessible.h
@@ -210,16 +210,21 @@ public:
 
   /**
    * Return the states of accessible, not taking into account ARIA states.
    * Use State() to get complete set of states.
    */
   virtual PRUint64 NativeState();
 
   /**
+   * Return bit set of invisible and offscreen states.
+   */
+  PRUint64 VisibilityState();
+
+  /**
    * Returns attributes for accessible without explicitly setted ARIA
    * attributes.
    */
   virtual nsresult GetAttributesInternal(nsIPersistentProperties *aAttributes);
 
   /**
    * Used by ChildAtPoint() method to get direct or deepest child at point.
    */
@@ -697,18 +702,16 @@ protected:
   /**
    * Return ARIA role (helper method).
    */
   mozilla::a11y::role ARIARoleInternal();
 
   virtual nsIFrame* GetBoundsFrame();
   virtual void GetBoundsRect(nsRect& aRect, nsIFrame** aRelativeFrame);
 
-  PRUint64 VisibilityState(); 
-
   //////////////////////////////////////////////////////////////////////////////
   // Name helpers
 
   /**
    * Compute the name of HTML node.
    */
   nsresult GetHTMLName(nsAString& aName);
 
--- a/accessible/src/base/nsDocAccessible.cpp
+++ b/accessible/src/base/nsDocAccessible.cpp
@@ -328,18 +328,17 @@ nsDocAccessible::NativeState()
     state |= states::BUSY;
 
   nsIFrame* frame = GetFrame();
   if (!frame ||
       !frame->IsVisibleConsideringAncestors(nsIFrame::VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY)) {
     state |= states::INVISIBLE | states::OFFSCREEN;
   }
 
-  nsCOMPtr<nsIEditor> editor;
-  GetAssociatedEditor(getter_AddRefs(editor));
+  nsCOMPtr<nsIEditor> editor = GetEditor();
   state |= editor ? states::EDITABLE : states::READONLY;
 
   return state;
 }
 
 // nsAccessible public method
 void
 nsDocAccessible::ApplyARIAState(PRUint64* aState)
@@ -548,47 +547,42 @@ nsDocAccessible::GetVirtualCursor(nsIAcc
     mVirtualCursor = new nsAccessiblePivot(this);
     mVirtualCursor->AddObserver(this);
   }
 
   NS_ADDREF(*aVirtualCursor = mVirtualCursor);
   return NS_OK;
 }
 
-// nsIAccessibleHyperText method
-NS_IMETHODIMP nsDocAccessible::GetAssociatedEditor(nsIEditor **aEditor)
+// nsHyperTextAccessible method
+already_AddRefed<nsIEditor>
+nsDocAccessible::GetEditor() const
 {
-  NS_ENSURE_ARG_POINTER(aEditor);
-  *aEditor = nsnull;
-
-  if (IsDefunct())
-    return NS_ERROR_FAILURE;
-
   // Check if document is editable (designMode="on" case). Otherwise check if
   // the html:body (for HTML document case) or document element is editable.
   if (!mDocument->HasFlag(NODE_IS_EDITABLE) &&
       !mContent->HasFlag(NODE_IS_EDITABLE))
-    return NS_OK;
+    return nsnull;
 
   nsCOMPtr<nsISupports> container = mDocument->GetContainer();
   nsCOMPtr<nsIEditingSession> editingSession(do_GetInterface(container));
   if (!editingSession)
-    return NS_OK; // No editing session interface
+    return nsnull; // No editing session interface
 
   nsCOMPtr<nsIEditor> editor;
   editingSession->GetEditorForWindow(mDocument->GetWindow(), getter_AddRefs(editor));
-  if (!editor) {
-    return NS_OK;
-  }
-  bool isEditable;
+  if (!editor)
+    return nsnull;
+
+  bool isEditable = false;
   editor->GetIsDocumentEditable(&isEditable);
-  if (isEditable) {
-    NS_ADDREF(*aEditor = editor);
-  }
-  return NS_OK;
+  if (isEditable)
+    return editor.forget();
+
+  return nsnull;
 }
 
 // nsDocAccessible public method
 nsAccessible*
 nsDocAccessible::GetAccessible(nsINode* aNode) const
 {
   nsAccessible* accessible = mNodeToAccessibleMap.Get(aNode);
 
--- a/accessible/src/base/nsDocAccessible.h
+++ b/accessible/src/base/nsDocAccessible.h
@@ -128,18 +128,18 @@ public:
   virtual void ApplyARIAState(PRUint64* aState);
 
   virtual void SetRoleMapEntry(nsRoleMapEntry* aRoleMapEntry);
 
 #ifdef DEBUG_ACCDOCMGR
   virtual nsresult HandleAccEvent(AccEvent* aAccEvent);
 #endif
 
-  // nsIAccessibleText
-  NS_IMETHOD GetAssociatedEditor(nsIEditor **aEditor);
+  // nsHyperTextAccessible
+  virtual already_AddRefed<nsIEditor> GetEditor() const;
 
   // nsDocAccessible
 
   /**
    * Return presentation shell for this document accessible.
    */
   nsIPresShell* PresShell() const { return mPresShell; }
 
--- a/accessible/src/html/nsHTMLFormControlAccessible.cpp
+++ b/accessible/src/html/nsHTMLFormControlAccessible.cpp
@@ -539,39 +539,40 @@ NS_IMETHODIMP nsHTMLTextFieldAccessible:
     if ( element ) {
       return element->Focus();
     }
     return NS_ERROR_FAILURE;
   }
   return NS_ERROR_INVALID_ARG;
 }
 
-NS_IMETHODIMP nsHTMLTextFieldAccessible::GetAssociatedEditor(nsIEditor **aEditor)
+already_AddRefed<nsIEditor>
+nsHTMLTextFieldAccessible::GetEditor() const
 {
-  *aEditor = nsnull;
   nsCOMPtr<nsIDOMNSEditableElement> editableElt(do_QueryInterface(mContent));
-  NS_ENSURE_TRUE(editableElt, NS_ERROR_FAILURE);
+  if (!editableElt)
+    return nsnull;
 
   // nsGenericHTMLElement::GetEditor has a security check.
   // Make sure we're not restricted by the permissions of
   // whatever script is currently running.
   nsCOMPtr<nsIJSContextStack> stack =
     do_GetService("@mozilla.org/js/xpc/ContextStack;1");
   bool pushed = stack && NS_SUCCEEDED(stack->Push(nsnull));
 
   nsCOMPtr<nsIEditor> editor;
-  nsresult rv = editableElt->GetEditor(aEditor);
+  editableElt->GetEditor(getter_AddRefs(editor));
 
   if (pushed) {
     JSContext* cx;
     stack->Pop(&cx);
     NS_ASSERTION(!cx, "context should be null");
   }
 
-  return rv;
+  return editor.forget();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsHTMLTextFieldAccessible: Widgets
 
 bool
 nsHTMLTextFieldAccessible::IsWidget() const
 {
--- a/accessible/src/html/nsHTMLFormControlAccessible.h
+++ b/accessible/src/html/nsHTMLFormControlAccessible.h
@@ -133,18 +133,18 @@ public:
 
   NS_DECL_ISUPPORTS_INHERITED
 
   // nsIAccessible
   NS_IMETHOD GetValue(nsAString& _retval); 
   NS_IMETHOD GetActionName(PRUint8 aIndex, nsAString& aName);
   NS_IMETHOD DoAction(PRUint8 index);
 
-  // nsIAccessibleEditableText
-  NS_IMETHOD GetAssociatedEditor(nsIEditor **aEditor);
+  // nsHyperTextAccessible
+  virtual already_AddRefed<nsIEditor> GetEditor() const;
 
   // nsAccessible
   virtual void ApplyARIAState(PRUint64* aState);
   virtual nsresult GetNameInternal(nsAString& aName);
   virtual mozilla::a11y::role NativeRole();
   virtual PRUint64 State();
   virtual PRUint64 NativeState();
 
--- a/accessible/src/html/nsHyperTextAccessible.cpp
+++ b/accessible/src/html/nsHyperTextAccessible.cpp
@@ -42,16 +42,17 @@
 #include "nsAccessibilityService.h"
 #include "nsAccUtils.h"
 #include "nsDocAccessible.h"
 #include "nsTextAttrs.h"
 #include "Role.h"
 #include "States.h"
 
 #include "nsIClipboard.h"
+#include "nsContentUtils.h"
 #include "nsFocusManager.h"
 #include "nsIDOMCharacterData.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMRange.h"
 #include "nsIDOMXULDocument.h"
 #include "nsIEditingSession.h"
 #include "nsIEditor.h"
 #include "nsIFrame.h"
@@ -160,18 +161,17 @@ nsHyperTextAccessible::NativeRole()
   return roles::TEXT_CONTAINER; // In ATK this works
 }
 
 PRUint64
 nsHyperTextAccessible::NativeState()
 {
   PRUint64 states = nsAccessibleWrap::NativeState();
 
-  nsCOMPtr<nsIEditor> editor;
-  GetAssociatedEditor(getter_AddRefs(editor));
+  nsCOMPtr<nsIEditor> editor = GetEditor();
   if (editor) {
     PRUint32 flags;
     editor->GetFlags(&flags);
     if (0 == (flags & nsIPlaintextEditor::eEditorReadonlyMask)) {
       states |= states::EDITABLE;
     }
   } else if (mContent->Tag() == nsGkAtoms::article) {
     // We want <article> to behave like a document in terms of readonly state.
@@ -705,18 +705,17 @@ nsHyperTextAccessible::HypertextOffsetsT
   *aEndNode = nsnull;
 
   NS_ENSURE_ARG_POINTER(aEndOffset);
   *aEndOffset = -1;
 
   // If the given offsets are 0 and associated editor is empty then return
   // collapsed range with editor root element as range container.
   if (aStartHTOffset == 0 && aEndHTOffset == 0) {
-    nsCOMPtr<nsIEditor> editor;
-    GetAssociatedEditor(getter_AddRefs(editor));
+    nsCOMPtr<nsIEditor> editor = GetEditor();
     if (editor) {
       bool isEmpty = false;
       editor->GetDocumentIsEmpty(&isEmpty);
       if (isEmpty) {
         nsCOMPtr<nsIDOMElement> editorRootElm;
         editor->GetRootElement(getter_AddRefs(editorRootElm));
 
         nsCOMPtr<nsIDOMNode> editorRoot(do_QueryInterface(editorRootElm));
@@ -1148,25 +1147,24 @@ nsHyperTextAccessible::GetTextAttributes
                               accAtOffsetIdx);
   nsresult rv = textAttrsMgr.GetAttributes(*aAttributes, &startOffset,
                                            &endOffset);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Compute spelling attributes on text accessible only.
   nsIFrame *offsetFrame = accAtOffset->GetFrame();
   if (offsetFrame && offsetFrame->GetType() == nsGkAtoms::textFrame) {
-    nsCOMPtr<nsIDOMNode> node = accAtOffset->DOMNode();
-
     PRInt32 nodeOffset = 0;
     nsresult rv = RenderedToContentOffset(offsetFrame, offsetInAcc,
                                           &nodeOffset);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Set 'misspelled' text attribute.
-    rv = GetSpellTextAttribute(node, nodeOffset, &startOffset, &endOffset,
+    rv = GetSpellTextAttribute(accAtOffset->GetNode(), nodeOffset,
+                               &startOffset, &endOffset,
                                aAttributes ? *aAttributes : nsnull);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   *aStartOffset = startOffset;
   *aEndOffset = endOffset;
   return NS_OK;
 }
@@ -1450,118 +1448,121 @@ NS_IMETHODIMP nsHyperTextAccessible::Set
     return InsertText(aText, 0);
   }
   return NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
 nsHyperTextAccessible::InsertText(const nsAString &aText, PRInt32 aPosition)
 {
-  nsCOMPtr<nsIEditor> editor;
-  GetAssociatedEditor(getter_AddRefs(editor));
+  if (IsDefunct())
+    return NS_ERROR_FAILURE;
+
+  nsCOMPtr<nsIEditor> editor = GetEditor();
 
   nsCOMPtr<nsIPlaintextEditor> peditor(do_QueryInterface(editor));
   NS_ENSURE_STATE(peditor);
 
   nsresult rv = SetSelectionRange(aPosition, aPosition);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return peditor->InsertText(aText);
 }
 
 NS_IMETHODIMP
 nsHyperTextAccessible::CopyText(PRInt32 aStartPos, PRInt32 aEndPos)
 {
-  nsCOMPtr<nsIEditor> editor;
-  GetAssociatedEditor(getter_AddRefs(editor));
+  if (IsDefunct())
+    return NS_ERROR_FAILURE;
+
+  nsCOMPtr<nsIEditor> editor = GetEditor();
   NS_ENSURE_STATE(editor);
 
   nsresult rv = SetSelectionRange(aStartPos, aEndPos);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return editor->Copy();
 }
 
 NS_IMETHODIMP
 nsHyperTextAccessible::CutText(PRInt32 aStartPos, PRInt32 aEndPos)
 {
-  nsCOMPtr<nsIEditor> editor;
-  GetAssociatedEditor(getter_AddRefs(editor));
+  if (IsDefunct())
+    return NS_ERROR_FAILURE;
+
+  nsCOMPtr<nsIEditor> editor = GetEditor();
   NS_ENSURE_STATE(editor);
 
   nsresult rv = SetSelectionRange(aStartPos, aEndPos);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return editor->Cut();
 }
 
 NS_IMETHODIMP
 nsHyperTextAccessible::DeleteText(PRInt32 aStartPos, PRInt32 aEndPos)
 {
-  nsCOMPtr<nsIEditor> editor;
-  GetAssociatedEditor(getter_AddRefs(editor));
+  if (IsDefunct())
+    return NS_ERROR_FAILURE;
+
+  nsCOMPtr<nsIEditor> editor = GetEditor();
   NS_ENSURE_STATE(editor);
 
   nsresult rv = SetSelectionRange(aStartPos, aEndPos);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return editor->DeleteSelection(nsIEditor::eNone);
 }
 
 NS_IMETHODIMP
 nsHyperTextAccessible::PasteText(PRInt32 aPosition)
 {
-  nsCOMPtr<nsIEditor> editor;
-  GetAssociatedEditor(getter_AddRefs(editor));
+  if (IsDefunct())
+    return NS_ERROR_FAILURE;
+
+  nsCOMPtr<nsIEditor> editor = GetEditor();
   NS_ENSURE_STATE(editor);
 
   nsresult rv = SetSelectionRange(aPosition, aPosition);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return editor->Paste(nsIClipboard::kGlobalClipboard);
 }
 
-NS_IMETHODIMP
-nsHyperTextAccessible::GetAssociatedEditor(nsIEditor **aEditor)
+already_AddRefed<nsIEditor>
+nsHyperTextAccessible::GetEditor() const
 {
-  NS_ENSURE_ARG_POINTER(aEditor);
-  *aEditor = nsnull;
-
-  if (IsDefunct())
-    return NS_ERROR_FAILURE;
-
   if (!mContent->HasFlag(NODE_IS_EDITABLE)) {
     // If we're inside an editable container, then return that container's editor
-    nsCOMPtr<nsIAccessible> ancestor, current = this;
-    while (NS_SUCCEEDED(current->GetParent(getter_AddRefs(ancestor))) && ancestor) {
-      nsRefPtr<nsHyperTextAccessible> ancestorTextAccessible;
-      ancestor->QueryInterface(NS_GET_IID(nsHyperTextAccessible),
-                               getter_AddRefs(ancestorTextAccessible));
-      if (ancestorTextAccessible) {
+    nsAccessible* ancestor = Parent();
+    while (ancestor) {
+      nsHyperTextAccessible* hyperText = ancestor->AsHyperText();
+      if (hyperText) {
         // Recursion will stop at container doc because it has its own impl
-        // of GetAssociatedEditor()
-        return ancestorTextAccessible->GetAssociatedEditor(aEditor);
+        // of GetEditor()
+        return hyperText->GetEditor();
       }
-      current = ancestor;
+
+      ancestor = ancestor->Parent();
     }
-    return NS_OK;
+
+    return nsnull;
   }
 
   nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem =
     nsCoreUtils::GetDocShellTreeItemFor(mContent);
   nsCOMPtr<nsIEditingSession> editingSession(do_GetInterface(docShellTreeItem));
   if (!editingSession)
-    return NS_OK; // No editing session interface
-
-  NS_ENSURE_TRUE(mDoc, NS_ERROR_FAILURE);
-  nsIDocument* docNode = mDoc->GetDocumentNode();
-  NS_ENSURE_TRUE(docNode, NS_ERROR_FAILURE);
+    return nsnull; // No editing session interface
 
   nsCOMPtr<nsIEditor> editor;
-  return editingSession->GetEditorForWindow(docNode->GetWindow(), aEditor);
+  nsIDocument* docNode = mDoc->GetDocumentNode();
+  editingSession->GetEditorForWindow(docNode->GetWindow(),
+                                     getter_AddRefs(editor));
+  return editor.forget();
 }
 
 /**
   * =================== Caret & Selection ======================
   */
 
 nsresult
 nsHyperTextAccessible::SetSelectionRange(PRInt32 aStartPos, PRInt32 aEndPos)
@@ -1765,18 +1766,17 @@ nsHyperTextAccessible::GetSelectionDOMRa
     return;
 
   nsISelection* domSel = frameSelection->GetSelection(aType);
   if (!domSel)
     return;
 
   nsCOMPtr<nsINode> startNode = GetNode();
 
-  nsCOMPtr<nsIEditor> editor;
-  GetAssociatedEditor(getter_AddRefs(editor));
+  nsCOMPtr<nsIEditor> editor = GetEditor();
   if (editor) {
     nsCOMPtr<nsIDOMElement> editorRoot;
     editor->GetRootElement(getter_AddRefs(editorRoot));
     startNode = do_QueryInterface(editorRoot);
   }
 
   if (!startNode)
     return;
@@ -1784,23 +1784,21 @@ nsHyperTextAccessible::GetSelectionDOMRa
   PRUint32 childCount = startNode->GetChildCount();
   nsCOMPtr<nsISelectionPrivate> privSel(do_QueryInterface(domSel));
   nsresult rv = privSel->
     GetRangesForIntervalArray(startNode, 0, startNode, childCount, true, aRanges);
   NS_ENSURE_SUCCESS(rv,);
 
   // Remove collapsed ranges
   PRUint32 numRanges = aRanges->Length();
-  for (PRUint32 count = 0; count < numRanges; count ++) {
-    bool isCollapsed = false;
-    (*aRanges)[count]->GetCollapsed(&isCollapsed);
-    if (isCollapsed) {
-      aRanges->RemoveElementAt(count);
+  for (PRUint32 idx = 0; idx < numRanges; idx ++) {
+    if ((*aRanges)[idx]->Collapsed()) {
+      aRanges->RemoveElementAt(idx);
       --numRanges;
-      --count;
+      --idx;
     }
   }
 }
 
 /*
  * Gets the number of selected regions.
  */
 NS_IMETHODIMP
@@ -1832,39 +1830,29 @@ nsHyperTextAccessible::GetSelectionBound
   GetSelectionDOMRanges(nsISelectionController::SELECTION_NORMAL, &ranges);
 
   PRUint32 rangeCount = ranges.Length();
   if (aSelectionNum < 0 || aSelectionNum >= rangeCount)
     return NS_ERROR_INVALID_ARG;
 
   nsRange* range = ranges[aSelectionNum];
 
-  // Get start point
-  nsCOMPtr<nsIDOMNode> startDOMNode;
-  range->GetStartContainer(getter_AddRefs(startDOMNode));
-  nsCOMPtr<nsINode> startNode(do_QueryInterface(startDOMNode));
-  PRInt32 startOffset = 0;
-  range->GetStartOffset(&startOffset);
+  // Get start and end points.
+  nsINode* startNode = range->GetStartParent();
+  nsINode* endNode = range->GetEndParent();
+  PRInt32 startOffset = range->StartOffset(), endOffset = range->EndOffset();
 
-  // Get end point
-  nsCOMPtr<nsIDOMNode> endDOMNode;
-  range->GetEndContainer(getter_AddRefs(endDOMNode));
-  nsCOMPtr<nsINode> endNode(do_QueryInterface(endDOMNode));
-  PRInt32 endOffset = 0;
-  range->GetEndOffset(&endOffset);
-
-  PRInt16 rangeCompareResult = 0;
-  nsresult rv = range->CompareBoundaryPoints(nsIDOMRange::START_TO_END, range,
-                                             &rangeCompareResult);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  if (rangeCompareResult < 0) {
-    // Make sure start is before end, by swapping offsets
-    // This occurs when the user selects backwards in the text
-    startNode.swap(endNode);
+  // Make sure start is before end, by swapping DOM points.  This occurs when
+  // the user selects backwards in the text.
+  PRInt32 rangeCompare = nsContentUtils::ComparePoints(endNode, endOffset,
+                                                       startNode, startOffset);
+  if (rangeCompare < 0) {
+    nsINode* tempNode = startNode;
+    startNode = endNode;
+    endNode = tempNode;
     PRInt32 tempOffset = startOffset;
     startOffset = endOffset;
     endOffset = tempOffset;
   }
 
   nsAccessible *startAccessible =
     DOMPointToHypertextOffset(startNode, startOffset, aStartOffset);
   if (!startAccessible) {
@@ -2319,78 +2307,64 @@ nsHyperTextAccessible::GetDOMPointByFram
 
 // nsHyperTextAccessible
 nsresult
 nsHyperTextAccessible::RangeBoundToHypertextOffset(nsRange *aRange,
                                                    bool aIsStartBound,
                                                    bool aIsStartHTOffset,
                                                    PRInt32 *aHTOffset)
 {
-  nsCOMPtr<nsIDOMNode> DOMNode;
+  nsINode* node = nsnull;
   PRInt32 nodeOffset = 0;
 
-  nsresult rv;
   if (aIsStartBound) {
-    rv = aRange->GetStartContainer(getter_AddRefs(DOMNode));
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = aRange->GetStartOffset(&nodeOffset);
-    NS_ENSURE_SUCCESS(rv, rv);
+    node = aRange->GetStartParent();
+    nodeOffset = aRange->StartOffset();
   } else {
-    rv = aRange->GetEndContainer(getter_AddRefs(DOMNode));
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = aRange->GetEndOffset(&nodeOffset);
-    NS_ENSURE_SUCCESS(rv, rv);
+    node = aRange->GetEndParent();
+    nodeOffset = aRange->EndOffset();
   }
 
-  nsCOMPtr<nsINode> node(do_QueryInterface(DOMNode));
   nsAccessible *startAcc =
     DOMPointToHypertextOffset(node, nodeOffset, aHTOffset);
 
   if (aIsStartHTOffset && !startAcc)
     *aHTOffset = 0;
 
   return NS_OK;
 }
 
 // nsHyperTextAccessible
 nsresult
-nsHyperTextAccessible::GetSpellTextAttribute(nsIDOMNode *aNode,
+nsHyperTextAccessible::GetSpellTextAttribute(nsINode* aNode,
                                              PRInt32 aNodeOffset,
                                              PRInt32 *aHTStartOffset,
                                              PRInt32 *aHTEndOffset,
                                              nsIPersistentProperties *aAttributes)
 {
   nsTArray<nsRange*> ranges;
   GetSelectionDOMRanges(nsISelectionController::SELECTION_SPELLCHECK, &ranges);
 
   PRUint32 rangeCount = ranges.Length();
   if (!rangeCount)
     return NS_OK;
 
+  nsCOMPtr<nsIDOMNode> DOMNode = do_QueryInterface(aNode);
   for (PRUint32 index = 0; index < rangeCount; index++) {
     nsRange* range = ranges[index];
 
     PRInt16 result;
-    nsresult rv = range->ComparePoint(aNode, aNodeOffset, &result);
+    nsresult rv = range->ComparePoint(DOMNode, aNodeOffset, &result);
     NS_ENSURE_SUCCESS(rv, rv);
     // ComparePoint checks boundary points, but we need to check that
     // text at aNodeOffset is inside the range.
     // See also bug 460690.
     if (result == 0) {
-      nsCOMPtr<nsIDOMNode> end;
-      rv = range->GetEndContainer(getter_AddRefs(end));
-      NS_ENSURE_SUCCESS(rv, rv);
-      PRInt32 endOffset;
-      rv = range->GetEndOffset(&endOffset);
-      NS_ENSURE_SUCCESS(rv, rv);
-      if (aNode == end && aNodeOffset == endOffset) {
+      if (aNode == range->GetEndParent() && aNodeOffset == range->EndOffset())
         result = 1;
-      }
     }
 
     if (result == 1) { // range is before point
       PRInt32 startHTOffset = 0;
       nsresult rv = RangeBoundToHypertextOffset(range, false, true,
                                                 &startHTOffset);
       NS_ENSURE_SUCCESS(rv, rv);
 
--- a/accessible/src/html/nsHyperTextAccessible.h
+++ b/accessible/src/html/nsHyperTextAccessible.h
@@ -259,16 +259,24 @@ public:
    *
    * @param  aOffset  [in] the given text offset
    */
   nsAccessible* GetChildAtOffset(PRUint32 aOffset)
   {
     return GetChildAt(GetChildIndexAtOffset(aOffset));
   }
 
+  //////////////////////////////////////////////////////////////////////////////
+  // EditableTextAccessible
+
+  /**
+   * Return the editor associated with the accessible.
+   */
+  virtual already_AddRefed<nsIEditor> GetEditor() const;
+
 protected:
   // nsHyperTextAccessible
 
   /**
    * Transform magic offset into text offset.
    */
   inline PRInt32 ConvertMagicOffset(PRInt32 aOffset)
   {
@@ -404,17 +412,17 @@ protected:
    *
    * @param aIncludeDefAttrs  [in] points whether text attributes having default
    *                          values of attributes should be included
    * @param aSourceNode       [in] the node we start to traverse from
    * @param aStartOffset      [in, out] the start offset
    * @param aEndOffset        [in, out] the end offset
    * @param aAttributes       [out, optional] result attributes
    */
-  nsresult GetSpellTextAttribute(nsIDOMNode *aNode, PRInt32 aNodeOffset,
+  nsresult GetSpellTextAttribute(nsINode* aNode, PRInt32 aNodeOffset,
                                  PRInt32 *aStartOffset,
                                  PRInt32 *aEndOffset,
                                  nsIPersistentProperties *aAttributes);
 
 private:
   /**
    * End text offsets array.
    */
--- a/accessible/src/xforms/nsXFormsAccessible.cpp
+++ b/accessible/src/xforms/nsXFormsAccessible.cpp
@@ -268,34 +268,36 @@ nsXFormsEditableAccessible::NativeState(
     bool isRelevant = false;
     rv = sXFormsService->IsRelevant(DOMNode, &isRelevant);
     NS_ENSURE_SUCCESS(rv, state);
     if (isRelevant) {
       state |= states::EDITABLE | states::SELECTABLE_TEXT;
     }
   }
 
-  nsCOMPtr<nsIEditor> editor;
-  GetAssociatedEditor(getter_AddRefs(editor));
+  nsCOMPtr<nsIEditor> editor = GetEditor();
   NS_ENSURE_TRUE(editor, state);
   PRUint32 flags;
   editor->GetFlags(&flags);
   if (flags & nsIPlaintextEditor::eEditorSingleLineMask)
     state |= states::SINGLE_LINE;
   else
     state |= states::MULTI_LINE;
 
   return state;
 }
 
-NS_IMETHODIMP
-nsXFormsEditableAccessible::GetAssociatedEditor(nsIEditor **aEditor)
+already_AddRefed<nsIEditor>
+nsXFormsEditableAccessible::GetEditor() const
 {
   nsCOMPtr<nsIDOMNode> DOMNode(do_QueryInterface(mContent));
-  return sXFormsService->GetEditor(DOMNode, aEditor);
+
+  nsCOMPtr<nsIEditor> editor;
+  sXFormsService->GetEditor(DOMNode, getter_AddRefs(editor));
+  return editor.forget();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsXFormsSelectableAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 nsXFormsSelectableAccessible::
   nsXFormsSelectableAccessible(nsIContent* aContent, nsDocAccessible* aDoc) :
--- a/accessible/src/xforms/nsXFormsAccessible.h
+++ b/accessible/src/xforms/nsXFormsAccessible.h
@@ -139,18 +139,18 @@ public:
  * The class is base for accessible objects for XForms elements that have
  * editable area.
  */
 class nsXFormsEditableAccessible : public nsXFormsAccessible
 {
 public:
   nsXFormsEditableAccessible(nsIContent* aContent, nsDocAccessible* aDoc);
 
-  // nsIAccessibleEditableText
-  NS_IMETHOD GetAssociatedEditor(nsIEditor **aEditor);
+  // nsHyperTextAccessible
+  virtual already_AddRefed<nsIEditor> GetEditor() const;
 
   // nsAccessible
   virtual PRUint64 NativeState();
 };
 
 
 /**
  * The class is base for accessible objects for XForms select and XForms
--- a/accessible/src/xul/nsXULFormControlAccessible.cpp
+++ b/accessible/src/xul/nsXULFormControlAccessible.cpp
@@ -845,24 +845,27 @@ NS_IMETHODIMP nsXULTextFieldAccessible::
 }
 
 bool
 nsXULTextFieldAccessible::CanHaveAnonChildren()
 {
   return false;
 }
 
-NS_IMETHODIMP nsXULTextFieldAccessible::GetAssociatedEditor(nsIEditor **aEditor)
+already_AddRefed<nsIEditor>
+nsXULTextFieldAccessible::GetEditor() const
 {
-  *aEditor = nsnull;
-
   nsCOMPtr<nsIContent> inputField = GetInputField();
   nsCOMPtr<nsIDOMNSEditableElement> editableElt(do_QueryInterface(inputField));
-  NS_ENSURE_TRUE(editableElt, NS_ERROR_FAILURE);
-  return editableElt->GetEditor(aEditor);
+  if (!editableElt)
+    return nsnull;
+
+  nsCOMPtr<nsIEditor> editor;
+  editableElt->GetEditor(getter_AddRefs(editor));
+  return editor.forget();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsXULTextFieldAccessible: nsAccessible protected
 
 void
 nsXULTextFieldAccessible::CacheChildren()
 {
--- a/accessible/src/xul/nsXULFormControlAccessible.h
+++ b/accessible/src/xul/nsXULFormControlAccessible.h
@@ -255,18 +255,18 @@ public:
 
   NS_DECL_ISUPPORTS_INHERITED
 
   // nsIAccessible
   NS_IMETHOD GetValue(nsAString& aValue);
   NS_IMETHOD GetActionName(PRUint8 aIndex, nsAString& aName);
   NS_IMETHOD DoAction(PRUint8 index);
 
-  // nsIAccessibleEditableText
-  NS_IMETHOD GetAssociatedEditor(nsIEditor **aEditor);
+  // nsHyperTextAccessible
+  virtual already_AddRefed<nsIEditor> GetEditor() const;
 
   // nsAccessible
   virtual void ApplyARIAState(PRUint64* aState);
   virtual mozilla::a11y::role NativeRole();
   virtual PRUint64 NativeState();
   virtual bool CanHaveAnonChildren();
 
   // ActionAccessible
--- a/accessible/tests/mochitest/common.js
+++ b/accessible/tests/mochitest/common.js
@@ -608,14 +608,14 @@ function getNodePrettyName(aNode)
   } catch (e) {
     return "' no node info '";
   }
 }
 
 function getObjAddress(aObj)
 {
   var exp = /native\s*@\s*(0x[a-f0-9]+)/g;
-  var match = exp.exec(aObj.valueOf());
+  var match = exp.exec(aObj.toString());
   if (match)
     return match[1];
 
-  return aObj.valueOf();
+  return aObj.toString();
 }
--- a/accessible/tests/mochitest/elm/test_nsApplicationAcc.html
+++ b/accessible/tests/mochitest/elm/test_nsApplicationAcc.html
@@ -10,51 +10,56 @@
   <script type="application/javascript" 
           src="../common.js"></script>
   <script type="application/javascript" 
           src="../role.js"></script>
 
   <script type="application/javascript">
     function doTest()
     {
-        var accessible = getApplicationAccessible();
-        if (!accessible) {
-          SimpleTest.finish();
-          return;
-        }
+      var accessible = getApplicationAccessible();
+      if (!accessible) {
+        SimpleTest.finish();
+        return;
+      }
+
+      var bundleServ =
+        Components.classes["@mozilla.org/intl/stringbundle;1"].
+        getService(Components.interfaces.nsIStringBundleService);
+      var brandBundle =
+        bundleServ.createBundle("chrome://branding/locale/brand.properties");
 
-        // nsIAccessible::name
-        var bundleServ = Components.classes["@mozilla.org/intl/stringbundle;1"]
-                         .getService(Components.interfaces.nsIStringBundleService);
-        var bundle = bundleServ.createBundle("chrome://branding/locale/brand.properties");
+      var appInfo = Components.classes["@mozilla.org/xre/app-info;1"].
+        getService(Components.interfaces.nsIXULAppInfo);
 
-        var applicationName = "";
-
+      // nsIAccessible::name
+      var applicationName = "";
+      if (LINUX || SOLARIS) {
+        applicationName = appInfo.name;
+      } else {
         try {
-            applicationName = bundle.GetStringFromName("brandShortName");
-        }  catch(e) {
+          applicationName = brandBundle.GetStringFromName("brandShortName");
+        } catch(e) {
         }
 
         if (applicationName == "")
-            applicationName = "Gecko based application";
-
-        is (accessible.name, applicationName, "wrong application accessible name");
-
-        // nsIAccessibleApplication
-        var appInfo = Components.classes["@mozilla.org/xre/app-info;1"].
-          getService(Components.interfaces.nsIXULAppInfo);
+          applicationName = "Gecko based application";
+      }
+      is (accessible.name, applicationName, "wrong application accessible name");
 
-        is(accessible.appName, appInfo.name, "Wrong application name");
-        is(accessible.appVersion, appInfo.version, "Wrong application version");
-        is(accessible.platformName, "Gecko", "Wrong platform name");
-        is(accessible.platformVersion, appInfo.platformVersion,
-           "Wrong platform version");
+      // nsIAccessibleApplication
+      is(accessible.appName, appInfo.name, "Wrong application name");
+      is(accessible.appVersion, appInfo.version, "Wrong application version");
+      is(accessible.platformName, "Gecko", "Wrong platform name");
+      is(accessible.platformVersion, appInfo.platformVersion,
+         "Wrong platform version");
 
-        SimpleTest.finish();
+      SimpleTest.finish();
     }
+
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   </script>
   </head>
   <body>
   <a target="_blank"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=456121"
     title="nsApplicationAccessible::GetName does not return a default value when brand.properties does not exist">
--- a/accessible/tests/mochitest/events.js
+++ b/accessible/tests/mochitest/events.js
@@ -299,22 +299,30 @@ function eventQueue(aEventType)
         SimpleTest.finish();
 
       return;
     }
 
     // Start processing of next invoker.
     invoker = this.getNextInvoker();
 
+    this.setEventHandler(invoker);
+
     if (gLogger.isEnabled()) {
       gLogger.logToConsole("Event queue: \n  invoke: " + invoker.getID());
       gLogger.logToDOM("EQ: invoke: " + invoker.getID(), true);
     }
 
-    this.setEventHandler(invoker);
+    var infoText = "Invoke the '" + invoker.getID() + "' test { ";
+    for (var idx = 0; idx < this.mEventSeq.length; idx++) {
+      infoText += this.isEventUnexpected(idx) ? "un" : "";
+      infoText += "expected '" + this.getEventTypeAsString(idx) + "' event; ";
+    }
+    infoText += " }";
+    info(infoText);
 
     if (invoker.invoke() == INVOKER_ACTION_FAILED) {
       // Invoker failed to prepare action, fail and finish tests.
       this.processNextInvoker();
       return;
     }
 
     if (this.areAllEventsUnexpected())
--- a/aclocal.m4
+++ b/aclocal.m4
@@ -13,16 +13,17 @@ builtin(include, build/autoconf/altoptio
 builtin(include, build/autoconf/mozprog.m4)dnl
 builtin(include, build/autoconf/mozheader.m4)dnl
 builtin(include, build/autoconf/mozcommonheader.m4)dnl
 builtin(include, build/autoconf/acwinpaths.m4)dnl
 builtin(include, build/autoconf/lto.m4)dnl
 builtin(include, build/autoconf/gcc-pr49911.m4)dnl
 builtin(include, build/autoconf/frameptr.m4)dnl
 builtin(include, build/autoconf/compiler-opts.m4)dnl
+builtin(include, build/autoconf/expandlibs.m4)dnl
 
 MOZ_PROG_CHECKMSYS()
 
 # Read the user's .mozconfig script.  We can't do this in
 # configure.in: autoconf puts the argument parsing code above anything
 # expanded from configure.in, and we need to get the configure options
 # from .mozconfig in place before that argument parsing code.
 MOZ_READ_MOZCONFIG(.)
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -41,17 +41,17 @@ pref("toolkit.defaultChromeURI", "chrome
 pref("browser.chromeURL", "chrome://browser/content/");
 #ifdef MOZ_OFFICIAL_BRANDING
 pref("browser.homescreenURL", "file:///system/home/homescreen.html");
 #else
 pref("browser.homescreenURL", "file:///data/local/homescreen.html,file:///system/home/homescreen.html");
 #endif
 
 // URL for the dialer application.
-pref("dom.telephony.app.phone.url", "http://localhost:7777/data/local/apps/dialer/dialer.html");
+pref("dom.telephony.app.phone.url", "http://localhost:7777/data/local/apps/dialer/dialer.html http://localhost:7777/data/local/apps/homescreen/homescreen.html http://localhost:7777/apps/dialer/dialer.html http://localhost:7777/apps/homescreen/homescreen.html");
 
 // Device pixel to CSS px ratio, in percent. Set to -1 to calculate based on display density.
 pref("browser.viewport.scaleRatio", -1);
 
 /* disable text selection */
 pref("browser.ignoreNativeFrameTextSelection", true);
 
 /* cache prefs */
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -61,17 +61,17 @@ pref("extensions.strictCompatibility", f
 // for it to be compatible by default.
 pref("extensions.minCompatibleAppVersion", "4.0");
 
 // Preferences for AMO integration
 pref("extensions.getAddons.cache.enabled", true);
 pref("extensions.getAddons.maxResults", 15);
 pref("extensions.getAddons.get.url", "https://services.addons.mozilla.org/%LOCALE%/firefox/api/%API_VERSION%/search/guid:%IDS%?src=firefox&appOS=%OS%&appVersion=%VERSION%");
 pref("extensions.getAddons.getWithPerformance.url", "https://services.addons.mozilla.org/%LOCALE%/firefox/api/%API_VERSION%/search/guid:%IDS%?src=firefox&appOS=%OS%&appVersion=%VERSION%&tMain=%TIME_MAIN%&tFirstPaint=%TIME_FIRST_PAINT%&tSessionRestored=%TIME_SESSION_RESTORED%");
-pref("extensions.getAddons.search.browseURL", "https://addons.mozilla.org/%LOCALE%/firefox/search?q=%TERMS%");
+pref("extensions.getAddons.search.browseURL", "https://addons.mozilla.org/%LOCALE%/firefox/search?q=%TERMS%&platform=%OS%&appver=%VERSION%");
 pref("extensions.getAddons.search.url", "https://services.addons.mozilla.org/%LOCALE%/firefox/api/%API_VERSION%/search/%TERMS%/all/%MAX_RESULTS%/%OS%/%VERSION%/%COMPATIBILITY_MODE%?src=firefox");
 pref("extensions.webservice.discoverURL", "https://services.addons.mozilla.org/%LOCALE%/firefox/discovery/pane/%VERSION%/%OS%/%COMPATIBILITY_MODE%");
 
 // Blocklist preferences
 pref("extensions.blocklist.enabled", true);
 pref("extensions.blocklist.interval", 86400);
 // Controls what level the blocklist switches from warning about items to forcibly
 // blocking them.
@@ -209,16 +209,17 @@ pref("app.update.service.enabled", true)
 // Symmetric (can be overridden by individual extensions) update preferences.
 // e.g.
 //  extensions.{GUID}.update.enabled
 //  extensions.{GUID}.update.url
 //  .. etc ..
 //
 pref("extensions.update.enabled", true);
 pref("extensions.update.url", "https://versioncheck.addons.mozilla.org/update/VersionCheck.php?reqVersion=%REQ_VERSION%&id=%ITEM_ID%&version=%ITEM_VERSION%&maxAppVersion=%ITEM_MAXAPPVERSION%&status=%ITEM_STATUS%&appID=%APP_ID%&appVersion=%APP_VERSION%&appOS=%APP_OS%&appABI=%APP_ABI%&locale=%APP_LOCALE%&currentAppVersion=%CURRENT_APP_VERSION%&updateType=%UPDATE_TYPE%&compatMode=%COMPATIBILITY_MODE%");
+pref("extensions.update.background.url", "https://versioncheck-bg.addons.mozilla.org/update/VersionCheck.php?reqVersion=%REQ_VERSION%&id=%ITEM_ID%&version=%ITEM_VERSION%&maxAppVersion=%ITEM_MAXAPPVERSION%&status=%ITEM_STATUS%&appID=%APP_ID%&appVersion=%APP_VERSION%&appOS=%APP_OS%&appABI=%APP_ABI%&locale=%APP_LOCALE%&currentAppVersion=%CURRENT_APP_VERSION%&updateType=%UPDATE_TYPE%&compatMode=%COMPATIBILITY_MODE%");
 pref("extensions.update.interval", 86400);  // Check for updates to Extensions and 
                                             // Themes every day
 // Non-symmetric (not shared by extensions) extension-specific [update] preferences
 pref("extensions.getMoreThemesURL", "https://addons.mozilla.org/%LOCALE%/firefox/getpersonas");
 pref("extensions.dss.enabled", false);          // Dynamic Skin Switching                                               
 pref("extensions.dss.switchPending", false);    // Non-dynamic switch pending after next
                                                 // restart.
 
@@ -1036,16 +1037,19 @@ pref("devtools.inspector.sidebarOpen", f
 pref("devtools.inspector.activeSidebar", "ruleview");
 
 // Enable the Debugger
 pref("devtools.debugger.enabled", false);
 
 // The default Debugger UI height
 pref("devtools.debugger.ui.height", 250);
 
+// Disable remote debugging protocol logging
+pref("devtools.debugger.log", false);
+
 // Enable the style inspector
 pref("devtools.styleinspector.enabled", true);
 
 // Enable the Tilt inspector
 pref("devtools.tilt.enabled", true);
 pref("devtools.tilt.intro_transition", true);
 pref("devtools.tilt.outro_transition", true);
 
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1558,59 +1558,16 @@ function delayedStartup(isLoadingBlank, 
   gBrowser.tabContainer.updateVisibility();
 
   gPrefService.addObserver(gHomeButton.prefDomain, gHomeButton, false);
 
   var homeButton = document.getElementById("home-button");
   gHomeButton.updateTooltip(homeButton);
   gHomeButton.updatePersonalToolbarStyle(homeButton);
 
-#ifdef HAVE_SHELL_SERVICE
-  // Perform default browser checking (after window opens).
-  var shell = getShellService();
-  if (shell) {
-#ifdef DEBUG
-    var shouldCheck = false;
-#else
-    var shouldCheck = shell.shouldCheckDefaultBrowser;
-#endif
-    var willRecoverSession = false;
-    try {
-      var ss = Cc["@mozilla.org/browser/sessionstartup;1"].
-               getService(Ci.nsISessionStartup);
-      willRecoverSession =
-        (ss.sessionType == Ci.nsISessionStartup.RECOVER_SESSION);
-    }
-    catch (ex) { /* never mind; suppose SessionStore is broken */ }
-    if (shouldCheck && !shell.isDefaultBrowser(true) && !willRecoverSession) {
-      // Delay the set-default-browser prompt so it doesn't block
-      // initialisation of the session store service.
-      setTimeout(function () {
-        var brandBundle = document.getElementById("bundle_brand");
-        var shellBundle = document.getElementById("bundle_shell");
-
-        var brandShortName = brandBundle.getString("brandShortName");
-        var promptTitle = shellBundle.getString("setDefaultBrowserTitle");
-        var promptMessage = shellBundle.getFormattedString("setDefaultBrowserMessage",
-                                                           [brandShortName]);
-        var checkboxLabel = shellBundle.getFormattedString("setDefaultBrowserDontAsk",
-                                                           [brandShortName]);
-        var checkEveryTime = { value: shouldCheck };
-        var ps = Services.prompt;
-        var rv = ps.confirmEx(window, promptTitle, promptMessage,
-                              ps.STD_YES_NO_BUTTONS,
-                              null, null, null, checkboxLabel, checkEveryTime);
-        if (rv == 0)
-          shell.setDefaultBrowser(true, false);
-        shell.shouldCheckDefaultBrowser = checkEveryTime.value;
-      }, 0);
-    }
-  }
-#endif
-
   // BiDi UI
   gBidiUI = isBidiEnabled();
   if (gBidiUI) {
     document.getElementById("documentDirection-separator").hidden = false;
     document.getElementById("documentDirection-swap").hidden = false;
     document.getElementById("textfieldDirection-separator").hidden = false;
     document.getElementById("textfieldDirection-swap").hidden = false;
   }
@@ -5369,29 +5326,33 @@ function setToolbarVisibility(toolbar, i
 
 #ifdef MENUBAR_CAN_AUTOHIDE
   updateAppButtonDisplay();
 #endif
 }
 
 var TabsOnTop = {
   init: function TabsOnTop_init() {
+    this._initialized = true;
     this.syncUI();
     Services.prefs.addObserver(this._prefName, this, false);
   },
 
   uninit: function TabsOnTop_uninit() {
     Services.prefs.removeObserver(this._prefName, this);
   },
 
   toggle: function () {
     this.enabled = !Services.prefs.getBoolPref(this._prefName);
   },
 
   syncUI: function () {
+    if (!this._initialized)
+      return;
+
     let userEnabled = Services.prefs.getBoolPref(this._prefName);
     let enabled = userEnabled && gBrowser.tabContainer.visible;
 
     document.getElementById("cmd_ToggleTabsOnTop")
             .setAttribute("checked", userEnabled);
 
     document.documentElement.setAttribute("tabsontop", enabled);
     document.getElementById("navigator-toolbox").setAttribute("tabsontop", enabled);
--- a/browser/base/content/newtab/dropTargetShim.js
+++ b/browser/base/content/newtab/dropTargetShim.js
@@ -35,20 +35,22 @@ let gDropTargetShim = {
     node.addEventListener("dragend", this._end.bind(this), true);
   },
 
   /**
    * Handles the 'dragstart' event.
    * @param aEvent The 'dragstart' event.
    */
   _start: function DropTargetShim_start(aEvent) {
-    gGrid.lock();
+    if (aEvent.target.classList.contains("site")) {
+      gGrid.lock();
 
-    // XXX bug 505521 - Listen for dragover on the document.
-    document.documentElement.addEventListener("dragover", this._dragover, false);
+      // XXX bug 505521 - Listen for dragover on the document.
+      document.documentElement.addEventListener("dragover", this._dragover, false);
+    }
   },
 
   /**
    * Handles the 'drag' event and determines the current drop target.
    * @param aEvent The 'drag' event.
    */
   _drag: function DropTargetShim_drag(aEvent) {
     // Let's see if we find a drop target.
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -845,17 +845,17 @@ nsContextMenu.prototype = {
   saveVideoFrameAsImage: function () {
     urlSecurityCheck(this.mediaURL, this.browser.contentPrincipal,
                      Ci.nsIScriptSecurityManager.DISALLOW_SCRIPT);
     let name = "";
     try {
       let uri = makeURI(this.mediaURL);
       let url = uri.QueryInterface(Ci.nsIURL);
       if (url.fileBaseName)
-        name = url.fileBaseName + ".jpg";
+        name = decodeURI(url.fileBaseName) + ".jpg";
     } catch (e) { }
     if (!name)
       name = "snapshot.jpg";
     var video = this.target;
     var canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
     canvas.width = video.videoWidth;
     canvas.height = video.videoHeight;
     var ctxDraw = canvas.getContext("2d");
--- a/browser/base/content/test/newtab/Makefile.in
+++ b/browser/base/content/test/newtab/Makefile.in
@@ -15,14 +15,16 @@ include $(topsrcdir)/config/rules.mk
 	browser_newtab_block.js \
 	browser_newtab_disable.js \
 	browser_newtab_drag_drop.js \
 	browser_newtab_drop_preview.js \
 	browser_newtab_private_browsing.js \
 	browser_newtab_reset.js \
 	browser_newtab_tabsync.js \
 	browser_newtab_unpin.js \
+	browser_newtab_bug722273.js \
 	browser_newtab_bug723102.js \
+	browser_newtab_bug723121.js \
 	head.js \
 	$(NULL)
 
 libs::	$(_BROWSER_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/newtab/browser_newtab_bug722273.js
@@ -0,0 +1,62 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const NOW = Date.now() * 1000;
+const URL = "http://fake-site.com/";
+
+let tmp = {};
+Cu.import("resource:///modules/NewTabUtils.jsm", tmp);
+Cc["@mozilla.org/moz/jssubscript-loader;1"]
+  .getService(Ci.mozIJSSubScriptLoader)
+  .loadSubScript("chrome://browser/content/sanitize.js", tmp);
+
+let {NewTabUtils, Sanitizer} = tmp;
+
+let bhist = Cc["@mozilla.org/browser/global-history;2"]
+  .getService(Ci.nsIBrowserHistory);
+
+function runTests() {
+  clearHistory();
+  fillHistory();
+  yield addNewTabPageTab();
+
+  is(cells[0].site.url, URL, "first site is our fake site");
+
+  let page = {
+    update: function () {
+      executeSoon(TestRunner.next);
+    },
+
+    observe: function () {}
+  };
+
+  NewTabUtils.allPages.register(page);
+  yield clearHistory();
+
+  NewTabUtils.allPages.unregister(page);
+  ok(!cells[0].site, "the fake site is gone");
+}
+
+function fillHistory() {
+  let uri = makeURI(URL);
+  for (let i = 59; i > 0; i--)
+    bhist.addPageWithDetails(uri, "fake site", NOW - i * 60 * 1000000);
+}
+
+function clearHistory() {
+  let s = new Sanitizer();
+  s.prefDomain = "privacy.cpd.";
+
+  let prefs = gPrefService.getBranch(s.prefDomain);
+  prefs.setBoolPref("history", true);
+  prefs.setBoolPref("downloads", false);
+  prefs.setBoolPref("cache", false);
+  prefs.setBoolPref("cookies", false);
+  prefs.setBoolPref("formdata", false);
+  prefs.setBoolPref("offlineApps", false);
+  prefs.setBoolPref("passwords", false);
+  prefs.setBoolPref("sessions", false);
+  prefs.setBoolPref("siteSettings", false);
+
+  s.sanitize();
+}
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/newtab/browser_newtab_bug723121.js
@@ -0,0 +1,44 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function runTests() {
+  setLinks("0,1,2,3,4,5,6,7,8");
+  setPinnedLinks("");
+
+  yield addNewTabPageTab();
+  checkGridLocked(false, "grid is unlocked");
+
+  let cell = cells[0].node;
+  let site = cells[0].site.node;
+
+  sendDragEvent(site, "dragstart");
+  checkGridLocked(true, "grid is now locked");
+
+  sendDragEvent(site, "dragend");
+  checkGridLocked(false, "grid isn't locked anymore");
+
+  sendDragEvent(cell, "dragstart");
+  checkGridLocked(false, "grid isn't locked - dragstart was ignored");
+}
+
+function checkGridLocked(aLocked, aMessage) {
+  is(cw.gGrid.node.hasAttribute("locked"), aLocked, aMessage);
+}
+
+function sendDragEvent(aNode, aType) {
+  let ifaceReq = cw.QueryInterface(Ci.nsIInterfaceRequestor);
+  let windowUtils = ifaceReq.getInterface(Ci.nsIDOMWindowUtils);
+
+  let dataTransfer = {
+    mozUserCancelled: false,
+    setData: function () null,
+    setDragImage: function () null,
+    getData: function () "about:blank"
+  };
+
+  let event = cw.document.createEvent("DragEvents");
+  event.initDragEvent(aType, true, true, cw, 0, 0, 0, 0, 0,
+                      false, false, false, false, 0, null, dataTransfer);
+
+  windowUtils.dispatchDOMEventViaPresShell(aNode, event, true);
+}
--- a/browser/base/content/test/newtab/head.js
+++ b/browser/base/content/test/newtab/head.js
@@ -124,20 +124,21 @@ function addNewTabPageTab() {
 
   // Wait for the new tab page to be loaded.
   browser.addEventListener("load", function onLoad() {
     browser.removeEventListener("load", onLoad, true);
 
     cw = browser.contentWindow;
 
     if (NewTabUtils.allPages.enabled) {
-      cells = cw.gGrid.cells;
-
       // Continue when the link cache has been populated.
-      NewTabUtils.links.populateCache(TestRunner.next);
+      NewTabUtils.links.populateCache(function () {
+        cells = cw.gGrid.cells;
+        executeSoon(TestRunner.next);
+      });
     } else {
       TestRunner.next();
     }
 
   }, true);
 }
 
 /**
@@ -241,16 +242,18 @@ function unpinCell(aCell) {
 
 /**
  * Simulates a drop and drop operation.
  * @param aDropTarget the cell that is the drop target
  * @param aDragSource the cell that contains the dragged site (optional)
  */
 function simulateDrop(aDropTarget, aDragSource) {
   let event = {
+    clientX: 0,
+    clientY: 0,
     dataTransfer: {
       mozUserCancelled: false,
       setData: function () null,
       setDragImage: function () null,
       getData: function () "about:blank#99\nblank"
     }
   };
 
--- a/browser/components/certerror/content/aboutCertError.css
+++ b/browser/components/certerror/content/aboutCertError.css
@@ -42,25 +42,11 @@
 /* Logical CSS rules belong here, but presentation & theming rules
    should live in the CSS of the appropriate theme */
 
 #technicalContentText {
   overflow: auto;
   white-space: pre-wrap;
 }
 
-#technicalContent > h2, #expertContent > h2 {
-  cursor: pointer;
-  -moz-padding-start: 20px;
-  position: relative;
-  left: -20px;
-}
-
-body[dir="rtl"] #technicalContent > h2,
-body[dir="rtl"] #expertContent > h2 {
-  left: auto;
-  right: -20px;
-}
-
-div[collapsed] > p,
-div[collapsed] > div {
+.expander[collapsed] + * {
   display: none;
 }
--- a/browser/components/certerror/content/aboutCertError.xhtml
+++ b/browser/components/certerror/content/aboutCertError.xhtml
@@ -255,28 +255,28 @@
           <div id="whatShouldIDoContentText">
             <p>&certerror.whatShouldIDo.content;</p>
             <button id='getMeOutOfHereButton'>&certerror.getMeOutOfHere.label;</button>
           </div>
         </div>
         
         <!-- The following sections can be unhidden by default by setting the
              "browser.xul.error_pages.expert_bad_cert" pref to true -->
-        <div id="technicalContent" collapsed="true">
-          <h2 onclick="toggle('technicalContent');" id="technicalContentHeading">&certerror.technical.heading;</h2>
-          <p id="technicalContentText"/>
-        </div>
+        <h2 id="technicalContent" class="expander" collapsed="true">
+          <button onclick="toggle('technicalContent');">&certerror.technical.heading;</button>
+        </h2>
+        <p id="technicalContentText"/>
         
-        <div id="expertContent" collapsed="true">
-          <h2 onclick="toggle('expertContent');" id="expertContentHeading">&certerror.expert.heading;</h2>
-          <div>
-            <p>&certerror.expert.content;</p>
-            <p>&certerror.expert.contentPara2;</p>
-            <button id='exceptionDialogButton'>&certerror.addException.label;</button>
-          </div>
+        <h2 id="expertContent" class="expander" collapsed="true">
+          <button onclick="toggle('expertContent');">&certerror.expert.heading;</button>
+        </h2>
+        <div>
+          <p>&certerror.expert.content;</p>
+          <p>&certerror.expert.contentPara2;</p>
+          <button id='exceptionDialogButton'>&certerror.addException.label;</button>
         </div>
       </div>
     </div>
 
     <!--
     - Note: It is important to run the script this way, instead of using
     - an onload handler. This is because error pages are loaded as
     - LOAD_BACKGROUND, which means that onload handlers will not be executed.
--- a/browser/components/dirprovider/DirectoryProvider.cpp
+++ b/browser/components/dirprovider/DirectoryProvider.cpp
@@ -51,16 +51,17 @@
 #include "nsCategoryManagerUtils.h"
 #include "nsComponentManagerUtils.h"
 #include "nsCOMArray.h"
 #include "nsDirectoryServiceUtils.h"
 #include "mozilla/ModuleUtils.h"
 #include "nsServiceManagerUtils.h"
 #include "nsStringAPI.h"
 #include "nsXULAppAPI.h"
+#include "nsIPrefLocalizedString.h"
 
 namespace mozilla {
 namespace browser {
 
 NS_IMPL_ISUPPORTS2(DirectoryProvider,
                    nsIDirectoryServiceProvider,
                    nsIDirectoryServiceProvider2)
 
@@ -195,17 +196,28 @@ AppendDistroSearchDirs(nsIProperties* aD
     nsCOMPtr<nsIFile> localePlugins;
     rv = searchPlugins->Clone(getter_AddRefs(localePlugins));
     if (NS_FAILED(rv))
       return;
 
     localePlugins->AppendNative(NS_LITERAL_CSTRING("locale"));
 
     nsCString locale;
-    rv = prefs->GetCharPref("general.useragent.locale", getter_Copies(locale));
+    nsCOMPtr<nsIPrefLocalizedString> prefString;
+    rv = prefs->GetComplexValue("general.useragent.locale",
+                                NS_GET_IID(nsIPrefLocalizedString),
+                                getter_AddRefs(prefString));
+    if (NS_SUCCEEDED(rv)) {
+      nsAutoString wLocale;
+      prefString->GetData(getter_Copies(wLocale));
+      CopyUTF16toUTF8(wLocale, locale);
+    } else {
+      rv = prefs->GetCharPref("general.useragent.locale", getter_Copies(locale));
+    }
+
     if (NS_SUCCEEDED(rv)) {
 
       nsCOMPtr<nsIFile> curLocalePlugins;
       rv = localePlugins->Clone(getter_AddRefs(curLocalePlugins));
       if (NS_SUCCEEDED(rv)) {
 
         curLocalePlugins->AppendNative(locale);
         rv = curLocalePlugins->Exists(&exists);
--- a/browser/components/migration/Makefile.in
+++ b/browser/components/migration/Makefile.in
@@ -38,16 +38,18 @@ DEPTH		= ../../..
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 DIRS		= public src
 
+TEST_DIRS += tests
+
 include $(topsrcdir)/config/rules.mk
 
 # Needed for preprocessor removal of IE Profile Migrator label - bug 236901
 ifeq ($(OS_ARCH),WINNT)
 ifdef GNU_CXX
 DEFINES += -DNO_IE_MIGRATOR=1
 endif
 endif
--- a/browser/components/migration/content/migration.js
+++ b/browser/components/migration/content/migration.js
@@ -60,16 +60,17 @@ var MigrationWizard = {
     os.addObserver(this, "Migration:Ended", false);
 
     this._wiz = document.documentElement;
 
     if ("arguments" in window && window.arguments.length > 1) {
       this._source = window.arguments[0];
       this._migrator = window.arguments[1].QueryInterface(kIMig);
       this._autoMigrate = window.arguments[2].QueryInterface(kIPStartup);
+      this._skipImportSourcePage = window.arguments[3];
 
       if (this._autoMigrate) {
         // Show the "nothing" option in the automigrate case to provide an
         // easily identifiable way to avoid migration and create a new profile.
         var nothing = document.getElementById("nothing");
         nothing.hidden = false;
       }
     }
@@ -89,17 +90,17 @@ var MigrationWizard = {
   },
 
   // 1 - Import Source
   onImportSourcePageShow: function ()
   {
     // Reference to the "From File" radio button 
     var fromfile = null;
 
-    //XXXquark This function is called before init, so check for bookmarks here
+    // init is not called when openDialog opens the wizard, so check for bookmarks here.
     if ("arguments" in window && window.arguments[0] == "bookmarks") {
       this._bookmarks = true;
 
       fromfile = document.getElementById("fromfile");
       fromfile.hidden = false;
 
       var importBookmarks = document.getElementById("importBookmarks");
       importBookmarks.hidden = false;
@@ -146,16 +147,22 @@ var MigrationWizard = {
       // We didn't find a migrator, notify the user
       document.getElementById("noSources").hidden = false;
 
       this._wiz.canAdvance = false;
 
       document.getElementById("importBookmarks").hidden = true;
       document.getElementById("importAll").hidden = true;
     }
+
+    // Advance to the next page if the caller told us to.
+    if (this._migrator && this._skipImportSourcePage) {
+      this._wiz.advance();
+      this._wiz.canRewind = false;
+    }
   },
   
   onImportSourcePageAdvanced: function ()
   {
     var newSource = document.getElementById("importSourceGroup").selectedItem.id;
     
     if (newSource == "nothing" || newSource == "fromfile") {
       if(newSource == "fromfile")
--- a/browser/components/migration/src/FirefoxProfileMigrator.js
+++ b/browser/components/migration/src/FirefoxProfileMigrator.js
@@ -222,16 +222,22 @@ FirefoxProfileMigrator.prototype = {
    *          profile directory path to migrate from
    */
   migrate : function Firefox_migrate(aItems, aStartup, aProfile)
   {
     if (aStartup) {
       this._replaceBookmarks = true;
     }
 
+    // Ensure that aProfile is not the current profile.
+    if (this._paths.currentProfile.path === this._sourceProfile.path) {
+      throw new Exception("Source and destination profiles are the same");
+      return;
+    }
+
     Services.obs.notifyObservers(null, "Migration:Started", null);
 
     // Reset pending count.  If this count becomes 0, "Migration:Ended"
     // notification is sent
     this._pendingCount = 1;
 
     if (aItems & MIGRATOR.HISTORY)
       this._migrateHistory();
@@ -273,16 +279,21 @@ FirefoxProfileMigrator.prototype = {
    * @todo    Bug 715315 - make sure source databases are not in-use
    */
   getMigrateData: function Firefox_getMigrateData(aProfile, aDoingStartup)
   {
     this._sourceProfile = Cc[LOCAL_FILE_CID].createInstance(Ci.nsILocalFile);
     this._sourceProfile.initWithPath(aProfile);
 
     let result = 0;
+
+    // Ensure that aProfile is not the current profile.
+    if (this._paths.currentProfile.path === this._sourceProfile.path)
+      return result;
+
     if (!this._sourceProfile.exists() || !this._sourceProfile.isReadable()) {
       Cu.reportError("source profile directory doesn't exist or is not readable");
       return result;
     }
 
     // Migration initiated from the UI is not supported.
     if (!aDoingStartup)
       return result;
--- a/browser/components/migration/src/ProfileMigrator.js
+++ b/browser/components/migration/src/ProfileMigrator.js
@@ -11,47 +11,67 @@ const Cu = Components.utils;
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/FileUtils.jsm");
 
 function ProfileMigrator() {
 }
 
 ProfileMigrator.prototype = {
-  migrate: function PM_migrate(aStartup) {
+  migrate: function PM_migrate(aStartup, aKey) {
     // By opening the wizard with a supplied migrator, it will automatically
     // migrate from it.
-    let [key, migrator] = this._getDefaultMigrator();
+    let key = null, migrator = null;
+    let skipImportSourcePage = Cc["@mozilla.org/supports-PRBool;1"]
+                                 .createInstance(Ci.nsISupportsPRBool);
+    if (aKey) {
+      key = aKey;
+      migrator = this._getMigratorIfSourceExists(key);
+      if (!migrator) {
+        Cu.reportError("Invalid migrator key specified or source does not exist.");
+        return;
+      }
+      // If the migrator was passed to us from the caller, use that migrator
+      // and skip the import source page.
+      skipImportSourcePage.data = true;
+    } else {
+      [key, migrator] = this._getDefaultMigrator();
+    }
     if (!key)
         return;
 
     let params = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
     params.appendElement(this._toCString(key), false);
     params.appendElement(migrator, false);
     params.appendElement(aStartup, false);
+    params.appendElement(skipImportSourcePage, false);
 
     Services.ww.openWindow(null,
                            "chrome://browser/content/migration/migration.xul",
                            "_blank",
                            "chrome,dialog,modal,centerscreen,titlebar",
                            params);
   },
 
   _toCString: function PM__toCString(aStr) {
     let cstr = Cc["@mozilla.org/supports-cstring;1"].
                createInstance(Ci.nsISupportsCString);
     cstr.data = aStr;
     return cstr;
   },
 
   _getMigratorIfSourceExists: function PM__getMigratorIfSourceExists(aKey) {
-    let cid = "@mozilla.org/profile/migrator;1?app=browser&type=" + aKey;
-    let migrator = Cc[cid].createInstance(Ci.nsIBrowserProfileMigrator);
-    if (migrator.sourceExists)
-      return migrator;
+    try {
+      let cid = "@mozilla.org/profile/migrator;1?app=browser&type=" + aKey;
+      let migrator = Cc[cid].createInstance(Ci.nsIBrowserProfileMigrator);
+      if (migrator.sourceExists)
+        return migrator;
+    } catch (ex) {
+      Cu.reportError("Could not get migrator: " + ex);
+    }
     return null;
   },
 
   // We don't yet support checking for the default browser on all platforms,
   // needless to say we don't have migrators for all browsers.  Thus, for each
   // platform, there's a fallback list of migrators used in these cases.
   _PLATFORM_FALLBACK_LIST:
 #ifdef XP_WIN
--- a/browser/components/migration/src/nsIEProfileMigrator.cpp
+++ b/browser/components/migration/src/nsIEProfileMigrator.cpp
@@ -1407,28 +1407,36 @@ nsIEProfileMigrator::CopyFavoritesBatche
     // Initialize the default bookmarks
     nsCOMPtr<nsIFile> profile;
     GetProfilePath(nsnull, profile);
     rv = InitializeBookmarks(profile);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Locate the Links toolbar folder, we want to replace the Personal Toolbar
     // content with Favorites in this folder.
+    // On versions minor or equal to IE6 the folder name is stored in the
+    // LinksFolderName registry key, but in newer versions it may be just a
+    // Links subfolder inside the default Favorites folder.
     nsCOMPtr<nsIWindowsRegKey> regKey =
       do_CreateInstance("@mozilla.org/windows-registry-key;1");
     if (regKey &&
         NS_SUCCEEDED(regKey->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
                                   REGISTRY_IE_TOOLBAR_KEY,
                                   nsIWindowsRegKey::ACCESS_READ))) {
       nsAutoString linksFolderName;
       if (NS_SUCCEEDED(regKey->ReadStringValue(
                          NS_LITERAL_STRING("LinksFolderName"),
-                         linksFolderName)))
+                         linksFolderName))) {
         personalToolbarFolderName = linksFolderName;
+      }
+      else {
+        personalToolbarFolderName.AssignLiteral("Links");
+      }
     }
+
     folder = bookmarksMenuFolderId;
   }
 
   nsCOMPtr<nsIProperties> fileLocator =
     do_GetService("@mozilla.org/file/directory_service;1", &rv);
   NS_ENSURE_SUCCESS(rv, rv);
   nsCOMPtr<nsIFile> favoritesDirectory;
   (void)fileLocator->Get(NS_WIN_FAVORITES_DIR, NS_GET_IID(nsIFile),
new file mode 100644
--- /dev/null
+++ b/browser/components/migration/tests/Makefile.in
@@ -0,0 +1,15 @@
+# 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/.
+
+DEPTH		  = ../../../..
+topsrcdir	= @top_srcdir@
+srcdir		= @srcdir@
+VPATH		  = @srcdir@
+relativesrcdir = browser/components/migration/tests
+
+include $(DEPTH)/config/autoconf.mk
+
+XPCSHELL_TESTS = unit
+
+include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/browser/components/migration/tests/unit/bookmarks.html
@@ -0,0 +1,16 @@
+<!DOCTYPE NETSCAPE-Bookmark-file-1>
+<!-- This is an automatically generated file.
+     It will be read and overwritten.
+     DO NOT EDIT! -->
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">
+<TITLE>Bookmarks</TITLE>
+<H1>Bookmarks Menu</H1>
+
+<DL><p>
+    <DT><A HREF="http://example.com/" ADD_DATE="1233157972" LAST_MODIFIED="1233157984">example</A>
+    <DT><H3 ADD_DATE="1233157910" LAST_MODIFIED="1233157972" PERSONAL_TOOLBAR_FOLDER="true">Bookmarks Toolbar</H3>
+<DD>Add bookmarks to this folder to see them displayed on the Bookmarks Toolbar
+    <DL><p>
+        <DT><A HREF="http://example.com/" ADD_DATE="1233157972" LAST_MODIFIED="1233157984">example</A>
+    </DL><p>
+</DL><p>
new file mode 100644
--- /dev/null
+++ b/browser/components/migration/tests/unit/head_migration.js
@@ -0,0 +1,28 @@
+/* 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 Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+
+const IMIGRATOR = Ci.nsIBrowserProfileMigrator;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
+                                  "resource://gre/modules/PlacesUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
+                                  "resource://gre/modules/FileUtils.jsm");
+
+// Initialize profile.
+let gProfD = do_get_profile();
+
+function newMigratorFor(aKey) {
+  let cid = "@mozilla.org/profile/migrator;1?app=browser&type=" + aKey;
+  return Cc[cid].createInstance(Ci.nsIBrowserProfileMigrator);
+}
+
+let (bookmarkshtml = do_get_file("bookmarks.html")) {
+  bookmarkshtml.copyTo(gProfD, "bookmarks.html");
+}
new file mode 100644
--- /dev/null
+++ b/browser/components/migration/tests/unit/test_IE_bookmarks.js
@@ -0,0 +1,28 @@
+/* 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/. */
+
+function run_test() {
+  let migrator = newMigratorFor("ie");
+
+  // Sanity check for the source.
+  do_check_true(migrator.sourceExists);
+
+  // Ensure bookmarks migration is available.
+  let availableSources = migrator.getMigrateData("FieldOfFlowers", false);
+  do_check_true((availableSources & IMIGRATOR.BOOKMARKS) > 0);
+
+  // Needed to enforce bookmarks replacing.
+  let startup = {
+    doStartup: function () {},
+    get directory() do_get_profile()
+  }
+  migrator.migrate(IMIGRATOR.BOOKMARKS, startup, "FieldOfFlowers");
+
+  // Check that at least two bookmark have been added to the menu and the
+  // toolbar.  The first one comes from bookmarks.html, the others from IE.
+  do_check_true(PlacesUtils.bookmarks
+                           .getIdForItemAt(PlacesUtils.bookmarksMenuFolderId, 1) > 0);
+  do_check_true(PlacesUtils.bookmarks
+                           .getIdForItemAt(PlacesUtils.toolbarFolderId, 1) > 0);
+}
new file mode 100644
--- /dev/null
+++ b/browser/components/migration/tests/unit/xpcshell.ini
@@ -0,0 +1,6 @@
+[DEFAULT]
+head = head_migration.js
+tail =
+
+[test_IE_bookmarks.js]
+skip-if = os != "win"
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -406,31 +406,76 @@ BrowserGlue.prototype = {
 
     // If there are plugins installed that are outdated, and the user hasn't
     // been warned about them yet, open the plugins update page.
     if (Services.prefs.getBoolPref(PREF_PLUGINS_NOTIFYUSER))
       this._showPluginUpdatePage();
 
     // For any add-ons that were installed disabled and can be enabled offer
     // them to the user
-    var win = this.getMostRecentBrowserWindow();
-    var browser = win.gBrowser;
     var changedIDs = AddonManager.getStartupChanges(AddonManager.STARTUP_CHANGE_INSTALLED);
-    AddonManager.getAddonsByIDs(changedIDs, function(aAddons) {
-      aAddons.forEach(function(aAddon) {
-        // If the add-on isn't user disabled or can't be enabled then skip it
-        if (!aAddon.userDisabled || !(aAddon.permissions & AddonManager.PERM_CAN_ENABLE))
-          return;
+    if (changedIDs.length > 0) {
+      AddonManager.getAddonsByIDs(changedIDs, function(aAddons) {
+        var win = this.getMostRecentBrowserWindow();
+        var browser = win.gBrowser;
+        aAddons.forEach(function(aAddon) {
+          // If the add-on isn't user disabled or can't be enabled then skip it.
+          if (!aAddon.userDisabled || !(aAddon.permissions & AddonManager.PERM_CAN_ENABLE))
+            return;
 
-        browser.selectedTab = browser.addTab("about:newaddon?id=" + aAddon.id);
-      })
-    });
+          browser.selectedTab = browser.addTab("about:newaddon?id=" + aAddon.id);
+        })
+      });
+    }
 
     let keywordURLUserSet = Services.prefs.prefHasUserValue("keyword.URL");
     Services.telemetry.getHistogramById("FX_KEYWORD_URL_USERSET").add(keywordURLUserSet);
+
+    // Perform default browser checking.
+    var shell;
+    try {
+      shell = Components.classes["@mozilla.org/browser/shell-service;1"]
+        .getService(Components.interfaces.nsIShellService);
+    } catch (e) { }
+    if (shell) {
+#ifdef DEBUG
+      var shouldCheck = false;
+#else
+      var shouldCheck = shell.shouldCheckDefaultBrowser;
+#endif
+      var willRecoverSession = false;
+      try {
+        var ss = Cc["@mozilla.org/browser/sessionstartup;1"].
+                 getService(Ci.nsISessionStartup);
+        willRecoverSession =
+          (ss.sessionType == Ci.nsISessionStartup.RECOVER_SESSION);
+      }
+      catch (ex) { /* never mind; suppose SessionStore is broken */ }
+      if (shouldCheck && !shell.isDefaultBrowser(true) && !willRecoverSession) {
+        Services.tm.mainThread.dispatch(function() {
+          var brandBundle = win.document.getElementById("bundle_brand");
+          var shellBundle = win.document.getElementById("bundle_shell");
+  
+          var brandShortName = brandBundle.getString("brandShortName");
+          var promptTitle = shellBundle.getString("setDefaultBrowserTitle");
+          var promptMessage = shellBundle.getFormattedString("setDefaultBrowserMessage",
+                                                             [brandShortName]);
+          var checkboxLabel = shellBundle.getFormattedString("setDefaultBrowserDontAsk",
+                                                             [brandShortName]);
+          var checkEveryTime = { value: shouldCheck };
+          var ps = Services.prompt;
+          var rv = ps.confirmEx(win, promptTitle, promptMessage,
+                                ps.STD_YES_NO_BUTTONS,
+                                null, null, null, checkboxLabel, checkEveryTime);
+          if (rv == 0)
+            shell.setDefaultBrowser(true, false);
+          shell.shouldCheckDefaultBrowser = checkEveryTime.value;
+        }, Ci.nsIThread.DISPATCH_NORMAL);
+      }
+    }
   },
 
   _onQuitRequest: function BG__onQuitRequest(aCancelQuit, aQuitType) {
     // If user has already dismissed quit request, then do nothing
     if ((aCancelQuit instanceof Ci.nsISupportsPRBool) && aCancelQuit.data)
       return;
 
     // There are several cases where we won't show a dialog here:
--- a/browser/components/places/content/browserPlacesViews.js
+++ b/browser/components/places/content/browserPlacesViews.js
@@ -162,120 +162,87 @@ PlacesViewBase.prototype = {
 
   destroyContextMenu: function PVB_destroyContextMenu(aPopup) {
     this._contextMenuShown = false;
     if (window.content)
       window.content.focus();
   },
 
   _cleanPopup: function PVB_cleanPopup(aPopup) {
-    // Remove places popup children and update markers to keep track of
-    // their indices.
-    let start = aPopup._startMarker != -1 ? aPopup._startMarker + 1 : 0;
-    let end = aPopup._endMarker != -1 ? aPopup._endMarker :
-                                        aPopup.childNodes.length;
-    let items = [];
-
-    // Automatically adjust the start and the end markers.
-    let firstNonStaticNodeFound = false;
-    for (let i = start; i < end; ++i) {
-      let item = aPopup.childNodes[i];
-      if (item.getAttribute("builder") == "end") {
-        // we need to do this for menus that have static content at the end but
-        // are initially empty, eg. the history menu, we need to know where to
-        // start inserting new items.
-        aPopup._endMarker = i;
-        break;
-      }
-
-      if (item._placesNode) {
-        items.push(item);
-        firstNonStaticNodeFound = true;
-      }
-      else {
-        // This is static content.
-        if (!firstNonStaticNodeFound) {
-          // We are at the beginning of the popup, in static content.
-          // The markers are initialized in menu.xml, in the base binding.
-          aPopup._startMarker++;
-        }
-        else {
-          // We are at the end of the popup, after places nodes
-          aPopup._endMarker = i;
-          break;
-        }
-      }
-    }
-
-    for (let i = 0; i < items.length; ++i) {
-      aPopup.removeChild(items[i]);
-      if (aPopup._endMarker != -1)
-        aPopup._endMarker--;
+    // Remove Places nodes from the popup.
+    let child = aPopup._startMarker;
+    while (child.nextSibling != aPopup._endMarker) {
+      if (child.nextSibling._placesNode)
+        aPopup.removeChild(child.nextSibling);
+      else
+        child = child.nextSibling;
     }
   },
 
   _rebuildPopup: function PVB__rebuildPopup(aPopup) {
-    this._cleanPopup(aPopup);
-
     let resultNode = aPopup._placesNode;
     if (!resultNode.containerOpen)
       return;
 
     if (resultNode._feedURI) {
-      aPopup.removeAttribute("emptyplacesresult");
-      if (aPopup._emptyMenuItem) {
-        aPopup._emptyMenuItem.hidden = true;
-      }
+      this._setEmptyPopupStatus(aPopup, false);
       aPopup._built = true;
       this._populateLivemarkPopup(aPopup);
       return;
     }
 
+    this._cleanPopup(aPopup);
+
     let cc = resultNode.childCount;
     if (cc > 0) {
-      aPopup.removeAttribute("emptyplacesresult");
-      if (aPopup._emptyMenuItem)
-        aPopup._emptyMenuItem.hidden = true;
+      this._setEmptyPopupStatus(aPopup, false);
 
       for (let i = 0; i < cc; ++i) {
         let child = resultNode.getChild(i);
         this._insertNewItemToPopup(child, aPopup, null);
       }
     }
     else {
-      aPopup.setAttribute("emptyplacesresult", "true");
-      // This menu is empty.  If there is no static content, add
-      // an element to show it is empty.
-      if (aPopup._startMarker == -1 && aPopup._endMarker == -1)
-        this._showEmptyMenuItem(aPopup);
+      this._setEmptyPopupStatus(aPopup, true);
     }
     aPopup._built = true;
   },
 
   _removeChild: function PVB__removeChild(aChild) {
     // If document.popupNode pointed to this child, null it out,
     // otherwise controller's command-updating may rely on the removed
     // item still being "selected".
     if (document.popupNode == aChild)
       document.popupNode = null;
 
     aChild.parentNode.removeChild(aChild);
   },
 
-  _showEmptyMenuItem: function PVB__showEmptyMenuItem(aPopup) {
-    if (aPopup._emptyMenuItem) {
-      aPopup._emptyMenuItem.hidden = false;
-      return;
+  _setEmptyPopupStatus:
+  function PVB__setEmptyPopupStatus(aPopup, aEmpty) {
+    if (!aPopup._emptyMenuitem) {
+      let label = PlacesUIUtils.getString("bookmarksMenuEmptyFolder");
+      aPopup._emptyMenuitem = document.createElement("menuitem");
+      aPopup._emptyMenuitem.setAttribute("label", label);
+      aPopup._emptyMenuitem.setAttribute("disabled", true);
     }
 
-    let label = PlacesUIUtils.getString("bookmarksMenuEmptyFolder");
-    aPopup._emptyMenuItem = document.createElement("menuitem");
-    aPopup._emptyMenuItem.setAttribute("label", label);
-    aPopup._emptyMenuItem.setAttribute("disabled", true);
-    aPopup.appendChild(aPopup._emptyMenuItem);
+    if (aEmpty) {
+      aPopup.setAttribute("emptyplacesresult", "true");
+      // Don't add the menuitem if there is static content.
+      if (!aPopup._startMarker.previousSibling &&
+          !aPopup._endMarker.nextSibling)
+        aPopup.insertBefore(aPopup._emptyMenuitem, aPopup._endMarker);
+    }
+    else {
+      aPopup.removeAttribute("emptyplacesresult");
+      try {
+        aPopup.removeChild(aPopup._emptyMenuitem);
+      } catch (ex) {}
+    }
   },
 
   _createMenuItemForPlacesNode:
   function PVB__createMenuItemForPlacesNode(aPlacesNode) {
     delete aPlacesNode._DOMElement;
     let element;
     let type = aPlacesNode.type;
     if (type == Ci.nsINavHistoryResultNode.RESULT_TYPE_SEPARATOR) {
@@ -303,33 +270,38 @@ PlacesViewBase.prototype = {
             element.setAttribute("hostContainer", "true");
         }
         else if (itemId != -1) {
           PlacesUtils.livemarks.getLivemark(
             { id: itemId },
             function (aStatus, aLivemark) {
               if (Components.isSuccessCode(aStatus)) {
                 element.setAttribute("livemark", "true");
+#ifdef XP_MACOSX
+                // OS X native menubar doesn't track list-style-images since
+                // it doesn't have a frame (bug 733415).  Thus enforce updating.
+                element.setAttribute("image", "");
+                element.removeAttribute("image");
+#endif
                 // Set an expando on the node, controller will use it to build
                 // its metadata.
                 aPlacesNode._feedURI = aLivemark.feedURI;
                 aPlacesNode._siteURI = aLivemark.siteURI;
               }
             }
           );
         }
 
         let popup = document.createElement("menupopup");
         popup._placesNode = PlacesUtils.asContainer(aPlacesNode);
-        if (this._nativeView) {
-          popup._startMarker = -1;
-          popup._endMarker = -1;
+
+        if (!this._nativeView) {
+          popup.setAttribute("placespopup", "true");
         }
-        else
-          popup.setAttribute("placespopup", "true");
+
 #ifdef XP_MACOSX
         // No context menu on mac.
         popup.setAttribute("context", "placesContext");
 #endif
         element.appendChild(popup);
         element.className = "menu-iconic bookmark-item";
 
         aPlacesNode._DOMElement = popup;
@@ -349,50 +321,30 @@ PlacesViewBase.prototype = {
       aPlacesNode._DOMElement = element;
 
     return element;
   },
 
   _insertNewItemToPopup:
   function PVB__insertNewItemToPopup(aNewChild, aPopup, aBefore) {
     let element = this._createMenuItemForPlacesNode(aNewChild);
-
-    if (aBefore) {
-      aPopup.insertBefore(element, aBefore);
-    }
-    else {
-      // Add the new element to the menu.  If there is static content at
-      // the end of the menu, add the element before that.  Otherwise,
-      // just add to the end.
-      if (aPopup._endMarker != -1) {
-        let lastElt = aPopup.childNodes[aPopup._endMarker];
-        aPopup.insertBefore(element, lastElt);
-      }
-      else {
-        aPopup.appendChild(element);
-      }
-    }
-
-    if (aPopup._endMarker != -1)
-      aPopup._endMarker++;
-
+    let before = aBefore || aPopup._endMarker;
+    aPopup.insertBefore(element, before);
     return element;
   },
 
   _setLivemarkSiteURIMenuItem:
   function PVB__setLivemarkSiteURIMenuItem(aPopup) {
     let siteUrl = aPopup._placesNode._siteURI ? aPopup._placesNode._siteURI.spec
                                               : null;
     if (!siteUrl && aPopup._siteURIMenuitem) {
       aPopup.removeChild(aPopup._siteURIMenuitem);
       aPopup._siteURIMenuitem = null;
-      aPopup._startMarker--;
       aPopup.removeChild(aPopup._siteURIMenuseparator);
       aPopup._siteURIMenuseparator = null;
-      aPopup._startMarker--;
     }
     else if (siteUrl && !aPopup._siteURIMenuitem) {
       // Add "Open (Feed Name)" menuitem.
       aPopup._siteURIMenuitem = document.createElement("menuitem");
       aPopup._siteURIMenuitem.className = "openlivemarksite-menuitem";
       aPopup._siteURIMenuitem.setAttribute("targetURI", siteUrl);
       aPopup._siteURIMenuitem.setAttribute("oncommand",
         "openUILink(this.getAttribute('targetURI'), event);");
@@ -402,65 +354,58 @@ PlacesViewBase.prototype = {
       // Note: stopPropagation is needed to avoid serving middle-click
       // with BT_onClick that would open all items in tabs.
       aPopup._siteURIMenuitem.setAttribute("onclick",
         "checkForMiddleClick(this, event); event.stopPropagation();");
       let label =
         PlacesUIUtils.getFormattedString("menuOpenLivemarkOrigin.label",
                                          [aPopup.parentNode.getAttribute("label")])
       aPopup._siteURIMenuitem.setAttribute("label", label);
-      aPopup.insertBefore(aPopup._siteURIMenuitem,
-                          aPopup.childNodes.item(aPopup._startMarker + 1));
-      aPopup._startMarker++;
+      aPopup.insertBefore(aPopup._siteURIMenuitem, aPopup._startMarker);
 
       aPopup._siteURIMenuseparator = document.createElement("menuseparator");
-      aPopup.insertBefore(aPopup._siteURIMenuseparator,
-                         aPopup.childNodes.item(aPopup._startMarker + 1));
-      aPopup._startMarker++;
+      aPopup.insertBefore(aPopup._siteURIMenuseparator, aPopup._startMarker);
     }
   },
 
   /**
    * Add, update or remove the livemark status menuitem.
    * @param aPopup
    *        The livemark container popup
    * @param aStatus
    *        The livemark status
    */
   _setLivemarkStatusMenuItem:
   function PVB_setLivemarkStatusMenuItem(aPopup, aStatus) {
-    let itemId = aPopup._placesNode.itemId;
     let statusMenuitem = aPopup._statusMenuitem;
     let stringId = "";
     if (aStatus == Ci.mozILivemark.STATUS_LOADING)
       stringId = "bookmarksLivemarkLoading";
     else if (aStatus == Ci.mozILivemark.STATUS_FAILED)
       stringId = "bookmarksLivemarkFailed";
 
     if (stringId && !statusMenuitem) {
       // Create the status menuitem and cache it in the popup object.
       statusMenuitem = document.createElement("menuitem");
       statusMenuitem.setAttribute("livemarkStatus", stringId);
+      statusMenuitem.className = "livemarkstatus-menuitem";
       statusMenuitem.setAttribute("label", PlacesUIUtils.getString(stringId));
       statusMenuitem.setAttribute("disabled", true);
-      aPopup.insertBefore(statusMenuitem,
-                          aPopup.childNodes.item(aPopup._startMarker + 1));
+      aPopup.insertBefore(statusMenuitem, aPopup._startMarker.nextSibling);
       aPopup._statusMenuitem = statusMenuitem;
-      aPopup._startMarker++;
     }
     else if (stringId &&
              statusMenuitem.getAttribute("livemarkStatus") != stringId) {
       // Status has changed, update the cached status menuitem.
       statusMenuitem.setAttribute("label", PlacesUIUtils.getString(stringId));
     }
     else if (!stringId && statusMenuitem) {
       // The livemark has finished loading.
       aPopup.removeChild(aPopup._statusMenuitem);
       aPopup._statusMenuitem = null;
-      aPopup._startMarker--;
     }
   },
 
   toggleCutNode: function PVB_toggleCutNode(aNode, aValue) {
     let elt = aNode._DOMElement;
     if (elt) {
       // We may get the popup for menus, but we need the menu itself.
       if (elt.localName == "menupopup")
@@ -512,16 +457,22 @@ PlacesViewBase.prototype = {
       throw "aPlacesNode must have _DOMElement set";
 
     // All livemarks have a feedURI, so use it as our indicator of a livemark
     // being modified.
     if (aAnno == PlacesUtils.LMANNO_FEEDURI) {
       let menu = elt.parentNode;
       if (!menu.hasAttribute("livemark")) {
         menu.setAttribute("livemark", "true");
+#ifdef XP_MACOSX
+        // OS X native menubar doesn't track list-style-images since
+        // it doesn't have a frame (bug 733415).  Thus enforce updating.
+        menu.setAttribute("image", "");
+        menu.removeAttribute("image");
+#endif
       }
 
       PlacesUtils.livemarks.getLivemark(
         { id: aPlacesNode.itemId },
         (function (aStatus, aLivemark) {
           if (Components.isSuccessCode(aStatus)) {
             // Set an expando on the node, controller will use it to build
             // its metadata.
@@ -575,23 +526,18 @@ PlacesViewBase.prototype = {
       elt = elt.parentNode;
 
     if (parentElt._built) {
       parentElt.removeChild(elt);
 
       // Figure out if we need to show the "<Empty>" menu-item.
       // TODO Bug 517701: This doesn't seem to handle the case of an empty
       // root.
-      if (!parentElt.hasChildNodes() ||
-          (parentElt.childNodes.length == 1 &&
-          parentElt.firstChild == parentElt._emptyMenuItem))
-        this._showEmptyMenuItem(parentElt);
-
-      if (parentElt._endMarker != -1)
-        parentElt._endMarker--;
+      if (parentElt._startMarker.nextSibling == parentElt._endMarker)
+        this._setEmptyPopupStatus(parentElt, true);
     }
   },
 
   nodeReplaced:
   function PVB_nodeReplaced(aParentPlacesNode, aOldPlacesNode, aNewPlacesNode, aIndex) {
     let parentElt = aParentPlacesNode._DOMElement;
     if (!parentElt)
       throw "aParentPlacesNode node must have _DOMElement set";
@@ -615,18 +561,19 @@ PlacesViewBase.prototype = {
     }
   },
 
   nodeHistoryDetailsChanged:
   function PVB_nodeHistoryDetailsChanged(aPlacesNode, aTime, aCount) {
     if (aPlacesNode.parent && aPlacesNode.parent._feedURI) {
       // Find the node in the parent.
       let popup = aPlacesNode.parent._DOMElement;
-      for (let i = popup._startMarker; i < popup.childNodes.length; i++) {
-        let child = popup.childNodes[i];
+      for (let child = popup._startMarker.nextSibling;
+           child != popup._endMarker;
+           child = child.nextSibling) {
         if (child._placesNode && child._placesNode.uri == aPlacesNode.uri) {
           if (aCount)
             child.setAttribute("visited", "true");
           else
             child.removeAttribute("visited");
           break;
         }
       }
@@ -644,21 +591,21 @@ PlacesViewBase.prototype = {
   function PVB_nodeInserted(aParentPlacesNode, aPlacesNode, aIndex) {
     let parentElt = aParentPlacesNode._DOMElement;
     if (!parentElt)
       throw "aParentPlacesNode node must have _DOMElement set";
 
     if (!parentElt._built)
       return;
 
-    let index = parentElt._startMarker + 1 + aIndex;
+    let index = Array.indexOf(parentElt.childNodes, parentElt._startMarker) +
+                aIndex + 1;
     this._insertNewItemToPopup(aPlacesNode, parentElt,
                                parentElt.childNodes[index]);
-    if (parentElt._emptyMenuItem)
-      parentElt._emptyMenuItem.hidden = true;
+    this._setEmptyPopupStatus(parentElt, false);
   },
 
   nodeMoved:
   function PBV_nodeMoved(aPlacesNode,
                          aOldParentPlacesNode, aOldIndex,
                          aNewParentPlacesNode, aNewIndex) {
     // Note: the current implementation of moveItem does not actually
     // use this notification when the item in question is moved from one
@@ -679,17 +626,18 @@ PlacesViewBase.prototype = {
 
     let parentElt = aNewParentPlacesNode._DOMElement;
     if (!parentElt)
       throw "aNewParentPlacesNode node must have _DOMElement set";
 
     if (parentElt._built) {
       // Move the node.
       parentElt.removeChild(elt);
-      let index = parentElt._startMarker + 1 + aNewIndex;
+      let index = Array.indexOf(parentElt.childNodes, parentElt._startMarker) +
+                  aNewIndex + 1;
       parentElt.insertBefore(elt, parentElt.childNodes[index]);
     }
   },
 
   containerStateChanged:
   function PVB_containerStateChanged(aPlacesNode, aOldState, aNewState) {
     if (aNewState == Ci.nsINavHistoryContainerResultNode.STATE_OPENED ||
         aNewState == Ci.nsINavHistoryContainerResultNode.STATE_CLOSED) {
@@ -704,17 +652,19 @@ PlacesViewBase.prototype = {
         PlacesUtils.livemarks.getLivemark({ id: aPlacesNode.itemId },
           (function (aStatus, aLivemark) {
             if (Components.isSuccessCode(aStatus)) {
               let shouldInvalidate = !aPlacesNode._feedURI;
               aPlacesNode._feedURI = aLivemark.feedURI;
               aPlacesNode._siteURI = aLivemark.siteURI;
               if (aNewState == Ci.nsINavHistoryContainerResultNode.STATE_OPENED) {
                 aLivemark.registerForUpdates(aPlacesNode, this);
+                // Prioritize the current livemark.
                 aLivemark.reload();
+                PlacesUtils.livemarks.reloadLivemarks();
                 if (shouldInvalidate)
                   this.invalidateContainer(aPlacesNode);
               }
               else {
                 aLivemark.unregisterForUpdates(aPlacesNode);
               }
             }
           }).bind(this)
@@ -816,48 +766,83 @@ PlacesViewBase.prototype = {
       hasMultipleURIs = numURINodes > 1;
     }
 
     if (!hasMultipleURIs) {
       // We don't have to show any option.
       if (aPopup._endOptOpenAllInTabs) {
         aPopup.removeChild(aPopup._endOptOpenAllInTabs);
         aPopup._endOptOpenAllInTabs = null;
-        aPopup._endMarker--;
 
         aPopup.removeChild(aPopup._endOptSeparator);
         aPopup._endOptSeparator = null;
-        aPopup._endMarker--;
       }
     }
     else if (!aPopup._endOptOpenAllInTabs) {
       // Create a separator before options.
       aPopup._endOptSeparator = document.createElement("menuseparator");
       aPopup._endOptSeparator.className = "bookmarks-actions-menuseparator";
       aPopup.appendChild(aPopup._endOptSeparator);
-      aPopup._endMarker++;
 
       // Add the "Open All in Tabs" menuitem.
       aPopup._endOptOpenAllInTabs = document.createElement("menuitem");
       aPopup._endOptOpenAllInTabs.className = "openintabs-menuitem";
       aPopup._endOptOpenAllInTabs.setAttribute("oncommand",
         "PlacesUIUtils.openContainerNodeInTabs(this.parentNode._placesNode, event, " +
                                                "PlacesUIUtils.getViewForNode(this));");
       aPopup._endOptOpenAllInTabs.setAttribute("onclick",
         "checkForMiddleClick(this, event); event.stopPropagation();");
       aPopup._endOptOpenAllInTabs.setAttribute("label",
         gNavigatorBundle.getString("menuOpenAllInTabs.label"));
       aPopup.appendChild(aPopup._endOptOpenAllInTabs);
-      aPopup._endMarker++;
+    }
+  },
+
+  _ensureMarkers: function PVB__ensureMarkers(aPopup) {
+    if (aPopup._startMarker)
+      return;
+
+    // _startMarker is an hidden menuseparator that lives before places nodes.
+    aPopup._startMarker = document.createElement("menuseparator");
+    aPopup._startMarker.hidden = true;
+    aPopup.insertBefore(aPopup._startMarker, aPopup.firstChild);
+
+    // _endMarker is an hidden menuseparator that lives after places nodes.
+    aPopup._endMarker = document.createElement("menuseparator");
+    aPopup._endMarker.hidden = true;
+    aPopup.appendChild(aPopup._endMarker);
+
+    // Move the markers to the right position.
+    let firstNonStaticNodeFound = false;
+    for (let i = 0; i < aPopup.childNodes.length; i++) {
+      let child = aPopup.childNodes[i];
+      // Menus that have static content at the end, but are initially empty,
+      // use a special "builder" attribute to figure out where to start
+      // inserting places nodes.
+      if (child.getAttribute("builder") == "end") {
+        aPopup.insertBefore(aPopup._endMarker, child);
+        break;
+      }
+
+      if (child._placesNode && !firstNonStaticNodeFound) {
+        firstNonStaticNodeFound = true;
+        aPopup.insertBefore(aPopup._startMarker, child);
+      }
+    }
+    if (!firstNonStaticNodeFound) {
+      aPopup.insertBefore(aPopup._startMarker, aPopup._endMarker);
     }
   },
 
   _onPopupShowing: function PVB__onPopupShowing(aEvent) {
     // Avoid handling popupshowing of inner views.
     let popup = aEvent.originalTarget;
+
+    this._ensureMarkers(popup);
+
     if (popup._placesNode && PlacesUIUtils.getViewForNode(popup) == this) {
       if (!popup._placesNode.containerOpen)
         popup._placesNode.containerOpen = true;
       if (!popup._built)
         this._rebuildPopup(popup);
 
       this._mayAddCommandsItems(popup);
     }
@@ -912,21 +897,16 @@ function PlacesToolbar(aPlace) {
   Services.telemetry.getHistogramById("FX_BOOKMARKS_TOOLBAR_INIT_MS")
                     .add(Date.now() - startTime);
 }
 
 PlacesToolbar.prototype = {
   __proto__: PlacesViewBase.prototype,
 
   _cbEvents: ["dragstart", "dragover", "dragexit", "dragend", "drop",
-#ifdef XP_UNIX
-#ifndef XP_MACOSX
-              "mousedown", "mouseup",
-#endif
-#endif
               "mousemove", "mouseover", "mouseout"],
 
   QueryInterface: function PT_QueryInterface(aIID) {
     if (aIID.equals(Ci.nsIDOMEventListener) ||
         aIID.equals(Ci.nsITimerCallback))
       return this;
 
     return PlacesViewBase.prototype.QueryInterface.apply(this, arguments);
@@ -1113,26 +1093,16 @@ PlacesToolbar.prototype = {
         this._onMouseOver(aEvent);
         break;
       case "mousemove":
         this._onMouseMove(aEvent);
         break;
       case "mouseout":
         this._onMouseOut(aEvent);
         break;
-#ifdef XP_UNIX
-#ifndef XP_MACOSX
-      case "mouseup":
-        this._onMouseUp(aEvent);
-        break;
-      case "mousedown":
-        this._onMouseDown(aEvent);
-        break;
-#endif
-#endif
       case "popupshowing":
         this._onPopupShowing(aEvent);
         break;
       case "popuphidden":
         this._onPopupHidden(aEvent);
         break;
       default:
         throw "Trying to handle unexpected event.";
@@ -1550,24 +1520,16 @@ PlacesToolbar.prototype = {
     let draggedElt = aEvent.target;
     if (draggedElt.parentNode != this._rootElt || !draggedElt._placesNode)
       return;
 
     if (draggedElt.localName == "toolbarbutton" &&
         draggedElt.getAttribute("type") == "menu") {
       // If the drag gesture on a container is toward down we open instead
       // of dragging.
-#ifdef XP_UNIX
-#ifndef XP_MACOSX
-      if (this._mouseDownTimer) {
-        this._mouseDownTimer.cancel();
-        this._mouseDownTimer = null;
-      }
-#endif
-#endif
       let translateY = this._cachedMouseMoveEvent.clientY - aEvent.clientY;
       let translateX = this._cachedMouseMoveEvent.clientX - aEvent.clientX;
       if ((translateY) >= Math.abs(translateX/2)) {
         // Don't start the drag.
         aEvent.preventDefault();
         // Open the menu.
         draggedElt.open = true;
         return;
@@ -1730,57 +1692,16 @@ PlacesToolbar.prototype = {
       // Clear the dragover attribute if present, if we are dragging into a
       // folder in the hierachy of current opened popup we don't clear
       // this attribute on clearOverFolder.  See Notify for closeTimer.
       if (parent.hasAttribute("dragover"))
         parent.removeAttribute("dragover");
     }
   },
 
-#ifdef XP_UNIX
-#ifndef XP_MACOSX
-  _onMouseDown: function PT__onMouseDown(aEvent) {
-    let target = aEvent.target;
-    if (aEvent.button == 0 &&
-        target.localName == "toolbarbutton" &&
-        target.getAttribute("type") == "menu") {
-      this._allowPopupShowing = false;
-      // On Linux we can open the popup only after a delay.
-      // Indeed as soon as the menupopup opens we are unable to start a
-      // drag aEvent.  See bug 500081 for details.
-      this._mouseDownTimer = Cc["@mozilla.org/timer;1"].
-                             createInstance(Ci.nsITimer);
-      let callback = {
-        _self: this,
-        _target: target,
-        notify: function(timer) {
-          this._target.open = true;
-          this._mouseDownTimer = null;
-        }
-      };
-
-      this._mouseDownTimer.initWithCallback(callback, 300,
-                                            Ci.nsITimer.TYPE_ONE_SHOT);
-    }
-  },
-
-  _onMouseUp: function PT__onMouseUp(aEvent) {
-    if (aEvent.button != 0)
-      return;
-
-    if (this._mouseDownTimer) {
-      // On a click (down/up), we should open the menu popup.
-      this._mouseDownTimer.cancel();
-      this._mouseDownTimer = null;
-      aEvent.target.open = true;
-    }
-  },
-#endif
-#endif
-
   _onMouseMove: function PT__onMouseMove(aEvent) {
     // Used in dragStart to prevent dragging folders when dragging down.
     this._cachedMouseMoveEvent = aEvent;
 
     if (this._openedMenuButton == null ||
         PlacesControllerDragHelper.getSession())
       return;
 
@@ -1803,18 +1724,16 @@ function PlacesMenu(aPopupShowingEvent, 
   this._viewElt = this._rootElt.parentNode;   // <menu>
   this._viewElt._placesView = this;
   this._addEventListeners(this._rootElt, ["popupshowing", "popuphidden"], true);
   this._addEventListeners(window, ["unload"], false);
 
 #ifdef XP_MACOSX
   if (this._viewElt.parentNode.localName == "menubar") {
     this._nativeView = true;
-    this._rootElt._startMarker = -1;
-    this._rootElt._endMarker = -1;
   }
 #endif
 
   PlacesViewBase.call(this, aPlace);
   this._onPopupShowing(aPopupShowingEvent);
 }
 
 PlacesMenu.prototype = {
@@ -1824,18 +1743,16 @@ PlacesMenu.prototype = {
     if (aIID.equals(Ci.nsIDOMEventListener))
       return this;
 
     return PlacesViewBase.prototype.QueryInterface.apply(this, arguments);
   },
 
   _removeChild: function PM_removeChild(aChild) {
     PlacesViewBase.prototype._removeChild.apply(this, arguments);
-    if (this._endMarker != -1)
-      this._endMarker--;
   },
 
   uninit: function PM_uninit() {
     this._removeEventListeners(this._rootElt, ["popupshowing", "popuphidden"],
                                true);
     this._removeEventListeners(window, ["unload"], false);
 
     PlacesViewBase.prototype.uninit.apply(this, arguments);
--- a/browser/components/places/content/editBookmarkOverlay.xul
+++ b/browser/components/places/content/editBookmarkOverlay.xul
@@ -85,29 +85,27 @@
         <row align="center" id="editBMPanel_feedLocationRow">
           <label value="&editBookmarkOverlay.feedLocation.label;"
                  class="editBMPanel_rowLabel"
                  accesskey="&editBookmarkOverlay.feedLocation.accesskey;"
                  control="editBMPanel_feedLocationField"
                  observes="paneElementsBroadcaster"/>
           <textbox id="editBMPanel_feedLocationField"
                    class="uri-element"
-                   onblur="gEditItemOverlay.onFeedLocationFieldBlur();"
                    observes="paneElementsBroadcaster"/>
         </row>
 
         <row align="center" id="editBMPanel_siteLocationRow">
           <label value="&editBookmarkOverlay.siteLocation.label;"
                  class="editBMPanel_rowLabel"
                  accesskey="&editBookmarkOverlay.siteLocation.accesskey;"
                  control="editBMPanel_siteLocationField"
                  observes="paneElementsBroadcaster"/>
           <textbox id="editBMPanel_siteLocationField"
                    class="uri-element"
-                   onblur="gEditItemOverlay.onSiteLocationFieldBlur();"
                    observes="paneElementsBroadcaster"/>
         </row>
 
         <row align="center" id="editBMPanel_folderRow">
           <label value="&editBookmarkOverlay.folder.label;"
                  class="editBMPanel_rowLabel"
                  control="editBMPanel_folderMenuList"
                  observes="paneElementsBroadcaster"/>
--- a/browser/components/places/content/menu.xml
+++ b/browser/components/places/content/menu.xml
@@ -67,38 +67,33 @@
                                                 "menupopup-drop-indicator-bar");
       </field>
 
       <field name="_scrollBox">
         document.getAnonymousElementByAttribute(this, "class",
                                                 "popup-internal-box");
       </field>
 
-      <!-- markers for start and end of valid places items -->
-      <field name="_startMarker">-1</field>
-      <field name="_endMarker">-1</field>
-
       <!-- This is the view that manage the popup -->
       <field name="_rootView">PlacesUIUtils.getViewForNode(this);</field>
 
       <!-- Check if we should hide the drop indicator for the target -->
       <method name="_hideDropIndicator">
         <parameter name="aEvent"/>
         <body><![CDATA[
-          var target = aEvent.target;
+          let target = aEvent.target;
 
-          // in some view we have _startMarker and _endMarker, we should not
-          // draw the drop indicator outside of them
-          var betweenMarkers = true;
-          if (this._startMarker != -1 &&
-              target.boxObject.y <= this.childNodes[this._startMarker].boxObject.y)
-            betweenMarkers = false;
-          if (this._endMarker != -1 &&
-              target.boxObject.y >= this.childNodes[this._endMarker].boxObject.y)
-            betweenMarkers = false;
+          // Don't draw the drop indicator outside of markers.
+          // The markers are hidden, since otherwise sometimes popups acquire
+          // scrollboxes on OS X, so we can't use them directly.
+          let firstChildTop = this._startMarker.nextSibling.boxObject.y;
+          let lastChildBottom = this._endMarker.previousSibling.boxObject.y +
+                                this._endMarker.previousSibling.boxObject.height;
+          let betweenMarkers = target.boxObject.y >= firstChildTop ||
+                               target.boxObject.y <= lastChildBottom;
 
           // Hide the dropmarker if current node is not a Places node.
           return !(target && target._placesNode && betweenMarkers);
         ]]></body>
       </method>
 
       <!-- This function returns information about where to drop when
            dragging over this popup insertion point -->
--- a/browser/components/places/content/treeView.js
+++ b/browser/components/places/content/treeView.js
@@ -891,17 +891,19 @@ PlacesTreeView.prototype = {
 
       PlacesUtils.livemarks.getLivemark({ id: aNode.itemId },
         (function (aStatus, aLivemark) {
           if (Components.isSuccessCode(aStatus)) {
             let shouldInvalidate = !aNode._feedURI;
             aNode._feedURI = aLivemark.feedURI;
             if (aNewState == Components.interfaces.nsINavHistoryContainerResultNode.STATE_OPENED) {
               aLivemark.registerForUpdates(aNode, this);
+              // Prioritize the current livemark.
               aLivemark.reload();
+              PlacesUtils.livemarks.reloadLivemarks();
               if (shouldInvalidate)
                 this.invalidateContainer(aNode);
             }
             else {
               aLivemark.unregisterForUpdates(aNode);
             }
           }
         }).bind(this)
--- a/browser/devtools/debugger/DebuggerUI.jsm
+++ b/browser/devtools/debugger/DebuggerUI.jsm
@@ -19,16 +19,17 @@
  *   Mozilla Foundation
  * Portions created by the Initial Developer are Copyright (C) 2011
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Dave Camp <dcamp@mozilla.com> (original author)
  *   Panos Astithas <past@mozilla.com>
  *   Victor Porof <vporof@mozilla.com>
+ *   Mihai Sucan <mihai.sucan@gmail.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
@@ -55,20 +56,48 @@ let EXPORTED_SYMBOLS = ["DebuggerUI"];
 
 /**
  * Creates a pane that will host the debugger UI.
  */
 function DebuggerPane(aTab) {
   this._tab = aTab;
   this._close = this.close.bind(this);
   this._debugTab = this.debugTab.bind(this);
+  this.breakpoints = {};
 }
 
 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,
+
+  /**
    * Creates and initializes the widgets contained in the debugger UI.
    */
   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");
@@ -82,44 +111,251 @@ DebuggerPane.prototype = {
 
     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
+        readOnly: true,
+        showAnnotationRuler: true,
+        showOverviewRuler: true,
       };
 
       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;
+    }
+
+    aEvent.added.forEach(this._onEditorBreakpointAdd, this);
+    aEvent.removed.forEach(this._onEditorBreakpointRemove, this);
+  },
+
+  /**
+   * 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.
+   */
+  _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));
+  },
+
+  /**
+   * 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 DP_removeBreakpoint(aBreakpoint, aCallback, aNoEditorUpdate) {
+    if (!(aBreakpoint.actor in this.breakpoints)) {
+      aCallback && aCallback(aBreakpoint.location);
+      return;
+    }
+
+    aBreakpoint.remove(function() {
+      delete this.breakpoints[aBreakpoint.actor];
+
+      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));
+  },
+
+  /**
+   * 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;
+  },
+
+  /**
    * Closes the debugger UI removing child nodes and event listeners.
    */
   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;
 
       this.frame.removeEventListener("unload", this._close, true);
@@ -187,17 +423,17 @@ DebuggerPane.prototype = {
             self.onConnected(self);
           }
         });
       });
     });
   },
 
   get debuggerWindow() {
-    return this.frame.contentWindow;
+    return this.frame ? this.frame.contentWindow : null;
   },
 
   get debuggerClient() {
     return this._client;
   },
 
   get activeThread() {
     try {
@@ -335,16 +571,17 @@ DebuggerUI.prototype = {
     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._updateEditorBreakpoints();
   }
 };
 
 /**
  * Various debugger UI preferences (currently just the pane height).
  */
 let DebuggerUIPreferences = {
 
--- a/browser/devtools/debugger/debugger-view.js
+++ b/browser/devtools/debugger/debugger-view.js
@@ -17,16 +17,17 @@
  *
  * The Initial Developer of the Original Code is
  *   Mozilla Foundation
  * Portions created by the Initial Developer are Copyright (C) 2011
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Victor Porof <vporof@mozilla.com> (original author)
+ *   Mihai Sucan <mihai.sucan@gmail.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
@@ -194,17 +195,16 @@ DebuggerView.Stackframes = {
    * @param boolean aSelect
    *        True if the frame should be selected, false otherwise.
    */
   highlightFrame: function DVF_highlightFrame(aDepth, aSelect) {
     let frame = document.getElementById("stackframe-" + aDepth);
 
     // the list item wasn't found in the stackframe container
     if (!frame) {
-      dump("The frame list item wasn't found in the stackframes container.");
       return;
     }
 
     // add the 'selected' css class if the frame isn't already selected
     if (aSelect && !frame.classList.contains("selected")) {
       frame.classList.add("selected");
 
     // remove the 'selected' css class if the frame is already selected
@@ -351,17 +351,16 @@ DebuggerView.Properties = {
     // compute the id of the element if not specified
     aId = aId || (aName.toLowerCase().trim().replace(" ", "-") + "-scope");
 
     // contains generic nodes and functionality
     let element = this._createPropertyElement(aName, aId, "scope", this._vars);
 
     // make sure the element was created successfully
     if (!element) {
-      dump("The debugger scope container wasn't created properly: " + aId);
       return null;
     }
 
     /**
      * @see DebuggerView.Properties._addVar
      */
     element.addVar = this._addVar.bind(this, element);
 
@@ -393,17 +392,16 @@ DebuggerView.Properties = {
     aId = aId || (aScope.id + "->" + aName + "-variable");
 
     // contains generic nodes and functionality
     let element = this._createPropertyElement(aName, aId, "variable",
                                               aScope.querySelector(".details"));
 
     // make sure the element was created successfully
     if (!element) {
-      dump("The debugger variable container wasn't created properly: " + aId);
       return null;
     }
 
     /**
      * @see DebuggerView.Properties._setGrip
      */
     element.setGrip = this._setGrip.bind(this, element);
 
@@ -461,17 +459,16 @@ DebuggerView.Properties = {
     if (!aVar) {
       return null;
     }
 
     let info = aVar.querySelector(".info") || aVar.target.info;
 
     // make sure the info node exists
     if (!info) {
-      dump("Could not set the grip for the corresponding variable: " + aVar.id);
       return null;
     }
 
     info.textContent = this._propertyString(aGrip);
     info.classList.add(this._propertyColor(aGrip));
 
     return aVar;
   },
@@ -564,17 +561,16 @@ DebuggerView.Properties = {
     aId = aId || (aVar.id + "->" + aProperty[0] + "-property");
 
     // contains generic nodes and functionality
     let element = this._createPropertyElement(aName, aId, "property",
                                               aVar.querySelector(".details"));
 
     // make sure the element was created successfully
     if (!element) {
-      dump("The debugger property container wasn't created properly.");
       return null;
     }
 
     /**
      * @see DebuggerView.Properties._setGrip
      */
     element.setGrip = this._setGrip.bind(this, element);
 
@@ -705,21 +701,19 @@ DebuggerView.Properties = {
    * @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
     if (document.getElementById(aId)) {
-      dump("Duplicating a property element id is not allowed.");
       return null;
     }
     if (!aParent) {
-      dump("A property element must have a valid parent node specified.");
       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");
@@ -1122,16 +1116,25 @@ DebuggerView.Scripts = {
     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
--- a/browser/devtools/debugger/debugger.js
+++ b/browser/devtools/debugger/debugger.js
@@ -73,17 +73,18 @@ function startDebuggingTab(aClient, aTab
 {
   gClient = aClient;
 
   gClient.attachTab(aTabGrip.actor, function(aResponse, aTabClient) {
     if (aTabClient) {
       gTabClient = aTabClient;
       gClient.attachThread(aResponse.threadActor, function(aResponse, aThreadClient) {
         if (!aThreadClient) {
-          dump("Couldn't attach to thread: "+aResponse.error+"\n");
+          Components.utils.reportError("Couldn't attach to thread: " +
+                                       aResponse.error);
           return;
         }
         ThreadState.connect(aThreadClient, function() {
           StackFrames.connect(aThreadClient, function() {
             SourceScripts.connect(aThreadClient, function() {
               aThreadClient.resume();
             });
           });
@@ -607,16 +608,17 @@ var SourceScripts = {
     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, aScript.url);
       document.documentElement.dispatchEvent(evt);
       window.editor.setText(DebuggerView.getStr("loadingText"));
     } else {
       window.editor.setText(aScript.text);
+      window.updateEditorBreakpoints();
     }
   }
 };
 
 SourceScripts.onPaused = SourceScripts.onPaused.bind(SourceScripts);
 SourceScripts.onScripts = SourceScripts.onScripts.bind(SourceScripts);
 SourceScripts.onNewScript = SourceScripts.onNewScript.bind(SourceScripts);
 SourceScripts.onScriptsCleared = SourceScripts.onScriptsCleared.bind(SourceScripts);
--- a/browser/devtools/debugger/test/Makefile.in
+++ b/browser/devtools/debugger/test/Makefile.in
@@ -68,16 +68,17 @@ include $(topsrcdir)/config/rules.mk
 	browser_dbg_stack-03.js \
 	browser_dbg_stack-04.js \
 	browser_dbg_location-changes.js \
 	browser_dbg_script-switching.js \
 	browser_dbg_pause-resume.js \
 	browser_dbg_update-editor-mode.js \
 	browser_dbg_select-line.js \
 	browser_dbg_clean-exit.js \
+	browser_dbg_bug723069_editor-breakpoints.js \
 	head.js \
 	$(NULL)
 
 _BROWSER_TEST_PAGES = \
 	browser_dbg_tab1.html \
 	browser_dbg_tab2.html \
 	browser_dbg_debuggerstatement.html \
 	browser_dbg_stack.html \
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/browser_dbg_bug723069_editor-breakpoints.js
@@ -0,0 +1,274 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Bug 723069: test the debugger breakpoint API and connection to the source
+ * editor.
+ */
+
+const TAB_URL = EXAMPLE_URL + "browser_dbg_script-switching.html";
+
+let gPane = null;
+let gTab = null;
+let gDebuggee = null;
+let gDebugger = null;
+let gScripts = null;
+let gEditor = null;
+let gBreakpoints = null;
+
+function test()
+{
+  let tempScope = {};
+  Cu.import("resource:///modules/source-editor.jsm", tempScope);
+  let SourceEditor = tempScope.SourceEditor;
+
+  debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
+    gTab = aTab;
+    gDebuggee = aDebuggee;
+    gPane = aPane;
+    gDebugger = gPane.debuggerWindow;
+
+    gPane.activeThread.addOneTimeListener("scriptsadded", function() {
+      Services.tm.currentThread.dispatch({ run: onScriptsAdded }, 0);
+    });
+    gDebuggee.firstCall();
+  });
+
+  function onScriptsAdded()
+  {
+    gScripts = gDebugger.DebuggerView.Scripts;
+
+    is(gDebugger.StackFrames.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],
+          "the correct sccript 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");
+
+
+    info("add the first breakpoint");
+    gEditor.addEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE,
+                             onEditorBreakpointAddFirst);
+    let location = {url: gScripts.selected, line: 6};
+    executeSoon(function() {
+      gPane.addBreakpoint(location, onBreakpointAddFirst);
+    });
+  }
+
+  let breakpointsAdded = 0;
+  let breakpointsRemoved = 0;
+  let editorBreakpointChanges = 0;
+
+  function onEditorBreakpointAddFirst(aEvent)
+  {
+    gEditor.removeEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE,
+                                onEditorBreakpointAddFirst);
+    editorBreakpointChanges++;
+
+    ok(aEvent, "breakpoint1 added to the editor");
+    is(aEvent.added.length, 1, "one breakpoint added to the editor");
+    is(aEvent.removed.length, 0, "no breakpoint was removed from the editor");
+    is(aEvent.added[0].line, 5, "editor breakpoint line is correct");
+
+    is(gEditor.getBreakpoints().length, 1,
+       "editor.getBreakpoints().length is correct");
+  }
+
+  function onBreakpointAddFirst(aBreakpointClient, aResponseError)
+  {
+    breakpointsAdded++;
+
+    ok(aBreakpointClient, "breakpoint1 added, client received");
+    ok(!aResponseError, "breakpoint1 added without errors");
+    is(aBreakpointClient.location.url, gScripts.selected,
+       "breakpoint1 client url is correct");
+    is(aBreakpointClient.location.line, 6,
+       "breakpoint1 client line is correct");
+
+    executeSoon(function() {
+      ok(aBreakpointClient.actor in gBreakpoints,
+         "breakpoint1 client found in the list of debugger breakpoints");
+      is(Object.keys(gBreakpoints).length, 1,
+         "the list of debugger breakpoints holds only one breakpoint");
+      is(gPane.getBreakpoint(gScripts.selected, 6), aBreakpointClient,
+         "getBreakpoint(selectedScript, 2) returns the correct breakpoint");
+
+      info("remove the first breakpoint");
+      gEditor.addEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE,
+                               onEditorBreakpointRemoveFirst);
+      gPane.removeBreakpoint(aBreakpointClient, onBreakpointRemoveFirst);
+    });
+  }
+
+  function onBreakpointRemoveFirst(aLocation)
+  {
+    breakpointsRemoved++;
+
+    ok(aLocation, "breakpoint1 removed");
+    is(aLocation.url, gScripts.selected, "breakpoint1 remove: url is correct");
+    is(aLocation.line, 6, "breakpoint1 remove: line is correct");
+
+    executeSoon(testBreakpointAddBackground);
+  }
+
+  function onEditorBreakpointRemoveFirst(aEvent)
+  {
+    gEditor.removeEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE,
+                                onEditorBreakpointRemoveFirst);
+    editorBreakpointChanges++;
+
+    ok(aEvent, "breakpoint1 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, 5, "editor breakpoint line is correct");
+
+    is(gEditor.getBreakpoints().length, 0, "editor.getBreakpoints().length is correct");
+  }
+
+  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];
+    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);
+  }
+
+  function onEditorBreakpointAddBackgroundTrap(aEvent)
+  {
+    // trap listener: no breakpoint must be added to the editor when a breakpoint
+    // is added to a script that is not currently selected.
+    gEditor.removeEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE,
+                                onEditorBreakpointAddBackgroundTrap);
+    editorBreakpointChanges++;
+    ok(false, "breakpoint2 must not be added to the editor");
+  }
+
+  function onBreakpointAddBackground(aBreakpointClient, aResponseError)
+  {
+    breakpointsAdded++;
+
+    ok(aBreakpointClient, "breakpoint2 added, client received");
+    ok(!aResponseError, "breakpoint2 added without errors");
+    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,
+         "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 });
+    });
+  }
+
+  function onEditorBreakpointAddSwitch(aEvent)
+  {
+    gEditor.removeEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE,
+                                onEditorBreakpointAddSwitch);
+    editorBreakpointChanges++;
+
+    ok(aEvent, "breakpoint2 added to the editor");
+    is(aEvent.added.length, 1, "one breakpoint added to the editor");
+    is(aEvent.removed.length, 0, "no breakpoint was removed from the editor");
+    is(aEvent.added[0].line, 4, "editor breakpoint line is correct");
+
+    is(gEditor.getBreakpoints().length, 1,
+       "editor.getBreakpoints().length is correct");
+  }
+
+  function onEditorTextChanged()
+  {
+    gEditor.removeEventListener(SourceEditor.EVENTS.TEXT_CHANGED,
+                                onEditorTextChanged);
+
+    is(gEditor.getText().indexOf("debugger"), -1,
+       "The second script is no longer displayed.");
+
+    isnot(gEditor.getText().indexOf("firstCall"), -1,
+          "The first script is displayed.");
+
+    executeSoon(function() {
+      info("remove the second breakpoint using the mouse");
+
+      gEditor.addEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE,
+                               onEditorBreakpointRemoveSecond);
+
+      let testWin = gEditor.editorElement.ownerDocument.defaultView;
+      EventUtils.synthesizeMouse(gEditor.editorElement, 10, 70, {}, testWin);
+    });
+
+  }
+
+  function onEditorBreakpointRemoveSecond(aEvent)
+  {
+    gEditor.removeEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE,
+                                onEditorBreakpointRemoveSecond);
+    editorBreakpointChanges++;
+
+    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);
+    });
+  }
+
+  registerCleanupFunction(function() {
+    is(Object.keys(gBreakpoints).length, 0, "no breakpoint in the debugger");
+    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;
+    gDebuggee = null;
+    gDebugger = null;
+    gScripts = null;
+    gEditor = null;
+    gBreakpoints = null;
+  });
+}
--- a/browser/devtools/debugger/test/browser_dbg_clean-exit.js
+++ b/browser/devtools/debugger/test/browser_dbg_clean-exit.js
@@ -2,39 +2,41 @@
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 // Test that closing a tab with the debugger in a paused state exits cleanly.
 
 var gPane = null;
 var gTab = null;
-var gDebuggee = null;
 var gDebugger = null;
 
 const DEBUGGER_TAB_URL = EXAMPLE_URL + "browser_dbg_debuggerstatement.html";
 
 function test() {
   debug_tab_pane(DEBUGGER_TAB_URL, function(aTab, aDebuggee, aPane) {
     gTab = aTab;
-    gDebuggee = aDebuggee;
     gPane = aPane;
     gDebugger = gPane.debuggerWindow;
 
     testCleanExit();
   });
 }
 
 function testCleanExit() {
   gPane.activeThread.addOneTimeListener("framesadded", function() {
     Services.tm.currentThread.dispatch({ run: function() {
       is(gDebugger.StackFrames.activeThread.paused, true,
         "Should be paused after the debugger statement.");
 
-      gPane._client.addOneTimeListener("tabDetached", function () {
-        finish();
-      });
-      removeTab(gTab);
+      closeDebuggerAndFinish(gTab);
     }}, 0);
   });
 
   gTab.linkedBrowser.contentWindow.wrappedJSObject.runDebuggerStatement();
 }
+
+registerCleanupFunction(function() {
+  removeTab(gTab);
+  gPane = null;
+  gTab = null;
+  gDebugger = null;
+});
--- a/browser/devtools/debugger/test/browser_dbg_location-changes.js
+++ b/browser/devtools/debugger/test/browser_dbg_location-changes.js
@@ -50,15 +50,22 @@ function testSimpleCall() {
 function testLocationChange()
 {
   gDebugger.StackFrames.activeThread.resume(function() {
     gPane._client.addOneTimeListener("tabNavigated", function(aEvent, aPacket) {
       ok(true, "tabNavigated event was fired.");
       gPane._client.addOneTimeListener("tabAttached", function(aEvent, aPacket) {
         ok(true, "Successfully reattached to the tab again.");
 
-        removeTab(gTab);
-        finish();
+        closeDebuggerAndFinish(gTab);
       });
     });
     content.location = TAB1_URL;
   });
 }
+
+registerCleanupFunction(function() {
+  removeTab(gTab);
+  gPane = null;
+  gTab = null;
+  gDebuggee = null;
+  gDebugger = null;
+});
--- a/browser/devtools/debugger/test/browser_dbg_pause-resume.js
+++ b/browser/devtools/debugger/test/browser_dbg_pause-resume.js
@@ -1,23 +1,21 @@
 /* vim:set ts=2 sw=2 sts=2 et: */
 /*
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var gPane = null;
 var gTab = null;
-var gDebuggee = null;
 var gDebugger = null;
 
 function test() {
   debug_tab_pane(STACK_URL, function(aTab, aDebuggee, aPane) {
     gTab = aTab;
-    gDebuggee = aDebuggee;
     gPane = aPane;
     gDebugger = gPane.debuggerWindow;
 
     testPause();
   });
 }
 
 function testPause() {
@@ -58,17 +56,22 @@ function testResume() {
 
       is(gDebugger.StackFrames.activeThread.paused, false,
         "Should be paused after an interrupt request.");
 
       let button = gDebugger.document.getElementById("resume");
       is(button.label, gDebugger.DebuggerView.getStr("pauseLabel"),
         "Button label should be pause when running.");
 
-      removeTab(gTab);
-      finish();
+      closeDebuggerAndFinish(gTab);
     }}, 0);
   });
 
   EventUtils.sendMouseEvent({ type: "click" },
     gDebugger.document.getElementById("resume"),
     gDebugger);
 }
+
+registerCleanupFunction(function() {
+  removeTab(gTab);
+  gPane = null;
+  gTab = null;
+});
--- a/browser/devtools/debugger/test/browser_dbg_propertyview-01.js
+++ b/browser/devtools/debugger/test/browser_dbg_propertyview-01.js
@@ -129,14 +129,20 @@ function resumeAndFinish() {
       ok(gDebugger.DebuggerView.Scripts.containsLabel("x/script.js"),
         "Script (4) label is incorrect.");
       ok(gDebugger.DebuggerView.Scripts.containsLabel("x/y/script.js"),
         "Script (5) label is incorrect.");
 
       is(vs._scripts.itemCount, 6,
         "Got too many script items in the list!");
 
-
-      removeTab(gTab);
-      finish();
+      closeDebuggerAndFinish(gTab);
     });
   });
 }
+
+registerCleanupFunction(function() {
+  removeTab(gTab);
+  gPane = null;
+  gTab = null;
+  gDebuggee = null;
+  gDebugger = null;
+});
--- a/browser/devtools/debugger/test/browser_dbg_propertyview-02.js
+++ b/browser/devtools/debugger/test/browser_dbg_propertyview-02.js
@@ -111,21 +111,24 @@ function testSimpleCall() {
 
       EventUtils.sendMouseEvent({ type: "click" },
         testScope.querySelector(".title"),
         gDebugger);
 
       ok(!testScope.expanded,
         "Clicking again the testScope tilte should collapse it.");
 
-      resumeAndFinish();
+      gDebugger.StackFrames.activeThread.resume(function() {
+        closeDebuggerAndFinish(gTab);
+      });
     }}, 0);
   });
 
   gDebuggee.simpleCall();
 }
 
-function resumeAndFinish() {
-  gDebugger.StackFrames.activeThread.resume(function() {
-    removeTab(gTab);
-    finish();
-  });
-}
+registerCleanupFunction(function() {
+  removeTab(gTab);
+  gPane = null;
+  gTab = null;
+  gDebuggee = null;
+  gDebugger = null;
+});
--- a/browser/devtools/debugger/test/browser_dbg_propertyview-03.js
+++ b/browser/devtools/debugger/test/browser_dbg_propertyview-03.js
@@ -113,21 +113,24 @@ 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.");
 
-      resumeAndFinish();
+      gDebugger.StackFrames.activeThread.resume(function() {
+        closeDebuggerAndFinish(gTab);
+      });
     }}, 0);
   });
 
   gDebuggee.simpleCall();
 }
 
-function resumeAndFinish() {
-  gDebugger.StackFrames.activeThread.resume(function() {
-    removeTab(gTab);
-    finish();
-  });
-}
+registerCleanupFunction(function() {
+  removeTab(gTab);
+  gPane = null;
+  gTab = null;
+  gDebuggee = null;
+  gDebugger = null;
+});
--- a/browser/devtools/debugger/test/browser_dbg_propertyview-04.js
+++ b/browser/devtools/debugger/test/browser_dbg_propertyview-04.js
@@ -68,21 +68,24 @@ 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.");
 
-      resumeAndFinish();
+      gDebugger.StackFrames.activeThread.resume(function() {
+        closeDebuggerAndFinish(gTab);
+      });
     }}, 0);
   });
 
   gDebuggee.simpleCall();
 }
 
-function resumeAndFinish() {
-  gDebugger.StackFrames.activeThread.resume(function() {
-    removeTab(gTab);
-    finish();
-  });
-}
+registerCleanupFunction(function() {
+  removeTab(gTab);
+  gPane = null;
+  gTab = null;
+  gDebuggee = null;
+  gDebugger = null;
+});
--- a/browser/devtools/debugger/test/browser_dbg_propertyview-05.js
+++ b/browser/devtools/debugger/test/browser_dbg_propertyview-05.js
@@ -76,21 +76,24 @@ 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.");
 
-      resumeAndFinish();
+      gDebugger.StackFrames.activeThread.resume(function() {
+        closeDebuggerAndFinish(gTab);
+      });
     }}, 0);
   });
 
   gDebuggee.simpleCall();
 }
 
-function resumeAndFinish() {
-  gDebugger.StackFrames.activeThread.resume(function() {
-    removeTab(gTab);
-    finish();
-  });
-}
+registerCleanupFunction(function() {
+  removeTab(gTab);
+  gPane = null;
+  gTab = null;
+  gDebuggee = null;
+  gDebugger = null;
+});
--- a/browser/devtools/debugger/test/browser_dbg_propertyview-06.js
+++ b/browser/devtools/debugger/test/browser_dbg_propertyview-06.js
@@ -112,22 +112,24 @@ 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.");
 
-
-      resumeAndFinish();
+      gDebugger.StackFrames.activeThread.resume(function() {
+        closeDebuggerAndFinish(gTab);
+      });
     }}, 0);
   });
 
   gDebuggee.simpleCall();
 }
 
-function resumeAndFinish() {
-  gDebugger.StackFrames.activeThread.resume(function() {
-    removeTab(gTab);
-    finish();
-  });
-}
+registerCleanupFunction(function() {
+  removeTab(gTab);
+  gPane = null;
+  gTab = null;
+  gDebuggee = null;
+  gDebugger = null;
+});
--- a/browser/devtools/debugger/test/browser_dbg_propertyview-07.js
+++ b/browser/devtools/debugger/test/browser_dbg_propertyview-07.js
@@ -5,24 +5,22 @@
 /**
  * Make sure that the property view displays function parameters.
  */
 
 const TAB_URL = EXAMPLE_URL + "browser_dbg_frame-parameters.html";
 
 var gPane = null;
 var gTab = null;
-var gDebuggee = null;
 var gDebugger = null;
 
 function test()
 {
   debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
     gTab = aTab;
-    gDebuggee = aDebuggee;
     gPane = aPane;
     gDebugger = gPane.debuggerWindow;
 
     testFrameParameters();
   });
 }
 
 function testFrameParameters()
@@ -82,15 +80,21 @@ function testFrameParameters()
 function resumeAndFinish() {
   gPane.activeThread.addOneTimeListener("framescleared", function() {
     Services.tm.currentThread.dispatch({ run: function() {
       var frames = gDebugger.DebuggerView.Stackframes._frames;
 
       is(frames.querySelectorAll(".dbg-stackframe").length, 0,
         "Should have no frames.");
 
-      removeTab(gTab);
-      finish();
+      closeDebuggerAndFinish(gTab);
     }}, 0);
   });
 
   gDebugger.StackFrames.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
@@ -5,24 +5,22 @@
 /**
  * Make sure that the property view displays the properties of objects.
  */
 
 const TAB_URL = EXAMPLE_URL + "browser_dbg_frame-parameters.html";
 
 var gPane = null;
 var gTab = null;
-var gDebuggee = null;
 var gDebugger = null;
 
 function test()
 {
   debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
     gTab = aTab;
-    gDebuggee = aDebuggee;
     gPane = aPane;
     gDebugger = gPane.debuggerWindow;
 
     testFrameParameters();
   });
 }
 
 function testFrameParameters()
@@ -98,15 +96,21 @@ function testFrameParameters()
 function resumeAndFinish() {
   gPane.activeThread.addOneTimeListener("framescleared", function() {
     Services.tm.currentThread.dispatch({ run: function() {
       var frames = gDebugger.DebuggerView.Stackframes._frames;
 
       is(frames.querySelectorAll(".dbg-stackframe").length, 0,
         "Should have no frames.");
 
-      removeTab(gTab);
-      finish();
+      closeDebuggerAndFinish(gTab);
     }}, 0);
   });
 
   gDebugger.StackFrames.activeThread.resume();
 }
+
+registerCleanupFunction(function() {
+  removeTab(gTab);
+  gPane = null;
+  gTab = null;
+  gDebugger = null;
+});
--- a/browser/devtools/debugger/test/browser_dbg_script-switching.js
+++ b/browser/devtools/debugger/test/browser_dbg_script-switching.js
@@ -98,11 +98,18 @@ function testSwitchPaused()
 function testSwitchRunning()
 {
   ok(gDebugger.editor.getText().search(/debugger/) != -1,
     "The second script is displayed again.");
 
   ok(gDebugger.editor.getText().search(/firstCall/) == -1,
     "The first script is no longer displayed.");
 
+  closeDebuggerAndFinish(gTab);
+}
+
+registerCleanupFunction(function() {
   removeTab(gTab);
-  finish();
-}
+  gPane = null;
+  gTab = null;
+  gDebuggee = null;
+  gDebugger = null;
+});
--- a/browser/devtools/debugger/test/browser_dbg_select-line.js
+++ b/browser/devtools/debugger/test/browser_dbg_select-line.js
@@ -63,23 +63,39 @@ 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() {
-              removeTab(gTab);
-              finish();
+              closeDebuggerAndFinish(gTab);
             });
           });
         });
 
+        // Scroll all the way down to ensure stackframe-3 is visible.
+        let stackframes = gDebugger.document.getElementById("stackframes");
+        stackframes.scrollTop = stackframes.scrollHeight;
+
         // Click the oldest stack frame.
+        let frames = gDebugger.DebuggerView.Stackframes._frames;
+        is(frames.querySelectorAll(".dbg-stackframe").length, 4,
+          "Should have four frames.");
+
         let element = gDebugger.document.getElementById("stackframe-3");
+        isnot(element, null, "Found the third stack frame.");
         EventUtils.synthesizeMouseAtCenter(element, {}, gDebugger);
       });
     }}, 0);
   });
 
   gDebuggee.firstCall();
 }
+
+registerCleanupFunction(function() {
+  removeTab(gTab);
+  gPane = null;
+  gTab = null;
+  gDebuggee = null;
+  gDebugger = null;
+});
--- a/browser/devtools/debugger/test/browser_dbg_stack-01.js
+++ b/browser/devtools/debugger/test/browser_dbg_stack-01.js
@@ -31,21 +31,24 @@ function testSimpleCall() {
         "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.");
 
-      resumeAndFinish();
+      gDebugger.StackFrames.activeThread.resume(function() {
+        closeDebuggerAndFinish(gTab);
+      });
     }}, 0);
   });
 
   gDebuggee.simpleCall();
 }
 
-function resumeAndFinish() {
-  gDebugger.StackFrames.activeThread.resume(function() {
-    removeTab(gTab);
-    finish();
-  });
-}
+registerCleanupFunction(function() {
+  removeTab(gTab);
+  gPane = null;
+  gTab = null;
+  gDebuggee = null;
+  gDebugger = null;
+});
--- a/browser/devtools/debugger/test/browser_dbg_stack-02.js
+++ b/browser/devtools/debugger/test/browser_dbg_stack-02.js
@@ -62,22 +62,24 @@ 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.");
 
-      resumeAndFinish();
+      gDebugger.StackFrames.activeThread.resume(function() {
+        closeDebuggerAndFinish(gTab);
+      });
     }}, 0);
   });
 
   gDebuggee.evalCall();
 }
 
-function resumeAndFinish() {
-  gDebugger.StackFrames.activeThread.resume(function() {
-    removeTab(gTab);
-    finish();
-  });
-}
-
+registerCleanupFunction(function() {
+  removeTab(gTab);
+  gPane = null;
+  gTab = null;
+  gDebuggee = null;
+  gDebugger = null;
+});
--- a/browser/devtools/debugger/test/browser_dbg_stack-03.js
+++ b/browser/devtools/debugger/test/browser_dbg_stack-03.js
@@ -42,27 +42,30 @@ function testRecurse() {
 
         is(frames.querySelectorAll(".dbg-stackframe").length, pageSize * 2,
           "Should now have twice the max limit of frames.");
 
         gPane.activeThread.addOneTimeListener("framesadded", function() {
           is(frames.querySelectorAll(".dbg-stackframe").length, recurseLimit,
             "Should have reached the recurse limit.");
 
-          resumeAndFinish();
+          gDebugger.StackFrames.activeThread.resume(function() {
+            closeDebuggerAndFinish(gTab);
+          });
         });
 
         frames.scrollTop = frames.scrollHeight;
       });
 
       frames.scrollTop = frames.scrollHeight;
     }}, 0);
   });
 
   gDebuggee.recurse();
 }
 
-function resumeAndFinish() {
-  gDebugger.StackFrames.activeThread.resume(function() {
-    removeTab(gTab);
-    finish();
-  });
-}
+registerCleanupFunction(function() {
+  removeTab(gTab);
+  gPane = null;
+  gTab = null;
+  gDebuggee = null;
+  gDebugger = null;
+});
--- a/browser/devtools/debugger/test/browser_dbg_stack-04.js
+++ b/browser/devtools/debugger/test/browser_dbg_stack-04.js
@@ -43,18 +43,25 @@ function testEvalCallResume() {
           "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.");
 
-        removeTab(gTab);
-        finish();
+        closeDebuggerAndFinish(gTab);
       });
 
       gPane.activeThread.resume();
     }}, 0);
   });
 
   gDebuggee.evalCall();
 }
+
+registerCleanupFunction(function() {
+  removeTab(gTab);
+  gPane = null;
+  gTab = null;
+  gDebuggee = null;
+  gDebugger = null;
+});
--- a/browser/devtools/debugger/test/browser_dbg_update-editor-mode.js
+++ b/browser/devtools/debugger/test/browser_dbg_update-editor-mode.js
@@ -67,12 +67,20 @@ function testSwitchPaused()
 
   ok(gDebugger.editor.getText().search(/firstCall/) != -1,
     "The first script is displayed.");
 
   is(gDebugger.editor.getMode(), SourceEditor.MODES.JAVASCRIPT,
      "Found the expected editor mode.");
 
   gDebugger.StackFrames.activeThread.resume(function() {
-    removeTab(gTab);
-    finish();
+    closeDebuggerAndFinish(gTab);
   });
 }
+
+registerCleanupFunction(function() {
+  removeTab(gTab);
+  gPane = null;
+  gTab = null;
+  gDebuggee = null;
+  gDebugger = null;
+  gScripts = null;
+});
--- a/browser/devtools/debugger/test/head.js
+++ b/browser/devtools/debugger/test/head.js
@@ -44,16 +44,24 @@ 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);
+    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) {
       if (tab.url == aURL) {
         aCallback(tab);
         return;
       }
     }
--- a/browser/devtools/sourceeditor/source-editor-orion.jsm
+++ b/browser/devtools/sourceeditor/source-editor-orion.jsm
@@ -18,16 +18,17 @@
  * The Mozilla Foundation.
  * Portions created by the Initial Developer are Copyright (C) 2011
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Mihai Sucan <mihai.sucan@gmail.com> (original author)
  *   Kenny Heaton <kennyheaton@gmail.com>
  *   Spyros Livathinos <livathinos.spyros@gmail.com>
+ *   Allen Eubank <adeubank@gmail.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
@@ -119,16 +120,28 @@ const DEFAULT_KEYBINDINGS = [
     accel: true,
     shift: true,
   },
   {
     action: "Unindent Lines",
     code: Ci.nsIDOMKeyEvent.DOM_VK_TAB,
     shift: true,
   },
+  {
+    action: "Move Lines Up",
+    code: Ci.nsIDOMKeyEvent.DOM_VK_UP,
+    ctrl: Services.appinfo.OS == "Darwin",
+    alt: true,
+  },
+  {
+    action: "Move Lines Down",
+    code: Ci.nsIDOMKeyEvent.DOM_VK_DOWN,
+    ctrl: Services.appinfo.OS == "Darwin",
+    alt: true,
+  },
 ];
 
 var EXPORTED_SYMBOLS = ["SourceEditor"];
 
 /**
  * The SourceEditor object constructor. The SourceEditor component allows you to
  * provide users with an editor tailored to the specific needs of editing source
  * code, aimed primarily at web developers.
@@ -362,26 +375,35 @@ SourceEditor.prototype = {
       "redo": [this.redo, this],
       "tab": [this._doTab, this],
       "Unindent Lines": [this._doUnindentLines, this],
       "enter": [this._doEnter, this],
       "Find...": [this.ui.find, this.ui],
       "Find Next Occurrence": [this.ui.findNext, this.ui],
       "Find Previous Occurrence": [this.ui.findPrevious, this.ui],
       "Goto Line...": [this.ui.gotoLine, this.ui],
+      "Move Lines Down": [this._moveLines, this],
     };
 
     for (let name in actions) {
       let action = actions[name];
       this._view.setAction(name, action[0].bind(action[1]));
     }
 
+    this._view.setAction("Move Lines Up", this._moveLines.bind(this, true));
+
     let keys = (config.keys || []).concat(DEFAULT_KEYBINDINGS);
     keys.forEach(function(aKey) {
-      let binding = new KeyBinding(aKey.code, aKey.accel, aKey.shift, aKey.alt);
+      // In Orion mod1 refers to Cmd on Macs and Ctrl on Windows and Linux.
+      // So, if ctrl is in aKey we use it on Windows and Linux, otherwise
+      // we use aKey.accel for mod1.
+      let mod1 = Services.appinfo.OS != "Darwin" &&
+                 "ctrl" in aKey ? aKey.ctrl : aKey.accel;
+      let binding = new KeyBinding(aKey.code, mod1, aKey.shift, aKey.alt,
+                                  aKey.ctrl);
       this._view.setKeyBinding(binding, aKey.action);
 
       if (aKey.callback) {
         this._view.setAction(aKey.action, aKey.callback);
       }
     }, this);
 
     this._initEventTarget();
@@ -574,16 +596,88 @@ SourceEditor.prototype = {
     }
 
     this.setText(this.getLineDelimiter() + prefix, selection.start,
                  selection.end);
     return true;
   },
 
   /**
+   * Move lines upwards or downwards, relative to the current caret location.
+   *
+   * @private
+   * @param boolean aLineAbove
+   *        True if moving lines up, false to move lines down.
+   */
+  _moveLines: function SE__moveLines(aLineAbove)
+  {
+    if (this.readOnly) {
+      return false;
+    }
+
+    let model = this._model;
+    let selection = this.getSelection();
+    let firstLine = model.getLineAtOffset(selection.start);
+    if (firstLine == 0 && aLineAbove) {
+      return true;
+    }
+
+    let lastLine = model.getLineAtOffset(selection.end);
+    let firstLineStart = model.getLineStart(firstLine);
+    let lastLineStart = model.getLineStart(lastLine);
+    if (selection.start != selection.end && lastLineStart == selection.end) {
+      lastLine--;
+    }
+    if (!aLineAbove && (lastLine + 1) == this.getLineCount()) {
+      return true;
+    }
+
+    let lastLineEnd = model.getLineEnd(lastLine, true);
+    let text = this.getText(firstLineStart, lastLineEnd);
+
+    if (aLineAbove) {
+      let aboveLine = firstLine - 1;
+      let aboveLineStart = model.getLineStart(aboveLine);
+
+      this.startCompoundChange();
+      if (lastLine == (this.getLineCount() - 1)) {
+        let delimiterStart = model.getLineEnd(aboveLine);
+        let delimiterEnd = model.getLineEnd(aboveLine, true);
+        let lineDelimiter = this.getText(delimiterStart, delimiterEnd);
+        text += lineDelimiter;
+        this.setText("", firstLineStart - lineDelimiter.length, lastLineEnd);
+      } else {
+        this.setText("", firstLineStart, lastLineEnd);
+      }
+      this.setText(text, aboveLineStart, aboveLineStart);
+      this.endCompoundChange();
+      this.setSelection(aboveLineStart, aboveLineStart + text.length);
+    } else {
+      let belowLine = lastLine + 1;
+      let belowLineEnd = model.getLineEnd(belowLine, true);
+
+      let insertAt = belowLineEnd - lastLineEnd + firstLineStart;
+      let lineDelimiter = "";
+      if (belowLine == this.getLineCount() - 1) {
+        let delimiterStart = model.getLineEnd(lastLine);
+        lineDelimiter = this.getText(delimiterStart, lastLineEnd);
+        text = lineDelimiter + text.substr(0, text.length -
+                                              lineDelimiter.length);
+      }
+      this.startCompoundChange();
+      this.setText("", firstLineStart, lastLineEnd);
+      this.setText(text, insertAt, insertAt);
+      this.endCompoundChange();
+      this.setSelection(insertAt + lineDelimiter.length,
+                        insertAt + text.length);
+    }
+    return true;
+  },
+
+  /**
    * The Orion Selection event handler. The current caret line is
    * highlighted and for Linux users the selected text is copied into the X11
    * PRIMARY buffer.
    *
    * @private
    * @param object aEvent
    *        The Orion Selection event object.
    */
--- a/browser/devtools/sourceeditor/source-editor.jsm
+++ b/browser/devtools/sourceeditor/source-editor.jsm
@@ -187,16 +187,17 @@ SourceEditor.DEFAULTS = {
   highlightCurrentLine: true,
 
   /**
    * An array of objects that allows you to define custom editor keyboard
    * bindings. Each object can have:
    *   - action - name of the editor action to invoke.
    *   - code - keyCode for the shortcut.
    *   - accel - boolean for the Accel key (Cmd on Macs, Ctrl on Linux/Windows).
+   *   - ctrl - boolean for the Control key
    *   - shift - boolean for the Shift key.
    *   - alt - boolean for the Alt key.
    *   - callback - optional function to invoke, if the action is not predefined
    *   in the editor.
    * @type array
    */
   keys: null,
 
--- a/browser/devtools/sourceeditor/test/Makefile.in
+++ b/browser/devtools/sourceeditor/test/Makefile.in
@@ -53,13 +53,14 @@ include $(topsrcdir)/config/rules.mk
 		browser_bug684546_reset_undo.js \
 		browser_bug695035_middle_click_paste.js \
 		browser_bug687160_line_api.js \
 		browser_bug650345_find.js \
 		browser_bug703692_focus_blur.js \
 		browser_bug725388_mouse_events.js \
 		browser_bug707987_debugger_breakpoints.js \
 		browser_bug712982_line_ruler_click.js \
+		browser_bug725618_moveLines_shortcut.js \
 		browser_bug700893_dirty_state.js \
 		head.js \
 
 libs:: $(_BROWSER_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/browser/devtools/sourceeditor/test/browser_bug725618_moveLines_shortcut.js
@@ -0,0 +1,117 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+let tempScope = {};
+Cu.import("resource:///modules/source-editor.jsm", tempScope);
+let SourceEditor = tempScope.SourceEditor;
+
+let editor;
+let testWin;
+
+function test()
+{
+  waitForExplicitFinish();
+
+  const windowUrl = "data:application/vnd.mozilla.xul+xml,<?xml version='1.0'?>" +
+    "<window xmlns='http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul'" +
+    " title='test for bug 725618 - moveLines shortcut' width='300' height='500'>" +
+    "<box flex='1'/></window>";
+  const windowFeatures = "chrome,titlebar,toolbar,centerscreen,resizable,dialog=no";
+
+  testWin = Services.ww.openWindow(null, windowUrl, "_blank", windowFeatures, null);
+  testWin.addEventListener("load", function onWindowLoad() {
+    testWin.removeEventListener("load", onWindowLoad, false);
+    waitForFocus(initEditor, testWin);
+  }, false);
+}
+
+function initEditor()
+{
+  let box = testWin.document.querySelector("box");
+
+  let text = "target\nfoo\nbar"
+  let config = {
+    initialText: text,
+  };
+
+  editor = new SourceEditor();
+  editor.init(box, config, editorLoaded);
+}
+
+function editorLoaded()
+{
+  editor.focus();
+
+  editor.setCaretOffset(0);
+
+  let modifiers = {altKey: true, ctrlKey: Services.appinfo.OS == "Darwin"};
+
+  EventUtils.synthesizeKey("VK_DOWN", modifiers, testWin);
+  is(editor.getText(), "foo\ntarget\nbar", "Move lines down works");
+  is(editor.getSelectedText(), "target\n", "selection is correct");
+
+  EventUtils.synthesizeKey("VK_DOWN", modifiers, testWin);
+  is(editor.getText(), "foo\nbar\ntarget", "Move lines down works");
+  is(editor.getSelectedText(), "target", "selection is correct");
+
+  EventUtils.synthesizeKey("VK_DOWN", modifiers, testWin);
+  is(editor.getText(), "foo\nbar\ntarget", "Check for bottom of editor works");
+  is(editor.getSelectedText(), "target", "selection is correct");
+
+  EventUtils.synthesizeKey("VK_UP", modifiers, testWin);
+  is(editor.getText(), "foo\ntarget\nbar", "Move lines up works");
+  is(editor.getSelectedText(), "target\n", "selection is correct");
+
+  EventUtils.synthesizeKey("VK_UP", modifiers, testWin);
+  is(editor.getText(), "target\nfoo\nbar", "Move lines up works");
+  is(editor.getSelectedText(), "target\n", "selection is correct");
+
+  EventUtils.synthesizeKey("VK_UP", modifiers, testWin);
+  is(editor.getText(), "target\nfoo\nbar", "Check for top of editor works");
+  is(editor.getSelectedText(), "target\n", "selection is correct");
+
+  editor.setSelection(0, 10);
+  info("text within selection =" + editor.getSelectedText());
+
+  EventUtils.synthesizeKey("VK_DOWN", modifiers, testWin);
+  is(editor.getText(), "bar\ntarget\nfoo", "Multiple line move down works");
+  is(editor.getSelectedText(), "target\nfoo", "selection is correct");
+
+  EventUtils.synthesizeKey("VK_DOWN", modifiers, testWin);
+  is(editor.getText(), "bar\ntarget\nfoo",
+      "Check for bottom of editor works with multiple line selection");
+  is(editor.getSelectedText(), "target\nfoo", "selection is correct");
+
+  EventUtils.synthesizeKey("VK_UP", modifiers, testWin);
+  is(editor.getText(), "target\nfoo\nbar", "Multiple line move up works");
+  is(editor.getSelectedText(), "target\nfoo\n", "selection is correct");
+
+  EventUtils.synthesizeKey("VK_UP", modifiers, testWin);
+  is(editor.getText(), "target\nfoo\nbar",
+      "Check for top of editor works with multiple line selection");
+  is(editor.getSelectedText(), "target\nfoo\n", "selection is correct");
+
+  editor.readOnly = true;
+
+  editor.setCaretOffset(0);
+
+  EventUtils.synthesizeKey("VK_UP", modifiers, testWin);
+  is(editor.getText(), "target\nfoo\nbar",
+     "Check for readOnly mode works with move lines up");
+
+  EventUtils.synthesizeKey("VK_DOWN", modifiers, testWin);
+  is(editor.getText(), "target\nfoo\nbar",
+     "Check for readOnly mode works with move lines down");
+
+  finish();
+}
+
+registerCleanupFunction(function()
+{
+  editor.destroy();
+  testWin.close();
+  testWin = editor = null;
+});
--- a/browser/devtools/styleinspector/CssLogic.jsm
+++ b/browser/devtools/styleinspector/CssLogic.jsm
@@ -1193,36 +1193,51 @@ CssSheet.prototype = {
  * argument must point to the element.
  * @constructor
  */
 function CssRule(aCssSheet, aDomRule, aElement)
 {
   this._cssSheet = aCssSheet;
   this._domRule = aDomRule;
 
+  let parentRule = aDomRule.parentRule;
+  if (parentRule && parentRule.type == Ci.nsIDOMCSSRule.MEDIA_RULE) {
+    this.mediaText = parentRule.media.mediaText;
+  }
+
   if (this._cssSheet) {
     // parse _domRule.selectorText on call to this.selectors
     this._selectors = null;
     this.line = this._cssSheet._cssLogic.domUtils.getRuleLine(this._domRule);
     this.source = this._cssSheet.shortSource + ":" + this.line;
+    if (this.mediaText) {
+      this.source += " @media " + this.mediaText;
+    }
     this.href = this._cssSheet.href;
     this.contentRule = this._cssSheet.contentSheet;
   } else if (aElement) {
     this._selectors = [ new CssSelector(this, "@element.style") ];
     this.line = -1;
     this.source = CssLogic.l10n("rule.sourceElement");
     this.href = "#";
     this.contentRule = true;
     this.sourceElement = aElement;
   }
 }
 
 CssRule.prototype = {
   _passId: null,
 
+  mediaText: "",
+
+  get isMediaRule()
+  {
+    return !!this.mediaText;
+  },
+
   /**
    * Check if the parent stylesheet is allowed by the CssLogic.sourceFilter.
    *
    * @return {boolean} true if the parent stylesheet is allowed by the current
    * sourceFilter, or false otherwise.
    */
   get sheetAllowed()
   {
--- a/browser/devtools/styleinspector/CssRuleView.jsm
+++ b/browser/devtools/styleinspector/CssRuleView.jsm
@@ -351,20 +351,30 @@ ElementStyle.prototype = {
  */
 function Rule(aElementStyle, aOptions)
 {
   this.elementStyle = aElementStyle;
   this.domRule = aOptions.domRule || null;
   this.style = aOptions.style || this.domRule.style;
   this.selectorText = aOptions.selectorText || this.domRule.selectorText;
   this.inherited = aOptions.inherited || null;
+
+  if (this.domRule) {
+    let parentRule = this.domRule.parentRule;
+    if (parentRule && parentRule.type == Ci.nsIDOMCSSRule.MEDIA_RULE) {
+      this.mediaText = parentRule.media.mediaText;
+    }
+  }
+
   this._getTextProperties();
 }
 
 Rule.prototype = {
+  mediaText: "",
+
   get title()
   {
     if (this._title) {
       return this._title;
     }
     this._title = CssLogic.shortSource(this.sheet);
     if (this.domRule) {
       this._title += ":" + this.ruleLine;
@@ -375,17 +385,17 @@ Rule.prototype = {
       if (this.inherited.id) {
         eltText += "#" + this.inherited.id;
       }
       let args = [eltText, this._title];
       this._title = CssLogic._strings.formatStringFromName("rule.inheritedSource",
                                                            args, args.length);
     }
 
-    return this._title;
+    return this._title + (this.mediaText ? " @media " + this.mediaText : "");
   },
 
   /**
    * The rule's stylesheet.
    */
   get sheet()
   {
     return this.domRule ? this.domRule.parentStyleSheet : null;
@@ -717,18 +727,36 @@ CssRuleView.prototype = {
     }
 
     this._elementStyle = new ElementStyle(aElement, this.store);
     this._elementStyle.onChanged = function() {
       this._changed();
     }.bind(this);
 
     this._createEditors();
+
+    // When creating a new property, we fake the normal property
+    // editor behavior (focusing a property's value after entering its
+    // name) by responding to the name's blur event, creating the
+    // value editor, and grabbing focus to the value editor.  But if
+    // focus has already moved to another document, we won't be able
+    // to move focus to the new editor.
+    // Create a focusable item at the end of the editors to catch these
+    // cases.
+    this._focusBackstop = createChild(this.element, "div", {
+      tabindex: 0,
+    });
+    this._backstopHandler = function() {
+      // If this item is actually focused long enough to get the focus
+      // event, allow focus to move on out of this document.
+      moveFocus(this.doc.defaultView, FOCUS_FORWARD);
+    }.bind(this);
+    this._focusBackstop.addEventListener("focus", this._backstopHandler, false);
   },
-  
+
   /**
    * Update the rules for the currently highlighted element.
    */
   nodeChanged: function CssRuleView_nodeChanged()
   {
     this._clearRules();
     this._elementStyle.populate();
     this._createEditors();
@@ -747,16 +775,22 @@ CssRuleView.prototype = {
   /**
    * Clear the rule view.
    */
   clear: function CssRuleView_clear()
   {
     this._clearRules();
     this._viewedElement = null;
     this._elementStyle = null;
+
+    if (this._focusBackstop) {
+      this._focusBackstop.removeEventListener("focus", this._backstopHandler, false);
+      this._backstopHandler = null;
+      this._focusBackstop = null;
+    }
   },
 
   /**
    * Called when the user has made changes to the ElementStyle.
    * Emits an event that clients can listen to.
    */
   _changed: function CssRuleView_changed()
   {
@@ -830,17 +864,16 @@ RuleEditor.prototype = {
 
     let selectors = createChild(header, "span", {
       class: "ruleview-selector",
       textContent: this.rule.selectorText
     });
 
     this.openBrace = createChild(header, "span", {
       class: "ruleview-ruleopen",
-      tabindex: "0",
       textContent: " {"
     });
 
     this.openBrace.addEventListener("click", function() {
       this.newProperty();
     }.bind(this), true);
 
     this.propertyList = createChild(code, "ul", {
--- a/browser/devtools/styleinspector/test/Makefile.in
+++ b/browser/devtools/styleinspector/test/Makefile.in
@@ -55,29 +55,33 @@ include $(topsrcdir)/config/rules.mk
   browser_bug_692400_element_style.js \
   browser_csslogic_inherited.js \
   browser_ruleview_editor.js \
   browser_ruleview_editor_changedvalues.js \
   browser_ruleview_inherit.js \
   browser_ruleview_manipulation.js \
   browser_ruleview_override.js \
   browser_ruleview_ui.js \
+  browser_ruleview_focus.js \
   browser_bug705707_is_content_stylesheet.js \
+  browser_bug722196_property_view_media_queries.js \
+  browser_bug722196_rule_view_media_queries.js \
   browser_bug_592743_specificity.js \
   head.js \
   $(NULL)
 
 _BROWSER_TEST_PAGES = \
   browser_bug683672.html \
   browser_bug705707_is_content_stylesheet.html \
   browser_bug705707_is_content_stylesheet_imported.css \
   browser_bug705707_is_content_stylesheet_imported2.css \
   browser_bug705707_is_content_stylesheet_linked.css \
   browser_bug705707_is_content_stylesheet_script.css \
   browser_bug705707_is_content_stylesheet.xul \
   browser_bug705707_is_content_stylesheet_xul.css \
+  browser_bug722196_identify_media_queries.html \
   $(NULL)
 
 libs:: $(_BROWSER_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
 
 libs:: $(_BROWSER_TEST_PAGES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleinspector/test/browser_bug722196_identify_media_queries.html
@@ -0,0 +1,24 @@
+<html>
+<head>
+  <title>test</title>
+  <script type="application/javascript;version=1.7">
+
+  </script>
+  <style>
+    div {
+      width: 1000px;
+      height: 100px;
+      background-color: #f00;
+    }
+
+    @media screen and (min-width: 1px) {
+      div {
+        width: 200px;
+      }
+    }
+  </style>
+</head>
+<body>
+<div></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleinspector/test/browser_bug722196_property_view_media_queries.js
@@ -0,0 +1,68 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that we correctly display appropriate media query titles in the
+// property view.
+
+let doc;
+let stylePanel;
+
+const TEST_URI = "http://example.com/browser/browser/devtools/styleinspector/" +
+  "test/browser_bug722196_identify_media_queries.html";
+
+function test()
+{
+  waitForExplicitFinish();
+  addTab(TEST_URI);
+  browser.addEventListener("load", docLoaded, true);
+}
+
+function docLoaded()
+{
+  browser.removeEventListener("load", docLoaded, true);
+  doc = content.document;
+  stylePanel = new StyleInspector(window);
+  Services.obs.addObserver(checkSheets, "StyleInspector-opened", false);
+  stylePanel.createPanel(false, function() {
+    stylePanel.open(doc.body);
+  });
+}
+
+function checkSheets()
+{
+  Services.obs.removeObserver(checkSheets, "StyleInspector-opened", false);
+
+  ok(stylePanel.isOpen(), "style inspector is open");
+
+  var div = doc.querySelector("div");
+  ok(div, "captain, we have the div");
+
+  stylePanel.selectNode(div);
+
+  let cssLogic = stylePanel.cssLogic;
+  cssLogic.processMatchedSelectors();
+
+  let _strings = Services.strings
+    .createBundle("chrome://browser/locale/devtools/styleinspector.properties");
+
+  let inline = _strings.GetStringFromName("rule.sourceInline");
+
+  let source1 = inline + ":8";
+  let source2 = inline + ":15 @media screen and (min-width: 1px)";
+  is(cssLogic._matchedRules[0][0].source, source1,
+    "rule.source gives correct output for rule 1");
+  is(cssLogic._matchedRules[1][0].source, source2,
+    "rule.source gives correct output for rule 2");
+
+  Services.obs.addObserver(finishUp, "StyleInspector-closed", false);
+  stylePanel.close();
+}
+
+function finishUp()
+{
+  Services.obs.removeObserver(finishUp, "StyleInspector-closed", false);
+  doc = null;
+  gBrowser.removeCurrentTab();
+  finish();
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleinspector/test/browser_bug722196_rule_view_media_queries.js
@@ -0,0 +1,55 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that we correctly display appropriate media query titles in the
+// rule view.
+
+let tempScope = {};
+Cu.import("resource:///modules/devtools/CssRuleView.jsm", tempScope);
+let _ElementStyle = tempScope._ElementStyle;
+let doc;
+
+const TEST_URI = "http://example.com/browser/browser/devtools/styleinspector/" +
+  "test/browser_bug722196_identify_media_queries.html";
+
+function test()
+{
+  waitForExplicitFinish();
+  addTab(TEST_URI);
+  browser.addEventListener("load", docLoaded, true);
+}
+
+function docLoaded()
+{
+  browser.removeEventListener("load", docLoaded, true);
+  doc = content.document;
+  checkSheets();
+}
+
+function checkSheets()
+{
+  var div = doc.querySelector("div");
+  ok(div, "captain, we have the div");
+
+  let elementStyle = new _ElementStyle(div);
+  is(elementStyle.rules.length, 3, "Should have 3 rules.");
+
+  let _strings = Services.strings
+    .createBundle("chrome://browser/locale/devtools/styleinspector.properties");
+
+  let inline = _strings.GetStringFromName("rule.sourceInline");
+
+  is(elementStyle.rules[0].title, inline, "check rule 0 title");
+  is(elementStyle.rules[1].title, inline +
+    ":15 @media screen and (min-width: 1px)", "check rule 1 title");
+  is(elementStyle.rules[2].title, inline + ":8", "check rule 2 title");
+  finishUp();
+}
+
+function finishUp()
+{
+  doc = null;
+  gBrowser.removeCurrentTab();
+  finish();
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleinspector/test/browser_ruleview_focus.js
@@ -0,0 +1,106 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test that focus doesn't leave the style editor when adding a property
+// (bug 719916)
+
+let doc;
+let stylePanel;
+
+function waitForRuleView(aCallback)
+{
+  if (InspectorUI.ruleView) {
+    aCallback();
+    return;
+  }
+
+  let ruleViewFrame = InspectorUI.getToolIframe(InspectorUI.ruleViewObject);
+  ruleViewFrame.addEventListener("load", function(evt) {
+    ruleViewFrame.removeEventListener(evt.type, arguments.callee, true);
+    executeSoon(function() {
+      aCallback();
+    });
+  }, true);
+}
+
+function waitForEditorFocus(aParent, aCallback)
+{
+  aParent.addEventListener("focus", function onFocus(evt) {
+    if (evt.target.inplaceEditor) {
+      aParent.removeEventListener("focus", onFocus, true);
+      let editor = evt.target.inplaceEditor;
+      executeSoon(function() {
+        aCallback(editor);
+      });
+    }
+  }, true);
+}
+
+function openRuleView()
+{
+  Services.obs.addObserver(function onOpened() {
+    Services.obs.removeObserver(onOpened,
+    InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
+
+    // Highlight a node.
+    let node = content.document.getElementsByTagName("h1")[0];
+    InspectorUI.inspectNode(node);
+    InspectorUI.stopInspecting();
+
+    // Open the rule view sidebar.
+    waitForRuleView(testFocus);
+
+    InspectorUI.showSidebar();
+    InspectorUI.ruleButton.click();
+
+    testFocus();
+  }, InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
+  InspectorUI.openInspectorUI();
+}
+
+function testFocus()
+{
+  let ruleViewFrame = InspectorUI.getToolIframe(InspectorUI.ruleViewObject);
+  let brace = ruleViewFrame.contentDocument.querySelectorAll(".ruleview-ruleclose")[0];
+  waitForEditorFocus(brace.parentNode, function onNewElement(aEditor) {
+    aEditor.input.value = "color";
+    waitForEditorFocus(brace.parentNode, function onEditingValue(aEditor) {
+      // If we actually get this focus we're ok.
+      ok(true, "We got focus.");
+      aEditor.input.value = "green";
+
+      // If we've retained focus, pressing return will start a new editor.
+      // If not, we'll wait here until we time out.
+      waitForEditorFocus(brace.parentNode, function onNewEditor(aEditor) {
+        aEditor.input.blur();
+        finishTest();
+      });
+      EventUtils.sendKey("return");
+    });
+    EventUtils.sendKey("return");
+  });
+
+  brace.focus();
+}
+
+function finishUp()
+{
+  doc = stylePanel = null;
+  gBrowser.removeCurrentTab();
+  finish();
+}
+
+function test()
+{
+  waitForExplicitFinish();
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.selectedBrowser.addEventListener("load", function(evt) {
+    gBrowser.selectedBrowser.removeEventListener(evt.type, arguments.callee, true);
+    doc = content.document;
+    doc.title = "Rule View Test";
+    waitForFocus(openRuleView, content);
+  }, true);
+
+  content.location = "data:text/html,<h1>Some header text</h1>";
+}
--- a/browser/devtools/webconsole/GcliCommands.jsm
+++ b/browser/devtools/webconsole/GcliCommands.jsm
@@ -15,16 +15,17 @@
  *
  * The Initial Developer of the Original Code is
  * The Mozilla Foundation.
  * Portions created by the Initial Developer are Copyright (C) 2011
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Joe Walker <jwalker@mozilla.com> (original author)
+ *   Mihai Sucan <mihai.sucan@gmail.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
@@ -155,18 +156,16 @@ gcli.addCommand({
    ],
    exec: function(args, context) {
      let hud = HUDService.getHudReferenceById(context.environment.hudId);
      let StyleEditor = hud.gcliterm.document.defaultView.StyleEditor;
      StyleEditor.openChrome(args.resource.element, args.line);
    }
 });
 
-let breakpoints = [];
-
 /**
  * 'break' command
  */
 gcli.addCommand({
   name: "break",
   description: gcli.lookup("breakDesc"),
   manual: gcli.lookup("breakManual")
 });
@@ -175,27 +174,35 @@ gcli.addCommand({
 /**
  * 'break list' command
  */
 gcli.addCommand({
   name: "break list",
   description: gcli.lookup("breaklistDesc"),
   returnType: "html",
   exec: function(args, context) {
-    if (breakpoints.length === 0) {
+    let win = HUDService.currentContext();
+    let dbg = win.DebuggerUI.getDebugger(win.gBrowser.selectedTab);
+    if (!dbg) {
+      return gcli.lookup("breakaddDebuggerStopped");
+    }
+    let breakpoints = dbg.breakpoints;
+
+    if (Object.keys(breakpoints).length === 0) {
       return gcli.lookup("breaklistNone");
     }
 
     let reply = gcli.lookup("breaklistIntro");
     reply += "<ol>";
-    breakpoints.forEach(function(breakpoint) {
+    for each (let breakpoint in breakpoints) {
       let text = gcli.lookupFormat("breaklistLineEntry",
-                                   [breakpoint.file, breakpoint.line]);
+                                   [breakpoint.location.url,
+                                    breakpoint.location.line]);
       reply += "<li>" + text + "</li>";
-    });
+    };
     reply += "</ol>";
     return reply;
   }
 });
 
 
 /**
  * 'break add' command
@@ -243,24 +250,21 @@ gcli.addCommand({
     args.type = "line";
     let win = HUDService.currentContext();
     let dbg = win.DebuggerUI.getDebugger(win.gBrowser.selectedTab);
     if (!dbg) {
       return gcli.lookup("breakaddDebuggerStopped");
     }
     var promise = context.createPromise();
     let position = { url: args.file, line: args.line };
-    dbg.activeThread.setBreakpoint(position, function(aResponse, aBpClient) {
-      if (aResponse.error) {
-        promise.resolve(gcli.lookupFormat("breakaddFailed",
-                        [ aResponse.error ]));
+    dbg.addBreakpoint(position, function(aBreakpoint, aError) {
+      if (aError) {
+        promise.resolve(gcli.lookupFormat("breakaddFailed", [aError]));
         return;
       }
-      args.client = aBpClient;
-      breakpoints.push(args);
       promise.resolve(gcli.lookup("breakaddAdded"));
     });
     return promise;
   }
 });
 
 
 /**
@@ -270,28 +274,46 @@ gcli.addCommand({
   name: "break del",
   description: gcli.lookup("breakdelDesc"),
   params: [
     {
       name: "breakid",
       type: {
         name: "number",
         min: 0,
-        max: function() { return breakpoints.length - 1; }
+        max: function() {
+          let win = HUDService.currentContext();
+          let dbg = win.DebuggerUI.getDebugger(win.gBrowser.selectedTab);
+          if (!dbg) {
+            return gcli.lookup("breakaddDebuggerStopped");
+          }
+          return Object.keys(dbg.breakpoints).length - 1;
+        },
       },
       description: gcli.lookup("breakdelBreakidDesc")
     }
   ],
   returnType: "html",
   exec: function(args, context) {
-    let breakpoint = breakpoints.splice(args.breakid, 1)[0];
-    var promise = context.createPromise();
+    let win = HUDService.currentContext();
+    let dbg = win.DebuggerUI.getDebugger(win.gBrowser.selectedTab);
+    if (!dbg) {
+      return gcli.lookup("breakaddDebuggerStopped");
+    }
+
+    let breakpoints = dbg.breakpoints;
+    let id = Object.keys(dbg.breakpoints)[args.breakid];
+    if (!id || !(id in breakpoints)) {
+      return gcli.lookup("breakNotFound");
+    }
+
+    let promise = context.createPromise();
     try {
-      breakpoint.client.remove(function(aResponse) {
-                                 promise.resolve(gcli.lookup("breakdelRemoved"));
-                               });
+      dbg.removeBreakpoint(breakpoints[id], function() {
+        promise.resolve(gcli.lookup("breakdelRemoved"));
+      });
     } catch (ex) {
       // If the debugger has been closed already, don't scare the user.
       promise.resolve(gcli.lookup("breakdelRemoved"));
     }
     return promise;
   }
 });
--- a/browser/locales/en-US/chrome/browser/devtools/gclicommands.properties
+++ b/browser/locales/en-US/chrome/browser/devtools/gclicommands.properties
@@ -250,16 +250,20 @@ breakdelDesc=Remove a breakpoint
 # LOCALIZATION NOTE (breakdelBreakidDesc) A very short string used to describe
 # the function of the index parameter in the 'break del' command.
 breakdelBreakidDesc=Index of breakpoint
 
 # LOCALIZATION NOTE (breakdelRemoved) Used in the output of the 'break del'
 # command to explain that a breakpoint was removed.
 breakdelRemoved=Breakpoint removed
 
+# LOCALIZATION NOTE (breakNotFound) Used in the output of the 'break del'
+# command to explain that the breakpoint was not found.
+breakNotFound=Breakpoint was not found
+
 # LOCALIZATION NOTE (consolecloseDesc) A very short description of the
 # 'console close' command. This string is designed to be shown in a menu
 # alongside the command name, which is why it should be as short as possible.
 consolecloseDesc=Close the console
 
 # LOCALIZATION NOTE (editDesc) A very short description of the 'edit'
 # command. See editManual for a fuller description of what it does. This
 # string is designed to be shown in a menu alongside the command name, which
--- a/browser/makefiles.sh
+++ b/browser/makefiles.sh
@@ -134,16 +134,17 @@ if [ "$ENABLE_TESTS" ]; then
     browser/components/certerror/test/Makefile
     browser/components/dirprovider/tests/Makefile
     browser/components/preferences/tests/Makefile
     browser/components/search/test/Makefile
     browser/components/sessionstore/test/Makefile
     browser/components/shell/test/Makefile
     browser/components/feeds/test/Makefile
     browser/components/feeds/test/chrome/Makefile
+    browser/components/migration/tests/Makefile
     browser/components/places/tests/Makefile
     browser/components/places/tests/chrome/Makefile
     browser/components/places/tests/browser/Makefile
     browser/components/privatebrowsing/test/Makefile
     browser/components/privatebrowsing/test/browser/Makefile
     browser/components/tabview/test/Makefile
     browser/components/test/Makefile
     browser/components/thumbnails/test/Makefile
--- a/browser/modules/NewTabUtils.jsm
+++ b/browser/modules/NewTabUtils.jsm
@@ -116,18 +116,16 @@ let Storage = {
       // Reset to normal DOM storage.
       this.currentStorage = this.domStorage;
 
       // When switching back from private browsing we need to reset the
       // grid and re-read its values from the underlying storage. We don't
       // want any data from private browsing to show up.
       PinnedLinks.resetCache();
       BlockedLinks.resetCache();
-
-      Pages.update();
     }
   },
 
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
                                          Ci.nsISupportsWeakReference])
 };
 
 /**
@@ -183,37 +181,27 @@ PrivateBrowsingStorage.prototype = {
  */
 let AllPages = {
   /**
    * The array containing all active pages.
    */
   _pages: [],
 
   /**
-   * Tells whether we already added a preference observer.
-   */
-  _observing: false,
-
-  /**
    * Cached value that tells whether the New Tab Page feature is enabled.
    */
   _enabled: null,
 
   /**
    * Adds a page to the internal list of pages.
    * @param aPage The page to register.
    */
   register: function AllPages_register(aPage) {
     this._pages.push(aPage);
-
-    // Add the preference observer if we haven't already.
-    if (!this._observing) {
-      this._observing = true;
-      Services.prefs.addObserver(PREF_NEWTAB_ENABLED, this, true);
-    }
+    this._addObserver();
   },
 
   /**
    * Removes a page from the internal list of pages.
    * @param aPage The page to unregister.
    */
   unregister: function AllPages_unregister(aPage) {
     let index = this._pages.indexOf(aPage);
@@ -234,16 +222,24 @@ let AllPages = {
    * Enables or disables the 'New Tab Page' feature.
    */
   set enabled(aEnabled) {
     if (this.enabled != aEnabled)
       Services.prefs.setBoolPref(PREF_NEWTAB_ENABLED, !!aEnabled);
   },
 
   /**
+   * Returns the number of registered New Tab Pages (i.e. the number of open
+   * about:newtab instances).
+   */
+  get length() {
+    return this._pages.length;
+  },
+
+  /**
    * Updates all currently active pages but the given one.
    * @param aExceptPage The page to exclude from updating.
    */
   update: function AllPages_update(aExceptPage) {
     this._pages.forEach(function (aPage) {
       if (aExceptPage != aPage)
         aPage.update();
     });
@@ -259,16 +255,25 @@ let AllPages = {
 
     let args = Array.slice(arguments);
 
     this._pages.forEach(function (aPage) {
       aPage.observe.apply(aPage, args);
     }, this);
   },
 
+  /**
+   * Adds a preference observer and turns itself into a no-op after the first
+   * invokation.
+   */
+  _addObserver: function AllPages_addObserver() {
+    Services.prefs.addObserver(PREF_NEWTAB_ENABLED, this, true);
+    this._addObserver = function () {};
+  },
+
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
                                          Ci.nsISupportsWeakReference])
 };
 
 /**
  * Singleton that keeps track of all pinned links and their positions in the
  * grid.
  */
@@ -507,16 +512,18 @@ let Links = {
 
     if (this._links && !aForce) {
       executeCallbacks();
     } else {
       this._provider.getLinks(function (aLinks) {
         this._links = aLinks;
         executeCallbacks();
       }.bind(this));
+
+      this._addObserver();
     }
   },
 
   /**
    * Gets the current set of links contained in the grid.
    * @return The links in the grid.
    */
   getLinks: function Links_getLinks() {
@@ -539,17 +546,42 @@ let Links = {
     return pinnedLinks;
   },
 
   /**
    * Resets the links cache.
    */
   resetCache: function Links_resetCache() {
     this._links = [];
-  }
+  },
+
+  /**
+   * Implements the nsIObserver interface to get notified about browser history
+   * sanitization.
+   */
+  observe: function Links_observe(aSubject, aTopic, aData) {
+    // Make sure to update open about:newtab instances. If there are no opened
+    // pages we can just wait for the next new tab to populate the cache again.
+    if (AllPages.length && AllPages.enabled)
+      this.populateCache(function () { AllPages.update() }, true);
+    else
+      this._links = null;
+  },
+
+  /**
+   * Adds a sanitization observer and turns itself into a no-op after the first
+   * invokation.
+   */
+  _addObserver: function Links_addObserver() {
+    Services.obs.addObserver(this, "browser:purge-session-history", true);
+    this._addObserver = function () {};
+  },
+
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
+                                         Ci.nsISupportsWeakReference])
 };
 
 /**
  * Singleton that provides the public API of this JSM.
  */
 let NewTabUtils = {
   /**
    * Resets the NewTabUtils module, its links and its storage.
--- a/browser/themes/gnomestripe/aboutCertError.css
+++ b/browser/themes/gnomestripe/aboutCertError.css
@@ -81,26 +81,29 @@ body[dir="rtl"] #errorPageContainer {
 #errorTitle {
   -moz-margin-start: 80px;
 }
 
 #errorLongContent {
   -moz-margin-start: 80px;
 }
 
-#technicalContent > h2, #expertContent > h2 {
-  background : url("chrome://browser/skin/section_expanded.png") left 0 no-repeat;
-}
-
-body[dir="rtl"] #technicalContent > h2,
-body[dir="rtl"] #expertContent > h2 {
-  background-position: right 0;
+.expander > button {
+  -moz-padding-start: 20px;
+  -moz-margin-start: -20px;
+  background: url("chrome://browser/skin/aboutCertError_sectionExpanded.png") left center no-repeat;
+  border: none;
+  font: inherit;
+  color: inherit;
+  cursor: pointer;
 }
 
-#technicalContent[collapsed] > h2,
-#expertContent[collapsed] > h2{
-  background-image: url("chrome://browser/skin/section_collapsed.png");
+body[dir="rtl"] .expander > button {
+  background-position: right center;
 }
 
-body[dir="rtl"] #technicalContent[collapsed] > h2,
-body[dir="rtl"] #expertContent[collapsed] > h2 {
-  background-image: url("chrome://browser/skin/section_collapsed-rtl.png");
+.expander[collapsed] > button {
+  background-image: url("chrome://browser/skin/aboutCertError_sectionCollapsed.png");
 }
+
+body[dir="rtl"] .expander[collapsed] > button {
+  background-image: url("chrome://browser/skin/aboutCertError_sectionCollapsed-rtl.png");
+}
rename from browser/themes/gnomestripe/section_collapsed-rtl.png
rename to browser/themes/gnomestripe/aboutCertError_sectionCollapsed-rtl.png
rename from browser/themes/gnomestripe/section_collapsed.png
rename to browser/themes/gnomestripe/aboutCertError_sectionCollapsed.png
rename from browser/themes/gnomestripe/section_expanded.png
rename to browser/themes/gnomestripe/aboutCertError_sectionExpanded.png
--- a/browser/themes/gnomestripe/browser.css
+++ b/browser/themes/gnomestripe/browser.css
@@ -194,17 +194,17 @@ menuitem.bookmark-item {
 }
 
 /* Bookmarks toolbar */
 #PlacesToolbarDropIndicator {
   list-style-image: url(chrome://browser/skin/places/toolbarDropMarker.png);
 }
 
 /* Bookmark items */
-.bookmark-item:not([container])  {
+.bookmark-item {
   list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png");
 }
 
 .bookmark-item[container] {
   list-style-image: url("moz-icon://stock/gtk-directory?size=menu");
 }
 
 .bookmark-item[container][livemark] { 
--- a/browser/themes/gnomestripe/devtools/csshtmltree.css
+++ b/browser/themes/gnomestripe/devtools/csshtmltree.css
@@ -141,16 +141,17 @@
   cursor: pointer;
 }
 
 /* This rule is necessary because Templater.jsm breaks LTR TDs in RTL docs */
 .rule-text {
   direction: ltr;
   padding: 0;
   -moz-padding-start: 20px;
+  vertical-align: text-bottom;
 }
 
 .bestmatch {
   color: black;
 }
 .matched {
   text-decoration: line-through;
 }
deleted file mode 100644
--- a/browser/themes/gnomestripe/fullscreen-video.css
+++ /dev/null
@@ -1,8 +0,0 @@
-#close {
-  position: absolute;
-  top: 0;
-  right: 0;
-  width: 32px;
-  height: 32px;
-  background: url(KUI-close.png) center center no-repeat;
-}
--- a/browser/themes/gnomestripe/jar.mn
+++ b/browser/themes/gnomestripe/jar.mn
@@ -1,40 +1,39 @@
 browser.jar:
 % skin browser classic/1.0 %skin/classic/browser/
 % override chrome://global/skin/icons/warning-16.png moz-icon://stock/gtk-dialog-warning?size=menu
   skin/classic/browser/sanitizeDialog.css             (sanitizeDialog.css)
 * skin/classic/browser/aboutPrivateBrowsing.css             (aboutPrivateBrowsing.css)
 * skin/classic/browser/aboutSessionRestore.css        (aboutSessionRestore.css)
   skin/classic/browser/aboutSessionRestore-window-icon.png
-  skin/classic/browser/aboutCertError.css             (aboutCertError.css)
+  skin/classic/browser/aboutCertError.css
+  skin/classic/browser/aboutCertError_sectionCollapsed.png
+  skin/classic/browser/aboutCertError_sectionCollapsed-rtl.png
+  skin/classic/browser/aboutCertError_sectionExpanded.png
 #ifdef MOZ_SERVICES_SYNC
   skin/classic/browser/aboutSyncTabs.css
 #endif
   skin/classic/browser/actionicon-tab.png
 * skin/classic/browser/browser.css                    (browser.css)
 * skin/classic/browser/engineManager.css              (engineManager.css)
-  skin/classic/browser/fullscreen-video.css
   skin/classic/browser/Geolocation-16.png
   skin/classic/browser/Geolocation-64.png
   skin/classic/browser/Go-arrow.png
   skin/classic/browser/identity.png
   skin/classic/browser/Info.png
   skin/classic/browser/KUI-close.png
   skin/classic/browser/monitor.png
   skin/classic/browser/monitor_16-10.png
 * skin/classic/browser/pageInfo.css
   skin/classic/browser/pageInfo.png
   skin/classic/browser/page-livemarks.png
   skin/classic/browser/Privacy-16.png
   skin/classic/browser/Privacy-48.png
   skin/classic/browser/searchbar.css                  (searchbar.css)
-  skin/classic/browser/section_collapsed.png
-  skin/classic/browser/section_collapsed-rtl.png
-  skin/classic/browser/section_expanded.png
   skin/classic/browser/Secure.png
   skin/classic/browser/Security-broken.png
   skin/classic/browser/setDesktopBackground.css
   skin/classic/browser/Toolbar.png
   skin/classic/browser/Toolbar-small.png
   skin/classic/browser/urlbar-arrow.png
   skin/classic/browser/feeds/feedIcon.png             (feeds/feedIcon.png)
   skin/classic/browser/feeds/feedIcon16.png           (feeds/feedIcon16.png)
--- a/browser/themes/pinstripe/aboutCertError.css
+++ b/browser/themes/pinstripe/aboutCertError.css
@@ -81,26 +81,29 @@ body[dir="rtl"] #errorPageContainer {
 #errorTitle {
   -moz-margin-start: 80px;
 }
 
 #errorLongContent {
   -moz-margin-start: 80px;
 }
 
-#technicalContent > h2, #expertContent > h2 {
-  background : url("chrome://browser/skin/section_expanded.png") left 0 no-repeat;
-}
-
-body[dir="rtl"] #technicalContent > h2,
-body[dir="rtl"] #expertContent > h2 {
-  background-position: right 0;
+.expander > button {
+  -moz-padding-start: 20px;
+  -moz-margin-start: -20px;
+  background: url("chrome://browser/skin/aboutCertError_sectionExpanded.png") left center no-repeat;
+  border: none;
+  font: inherit;
+  color: inherit;
+  cursor: pointer;
 }
 
-#technicalContent[collapsed] > h2,
-#expertContent[collapsed] > h2{
-  background-image: url("chrome://browser/skin/section_collapsed.png");
+body[dir="rtl"] .expander > button {
+  background-position: right center;
 }
 
-body[dir="rtl"] #technicalContent[collapsed] > h2,
-body[dir="rtl"] #expertContent[collapsed] > h2 {
-  background-image: url("chrome://browser/skin/section_collapsed-rtl.png");
+.expander[collapsed] > button {
+  background-image: url("chrome://browser/skin/aboutCertError_sectionCollapsed.png");
 }
+
+body[dir="rtl"] .expander[collapsed] > button {
+  background-image: url("chrome://browser/skin/aboutCertError_sectionCollapsed-rtl.png");
+}
rename from browser/themes/pinstripe/section_collapsed-rtl.png
rename to browser/themes/pinstripe/aboutCertError_sectionCollapsed-rtl.png
rename from browser/themes/pinstripe/section_collapsed.png
rename to browser/themes/pinstripe/aboutCertError_sectionCollapsed.png
rename from browser/themes/pinstripe/section_expanded.png
rename to browser/themes/pinstripe/aboutCertError_sectionExpanded.png
--- a/browser/themes/pinstripe/browser.css
+++ b/browser/themes/pinstripe/browser.css
@@ -224,25 +224,30 @@ toolbarbutton.bookmark-item > menupopup 
 .bookmark-item > .toolbarbutton-icon[type="menu"] {
   -moz-margin-end: 5px;
 }
 
 .bookmark-item[container] {
   list-style-image: url("chrome://global/skin/tree/folder.png");
 }
 
-.query-item[container] {
-  list-style-image: url("chrome://browser/skin/places/history.png");
-}
-
-.bookmark-item[livemark] {
+.bookmark-item[container][livemark] {
   list-style-image: url("chrome://browser/skin/page-livemarks.png");
 }
 
-.bookmark-item[query] {
+.bookmark-item[container][livemark] .bookmark-item {
+  list-style-image: url("chrome://browser/skin/places/livemark-item.png");
+  -moz-image-region: rect(0px, 16px, 16px, 0px);
+}
+
+.bookmark-item[container][livemark] .bookmark-item[visited] {
+  -moz-image-region: rect(0px, 32px, 16px, 16px);
+}
+
+.bookmark-item[container][query] {
   list-style-image: url("chrome://browser/skin/places/query.png");
 }
 
 .bookmark-item[query][tagContainer] {
   list-style-image: url("chrome://browser/skin/places/tag.png");
 }
 
 .bookmark-item[query][dayContainer] {
@@ -252,56 +257,49 @@ toolbarbutton.bookmark-item > menupopup 
 .bookmark-item[query][hostContainer] {
   list-style-image: url("chrome://global/skin/tree/folder.png");
 }
 
 .bookmark-item[query][hostContainer][open] {
   list-style-image: url("chrome://global/skin/tree/folder.png");
 }
 
-.bookmark-item[livemark] .menuitem-iconic {
-  list-style-image: url("chrome://browser/skin/places/livemark-item.png");
-  -moz-image-region: rect(0px, 16px, 16px, 0px);
-}
-
-.bookmark-item[livemark] .menuitem-iconic[visited] {
-  -moz-image-region: rect(0px, 32px, 16px, 16px);
-}
-
-.bookmark-item menuitem[openInTabs],
-.bookmark-item menuitem[siteURI] {
+/* Workaround for native menubar inheritance */
+.openintabs-menuitem,
+.openlivemarksite-menuitem,
+.livemarkstatus-menuitem {
   list-style-image: none;
 }
 
+.bookmark-item[cutting] > .toolbarbutton-icon,
+.bookmark-item[cutting] > .menu-iconic-left > .menu-iconic-icon {
+  opacity: 0.5;
+}
+
+.bookmark-item[cutting] > .toolbarbutton-text,
+.bookmark-item[cutting] > .menu-iconic-left > .menu-iconic-text {
+  opacity: 0.7;
+}
+
 #wrapper-personal-bookmarks[place="palette"] > .toolbarpaletteitem-box {
   background: url("chrome://browser/skin/places/bookmarksToolbar.png") no-repeat center;
 }
 
 .bookmarks-toolbar-customize {
   max-width: 15em !important;
   list-style-image: url("chrome://browser/skin/places/bookmarksToolbar.png") !important;
 }
 
 /* ----- BOOKMARK MENUS ----- */
 
 .bookmark-item > .menu-iconic-left > .menu-iconic-icon {
   width: 16px;
   height: 16px;
 }
 
-.bookmark-item[cutting] > .toolbarbutton-icon,
-.bookmark-item[cutting] > .menu-iconic-left > .menu-iconic-icon {
-  opacity: 0.5;
-}
-
-.bookmark-item[cutting] > .toolbarbutton-text,
-.bookmark-item[cutting] > .menu-iconic-left > .menu-iconic-text {
-  opacity: 0.7;
-}
-
 #bookmarksToolbarFolderMenu,
 #BMB_bookmarksToolbar {
   list-style-image: url("chrome://browser/skin/places/bookmarksToolbar.png");
 }
 
 #BMB_unsortedBookmarks {
   list-style-image: url("chrome://browser/skin/places/unfiledBookmarks.png");
 }
--- a/browser/themes/pinstripe/devtools/csshtmltree.css
+++ b/browser/themes/pinstripe/devtools/csshtmltree.css
@@ -143,16 +143,17 @@
   cursor: pointer;
 }
 
 /* This rule is necessary because Templater.jsm breaks LTR TDs in RTL docs */
 .rule-text {
   direction: ltr;
   padding: 0;
   -moz-padding-start: 20px;
+  vertical-align: text-bottom;
 }
 
 .bestmatch {
   color: black;
 }
 .matched {
   text-decoration: line-through;
 }
deleted file mode 100644
--- a/browser/themes/pinstripe/fullscreen-video.css
+++ /dev/null
@@ -1,8 +0,0 @@
-#close {
-  position: absolute;
-  top: 0;
-  left: 0;
-  width: 32px;
-  height: 32px;
-  background: url(KUI-close.png) center center no-repeat;
-}
--- a/browser/themes/pinstripe/jar.mn
+++ b/browser/themes/pinstripe/jar.mn
@@ -1,22 +1,24 @@
 browser.jar:
 % skin browser classic/1.0 %skin/classic/browser/
   skin/classic/browser/sanitizeDialog.css                   (sanitizeDialog.css)
 * skin/classic/browser/aboutPrivateBrowsing.css             (aboutPrivateBrowsing.css)
 * skin/classic/browser/aboutSessionRestore.css              (aboutSessionRestore.css)
   skin/classic/browser/aboutSessionRestore-window-icon.png
-  skin/classic/browser/aboutCertError.css                   (aboutCertError.css)
+  skin/classic/browser/aboutCertError.css
+  skin/classic/browser/aboutCertError_sectionCollapsed.png
+  skin/classic/browser/aboutCertError_sectionCollapsed-rtl.png
+  skin/classic/browser/aboutCertError_sectionExpanded.png
 #ifdef MOZ_SERVICES_SYNC
   skin/classic/browser/aboutSyncTabs.css
 #endif
   skin/classic/browser/actionicon-tab.png
 * skin/classic/browser/browser.css                          (browser.css)
 * skin/classic/browser/engineManager.css                    (engineManager.css)
-  skin/classic/browser/fullscreen-video.css
   skin/classic/browser/Geolocation-16.png
   skin/classic/browser/Geolocation-64.png
   skin/classic/browser/home.png
   skin/classic/browser/hud-style-check-box-checked.png
   skin/classic/browser/hud-style-check-box-empty.png
   skin/classic/browser/hud-style-dropmarker-double-arrows.png
   skin/classic/browser/hud-style-expander-closed.png
   skin/classic/browser/hud-style-expander-open.png
@@ -31,19 +33,16 @@ browser.jar:
   skin/classic/browser/page-livemarks.png
   skin/classic/browser/pageInfo.css
   skin/classic/browser/Privacy-16.png
   skin/classic/browser/Privacy-48.png
   skin/classic/browser/reload-stop-go.png
   skin/classic/browser/searchbar-dropmarker.png
   skin/classic/browser/searchbar.css
   skin/classic/browser/Search.png
-  skin/classic/browser/section_collapsed.png
-  skin/classic/browser/section_collapsed-rtl.png
-  skin/classic/browser/section_expanded.png
   skin/classic/browser/Secure-Glyph-White.png
   skin/classic/browser/keyhole-circle.png
   skin/classic/browser/Toolbar.png
   skin/classic/browser/toolbarbutton-dropmarker.png
   skin/classic/browser/urlbar-history-dropmarker.png
   skin/classic/browser/urlbar-arrow.png
   skin/classic/browser/urlbar-popup-blocked.png
   skin/classic/browser/feeds/subscribe.css                  (feeds/subscribe.css)
--- a/browser/themes/winstripe/aboutCertError.css
+++ b/browser/themes/winstripe/aboutCertError.css
@@ -81,26 +81,29 @@ body[dir="rtl"] #errorPageContainer {
 #errorTitle {
   -moz-margin-start: 80px;
 }
 
 #errorLongContent {
   -moz-margin-start: 80px;
 }
 
-#technicalContent > h2, #expertContent > h2 {
-  background : url("chrome://browser/skin/section_expanded.png") left center no-repeat;
+.expander > button {
+  -moz-padding-start: 20px;
+  -moz-margin-start: -20px;
+  background: url("chrome://browser/skin/aboutCertError_sectionExpanded.png") left center no-repeat;
+  border: none;
+  font: inherit;
+  color: inherit;
+  cursor: pointer;
 }
 
-body[dir="rtl"] #technicalContent > h2,
-body[dir="rtl"] #expertContent > h2 {
+body[dir="rtl"] .expander > button {
   background-position: right center;
 }
 
-#technicalContent[collapsed] > h2,
-#expertContent[collapsed] > h2{
-  background-image: url("chrome://browser/skin/section_collapsed.png");
+.expander[collapsed] > button {
+  background-image: url("chrome://browser/skin/aboutCertError_sectionCollapsed.png");
 }
 
-body[dir="rtl"] #technicalContent[collapsed] > h2,
-body[dir="rtl"] #expertContent[collapsed] > h2 {
-  background-image: url("chrome://browser/skin/section_collapsed-rtl.png");
+body[dir="rtl"] .expander[collapsed] > button {
+  background-image: url("chrome://browser/skin/aboutCertError_sectionCollapsed-rtl.png");
 }
rename from browser/themes/winstripe/section_collapsed-rtl.png
rename to browser/themes/winstripe/aboutCertError_sectionCollapsed-rtl.png
rename from browser/themes/winstripe/section_collapsed.png
rename to browser/themes/winstripe/aboutCertError_sectionCollapsed.png
rename from browser/themes/winstripe/section_expanded.png
rename to browser/themes/winstripe/aboutCertError_sectionExpanded.png
--- a/browser/themes/winstripe/devtools/csshtmltree.css
+++ b/browser/themes/winstripe/devtools/csshtmltree.css
@@ -141,16 +141,17 @@
   cursor: pointer;
 }
 
 /* This rule is necessary because Templater.jsm breaks LTR TDs in RTL docs */
 .rule-text {
   direction: ltr;
   padding: 0;
   -moz-padding-start: 20px;
+  vertical-align: text-bottom;
 }
 
 .bestmatch {
   color: black;
 }
 .matched {
   text-decoration: line-through;
 }
deleted file mode 100644
--- a/browser/themes/winstripe/fullscreen-video.css
+++ /dev/null
@@ -1,8 +0,0 @@
-#close {
-  position: absolute;
-  top: 0;
-  right: 0;
-  width: 32px;
-  height: 32px;
-  background: url(KUI-close.png) center center no-repeat;
-}
--- a/browser/themes/winstripe/jar.mn
+++ b/browser/themes/winstripe/jar.mn
@@ -2,26 +2,28 @@ browser.jar:
 % skin browser classic/1.0 %skin/classic/browser/ os=WINNT osversion<6
 % skin browser classic/1.0 %skin/classic/browser/ os!=WINNT
 # NOTE: If you add a new file here, you'll need to add it to the aero
 # section at the bottom of this file
         skin/classic/browser/sanitizeDialog.css                      (sanitizeDialog.css)
 *       skin/classic/browser/aboutPrivateBrowsing.css                (aboutPrivateBrowsing.css)
 *       skin/classic/browser/aboutSessionRestore.css                 (aboutSessionRestore.css)
         skin/classic/browser/aboutSessionRestore-window-icon.png     (preferences/application.png)
-        skin/classic/browser/aboutCertError.css                      (aboutCertError.css)
+        skin/classic/browser/aboutCertError.css
+        skin/classic/browser/aboutCertError_sectionCollapsed.png
+        skin/classic/browser/aboutCertError_sectionCollapsed-rtl.png
+        skin/classic/browser/aboutCertError_sectionExpanded.png
 #ifdef MOZ_SERVICES_SYNC
         skin/classic/browser/aboutSyncTabs.css
 #endif
         skin/classic/browser/actionicon-tab.png
         skin/classic/browser/appmenu-icons.png
         skin/classic/browser/appmenu-dropmarker.png
 *       skin/classic/browser/browser.css                             (browser.css)
 *       skin/classic/browser/engineManager.css                       (engineManager.css)
-        skin/classic/browser/fullscreen-video.css
         skin/classic/browser/Geolocation-16.png
         skin/classic/browser/Geolocation-64.png
         skin/classic/browser/Info.png                                (Info.png)
         skin/classic/browser/identity.png                            (identity.png)
         skin/classic/browser/keyhole-forward-mask.svg
         skin/classic/browser/KUI-background.png
         skin/classic/browser/KUI-close.png
         skin/classic/browser/pageInfo.css
@@ -33,19 +35,16 @@ browser.jar:
         skin/classic/browser/reload-stop-go.png
         skin/classic/browser/Secure24.png                            (Secure24.png)
         skin/classic/browser/Toolbar.png                             (Toolbar.png)
         skin/classic/browser/Toolbar-inverted.png
         skin/classic/browser/toolbarbutton-dropdown-arrow.png
         skin/classic/browser/toolbarbutton-dropdown-arrow-inverted.png
 *       skin/classic/browser/searchbar.css                           (searchbar.css)
         skin/classic/browser/searchbar-dropdown-arrow.png
-        skin/classic/browser/section_collapsed.png
-        skin/classic/browser/section_collapsed-rtl.png
-        skin/classic/browser/section_expanded.png
         skin/classic/browser/setDesktopBackground.css
         skin/classic/browser/menu-back.png                           (menu-back.png)
         skin/classic/browser/menu-forward.png                        (menu-forward.png)
         skin/classic/browser/monitor.png
         skin/classic/browser/monitor_16-10.png
         skin/classic/browser/urlbar-arrow.png
         skin/classic/browser/urlbar-popup-blocked.png
         skin/classic/browser/urlbar-history-dropmarker.png
@@ -172,26 +171,28 @@ browser.jar:
 
 #ifdef XP_WIN
 browser.jar:
 % skin browser classic/1.0 %skin/classic/aero/browser/ os=WINNT osversion>=6
         skin/classic/aero/browser/sanitizeDialog.css                       (sanitizeDialog.css)
 *       skin/classic/aero/browser/aboutPrivateBrowsing.css           (aboutPrivateBrowsing.css)
 *       skin/classic/aero/browser/aboutSessionRestore.css            (aboutSessionRestore.css)
         skin/classic/aero/browser/aboutSessionRestore-window-icon.png (aboutSessionRestore-window-icon-aero.png)
-        skin/classic/aero/browser/aboutCertError.css                 (aboutCertError.css)
+        skin/classic/aero/browser/aboutCertError.css
+        skin/classic/aero/browser/aboutCertError_sectionCollapsed.png
+        skin/classic/aero/browser/aboutCertError_sectionCollapsed-rtl.png
+        skin/classic/aero/browser/aboutCertError_sectionExpanded.png
 #ifdef MOZ_SERVICES_SYNC
         skin/classic/aero/browser/aboutSyncTabs.css
 #endif
         skin/classic/aero/browser/actionicon-tab.png                 (actionicon-tab.png)
         skin/classic/aero/browser/appmenu-dropmarker.png
         skin/classic/aero/browser/appmenu-icons.png
 *       skin/classic/aero/browser/browser.css                        (browser-aero.css)
 *       skin/classic/aero/browser/engineManager.css                  (engineManager.css)
-        skin/classic/aero/browser/fullscreen-video.css
         skin/classic/aero/browser/Geolocation-16.png
         skin/classic/aero/browser/Geolocation-64.png
         skin/classic/aero/browser/Info.png                           (Info-aero.png)
         skin/classic/aero/browser/identity.png                       (identity-aero.png)
         skin/classic/aero/browser/keyhole-forward-mask.svg
         skin/classic/aero/browser/KUI-background.png
         skin/classic/aero/browser/KUI-close.png
         skin/classic/aero/browser/pageInfo.css
@@ -203,19 +204,16 @@ browser.jar:
         skin/classic/aero/browser/reload-stop-go.png
         skin/classic/aero/browser/Secure24.png                       (Secure24-aero.png)
         skin/classic/aero/browser/Toolbar.png
         skin/classic/aero/browser/Toolbar-inverted.png
         skin/classic/aero/browser/toolbarbutton-dropdown-arrow.png
         skin/classic/aero/browser/toolbarbutton-dropdown-arrow-inverted.png
 *       skin/classic/aero/browser/searchbar.css                      (searchbar.css)
         skin/classic/aero/browser/searchbar-dropdown-arrow.png       (searchbar-dropdown-arrow-aero.png)
-        skin/classic/aero/browser/section_collapsed.png
-        skin/classic/aero/browser/section_collapsed-rtl.png
-        skin/classic/aero/browser/section_expanded.png
         skin/classic/aero/browser/setDesktopBackground.css
         skin/classic/aero/browser/menu-back.png                      (menu-back-aero.png)
         skin/classic/aero/browser/menu-forward.png                   (menu-forward-aero.png)
         skin/classic/aero/browser/monitor.png
         skin/classic/aero/browser/monitor_16-10.png
         skin/classic/aero/browser/urlbar-arrow.png
         skin/classic/aero/browser/urlbar-popup-blocked.png
         skin/classic/aero/browser/urlbar-history-dropmarker.png
rename from build/unix/check_debug_ranges.py
rename to build/autoconf/check_debug_ranges.py
--- a/build/autoconf/compiler-opts.m4
+++ b/build/autoconf/compiler-opts.m4
@@ -4,10 +4,81 @@ AC_DEFUN([MOZ_COMPILER_OPTS],
 [
 if test "$CLANG_CXX"; then
     ## We disable return-type-c-linkage because jsval is defined as a C++ type but is
     ## returned by C functions. This is possible because we use knowledge about the ABI
     ## to typedef it to a C type with the same layout when the headers are included
     ## from C.
     _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wno-unknown-warning-option -Wno-return-type-c-linkage"
 fi
+
+if test "$GNU_CC"; then
+    CFLAGS="$CFLAGS -ffunction-sections -fdata-sections"
+    CXXFLAGS="$CXXFLAGS -ffunction-sections -fdata-sections"
+fi
+
+dnl ========================================================
+dnl = Identical Code Folding
+dnl ========================================================
+
+MOZ_ARG_DISABLE_BOOL(icf,
+[  --disable-icf          Disable Identical Code Folding],
+    MOZ_DISABLE_ICF=1,
+    MOZ_DISABLE_ICF= )
+
+if test "$GNU_CC" -a "$GCC_USE_GNU_LD" -a -z "$MOZ_DISABLE_ICF"; then
+    AC_CACHE_CHECK([whether the linker supports Identical Code Folding],
+        LD_SUPPORTS_ICF,
+        [echo 'int foo() {return 42;}' \
+              'int bar() {return 42;}' \
+              'int main() {return foo() - bar();}' > conftest.${ac_ext}
+        # If the linker supports ICF, foo and bar symbols will have
+        # the same address
+        if AC_TRY_COMMAND([${CC-cc} -o conftest${ac_exeext} $LDFLAGS -Wl,--icf=safe -ffunction-sections conftest.${ac_ext} $LIBS 1>&2]) &&
+           test -s conftest${ac_exeext} &&
+           objdump -t conftest${ac_exeext} | awk changequote(<<, >>)'{a[<<$>>6] = <<$>>1} END {if (a["foo"] && (a["foo"] != a["bar"])) { exit 1 }}'changequote([, ]); then
+            LD_SUPPORTS_ICF=yes
+        else
+            LD_SUPPORTS_ICF=no
+        fi
+        rm -rf conftest*])
+    if test "$LD_SUPPORTS_ICF" = yes; then
+        _SAVE_LDFLAGS="$LDFLAGS -Wl,--icf=safe"
+        LDFLAGS="$LDFLAGS -Wl,--icf=safe -Wl,--print-icf-sections"
+        AC_TRY_LINK([], [],
+                    [LD_PRINT_ICF_SECTIONS=-Wl,--print-icf-sections],
+                    [LD_PRINT_ICF_SECTIONS=])
+        AC_SUBST([LD_PRINT_ICF_SECTIONS])
+        LDFLAGS="$_SAVE_LDFLAGS"
+    fi
+fi
+
+dnl ========================================================
+dnl = Automatically remove dead symbols
+dnl ========================================================
+
+if test "$GNU_CC" -a "$GCC_USE_GNU_LD" -a -n "$MOZ_DEBUG_FLAGS"; then
+   dnl See bug 670659
+   AC_CACHE_CHECK([whether removing dead symbols breaks debugging],
+       GC_SECTIONS_BREAKS_DEBUG_RANGES,
+       [echo 'int foo() {return 42;}' \
+             'int bar() {return 1;}' \
+             'int main() {return foo();}' > conftest.${ac_ext}
+        if AC_TRY_COMMAND([${CC-cc} -o conftest.${ac_objext} $CFLAGS $MOZ_DEBUG_FLAGS -c conftest.${ac_ext} 1>&2]) &&
+           AC_TRY_COMMAND([${CC-cc} -o conftest${ac_exeext} $LDFLAGS $MOZ_DEBUG_FLAGS -Wl,--gc-sections conftest.${ac_objext} $LIBS 1>&2]) &&
+           test -s conftest${ac_exeext} -a -s conftest.${ac_objext}; then
+            if test "`$PYTHON "$_topsrcdir"/build/autoconf/check_debug_ranges.py conftest.${ac_objext} conftest.${ac_ext}`" = \
+                    "`$PYTHON "$_topsrcdir"/build/autoconf/check_debug_ranges.py conftest${ac_exeext} conftest.${ac_ext}`"; then
+                GC_SECTIONS_BREAKS_DEBUG_RANGES=no
+            else
+                GC_SECTIONS_BREAKS_DEBUG_RANGES=yes
+            fi
+        else
+             dnl We really don't expect to get here, but just in case
+             GC_SECTIONS_BREAKS_DEBUG_RANGES="no, but it's broken in some other way"
+        fi
+        rm -rf conftest*])
+    if test "$GC_SECTIONS_BREAKS_DEBUG_RANGES" = no; then
+        DSO_LDOPTS="$DSO_LDOPTS -Wl,--gc-sections"
+    fi
+fi
+
 ])
-
new file mode 100644
--- /dev/null
+++ b/build/autoconf/expandlibs.m4
@@ -0,0 +1,56 @@
+AC_DEFUN([MOZ_EXPAND_LIBS],
+[
+dnl ========================================================
+dnl =
+dnl = Check what kind of list files are supported by the
+dnl = linker
+dnl =
+dnl ========================================================
+
+AC_CACHE_CHECK(what kind of list files are supported by the linker,
+    EXPAND_LIBS_LIST_STYLE,
+    [echo "int main() {return 0;}" > conftest.${ac_ext}
+     if AC_TRY_COMMAND(${CC-cc} -o conftest.${OBJ_SUFFIX} -c $CFLAGS $CPPFLAGS conftest.${ac_ext} 1>&5) && test -s conftest.${OBJ_SUFFIX}; then
+         echo "INPUT(conftest.${OBJ_SUFFIX})" > conftest.list
+         if AC_TRY_COMMAND(${CC-cc} -o conftest${ac_exeext} $LDFLAGS conftest.list $LIBS 1>&5) && test -s conftest${ac_exeext}; then
+             EXPAND_LIBS_LIST_STYLE=linkerscript
+         else
+             echo "conftest.${OBJ_SUFFIX}" > conftest.list
+             if AC_TRY_COMMAND(${CC-cc} -o conftest${ac_exeext} $LDFLAGS @conftest.list $LIBS 1>&5) && test -s conftest${ac_exeext}; then
+                 EXPAND_LIBS_LIST_STYLE=list
+             else
+                 EXPAND_LIBS_LIST_STYLE=none
+             fi
+         fi
+     else
+         dnl We really don't expect to get here, but just in case
+         AC_ERROR([couldn't compile a simple C file])
+     fi
+     rm -rf conftest*])
+
+LIBS_DESC_SUFFIX=desc
+AC_SUBST(LIBS_DESC_SUFFIX)
+AC_SUBST(EXPAND_LIBS_LIST_STYLE)
+
+if test "$GCC_USE_GNU_LD"; then
+    AC_CACHE_CHECK(what kind of ordering can be done with the linker,
+        EXPAND_LIBS_ORDER_STYLE,
+        [> conftest.order
+         _SAVE_LDFLAGS="$LDFLAGS"
+         LDFLAGS="${LDFLAGS} -Wl,--section-ordering-file,conftest.order"
+         AC_TRY_LINK([], [],
+             EXPAND_LIBS_ORDER_STYLE=section-ordering-file,
+             EXPAND_LIBS_ORDER_STYLE=)
+         LDFLAGS="$_SAVE_LDFLAGS"
+         if test -z "$EXPAND_LIBS_ORDER_STYLE"; then
+             if AC_TRY_COMMAND(${CC-cc} ${DSO_LDOPTS} ${LDFLAGS} -o ${DLL_PREFIX}conftest${DLL_SUFFIX} -Wl,--verbose 2> /dev/null | sed -n '/^===/,/^===/p' | grep '\.text'); then
+                 EXPAND_LIBS_ORDER_STYLE=linkerscript
+             else
+                 EXPAND_LIBS_ORDER_STYLE=none
+             fi
+             rm -f ${DLL_PREFIX}conftest${DLL_SUFFIX}
+         fi])
+fi
+AC_SUBST(EXPAND_LIBS_ORDER_STYLE)
+
+])
--- a/build/automation.py.in
+++ b/build/automation.py.in
@@ -390,16 +390,17 @@ user_pref("camino.warn_when_closing", fa
 // Make url-classifier updates so rare that they won't affect tests
 user_pref("urlclassifier.updateinterval", 172800);
 // Point the url-classifier to the local testing server for fast failures
 user_pref("browser.safebrowsing.provider.0.gethashURL", "http://%(server)s/safebrowsing-dummy/gethash");
 user_pref("browser.safebrowsing.provider.0.keyURL", "http://%(server)s/safebrowsing-dummy/newkey");
 user_pref("browser.safebrowsing.provider.0.updateURL", "http://%(server)s/safebrowsing-dummy/update");
 // Point update checks to the local testing server for fast failures
 user_pref("extensions.update.url", "http://%(server)s/extensions-dummy/updateURL");
+user_pref("extensions.update.background.url", "http://%(server)s/extensions-dummy/updateBackgroundURL");
 user_pref("extensions.blocklist.url", "http://%(server)s/extensions-dummy/blocklistURL");
 user_pref("extensions.hotfix.url", "http://%(server)s/extensions-dummy/hotfixURL");
 // Make sure opening about:addons won't hit the network
 user_pref("extensions.webservice.discoverURL", "http://%(server)s/extensions-dummy/discoveryURL");
 // Make sure AddonRepository won't hit the network
 user_pref("extensions.getAddons.maxResults", 0);
 user_pref("extensions.getAddons.get.url", "http://%(server)s/extensions-dummy/repositoryGetURL");
 user_pref("extensions.getAddons.getWithPerformance.url", "http://%(server)s/extensions-dummy/repositoryGetWithPerformanceURL");
@@ -732,18 +733,21 @@ user_pref("camino.use_system_proxy_setti
         self.log.info("Failed to read image from %s", imgoutput)
 
     import base64
     encoded = base64.b64encode(image)
     self.log.info("SCREENSHOT: data:image/png;base64,%s", encoded)
 
   def killAndGetStack(self, proc, utilityPath, debuggerInfo):
     """Kill the process, preferrably in a way that gets us a stack trace."""
-    if not debuggerInfo and not self.haveDumpedScreen:
-      self.dumpScreen(utilityPath)
+    if not debuggerInfo:
+      if self.haveDumpedScreen:
+        self.log.info("Not taking screenshot here: see the one that was previously logged")
+      else:
+        self.dumpScreen(utilityPath)
 
     if self.CRASHREPORTER and not debuggerInfo:
       if self.UNIXISH:
         # ABRT will get picked up by Breakpad's signal handler
         os.kill(proc.pid, signal.SIGABRT)
         return
       elif self.IS_WIN32:
         # We should have a "crashinject" program in our utility path
@@ -790,18 +794,21 @@ user_pref("camino.use_system_proxy_setti
       while line != "" and not didTimeout:
         if logger:
           logger.log(line)
         if "TEST-START" in line and "|" in line:
           self.lastTestSeen = line.split("|")[1].strip()
         if stackFixerFunction:
           line = stackFixerFunction(line)
         self.log.info(line.rstrip().decode("UTF-8", "ignore"))
-        if not debuggerInfo and not self.haveDumpedScreen and "TEST-UNEXPECTED-FAIL" in line and "Test timed out" in line:
-          self.dumpScreen(utilityPath)
+        if not debuggerInfo and "TEST-UNEXPECTED-FAIL" in line and "Test timed out" in line:
+          if self.haveDumpedScreen:
+            self.log.info("Not taking screenshot here: see the one that was previously logged")
+          else:
+            self.dumpScreen(utilityPath)
 
         (line, didTimeout) = self.readWithTimeout(logsource, timeout)
         if not hitMaxTime and maxTime and datetime.now() - startTime > timedelta(seconds = maxTime):
           # Kill the application, but continue reading from stack fixer so as not to deadlock on stackFixerProcess.wait().
           hitMaxTime = True
           self.log.info("TEST-UNEXPECTED-FAIL | %s | application ran for longer than allowed maximum time of %d seconds", self.lastTestSeen, int(maxTime))
           self.killAndGetStack(proc, utilityPath, debuggerInfo)
       if didTimeout:
--- a/build/automationutils.py
+++ b/build/automationutils.py
@@ -454,17 +454,17 @@ def wrapCommand(cmd):
   return cmd
 
 class ShutdownLeakLogger(object):
   """
   Parses the mochitest run log when running a debug build, assigns all leaked
   DOM windows (that are still around after test suite shutdown, despite running
   the GC) to the tests that created them and prints leak statistics.
   """
-  MAX_LEAK_COUNT = 130
+  MAX_LEAK_COUNT = 120
 
   def __init__(self, logger):
     self.logger = logger
     self.tests = []
     self.leakedWindows = {}
     self.leakedDocShells = set()
     self.currentTest = None
     self.seenShutdown = False
--- a/build/mobile/robocop/Driver.java.in
+++ b/build/mobile/robocop/Driver.java.in
@@ -72,10 +72,10 @@ public interface Driver {
     void startCheckerboardRecording();
     float stopCheckerboardRecording();
 
     /**
      * Get a copy of the painted content region.
      * @return A 2-D array of pixels (indexed by y, then x). The pixels
      * are in ARGB-8888 format.
      */
-    int[][] getPaintedSurface();
+    PaintedSurface getPaintedSurface();
 }
--- a/build/mobile/robocop/FennecNativeDriver.java.in
+++ b/build/mobile/robocop/FennecNativeDriver.java.in
@@ -43,16 +43,18 @@ import java.io.BufferedReader;
 import java.io.BufferedWriter;
 import java.io.File;
 import java.io.FileReader;
 import java.io.FileWriter;
 import java.io.IOException;
 import java.nio.IntBuffer;
 import java.util.HashMap;
 import java.util.List;
+import java.io.FileOutputStream;
+import java.io.DataOutputStream;
 
 import java.lang.Class;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Proxy;
 import java.lang.reflect.InvocationHandler;
 import java.lang.Long;
 
@@ -286,17 +288,17 @@ public class FennecNativeDriver implemen
         for (View v : mSolo.getCurrentViews()) {
             if (v instanceof GLSurfaceView) {
                 return (GLSurfaceView)v;
             }
         }
         return null;
     }
 
-    public int[][] getPaintedSurface() {
+    public PaintedSurface getPaintedSurface() {
         GLSurfaceView view = getSurfaceView();
         if (view == null) {
             return null;
         }
         IntBuffer pixelBuffer;
         try {
             pixelBuffer = (IntBuffer)_getPixels.invoke(view);
         } catch (Exception e) {
@@ -304,24 +306,44 @@ public class FennecNativeDriver implemen
             return null;
         }
 
         // now we need to (1) flip the image, because GL likes to do things up-side-down,
         // and (2) rearrange the bits from AGBR-8888 to ARGB-8888.
         int w = view.getWidth();
         int h = view.getHeight();
         pixelBuffer.position(0);
-        int[][] pixels = new int[h][w];
-        for (int y = h - 1; y >= 0; y--) {
-            for (int x = 0; x < w; x++) {
-                int agbr = pixelBuffer.get();
-                pixels[y][x] = (agbr & 0xFF00FF00) | ((agbr >> 16) & 0x000000FF) | ((agbr << 16) & 0x00FF0000);
+        String mapFile = "/mnt/sdcard/pixels.map";
+
+        FileOutputStream fos = null;
+        DataOutputStream dos = null;
+        try {
+            fos = new FileOutputStream(mapFile);
+            dos = new DataOutputStream(fos);
+
+            for (int y = h - 1; y >= 0; y--) {
+                for (int x = 0; x < w; x++) {
+                    int agbr = pixelBuffer.get();
+                    dos.writeInt((agbr & 0xFF00FF00) | ((agbr >> 16) & 0x000000FF) | ((agbr << 16) & 0x00FF0000));
+                }
+            }
+            return new PaintedSurface(mapFile, w, h);
+        } catch (IOException e) {
+            throw new RoboCopException("exception with pixel writer on file: " + mapFile);
+        } finally {
+            try {
+                if (dos != null && fos != null) {
+                    dos.flush();
+                    dos.close();
+                    fos.close();
+                }
+            } catch (IOException e) {
+                throw new RoboCopException("exception closing pixel writer on file: " + mapFile);
             }
         }
-        return pixels;
     }
 
     public int mHeight=0;
     public int mScrollHeight=0;
     public int mPageHeight=10;
 
     class scrollHandler implements InvocationHandler {
         public scrollHandler(){};
--- a/build/mobile/robocop/Makefile.in
+++ b/build/mobile/robocop/Makefile.in
@@ -56,21 +56,25 @@ JAVAFILES = \
   Driver.java \
   Element.java \
   FennecNativeActions.java \
   FennecMochitestAssert.java \
   FennecTalosAssert.java \
   FennecNativeDriver.java \
   FennecNativeElement.java \
   RoboCopException.java \
+  PaintedSurface.java \
   $(NULL)
 
 _JAVA_TESTS = $(patsubst $(TESTPATH)/%.in,%,$(wildcard $(TESTPATH)/*.java.in))
 
-_TEST_FILES = $(wildcard $(TESTPATH)/*.html)
+_TEST_FILES = \
+  $(wildcard $(TESTPATH)/*.html) \
+  $(wildcard $(TESTPATH)/*.sjs) \
+  $(NULL)
 
 _ROBOCOP_TOOLS = \
   $(TESTPATH)/robocop.ini \
   parse_ids.py \
   $(NULL)
 
 GARBAGE += \
   AndroidManifest.xml \
new file mode 100644
--- /dev/null
+++ b/build/mobile/robocop/PaintedSurface.java.in
@@ -0,0 +1,62 @@
+#filter substitution
+/* 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/. */
+
+package @ANDROID_PACKAGE_NAME@;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileInputStream;
+import java.nio.MappedByteBuffer;
+import java.nio.channels.FileChannel;
+
+public class PaintedSurface {
+    private String mFileName = null;
+    private int mWidth = -1;
+    private int mHeight = -1;
+    private MappedByteBuffer mPixelBuffer = null;
+
+    public PaintedSurface(String filename, int width, int height) {
+        mFileName = filename;
+        mWidth = width;
+        mHeight = height;
+        
+        try {
+            File f = new File(filename);
+            int pixelSize = (int)f.length();
+            
+            FileInputStream pixelFile = new FileInputStream(filename);
+            mPixelBuffer = pixelFile.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, pixelSize);
+        } catch (java.io.FileNotFoundException e) {
+            e.printStackTrace();
+        } catch (java.io.IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    public final int getPixelAt(int x, int y) {
+        if (mPixelBuffer == null) {
+            throw new RoboCopException("Trying to access PaintedSurface with no active PixelBuffer");
+        }
+
+        if (x >= mWidth || x < 0) {
+            throw new RoboCopException("Trying to access PaintedSurface with invalid x value");
+        }
+
+        if (y >= mHeight || y < 0) {
+            throw new RoboCopException("Trying to access PaintedSurface with invalid y value");
+        }
+
+        // The rows are reversed so row 0 is at the end and we start with the last row.
+        // This is why we do mHeight-y;
+        int index = (x + ((mHeight - y - 1) * mWidth)) * 4;
+        int b1 = mPixelBuffer.get(index) & 0xFF;
+        int b2 = mPixelBuffer.get(index + 1) & 0xFF;
+        int b3 = mPixelBuffer.get(index + 2) & 0xFF;
+        int b4 = mPixelBuffer.get(index + 3) & 0xFF;
+        int value = (b1 << 24) + (b2 << 16) + (b3 << 8) + (b4 << 0);
+        return value;
+    }
+}
+
--- a/caps/idl/nsIPrincipal.idl
+++ b/caps/idl/nsIPrincipal.idl
@@ -47,17 +47,17 @@ struct JSPrincipals;
 %}
 
 interface nsIURI;
 interface nsIContentSecurityPolicy;
 
 [ptr] native JSContext(JSContext);
 [ptr] native JSPrincipals(JSPrincipals);
 
-[scriptable, uuid(1f83b0e0-6b63-4bdc-a50a-b9afe256bd25)]
+[scriptable, uuid(f8c4c89a-d726-421b-8415-3e34b241175b)]
 interface nsIPrincipal : nsISerializable
 {
     /**
      * Values of capabilities for each principal. Order is
      * significant: if an operation is performed on a set
      * of capabilities, the minimum is computed.
      */
     const short ENABLE_DENIED                = 1;
@@ -92,22 +92,16 @@ interface nsIPrincipal : nsISerializable
     boolean equalsIgnoringDomain(in nsIPrincipal other);
 
     /**
      * Returns a hash value for the principal.
      */
     [noscript] readonly attribute unsigned long hashValue;
 
     /**
-     * Returns the JS equivalent of the principal.
-     * @see JSPrincipals.h
-     */
-    [noscript] JSPrincipals getJSPrincipals(in JSContext cx);
-
-    /**
      * The domain security policy of the principal.
      */
     // XXXcaa should this be here?  The script security manager is the only
     // thing that should care about this.  Wouldn't storing this data in one
     // of the hashtables in nsScriptSecurityManager be better?
     // XXXbz why is this writable?  Who should have write access to this?  What
     // happens if this principal is in our hashtable and we pass it out of the
     // security manager and someone writes to this field?  Especially if they
--- a/caps/include/nsJSPrincipals.h
+++ b/caps/include/nsJSPrincipals.h
@@ -38,19 +38,52 @@
 
 #ifndef nsJSPrincipals_h__
 #define nsJSPrincipals_h__
 #include "jsapi.h"
 #include "nsIPrincipal.h"
 
 class nsCString;
 
-struct nsJSPrincipals : JSPrincipals
+struct nsJSPrincipals : nsIPrincipal, JSPrincipals
 {
-  static nsresult Startup();
-  nsJSPrincipals();
-  nsresult Init(nsIPrincipal* aPrincipal, const nsCString& aCodebase);
-  ~nsJSPrincipals(void);
+  static JSBool Subsume(JSPrincipals *jsprin, JSPrincipals *other);
+  static void Destroy(JSPrincipals *jsprin);
+  static JSBool Transcode(JSXDRState *xdr, JSPrincipals **jsprinp);
+
+  /*
+   * Get a weak reference to nsIPrincipal associated with the given JS
+   * principal.
+   */
+  static nsJSPrincipals* get(JSPrincipals *principals) {
+    nsJSPrincipals *self = static_cast<nsJSPrincipals *>(principals);
+    MOZ_ASSERT_IF(self, self->debugToken == DEBUG_TOKEN);
+    return self;
+  }
+  
+  static nsJSPrincipals* get(nsIPrincipal *principal) {
+    nsJSPrincipals *self = static_cast<nsJSPrincipals *>(principal);
+    MOZ_ASSERT_IF(self, self->debugToken == DEBUG_TOKEN);
+    return self;
+  }
 
-  nsIPrincipal *nsIPrincipalPtr; // [WEAK] it owns us.
+  nsJSPrincipals() {
+    refcount = 0;
+    setDebugToken(DEBUG_TOKEN);
+  }
+
+  virtual ~nsJSPrincipals() {
+    setDebugToken(0);
+  }
+
+  /**
+   * Return a string that can be used as JS script filename in error reports.
+   */
+  virtual void GetScriptLocation(nsACString &aStr) = 0;
+
+#ifdef DEBUG
+  virtual void dumpImpl() = 0;
+#endif
+
+  static const uint32_t DEBUG_TOKEN = 0x0bf41760;
 };
 
 #endif /* nsJSPrincipals_h__ */
--- a/caps/include/nsNullPrincipal.h
+++ b/caps/include/nsNullPrincipal.h
@@ -54,33 +54,38 @@ class nsIURI;
 #define NS_NULLPRINCIPAL_CLASSNAME "nullprincipal"
 #define NS_NULLPRINCIPAL_CID \
 { 0xdd156d62, 0xd26f, 0x4441, \
  { 0x9c, 0xdb, 0xe8, 0xf0, 0x91, 0x07, 0xc2, 0x73 } }
 #define NS_NULLPRINCIPAL_CONTRACTID "@mozilla.org/nullprincipal;1"
 
 #define NS_NULLPRINCIPAL_SCHEME "moz-nullprincipal"
 
-class nsNullPrincipal : public nsIPrincipal
+class nsNullPrincipal : public nsJSPrincipals
 {
 public:
   nsNullPrincipal();
   
-  // Our refcount is managed by mJSPrincipals.  Use this macro to avoid an
+  // Our refcount is managed by nsJSPrincipals.  Use this macro to avoid an
   // extra refcount member.
 
   // FIXME: bug 327245 -- I sorta wish there were a clean way to share the
-  // mJSPrincipals munging code between the various principal classes without
+  // nsJSPrincipals munging code between the various principal classes without
   // giving up the NS_DECL_NSIPRINCIPAL goodness.
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIPRINCIPAL
   NS_DECL_NSISERIALIZABLE
 
   nsresult Init();
 
-protected:
+  virtual void GetScriptLocation(nsACString &aStr) MOZ_OVERRIDE;
+
+#ifdef DEBUG
+  virtual void dumpImpl() MOZ_OVERRIDE;
+#endif 
+
+ protected:
   virtual ~nsNullPrincipal();
 
-  nsJSPrincipals mJSPrincipals;
   nsCOMPtr<nsIURI> mURI;
 };
 
 #endif // nsNullPrincipal_h__
--- a/caps/include/nsPrincipal.h
+++ b/caps/include/nsPrincipal.h
@@ -46,26 +46,26 @@
 #include "nsHashtable.h"
 #include "nsJSPrincipals.h"
 #include "nsTArray.h"
 #include "nsAutoPtr.h"
 
 class nsIObjectInputStream;
 class nsIObjectOutputStream;
 
-class nsPrincipal : public nsIPrincipal
+class nsPrincipal : public nsJSPrincipals
 {
 public:
   nsPrincipal();
 
 protected:
   virtual ~nsPrincipal();
 
 public:
-  // Our refcount is managed by mJSPrincipals.  Use this macro to avoid
+  // Our refcount is managed by nsJSPrincipals.  Use this macro to avoid
   // an extra refcount member.
   NS_DECL_ISUPPORTS_INHERITED
 public:
 
   NS_DECL_NSIPRINCIPAL
   NS_DECL_NSISERIALIZABLE
 
   // Either Init() or InitFromPersistent() must be called before
@@ -95,18 +95,23 @@ public:
   enum AnnotationValue { AnnotationEnabled=1, AnnotationDisabled };
 
   void SetURI(nsIURI *aURI);
   nsresult SetCapability(const char *capability, void **annotation, 
                          AnnotationValue value);
 
   static const char sInvalid[];
 
+  virtual void GetScriptLocation(nsACString &aStr) MOZ_OVERRIDE;
+
+#ifdef DEBUG
+  virtual void dumpImpl() MOZ_OVERRIDE;
+#endif 
+
 protected:
-  nsJSPrincipals mJSPrincipals;
   nsTArray< nsAutoPtr<nsHashtable> > mAnnotations;
   nsHashtable* mCapabilities;
   nsCString mPrefName;
   static PRInt32 sCapabilitiesOrdinal;
 
   // XXXcaa This is a semi-hack.  The best solution here is to keep
   // a reference to an interface here, except there is no interface
   // that we can use yet.
--- a/caps/include/nsScriptSecurityManager.h
+++ b/caps/include/nsScriptSecurityManager.h
@@ -423,16 +423,19 @@ private:
     nsScriptSecurityManager();
     virtual ~nsScriptSecurityManager();
 
     static JSBool
     CheckObjectAccess(JSContext *cx, JSObject *obj,
                       jsid id, JSAccessMode mode,
                       jsval *vp);
 
+    static JSPrincipals *
+    ObjectPrincipalFinder(JSObject *obj);
+    
     // Decides, based on CSP, whether or not eval() and stuff can be executed.
     static JSBool
     ContentSecurityPolicyPermitsJSAction(JSContext *cx);
 
     // Returns null if a principal cannot be found; generally callers
     // should error out at that point.
     static nsIPrincipal*
     doGetObjectPrincipal(JSObject *obj
--- a/caps/include/nsSystemPrincipal.h
+++ b/caps/include/nsSystemPrincipal.h
@@ -45,30 +45,33 @@
 
 #define NS_SYSTEMPRINCIPAL_CLASSNAME "systemprincipal"
 #define NS_SYSTEMPRINCIPAL_CID \
 { 0x4a6212db, 0xaccb, 0x11d3, \
 { 0xb7, 0x65, 0x0, 0x60, 0xb0, 0xb6, 0xce, 0xcb }}
 #define NS_SYSTEMPRINCIPAL_CONTRACTID "@mozilla.org/systemprincipal;1"
 
 
-class nsSystemPrincipal : public nsIPrincipal
+class nsSystemPrincipal : public nsJSPrincipals
 {
 public:
-    // Our refcount is managed by mJSPrincipals.  Use this macro to avoid
+    // Our refcount is managed by nsJSPrincipals.  Use this macro to avoid
     // an extra refcount member.
     NS_DECL_ISUPPORTS_INHERITED
     NS_DECL_NSIPRINCIPAL
     NS_DECL_NSISERIALIZABLE
 
-    nsresult Init(JSPrincipals **jsprin);
+    nsSystemPrincipal();
+
+    virtual void GetScriptLocation(nsACString &aStr) MOZ_OVERRIDE;
 
-    nsSystemPrincipal();
+#ifdef DEBUG
+    virtual void dumpImpl() MOZ_OVERRIDE;
+#endif 
 
 protected:
     virtual ~nsSystemPrincipal(void);
 
-    nsJSPrincipals mJSPrincipals;
     // XXX Probably unnecessary.  See bug 143559.
     NS_DECL_OWNINGTHREAD
 };
 
 #endif // nsSystemPrincipal_h__
--- a/caps/src/nsJSPrincipals.cpp
+++ b/caps/src/nsJSPrincipals.cpp
@@ -45,56 +45,54 @@
 #include "nsCOMPtr.h"
 #include "jsapi.h"
 #include "jsxdrapi.h"
 #include "nsIJSRuntimeService.h"
 #include "nsIServiceManager.h"
 #include "nsMemory.h"
 #include "nsStringBuffer.h"
 
-static JSBool
-nsJSPrincipalsSubsume(JSPrincipals *jsprin, JSPrincipals *other)
+// for mozilla::dom::workers::kJSPrincipalsDebugToken
+#include "mozilla/dom/workers/Workers.h"
+
+/* static */ JSBool
+nsJSPrincipals::Subsume(JSPrincipals *jsprin, JSPrincipals *other)
 {
-    nsJSPrincipals *nsjsprin = static_cast<nsJSPrincipals *>(jsprin);
-    nsJSPrincipals *nsother  = static_cast<nsJSPrincipals *>(other);
-
     bool result;
-    nsresult rv = nsjsprin->nsIPrincipalPtr->Subsumes(nsother->nsIPrincipalPtr,
-                                                      &result);
+    nsresult rv = nsJSPrincipals::get(jsprin)->Subsumes(nsJSPrincipals::get(other), &result);
     return NS_SUCCEEDED(rv) && result;
 }
 
-static void
-nsDestroyJSPrincipals(JSContext *cx, struct JSPrincipals *jsprin)
+/* static */ void
+nsJSPrincipals::Destroy(JSPrincipals *jsprin)
 {
-    nsJSPrincipals *nsjsprin = static_cast<nsJSPrincipals *>(jsprin);
+    // The JS runtime can call this method during the last GC when
+    // nsScriptSecurityManager is destroyed. So we must not assume here that
+    // the security manager still exists.
+
+    nsJSPrincipals *nsjsprin = nsJSPrincipals::get(jsprin);
 
     // We need to destroy the nsIPrincipal. We'll do this by adding
     // to the refcount and calling release
 
-    // Note that we don't want to use NS_IF_RELEASE because it will try
-    // to set nsjsprin->nsIPrincipalPtr to nsnull *after* nsjsprin has
-    // already been destroyed.
 #ifdef NS_BUILD_REFCNT_LOGGING
     // The refcount logging considers AddRef-to-1 to indicate creation,
     // so trick it into thinking it's otherwise, but balance the
     // Release() we do below.
     nsjsprin->refcount++;
-    nsjsprin->nsIPrincipalPtr->AddRef();
+    nsjsprin->AddRef();
     nsjsprin->refcount--;
 #else
     nsjsprin->refcount++;
 #endif
-    nsjsprin->nsIPrincipalPtr->Release();
-    // The nsIPrincipal that we release owns the JSPrincipal struct,
-    // so we don't need to worry about "codebase"
+    nsjsprin->Release();
 }
 
-static JSBool
-nsTranscodeJSPrincipals(JSXDRState *xdr, JSPrincipals **jsprinp)
+/* static */ JSBool
+nsJSPrincipals::Transcode(JSXDRState *xdr, JSPrincipals **jsprinp)
 {
     nsresult rv;
 
     if (xdr->mode == JSXDR_ENCODE) {
         nsIObjectOutputStream *stream =
             reinterpret_cast<nsIObjectOutputStream*>(xdr->userdata);
 
         // Flush xdr'ed data to the underlying object output stream.
@@ -102,22 +100,17 @@ nsTranscodeJSPrincipals(JSXDRState *xdr,
         char *data = (char*) ::JS_XDRMemGetData(xdr, &size);
 
         rv = stream->Write32(size);
         if (NS_SUCCEEDED(rv)) {
             rv = stream->WriteBytes(data, size);
             if (NS_SUCCEEDED(rv)) {
                 ::JS_XDRMemResetData(xdr);
 
-                // Require that GetJSPrincipals has been called already by the
-                // code that compiled the script that owns the principals.
-                nsJSPrincipals *nsjsprin =
-                    static_cast<nsJSPrincipals*>(*jsprinp);
-
-                rv = stream->WriteObject(nsjsprin->nsIPrincipalPtr, true);
+                rv = stream->WriteObject(nsJSPrincipals::get(*jsprinp), true);
             }
         }
     } else {
         NS_ASSERTION(JS_XDRMemDataLeft(xdr) == 0, "XDR out of sync?!");
         nsIObjectInputStream *stream =
             reinterpret_cast<nsIObjectInputStream*>(xdr->userdata);
 
         nsCOMPtr<nsIPrincipal> prin;
@@ -136,89 +129,43 @@ nsTranscodeJSPrincipals(JSXDRState *xdr,
                     // Any decode-mode JSXDRState whose userdata points to an
                     // nsIObjectInputStream instance must use nsMemory to Alloc
                     // and Free its data buffer.  Swap the new buffer we just
                     // read for the old, exhausted data.
                     olddata = (char*) ::JS_XDRMemGetData(xdr, &oldsize);
                     nsMemory::Free(olddata);
                     ::JS_XDRMemSetData(xdr, data, size);
 
-                    prin->GetJSPrincipals(xdr->cx, jsprinp);
+                    *jsprinp = nsJSPrincipals::get(prin);
+                    JS_HoldPrincipals(*jsprinp);
                 }
             }
         }
     }
 
     if (NS_FAILED(rv)) {
         ::JS_ReportError(xdr->cx, "can't %scode principals (failure code %x)",
                          (xdr->mode == JSXDR_ENCODE) ? "en" : "de",
                          (unsigned int) rv);
         return JS_FALSE;
     }
     return JS_TRUE;
 }
 
-nsresult
-nsJSPrincipals::Startup()
-{
-    nsCOMPtr<nsIJSRuntimeService> rtsvc = nsXPConnect::GetXPConnect();
-    if (!rtsvc)
-        return NS_ERROR_FAILURE;
-
-    JSRuntime *rt;
-    rtsvc->GetRuntime(&rt);
-    NS_ASSERTION(rt != nsnull, "no JSRuntime?!");
-
-    JSSecurityCallbacks *callbacks = JS_GetRuntimeSecurityCallbacks(rt);
-    NS_ASSERTION(callbacks, "Need a callbacks struct by now!");
+#ifdef DEBUG
 
-    NS_ASSERTION(!callbacks->principalsTranscoder,
-                 "oops, JS_SetPrincipalsTranscoder wars!");
-
-    callbacks->principalsTranscoder = nsTranscodeJSPrincipals;
-    return NS_OK;
-}
-
-nsJSPrincipals::nsJSPrincipals()
+// Defined here so one can do principals->dump() in the debugger
+JS_EXPORT_API(void)
+JSPrincipals::dump()
 {
-    codebase = nsnull;
-    refcount = 0;
-    destroy = nsDestroyJSPrincipals;
-    subsume = nsJSPrincipalsSubsume;
-    nsIPrincipalPtr = nsnull;
+    if (debugToken == nsJSPrincipals::DEBUG_TOKEN) {
+        static_cast<nsJSPrincipals *>(this)->dumpImpl();
+    } else if (debugToken == mozilla::dom::workers::kJSPrincipalsDebugToken) {
+        fprintf(stderr, "Web Worker principal singleton (%p)\n", this);
+    } else {
+        fprintf(stderr,
+                "!!! JSPrincipals (%p) is not nsJSPrincipals instance - bad token: "
+                "actual=0x%x expected=0x%x\n",
+                this, unsigned(debugToken), unsigned(nsJSPrincipals::DEBUG_TOKEN));
+    }
 }
 
-nsresult
-nsJSPrincipals::Init(nsIPrincipal *aPrincipal, const nsCString& aCodebase)
-{
-    if (nsIPrincipalPtr) {
-        NS_ERROR("Init called twice!");
-        return NS_ERROR_UNEXPECTED;
-    }
-
-    nsIPrincipalPtr = aPrincipal;
-    nsStringBuffer* buf = nsStringBuffer::FromString(aCodebase);
-    char* data;
-    if (buf) {
-        buf->AddRef();
-        data = static_cast<char*>(buf->Data());
-    } else {
-        PRUint32 len = aCodebase.Length();
-        buf = nsStringBuffer::Alloc(len + 1); // addrefs
-        if (!buf) {
-            return NS_ERROR_OUT_OF_MEMORY;
-        }
-        data = static_cast<char*>(buf->Data());
-        memcpy(data, aCodebase.get(), len);
-        data[len] = '\0';
-    }
-    
-    codebase = data;
-
-    return NS_OK;
-}
-
-nsJSPrincipals::~nsJSPrincipals()
-{
-    if (codebase) {
-        nsStringBuffer::FromData(codebase)->Release();
-    }
-}
+#endif 
--- a/caps/src/nsNullPrincipal.cpp
+++ b/caps/src/nsNullPrincipal.cpp
@@ -64,27 +64,27 @@ NS_IMPL_QUERY_INTERFACE2_CI(nsNullPrinci
                             nsISerializable)
 NS_IMPL_CI_INTERFACE_GETTER2(nsNullPrincipal,
                              nsIPrincipal,
                              nsISerializable)
 
 NS_IMETHODIMP_(nsrefcnt) 
 nsNullPrincipal::AddRef()
 {
-  NS_PRECONDITION(PRInt32(mJSPrincipals.refcount) >= 0, "illegal refcnt");
-  nsrefcnt count = PR_ATOMIC_INCREMENT(&mJSPrincipals.refcount);
+  NS_PRECONDITION(PRInt32(refcount) >= 0, "illegal refcnt");
+  nsrefcnt count = PR_ATOMIC_INCREMENT(&refcount);
   NS_LOG_ADDREF(this, count, "nsNullPrincipal", sizeof(*this));
   return count;
 }
 
 NS_IMETHODIMP_(nsrefcnt)
 nsNullPrincipal::Release()
 {
-  NS_PRECONDITION(0 != mJSPrincipals.refcount, "dup release");
-  nsrefcnt count = PR_ATOMIC_DECREMENT(&mJSPrincipals.refcount);
+  NS_PRECONDITION(0 != refcount, "dup release");
+  nsrefcnt count = PR_ATOMIC_DECREMENT(&refcount);
   NS_LOG_RELEASE(this, count, "nsNullPrincipal");
   if (count == 0) {
     delete this;
   }
 
   return count;
 }
 
@@ -128,19 +128,34 @@ nsNullPrincipal::Init()
   if (str.Length() != prefixLen + suffixLen) {
     NS_WARNING("Out of memory allocating null-principal URI");
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   mURI = new nsNullPrincipalURI(str);
   NS_ENSURE_TRUE(mURI, NS_ERROR_OUT_OF_MEMORY);
 
-  return mJSPrincipals.Init(this, str);
+  return NS_OK;
+}
+
+void
+nsNullPrincipal::GetScriptLocation(nsACString &aStr)
+{
+  mURI->GetSpec(aStr);
 }
 
+#ifdef DEBUG
+void nsNullPrincipal::dumpImpl()
+{
+  nsCAutoString str;
+  mURI->GetSpec(str);
+  fprintf(stderr, "nsNullPrincipal (%p) = %s\n", this, str.get());
+}
+#endif 
+
 /**
  * nsIPrincipal implementation
  */
 
 NS_IMETHODIMP
 nsNullPrincipal::GetPreferences(char** aPrefName, char** aID,
                                 char** aSubjectName,
                                 char** aGrantedList, char** aDeniedList,
@@ -175,27 +190,16 @@ nsNullPrincipal::EqualsIgnoringDomain(ns
 NS_IMETHODIMP
 nsNullPrincipal::GetHashValue(PRUint32 *aResult)
 {
   *aResult = (NS_PTR_TO_INT32(this) >> 2);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsNullPrincipal::GetJSPrincipals(JSContext *cx, JSPrincipals **aJsprin)
-{
-  NS_PRECONDITION(mJSPrincipals.nsIPrincipalPtr,
-                  "mJSPrincipals is uninitalized!");
-
-  JSPRINCIPALS_HOLD(cx, &mJSPrincipals);
-  *aJsprin = &mJSPrincipals;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 nsNullPrincipal::GetSecurityPolicy(void** aSecurityPolicy)
 {
   // We don't actually do security policy caching.  And it's not like anyone
   // can set a security policy for us anyway.
   *aSecurityPolicy = nsnull;
   return NS_OK;
 }
 
--- a/caps/src/nsPrincipal.cpp
+++ b/caps/src/nsPrincipal.cpp
@@ -88,28 +88,28 @@ NS_IMPL_QUERY_INTERFACE2_CI(nsPrincipal,
                             nsISerializable)
 NS_IMPL_CI_INTERFACE_GETTER2(nsPrincipal,
                              nsIPrincipal,
                              nsISerializable)
 
 NS_IMETHODIMP_(nsrefcnt)
 nsPrincipal::AddRef()
 {
-  NS_PRECONDITION(PRInt32(mJSPrincipals.refcount) >= 0, "illegal refcnt");
+  NS_PRECONDITION(PRInt32(refcount) >= 0, "illegal refcnt");
   // XXXcaa does this need to be threadsafe?  See bug 143559.
-  nsrefcnt count = PR_ATOMIC_INCREMENT(&mJSPrincipals.refcount);
+  nsrefcnt count = PR_ATOMIC_INCREMENT(&refcount);
   NS_LOG_ADDREF(this, count, "nsPrincipal", sizeof(*this));
   return count;
 }
 
 NS_IMETHODIMP_(nsrefcnt)
 nsPrincipal::Release()
 {
-  NS_PRECONDITION(0 != mJSPrincipals.refcount, "dup release");
-  nsrefcnt count = PR_ATOMIC_DECREMENT(&mJSPrincipals.refcount);
+  NS_PRECONDITION(0 != refcount, "dup release");
+  nsrefcnt count = PR_ATOMIC_DECREMENT(&refcount);
   NS_LOG_RELEASE(this, count, "nsPrincipal");
   if (count == 0) {
     delete this;
   }
 
   return count;
 }
 
@@ -142,51 +142,46 @@ nsPrincipal::Init(const nsACString& aCer
   NS_ENSURE_STATE(!mInitialized);
   NS_ENSURE_ARG(!aCertFingerprint.IsEmpty() || aCodebase); // better have one of these.
 
   mInitialized = true;
 
   mCodebase = NS_TryToMakeImmutable(aCodebase);
   mCodebaseImmutable = URIIsImmutable(mCodebase);
 
-  nsresult rv;
-  if (!aCertFingerprint.IsEmpty()) {
-    rv = SetCertificate(aCertFingerprint, aSubjectName, aPrettyName, aCert);
-    if (NS_SUCCEEDED(rv)) {
-      rv = mJSPrincipals.Init(this, mCert->fingerprint);
-    }
-  }
-  else {
-    nsCAutoString spec;
-    rv = mCodebase->GetSpec(spec);
-    if (NS_SUCCEEDED(rv)) {
-      rv = mJSPrincipals.Init(this, spec);
-    }
-  }
+  if (aCertFingerprint.IsEmpty())
+    return NS_OK;
 
-  NS_ASSERTION(NS_SUCCEEDED(rv), "nsPrincipal::Init() failed");
-
-  return rv;
+  return SetCertificate(aCertFingerprint, aSubjectName, aPrettyName, aCert);
 }
 
 nsPrincipal::~nsPrincipal(void)
 {
   SetSecurityPolicy(nsnull); 
   delete mCapabilities;
 }
 
-NS_IMETHODIMP
-nsPrincipal::GetJSPrincipals(JSContext *cx, JSPrincipals **jsprin)
+void
+nsPrincipal::GetScriptLocation(nsACString &aStr)
 {
-  NS_PRECONDITION(mJSPrincipals.nsIPrincipalPtr, "mJSPrincipals is uninitialized!");
+  if (mCert) {
+    aStr.Assign(mCert->fingerprint);
+  } else {
+    mCodebase->GetSpec(aStr);
+  }
+}
 
-  JSPRINCIPALS_HOLD(cx, &mJSPrincipals);
-  *jsprin = &mJSPrincipals;
-  return NS_OK;
+#ifdef DEBUG
+void nsPrincipal::dumpImpl()
+{
+  nsCAutoString str;
+  GetScriptLocation(str);
+  fprintf(stderr, "nsPrincipal (%p) = %s\n", this, str.get());
 }
+#endif 
 
 NS_IMETHODIMP
 nsPrincipal::GetOrigin(char **aOrigin)
 {
   *aOrigin = nsnull;
 
   nsCOMPtr<nsIURI> origin;
   if (mCodebase) {
@@ -878,19 +873,16 @@ nsPrincipal::InitFromPersistent(const ch
     }
 
     NS_TryToSetImmutable(mCodebase);
     mCodebaseImmutable = URIIsImmutable(mCodebase);
 
     mTrusted = aTrusted;
   }
 
-  rv = mJSPrincipals.Init(this, aToken);
-  NS_ENSURE_SUCCESS(rv, rv);
-
   //-- Save the preference name
   mPrefName = aPrefName;
 
   const char* ordinalBegin = PL_strpbrk(aPrefName, "1234567890");
   if (ordinalBegin) {
     PRIntn n = atoi(ordinalBegin);
     if (sCapabilitiesOrdinal <= n) {
       sCapabilitiesOrdinal = n + 1;
--- a/caps/src/nsScriptSecurityManager.cpp
+++ b/caps/src/nsScriptSecurityManager.cpp
@@ -512,16 +512,23 @@ NS_IMPL_ISUPPORTS4(nsScriptSecurityManag
                    nsIChannelEventSink,
                    nsIObserver)
 
 ///////////////////////////////////////////////////
 // Methods implementing nsIScriptSecurityManager //
 ///////////////////////////////////////////////////
 
 ///////////////// Security Checks /////////////////
+
+/* static */ JSPrincipals *
+nsScriptSecurityManager::ObjectPrincipalFinder(JSObject *aObj)
+{
+    return nsJSPrincipals::get(doGetObjectPrincipal(aObj));
+}
+
 JSBool
 nsScriptSecurityManager::ContentSecurityPolicyPermitsJSAction(JSContext *cx)
 {
     // Get the security manager
     nsScriptSecurityManager *ssm =
         nsScriptSecurityManager::GetScriptSecurityManager();
 
     NS_ASSERTION(ssm, "Failed to get security manager service");
@@ -532,17 +539,17 @@ nsScriptSecurityManager::ContentSecurity
     nsIPrincipal* subjectPrincipal = ssm->GetSubjectPrincipal(cx, &rv);
 
     NS_ASSERTION(NS_SUCCEEDED(rv), "CSP: Failed to get nsIPrincipal from js context");
     if (NS_FAILED(rv))
         return JS_FALSE; // Not just absence of principal, but failure.
 
     if (!subjectPrincipal) {
         // See bug 553448 for discussion of this case.
-        NS_ASSERTION(!JS_GetSecurityCallbacks(cx)->findObjectPrincipals,
+        NS_ASSERTION(!JS_GetSecurityCallbacks(js::GetRuntime(cx))->findObjectPrincipals,
                      "CSP: Should have been able to find subject principal. "
                      "Reluctantly granting access.");
         return JS_TRUE;
     }
 
     nsCOMPtr<nsIContentSecurityPolicy> csp;
     rv = subjectPrincipal->GetCsp(getter_AddRefs(csp));
     NS_ASSERTION(NS_SUCCEEDED(rv), "CSP: Failed to get CSP from principal.");
@@ -2174,21 +2181,17 @@ nsScriptSecurityManager::GetScriptPrinci
         return nsnull;
     }
     JSPrincipals *jsp = JS_GetScriptPrincipals(cx, script);
     if (!jsp) {
         *rv = NS_ERROR_FAILURE;
         NS_ERROR("Script compiled without principals!");
         return nsnull;
     }
-    nsJSPrincipals *nsJSPrin = static_cast<nsJSPrincipals *>(jsp);
-    nsIPrincipal* result = nsJSPrin->nsIPrincipalPtr;
-    if (!result)
-        *rv = NS_ERROR_FAILURE;
-    return result;
+    return nsJSPrincipals::get(jsp);
 }
 
 // static
 nsIPrincipal*
 nsScriptSecurityManager::GetFunctionObjectPrincipal(JSContext *cx,
                                                     JSObject *obj,
                                                     JSStackFrame *fp,
                                                     nsresult *rv)
@@ -3325,17 +3328,16 @@ nsScriptSecurityManager::nsScriptSecurit
       mPolicyPrefsChanged(true)
 {
     NS_ASSERTION(sizeof(PRWord) == sizeof(void*),
                  "PRWord and void* have different lengths on this platform. "
                  "This may cause a security failure with the SecurityLevel union.");
     mPrincipals.Init(31);
 }
 
-
 nsresult nsScriptSecurityManager::Init()
 {
     nsXPConnect* xpconnect = nsXPConnect::GetXPConnect();
      if (!xpconnect)
         return NS_ERROR_FAILURE;
 
     NS_ADDREF(sXPConnect = xpconnect);
     NS_ADDREF(sJSContextStack = xpconnect);
@@ -3360,45 +3362,40 @@ nsresult nsScriptSecurityManager::Init()
 
     rv = bundleService->CreateBundle("chrome://global/locale/security/caps.properties", &sStrBundle);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Create our system principal singleton
     nsRefPtr<nsSystemPrincipal> system = new nsSystemPrincipal();
     NS_ENSURE_TRUE(system, NS_ERROR_OUT_OF_MEMORY);
 
-    JSPrincipals *jsprin;
-    rv = system->Init(&jsprin);
-    NS_ENSURE_SUCCESS(rv, rv);
-
     mSystemPrincipal = system;
 
     //-- Register security check callback in the JS engine
     //   Currently this is used to control access to function.caller
     nsCOMPtr<nsIJSRuntimeService> runtimeService =
         do_QueryInterface(sXPConnect, &rv);
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = runtimeService->GetRuntime(&sRuntime);
     NS_ENSURE_SUCCESS(rv, rv);
 
-    static JSSecurityCallbacks securityCallbacks = {
+    static const JSSecurityCallbacks securityCallbacks = {
         CheckObjectAccess,
-        NULL,
-        NULL,
+        nsJSPrincipals::Subsume,
+        nsJSPrincipals::Transcode,
+        ObjectPrincipalFinder,
         ContentSecurityPolicyPermitsJSAction
     };
 
-#ifdef DEBUG
-    JSSecurityCallbacks *oldcallbacks =
-#endif
-    JS_SetRuntimeSecurityCallbacks(sRuntime, &securityCallbacks);
-    NS_ASSERTION(!oldcallbacks, "Someone else set security callbacks!");
-
-    JS_SetTrustedPrincipals(sRuntime, jsprin);
+    MOZ_ASSERT(!JS_GetSecurityCallbacks(sRuntime));
+    JS_SetSecurityCallbacks(sRuntime, &securityCallbacks);
+    JS_InitDestroyPrincipalsCallback(sRuntime, nsJSPrincipals::Destroy);
+
+    JS_SetTrustedPrincipals(sRuntime, system);
 
     return NS_OK;
 }
 
 static nsScriptSecurityManager *gScriptSecMan = nsnull;
 
 jsid nsScriptSecurityManager::sEnabledID   = JSID_VOID;
 
@@ -3412,17 +3409,17 @@ nsScriptSecurityManager::~nsScriptSecuri
     delete mCapabilities;
     gScriptSecMan = nsnull;
 }
 
 void
 nsScriptSecurityManager::Shutdown()
 {
     if (sRuntime) {
-        JS_SetRuntimeSecurityCallbacks(sRuntime, NULL);
+        JS_SetSecurityCallbacks(sRuntime, NULL);
         JS_SetTrustedPrincipals(sRuntime, NULL);
         sRuntime = nsnull;
     }
     sEnabledID = JSID_VOID;
 
     NS_IF_RELEASE(sIOService);
     NS_IF_RELEASE(sXPConnect);
     NS_IF_RELEASE(sJSContextStack);
@@ -3440,23 +3437,16 @@ nsScriptSecurityManager::GetScriptSecuri
         nsresult rv;
         rv = ssManager->Init();
         NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to initialize nsScriptSecurityManager");
         if (NS_FAILED(rv)) {
             delete ssManager;
             return nsnull;
         }
  
-        rv = nsJSPrincipals::Startup();
-        if (NS_FAILED(rv)) {
-            NS_WARNING("can't initialize JS engine security protocol glue!");
-            delete ssManager;
-            return nsnull;
-        }
- 
         rv = sXPConnect->SetDefaultSecurityManager(ssManager,
                                                    nsIXPCSecurityManager::HOOK_ALL);
         if (NS_FAILED(rv)) {
             NS_WARNING("Failed to install xpconnect security manager!");
             delete ssManager;
             return nsnull;
         }
 
--- a/caps/src/nsSystemPrincipal.cpp
+++ b/caps/src/nsSystemPrincipal.cpp
@@ -57,42 +57,55 @@ NS_IMPL_QUERY_INTERFACE2_CI(nsSystemPrin
                             nsISerializable)
 NS_IMPL_CI_INTERFACE_GETTER2(nsSystemPrincipal,
                              nsIPrincipal,
                              nsISerializable)
 
 NS_IMETHODIMP_(nsrefcnt) 
 nsSystemPrincipal::AddRef()
 {
-  NS_PRECONDITION(PRInt32(mJSPrincipals.refcount) >= 0, "illegal refcnt");
-  nsrefcnt count = PR_ATOMIC_INCREMENT(&mJSPrincipals.refcount);
+  NS_PRECONDITION(PRInt32(refcount) >= 0, "illegal refcnt");
+  nsrefcnt count = PR_ATOMIC_INCREMENT(&refcount);
   NS_LOG_ADDREF(this, count, "nsSystemPrincipal", sizeof(*this));
   return count;
 }
 
 NS_IMETHODIMP_(nsrefcnt)
 nsSystemPrincipal::Release()
 {
-  NS_PRECONDITION(0 != mJSPrincipals.refcount, "dup release");
-  nsrefcnt count = PR_ATOMIC_DECREMENT(&mJSPrincipals.refcount);
+  NS_PRECONDITION(0 != refcount, "dup release");
+  nsrefcnt count = PR_ATOMIC_DECREMENT(&refcount);
   NS_LOG_RELEASE(this, count, "nsSystemPrincipal");
   if (count == 0) {
     delete this;
   }
 
   return count;
 }
 
+static const char SYSTEM_PRINCIPAL_SPEC[] = "[System Principal]";
+
+void
+nsSystemPrincipal::GetScriptLocation(nsACString &aStr)
+{
+    aStr.Assign(SYSTEM_PRINCIPAL_SPEC);
+}
+
+#ifdef DEBUG
+void nsSystemPrincipal::dumpImpl()
+{
+  fprintf(stderr, "nsSystemPrincipal (%p)\n", this);
+}
+#endif 
+
 
 ///////////////////////////////////////
 // Methods implementing nsIPrincipal //
 ///////////////////////////////////////
 
-#define SYSTEM_PRINCIPAL_SPEC "[System Principal]"
-
 NS_IMETHODIMP
 nsSystemPrincipal::GetPreferences(char** aPrefName, char** aID,
                                   char** aSubjectName,
                                   char** aGrantedList, char** aDeniedList,
                                   bool* aIsTrusted)
 {
     // The system principal should never be streamed out
     *aPrefName = nsnull;
@@ -275,26 +288,16 @@ nsSystemPrincipal::GetSecurityPolicy(voi
 }
 
 NS_IMETHODIMP
 nsSystemPrincipal::SetSecurityPolicy(void* aSecurityPolicy)
 {
     return NS_OK;
 }
 
-NS_IMETHODIMP
-nsSystemPrincipal::GetJSPrincipals(JSContext *cx, JSPrincipals **jsprin)
-{
-    NS_PRECONDITION(mJSPrincipals.nsIPrincipalPtr, "mJSPrincipals is uninitialized!");
-
-    JSPRINCIPALS_HOLD(cx, &mJSPrincipals);
-    *jsprin = &mJSPrincipals;
-    return NS_OK;
-}
-
 
 //////////////////////////////////////////
 // Methods implementing nsISerializable //
 //////////////////////////////////////////
 
 NS_IMETHODIMP
 nsSystemPrincipal::Read(nsIObjectInputStream* aStream)
 {
@@ -312,29 +315,11 @@ nsSystemPrincipal::Write(nsIObjectOutput
 /////////////////////////////////////////////
 // Constructor, Destructor, initialization //
 /////////////////////////////////////////////
 
 nsSystemPrincipal::nsSystemPrincipal()
 {
 }
 
-nsresult
-nsSystemPrincipal::Init(JSPrincipals **jsprin)
-{
-    // Use an nsCString so we only do the allocation once here and then
-    // share with nsJSPrincipals
-    nsCString str(SYSTEM_PRINCIPAL_SPEC);
-    if (!str.EqualsLiteral(SYSTEM_PRINCIPAL_SPEC)) {
-        NS_WARNING("Out of memory initializing system principal");
-        return NS_ERROR_OUT_OF_MEMORY;
-    }
-
-    nsresult rv = mJSPrincipals.Init(this, str);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    *jsprin = &mJSPrincipals;
-    return NS_OK;
-}
-
-nsSystemPrincipal::~nsSystemPrincipal(void)
+nsSystemPrincipal::~nsSystemPrincipal()
 {
 }
--- a/config/autoconf.mk.in
+++ b/config/autoconf.mk.in
@@ -296,16 +296,20 @@ MOZ_GIO_CFLAGS = @MOZ_GIO_CFLAGS@
 MOZ_GIO_LIBS = @MOZ_GIO_LIBS@
 
 MOZ_NATIVE_NSPR = @MOZ_NATIVE_NSPR@
 MOZ_NATIVE_NSS = @MOZ_NATIVE_NSS@
 
 MOZ_B2G_RIL = @MOZ_B2G_RIL@
 MOZ_B2G_BT = @MOZ_B2G_BT@
 
+MOZ_ASAN = @MOZ_ASAN@
+MOZ_CFLAGS_NSS = @MOZ_CFLAGS_NSS@
+MOZ_NO_WLZDEFS = @MOZ_NO_WLZDEFS@
+
 BUILD_CTYPES = @BUILD_CTYPES@
 
 COMPILE_ENVIRONMENT = @COMPILE_ENVIRONMENT@
 CROSS_COMPILE   = @CROSS_COMPILE@
 
 WCHAR_CFLAGS	= @WCHAR_CFLAGS@
 
 OS_CPPFLAGS	= @CPPFLAGS@
--- a/config/config.mk
+++ b/config/config.mk
@@ -784,18 +784,22 @@ OPTIMIZE_JARS_CMD = $(PYTHON) $(call cor
 CREATE_PRECOMPLETE_CMD = $(PYTHON) $(call core_abspath,$(topsrcdir)/config/createprecomplete.py)
 
 EXPAND_LIBS = $(PYTHON) -I$(DEPTH)/config $(topsrcdir)/config/expandlibs.py
 EXPAND_LIBS_EXEC = $(PYTHON) $(topsrcdir)/config/pythonpath.py -I$(DEPTH)/config $(topsrcdir)/config/expandlibs_exec.py
 EXPAND_LIBS_GEN = $(PYTHON) $(topsrcdir)/config/pythonpath.py -I$(DEPTH)/config $(topsrcdir)/config/expandlibs_gen.py
 EXPAND_AR = $(EXPAND_LIBS_EXEC) --extract -- $(AR)
 EXPAND_CC = $(EXPAND_LIBS_EXEC) --uselist -- $(CC)
 EXPAND_CCC = $(EXPAND_LIBS_EXEC) --uselist -- $(CCC)
-EXPAND_LD = $(EXPAND_LIBS_EXEC) --uselist $(if $(REORDER),--reorder $(REORDER))-- $(LD)
-EXPAND_MKSHLIB = $(EXPAND_LIBS_EXEC) --uselist $(if $(REORDER),--reorder $(REORDER))-- $(MKSHLIB)
+EXPAND_LD = $(EXPAND_LIBS_EXEC) --uselist -- $(LD)
+EXPAND_MKSHLIB_ARGS = --uselist
+ifdef SYMBOL_ORDER
+EXPAND_MKSHLIB_ARGS += --symbol-order $(SYMBOL_ORDER)
+endif
+EXPAND_MKSHLIB = $(EXPAND_LIBS_EXEC) $(EXPAND_MKSHLIB_ARGS) -- $(MKSHLIB)
 
 ifdef STDCXX_COMPAT
 ifneq ($(OS_ARCH),Darwin)
 CHECK_STDCXX = objdump -p $(1) | grep -e 'GLIBCXX_3\.4\.\(9\|[1-9][0-9]\)' > /dev/null && echo "TEST-UNEXPECTED-FAIL | | We don't want these libstdc++ symbols to be used:" && objdump -T $(1) | grep -e 'GLIBCXX_3\.4\.\(9\|[1-9][0-9]\)' && exit 1 || exit 0
 endif
 endif
 
 # autoconf.mk sets OBJ_SUFFIX to an error to avoid use before including
--- a/config/expandlibs_config.py.in
+++ b/config/expandlibs_config.py.in
@@ -49,8 +49,10 @@ AR_EXTRACT = "@AR_EXTRACT@".replace('$(A
 DLL_PREFIX = "@DLL_PREFIX@"
 LIB_PREFIX = "@LIB_PREFIX@"
 OBJ_SUFFIX = normalize_suffix("@OBJ_SUFFIX@")
 LIB_SUFFIX = normalize_suffix("@LIB_SUFFIX@")
 DLL_SUFFIX = normalize_suffix("@DLL_SUFFIX@")
 IMPORT_LIB_SUFFIX = normalize_suffix("@IMPORT_LIB_SUFFIX@")
 LIBS_DESC_SUFFIX = normalize_suffix("@LIBS_DESC_SUFFIX@")
 EXPAND_LIBS_LIST_STYLE = "@EXPAND_LIBS_LIST_STYLE@"
+EXPAND_LIBS_ORDER_STYLE = "@EXPAND_LIBS_ORDER_STYLE@"
+LD_PRINT_ICF_SECTIONS = "@LD_PRINT_ICF_SECTIONS@"
--- a/config/expandlibs_exec.py
+++ b/config/expandlibs_exec.py
@@ -44,29 +44,41 @@ from static libraries (or use those list
 
 With the --uselist argument (useful for e.g. $(CC)), it replaces all object
 files with a list file. This can be used to avoid limitations in the length
 of a command line. The kind of list file format used depends on the
 EXPAND_LIBS_LIST_STYLE variable: 'list' for MSVC style lists (@file.list)
 or 'linkerscript' for GNU ld linker scripts.
 See https://bugzilla.mozilla.org/show_bug.cgi?id=584474#c59 for more details.
 
-With the --reorder argument, followed by a file name, it will reorder the
-object files from the command line according to the order given in the file.
-Implies --extract.
+With the --symbol-order argument, followed by a file name, it will add the
+relevant linker options to change the order in which the linker puts the
+symbols appear in the resulting binary. Only works for ELF targets.
 '''
 from __future__ import with_statement
 import sys
 import os
 from expandlibs import ExpandArgs, relativize, isObject
 import expandlibs_config as conf
 from optparse import OptionParser
 import subprocess
 import tempfile
 import shutil
+import subprocess
+import re
+
+# The are the insert points for a GNU ld linker script, assuming a more
+# or less "standard" default linker script. This is not a dict because
+# order is important.
+SECTION_INSERT_BEFORE = [
+  ('.text', '.fini'),
+  ('.rodata', '.rodata1'),
+  ('.data.rel.ro', '.dynamic'),
+  ('.data', '.data1'),
+]
 
 class ExpandArgsMore(ExpandArgs):
     ''' Meant to be used as 'with ExpandArgsMore(args) as ...: '''
     def __enter__(self):
         self.tmp = []
         return self
         
     def __exit__(self, type, value, tb):
@@ -114,61 +126,203 @@ class ExpandArgsMore(ExpandArgs):
         fd, tmp = tempfile.mkstemp(suffix=".list",dir=os.curdir)
         if conf.EXPAND_LIBS_LIST_STYLE == "linkerscript":
             content = ["INPUT(%s)\n" % obj for obj in objs]
             ref = tmp
         elif conf.EXPAND_LIBS_LIST_STYLE == "list":
             content = ["%s\n" % obj for obj in objs]
             ref = "@" + tmp
         else:
+            os.close(fd)
             os.remove(tmp)
             return
         self.tmp.append(tmp)
         f = os.fdopen(fd, "w")
         f.writelines(content)
         f.close()
         idx = self.index(objs[0])
         newlist = self[0:idx] + [ref] + [item for item in self[idx:] if item not in objs]
         self[0:] = newlist
 
-    def reorder(self, order_list):
-        '''Given a list of file names without OBJ_SUFFIX, rearrange self
-        so that the object file names it contains are ordered according to
-        that list.
-        '''
-        objs = [o for o in self if isObject(o)]
-        if not objs: return
-        idx = self.index(objs[0])
-        # Keep everything before the first object, then the ordered objects,
-        # then any other objects, then any non-objects after the first object
-        objnames = dict([(os.path.splitext(os.path.basename(o))[0], o) for o in objs])
-        self[0:] = self[0:idx] + [objnames[o] for o in order_list if o in objnames] + \
-                   [o for o in objs if os.path.splitext(os.path.basename(o))[0] not in order_list] + \
-                   [x for x in self[idx:] if not isObject(x)]
+    def _getFoldedSections(self):
+        '''Returns a dict about folded sections.
+        When section A and B are folded into section C, the dict contains:
+        { 'A': 'C',
+          'B': 'C',
+          'C': ['A', 'B'] }'''
+        if not conf.LD_PRINT_ICF_SECTIONS:
+            return {}
+
+        proc = subprocess.Popen(self + [conf.LD_PRINT_ICF_SECTIONS], stdout = subprocess.PIPE, stderr = subprocess.PIPE)
+        (stdout, stderr) = proc.communicate()
+        result = {}
+        # gold's --print-icf-sections output looks like the following:
+        # ld: ICF folding section '.section' in file 'file.o'into '.section' in file 'file.o'
+        # In terms of words, chances are this will change in the future,
+        # especially considering "into" is misplaced. Splitting on quotes
+        # seems safer.
+        for l in stderr.split('\n'):
+            quoted = l.split("'")
+            if len(quoted) > 5 and quoted[1] != quoted[5]:
+                result[quoted[1]] = quoted[5]
+                if quoted[5] in result:
+                    result[quoted[5]].append(quoted[1])
+                else:
+                    result[quoted[5]] = [quoted[1]]
+        return result
+
+    def _getOrderedSections(self, ordered_symbols):
+        '''Given an ordered list of symbols, returns the corresponding list
+        of sections following the order.'''
+        if not conf.EXPAND_LIBS_ORDER_STYLE in ['linkerscript', 'section-ordering-file']:
+            raise Exception('EXPAND_LIBS_ORDER_STYLE "%s" is not supported' % conf.EXPAND_LIBS_ORDER_STYLE)
+        finder = SectionFinder([arg for arg in self if isObject(arg) or os.path.splitext(arg)[1] == conf.LIB_SUFFIX])
+        folded = self._getFoldedSections()
+        sections = set()
+        ordered_sections = []
+        for symbol in ordered_symbols:
+            symbol_sections = finder.getSections(symbol)
+            all_symbol_sections = []
+            for section in symbol_sections:
+                if section in folded:
+                    if isinstance(folded[section], str):
+                        section = folded[section]
+                    all_symbol_sections.append(section)
+                    all_symbol_sections.extend(folded[section])
+                else:
+                    all_symbol_sections.append(section)
+            for section in all_symbol_sections:
+                if not section in sections:
+                    ordered_sections.append(section)
+                    sections.add(section)
+        return ordered_sections
+
+    def orderSymbols(self, order):
+        '''Given a file containing a list of symbols, adds the appropriate
+        argument to make the linker put the symbols in that order.'''
+        with open(order) as file:
+            sections = self._getOrderedSections([l.strip() for l in file.readlines() if l.strip()])
+        split_sections = {}
+        linked_sections = [s[0] for s in SECTION_INSERT_BEFORE]
+        for s in sections:
+            for linked_section in linked_sections:
+                if s.startswith(linked_section):
+                    if linked_section in split_sections:
+                        split_sections[linked_section].append(s)
+                    else:
+                        split_sections[linked_section] = [s]
+                    break
+        content = []
+        # Order is important
+        linked_sections = [s for s in linked_sections if s in split_sections]
 
+        if conf.EXPAND_LIBS_ORDER_STYLE == 'section-ordering-file':
+            option = '-Wl,--section-ordering-file,%s'
+            content = sections
+            for linked_section in linked_sections:
+                content.extend(split_sections[linked_section])
+                content.append('%s.*' % linked_section)
+                content.append(linked_section)
+
+        elif conf.EXPAND_LIBS_ORDER_STYLE == 'linkerscript':
+            option = '-Wl,-T,%s'
+            section_insert_before = dict(SECTION_INSERT_BEFORE)
+            for linked_section in linked_sections:
+                content.append('SECTIONS {')
+                content.append('  %s : {' % linked_section)
+                content.extend('    *(%s)' % s for s in split_sections[linked_section])
+                content.append('  }')
+                content.append('}')
+                content.append('INSERT BEFORE %s' % section_insert_before[linked_section])
+        else:
+            raise Exception('EXPAND_LIBS_ORDER_STYLE "%s" is not supported' % conf.EXPAND_LIBS_ORDER_STYLE)
+
+        fd, tmp = tempfile.mkstemp(dir=os.curdir)
+        f = os.fdopen(fd, "w")
+        f.write('\n'.join(content)+'\n')
+        f.close()
+        self.tmp.append(tmp)
+        self.append(option % tmp)
+
+class SectionFinder(object):
+    '''Instances of this class allow to map symbol names to sections in
+    object files.'''
+
+    def __init__(self, objs):
+        '''Creates an instance, given a list of object files.'''
+        if not conf.EXPAND_LIBS_ORDER_STYLE in ['linkerscript', 'section-ordering-file']:
+            raise Exception('EXPAND_LIBS_ORDER_STYLE "%s" is not supported' % conf.EXPAND_LIBS_ORDER_STYLE)
+        self.mapping = {}
+        for obj in objs:
+            if not isObject(obj) and os.path.splitext(obj)[1] != conf.LIB_SUFFIX:
+                raise Exception('%s is not an object nor a static library' % obj)
+            for symbol, section in SectionFinder._getSymbols(obj):
+                sym = SectionFinder._normalize(symbol)
+                if sym in self.mapping:
+                    if not section in self.mapping[sym]:
+                        self.mapping[sym].append(section)
+                else:
+                    self.mapping[sym] = [section]
+
+    def getSections(self, symbol):
+        '''Given a symbol, returns a list of sections containing it or the
+        corresponding thunks. When the given symbol is a thunk, returns the
+        list of sections containing its corresponding normal symbol and the
+        other thunks for that symbol.'''
+        sym = SectionFinder._normalize(symbol)
+        if sym in self.mapping:
+            return self.mapping[sym]
+        return []
+
+    @staticmethod
+    def _normalize(symbol):
+        '''For normal symbols, return the given symbol. For thunks, return
+        the corresponding normal symbol.'''
+        if re.match('^_ZThn[0-9]+_', symbol):
+            return re.sub('^_ZThn[0-9]+_', '_Z', symbol)
+        return symbol
+
+    @staticmethod
+    def _getSymbols(obj):
+        '''Returns a list of (symbol, section) contained in the given object
+        file.'''
+        proc = subprocess.Popen(['objdump', '-t', obj], stdout = subprocess.PIPE, stderr = subprocess.PIPE)
+        (stdout, stderr) = proc.communicate()
+        syms = []
+        for line in stdout.splitlines():
+            # Each line has the following format:
+            # <addr> [lgu!][w ][C ][W ][Ii ][dD ][FfO ] <section>\t<length> <symbol>
+            tmp = line.split(' ',1)
+            # This gives us ["<addr>", "[lgu!][w ][C ][W ][Ii ][dD ][FfO ] <section>\t<length> <symbol>"]
+            # We only need to consider cases where "<section>\t<length> <symbol>" is present,
+            # and where the [FfO] flag is either F (function) or O (object).
+            if len(tmp) > 1 and len(tmp[1]) > 6 and tmp[1][6] in ['O', 'F']:
+                tmp = tmp[1][8:].split()
+                # That gives us ["<section>","<length>", "<symbol>"]
+                syms.append((tmp[-1], tmp[0]))
+        return syms
 
 def main():
     parser = OptionParser()
     parser.add_option("--extract", action="store_true", dest="extract",
         help="when a library has no descriptor file, extract it first, when possible")
     parser.add_option("--uselist", action="store_true", dest="uselist",
         help="use a list file for objects when executing a command")
     parser.add_option("--verbose", action="store_true", dest="verbose",
         help="display executed command and temporary files content")
-    parser.add_option("--reorder", dest="reorder",
-        help="reorder the objects according to the given list", metavar="FILE")
+    parser.add_option("--symbol-order", dest="symbol_order", metavar="FILE",