Merge mozilla-central & fx-team
authorPaul O’Shannessy <paul@oshannessy.com>
Thu, 03 Nov 2011 16:12:06 -0700
changeset 79707 3491b2f021bf73abb8e9ed8afba6bd60b47f42e3
parent 79706 87ce80260f222ea7e7c831b55e677d8ebfe08ad5 (current diff)
parent 79695 f8d66a792ddc291c66104f3c5280b53c17319432 (diff)
child 79748 e6893e6c883f03e25337e2e7c4ce5d6fca85ad53
child 79816 8a0ec6782f96e2b1d89102c3d89543b406091f02
push id21420
push userposhannessy@mozilla.com
push dateFri, 04 Nov 2011 01:43:16 +0000
treeherdermozilla-central@3491b2f021bf [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone10.0a1
first release with
nightly linux32
3491b2f021bf / 10.0a1 / 20111104031147 / files
nightly linux64
3491b2f021bf / 10.0a1 / 20111104031147 / files
nightly mac
3491b2f021bf / 10.0a1 / 20111104031147 / files
nightly win32
3491b2f021bf / 10.0a1 / 20111104031147 / files
nightly win64
3491b2f021bf / 10.0a1 / 20111104031147 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central & fx-team
new file mode 100644
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,41 @@
+# .gitignore - List of filenames git should ignore
+
+# Filenames that should be ignored wherever they appear
+*~
+*.pyc
+*.pyo
+TAGS
+tags
+ID
+.DS_Store*
+
+# Vim swap files.
+.*.sw[a-z]
+
+# User files that may appear at the root
+.mozconfig
+mozconfig
+configure
+config.cache
+config.log
+
+# Empty marker file that's generated when we check out NSS
+security/manager/.nss.checkout
+
+# Build directories
+obj/*
+
+# Build directories for js shell
+*/_DBG.OBJ/
+*/_OPT.OBJ/
+
+# SpiderMonkey configury
+js/src/configure
+js/src/autom4te.cache
+# SpiderMonkey test result logs
+js/src/tests/results-*.html
+js/src/tests/results-*.txt
+
+# Java HTML5 parser classes
+parser/html/java/htmlparser/
+parser/html/java/javaparser/
--- a/accessible/public/nsIAccessibleEvent.idl
+++ b/accessible/public/nsIAccessibleEvent.idl
@@ -54,17 +54,17 @@ interface nsIDOMNode;
  * the event and its target. To listen to in-process accessibility invents,
  * make your object an nsIObserver, and listen for accessible-event by 
  * using code something like this:
  *   nsCOMPtr<nsIObserverService> observerService = 
  *     do_GetService("@mozilla.org/observer-service;1", &rv);
  *   if (NS_SUCCEEDED(rv)) 
  *     rv = observerService->AddObserver(this, "accessible-event", PR_TRUE);
  */
-[scriptable, uuid(fd1378c5-c606-4a5e-a321-8e7fc107e5cf)]
+[scriptable, uuid(7f66a33a-9ed7-4fd4-87a8-e431b0f43368)]
 interface nsIAccessibleEvent : nsISupports
 {
   /**
    * An object has been created.
    */
   const unsigned long EVENT_SHOW = 0x0001;
 
   /**
@@ -275,17 +275,22 @@ interface nsIAccessibleEvent : nsISuppor
   const unsigned long EVENT_DOCUMENT_ATTRIBUTES_CHANGED = 0x002A;
 
   /**
    * The contents of the document have changed.
    */
   const unsigned long EVENT_DOCUMENT_CONTENT_CHANGED = 0x002B;
 
   const unsigned long EVENT_PROPERTY_CHANGED = 0x002C;
-  const unsigned long EVENT_SELECTION_CHANGED = 0x002D;
+
+  /**
+   * A slide changed in a presentation document or a page boundary was
+   * crossed in a word processing document.
+   */
+  const unsigned long EVENT_PAGE_CHANGED = 0x002D;
 
   /**
    * A text object's attributes changed.
    * Also see EVENT_OBJECT_ATTRIBUTE_CHANGED.
    */
   const unsigned long EVENT_TEXT_ATTRIBUTE_CHANGED = 0x002E;
 
   /**
@@ -432,25 +437,19 @@ interface nsIAccessibleEvent : nsISuppor
   const unsigned long EVENT_HYPERTEXT_NLINKS_CHANGED = 0x0054;
 
   /**
    * An object's attributes changed. Also see EVENT_TEXT_ATTRIBUTE_CHANGED.
    */
   const unsigned long EVENT_OBJECT_ATTRIBUTE_CHANGED = 0x0055;
 
   /**
-   * A slide changed in a presentation document or a page boundary was
-   * crossed in a word processing document.
-   */
-  const unsigned long EVENT_PAGE_CHANGED = 0x0056;
-
-  /**
    * Help make sure event map does not get out-of-line.
    */
-  const unsigned long EVENT_LAST_ENTRY = 0x0057;
+  const unsigned long EVENT_LAST_ENTRY = 0x0056;
 
   /**
    * The type of event, based on the enumerated event values
    * defined in this interface.
    */
   readonly attribute unsigned long eventType;
   
   /**
--- a/accessible/src/atk/nsAccessibleWrap.cpp
+++ b/accessible/src/atk/nsAccessibleWrap.cpp
@@ -1084,20 +1084,34 @@ nsAccessibleWrap::FirePlatformEvent(AccE
         nsCOMPtr<nsIAccessibleValue> value(do_QueryObject(accessible));
         if (value) {    // Make sure this is a numeric value
             // Don't fire for MSAA string value changes (e.g. text editing)
             // ATK values are always numeric
             g_object_notify( (GObject*)atkObj, "accessible-value" );
         }
       } break;
 
-    case nsIAccessibleEvent::EVENT_SELECTION_CHANGED:
-        MAI_LOG_DEBUG(("\n\nReceived: EVENT_SELECTION_CHANGED\n"));
-        g_signal_emit_by_name(atkObj, "selection_changed");
-        break;
+    case nsIAccessibleEvent::EVENT_SELECTION:
+    case nsIAccessibleEvent::EVENT_SELECTION_ADD:
+    case nsIAccessibleEvent::EVENT_SELECTION_REMOVE:
+    {
+      // XXX: dupe events may be fired
+      MAI_LOG_DEBUG(("\n\nReceived: EVENT_SELECTION_CHANGED\n"));
+      AccSelChangeEvent* selChangeEvent = downcast_accEvent(aEvent);
+      g_signal_emit_by_name(nsAccessibleWrap::GetAtkObject(selChangeEvent->Widget()),
+                            "selection_changed");
+      break;
+    }
+
+    case nsIAccessibleEvent::EVENT_SELECTION_WITHIN:
+    {
+      MAI_LOG_DEBUG(("\n\nReceived: EVENT_SELECTION_CHANGED\n"));
+      g_signal_emit_by_name(atkObj, "selection_changed");
+      break;
+    }
 
     case nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED:
         MAI_LOG_DEBUG(("\n\nReceived: EVENT_TEXT_SELECTION_CHANGED\n"));
         g_signal_emit_by_name(atkObj, "text_selection_changed");
         break;
 
     case nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED:
       {
--- a/accessible/src/base/AccEvent.cpp
+++ b/accessible/src/base/AccEvent.cpp
@@ -326,16 +326,38 @@ AccCaretMoveEvent::CreateXPCOMObject()
 {
   nsAccEvent* event = new nsAccCaretMoveEvent(this);
   NS_IF_ADDREF(event);
   return event;
 }
 
 
 ////////////////////////////////////////////////////////////////////////////////
+// AccSelChangeEvent
+////////////////////////////////////////////////////////////////////////////////
+
+AccSelChangeEvent::
+  AccSelChangeEvent(nsAccessible* aWidget, nsAccessible* aItem,
+                    SelChangeType aSelChangeType) :
+    AccEvent(0, aItem, eAutoDetect, eCoalesceSelectionChange),
+    mWidget(aWidget), mItem(aItem), mSelChangeType(aSelChangeType),
+    mPreceedingCount(0), mPackedEvent(nsnull)
+{
+  if (aSelChangeType == eSelectionAdd) {
+    if (mWidget->GetSelectedItem(1))
+      mEventType = nsIAccessibleEvent::EVENT_SELECTION_ADD;
+    else
+      mEventType = nsIAccessibleEvent::EVENT_SELECTION;
+  } else {
+    mEventType = nsIAccessibleEvent::EVENT_SELECTION_REMOVE;
+  }
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
 // AccTableChangeEvent
 ////////////////////////////////////////////////////////////////////////////////
 
 AccTableChangeEvent::
   AccTableChangeEvent(nsAccessible* aAccessible, PRUint32 aEventType,
                       PRInt32 aRowOrColIndex, PRInt32 aNumRowsOrCols) :
   AccEvent(aEventType, aAccessible),
   mRowOrColIndex(aRowOrColIndex), mNumRowsOrCols(aNumRowsOrCols)
--- a/accessible/src/base/AccEvent.h
+++ b/accessible/src/base/AccEvent.h
@@ -77,16 +77,19 @@ public:
      //    subtree or the same node, only the umbrella event on the ancestor
      //    will be emitted.
      eCoalesceFromSameSubtree,
 
     // eCoalesceOfSameType : For events of the same type, only the newest event
     // will be processed.
     eCoalesceOfSameType,
 
+    // eCoalesceSelectionChange: coalescence of selection change events.
+    eCoalesceSelectionChange,
+
      // eRemoveDupes : For repeat events, only the newest event in queue
      //    will be emitted.
      eRemoveDupes,
 
      // eDoNotEmit : This event is confirmed as a duplicate, do not emit it.
      eDoNotEmit
   };
 
@@ -120,16 +123,17 @@ public:
   enum EventGroup {
     eGenericEvent,
     eStateChangeEvent,
     eTextChangeEvent,
     eMutationEvent,
     eHideEvent,
     eShowEvent,
     eCaretMoveEvent,
+    eSelectionChangeEvent,
     eTableChangeEvent
   };
 
   static const EventGroup kEventGroup = eGenericEvent;
   virtual unsigned int GetEventGroups() const
   {
     return 1U << eGenericEvent;
   }
@@ -322,20 +326,47 @@ public:
 private:
   PRInt32 mCaretOffset;
 };
 
 
 /**
  * Accessible widget selection change event.
  */
-class AccSelectionChangeEvent : public AccEvent
+class AccSelChangeEvent : public AccEvent
 {
 public:
+  enum SelChangeType {
+    eSelectionAdd,
+    eSelectionRemove
+  };
 
+  AccSelChangeEvent(nsAccessible* aWidget, nsAccessible* aItem,
+                    SelChangeType aSelChangeType);
+
+  virtual ~AccSelChangeEvent() { }
+
+  // AccEvent
+  static const EventGroup kEventGroup = eSelectionChangeEvent;
+  virtual unsigned int GetEventGroups() const
+  {
+    return AccEvent::GetEventGroups() | (1U << eSelectionChangeEvent);
+  }
+
+  // AccSelChangeEvent
+  nsAccessible* Widget() const { return mWidget; }
+
+private:
+  nsRefPtr<nsAccessible> mWidget;
+  nsRefPtr<nsAccessible> mItem;
+  SelChangeType mSelChangeType;
+  PRUint32 mPreceedingCount;
+  AccSelChangeEvent* mPackedEvent;
+
+  friend class NotificationController;
 };
 
 
 /**
  * Accessible table change event.
  */
 class AccTableChangeEvent : public AccEvent
 {
--- a/accessible/src/base/NotificationController.cpp
+++ b/accessible/src/base/NotificationController.cpp
@@ -46,16 +46,20 @@
 #include "nsTextAccessible.h"
 #include "FocusManager.h"
 #include "TextUpdater.h"
 
 #include "mozilla/dom/Element.h"
 
 using namespace mozilla::a11y;
 
+// Defines the number of selection add/remove events in the queue when they
+// aren't packed into single selection within event.
+const unsigned int kSelChangeCountToPack = 5;
+
 ////////////////////////////////////////////////////////////////////////////////
 // NotificationCollector
 ////////////////////////////////////////////////////////////////////////////////
 
 NotificationController::NotificationController(nsDocAccessible* aDocument,
                                                nsIPresShell* aPresShell) :
   mObservingState(eNotObservingRefresh), mDocument(aDocument),
   mPresShell(aPresShell)
@@ -486,16 +490,36 @@ NotificationController::CoalesceEvents()
             accEvent->mEventRule == tailEvent->mEventRule &&
             accEvent->mNode == tailEvent->mNode) {
           tailEvent->mEventRule = AccEvent::eDoNotEmit;
           return;
         }
       }
     } break; // case eRemoveDupes
 
+    case AccEvent::eCoalesceSelectionChange:
+    {
+      AccSelChangeEvent* tailSelChangeEvent = downcast_accEvent(tailEvent);
+      PRInt32 index = tail - 1;
+      for (; index >= 0; index--) {
+        AccEvent* thisEvent = mEvents[index];
+        if (thisEvent->mEventRule == tailEvent->mEventRule) {
+          AccSelChangeEvent* thisSelChangeEvent =
+            downcast_accEvent(thisEvent);
+
+          // Coalesce selection change events within same control.
+          if (tailSelChangeEvent->mWidget == thisSelChangeEvent->mWidget) {
+            CoalesceSelChangeEvents(tailSelChangeEvent, thisSelChangeEvent, index);
+            return;
+          }
+        }
+      }
+
+    } break; // eCoalesceSelectionChange
+
     default:
       break; // case eAllowDupes, eDoNotEmit
   } // switch
 }
 
 void
 NotificationController::ApplyToSiblings(PRUint32 aStart, PRUint32 aEnd,
                                         PRUint32 aEventType, nsINode* aNode,
@@ -507,16 +531,96 @@ NotificationController::ApplyToSiblings(
         accEvent->mEventRule != AccEvent::eDoNotEmit && accEvent->mNode &&
         accEvent->mNode->GetNodeParent() == aNode->GetNodeParent()) {
       accEvent->mEventRule = aEventRule;
     }
   }
 }
 
 void
+NotificationController::CoalesceSelChangeEvents(AccSelChangeEvent* aTailEvent,
+                                                AccSelChangeEvent* aThisEvent,
+                                                PRInt32 aThisIndex)
+{
+  aTailEvent->mPreceedingCount = aThisEvent->mPreceedingCount + 1;
+
+  // Pack all preceding events into single selection within event
+  // when we receive too much selection add/remove events.
+  if (aTailEvent->mPreceedingCount >= kSelChangeCountToPack) {
+    aTailEvent->mEventType = nsIAccessibleEvent::EVENT_SELECTION_WITHIN;
+    aTailEvent->mAccessible = aTailEvent->mWidget;
+    aThisEvent->mEventRule = AccEvent::eDoNotEmit;
+
+    // Do not emit any preceding selection events for same widget if they
+    // weren't coalesced yet.
+    if (aThisEvent->mEventType != nsIAccessibleEvent::EVENT_SELECTION_WITHIN) {
+      for (PRInt32 jdx = aThisIndex - 1; jdx >= 0; jdx--) {
+        AccEvent* prevEvent = mEvents[jdx];
+        if (prevEvent->mEventRule == aTailEvent->mEventRule) {
+          AccSelChangeEvent* prevSelChangeEvent =
+            downcast_accEvent(prevEvent);
+          if (prevSelChangeEvent->mWidget == aTailEvent->mWidget)
+            prevSelChangeEvent->mEventRule = AccEvent::eDoNotEmit;
+        }
+      }
+    }
+    return;
+  }
+
+  // Pack sequential selection remove and selection add events into
+  // single selection change event.
+  if (aTailEvent->mPreceedingCount == 1 &&
+      aTailEvent->mItem != aThisEvent->mItem) {
+    if (aTailEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd &&
+        aThisEvent->mSelChangeType == AccSelChangeEvent::eSelectionRemove) {
+      aThisEvent->mEventRule = AccEvent::eDoNotEmit;
+      aTailEvent->mEventType = nsIAccessibleEvent::EVENT_SELECTION;
+      aTailEvent->mPackedEvent = aThisEvent;
+      return;
+    }
+
+    if (aThisEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd &&
+        aTailEvent->mSelChangeType == AccSelChangeEvent::eSelectionRemove) {
+      aTailEvent->mEventRule = AccEvent::eDoNotEmit;
+      aThisEvent->mEventType = nsIAccessibleEvent::EVENT_SELECTION;
+      aThisEvent->mPackedEvent = aThisEvent;
+      return;
+    }
+  }
+
+  // Unpack the packed selection change event because we've got one
+  // more selection add/remove.
+  if (aThisEvent->mEventType == nsIAccessibleEvent::EVENT_SELECTION) {
+    if (aThisEvent->mPackedEvent) {
+      aThisEvent->mPackedEvent->mEventType =
+        aThisEvent->mPackedEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd ?
+          nsIAccessibleEvent::EVENT_SELECTION_ADD :
+          nsIAccessibleEvent::EVENT_SELECTION_REMOVE;
+
+      aThisEvent->mPackedEvent->mEventRule =
+        AccEvent::eCoalesceSelectionChange;
+
+      aThisEvent->mPackedEvent = nsnull;
+    }
+
+    aThisEvent->mEventType =
+      aThisEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd ?
+        nsIAccessibleEvent::EVENT_SELECTION_ADD :
+        nsIAccessibleEvent::EVENT_SELECTION_REMOVE;
+
+    return;
+  }
+
+  // Convert into selection add since control has single selection but other
+  // selection events for this control are queued.
+  if (aTailEvent->mEventType == nsIAccessibleEvent::EVENT_SELECTION)
+    aTailEvent->mEventType = nsIAccessibleEvent::EVENT_SELECTION_ADD;
+}
+
+void
 NotificationController::CoalesceTextChangeEventsFor(AccHideEvent* aTailEvent,
                                                     AccHideEvent* aThisEvent)
 {
   // XXX: we need a way to ignore SplitNode and JoinNode() when they do not
   // affect the text within the hypertext.
 
   AccTextChangeEvent* textEvent = aThisEvent->mTextChangeEvent;
   if (!textEvent)
--- a/accessible/src/base/NotificationController.h
+++ b/accessible/src/base/NotificationController.h
@@ -246,21 +246,21 @@ private:
    * @param aEventRule       the event rule to be applied
    *                         (should be eDoNotEmit or eAllowDupes)
    */
   void ApplyToSiblings(PRUint32 aStart, PRUint32 aEnd,
                        PRUint32 aEventType, nsINode* aNode,
                        AccEvent::EEventRule aEventRule);
 
   /**
-   * Do not emit one of two given reorder events fired for DOM nodes in the case
-   * when one DOM node is in parent chain of second one.
+   * Coalesce two selection change events within the same select control.
    */
-  void CoalesceReorderEventsFromSameTree(AccEvent* aAccEvent,
-                                         AccEvent* aDescendantAccEvent);
+  void CoalesceSelChangeEvents(AccSelChangeEvent* aTailEvent,
+                               AccSelChangeEvent* aThisEvent,
+                               PRInt32 aThisIndex);
 
   /**
    * Coalesce text change events caused by sibling hide events.
    */
   void CoalesceTextChangeEventsFor(AccHideEvent* aTailEvent,
                                    AccHideEvent* aThisEvent);
   void CoalesceTextChangeEventsFor(AccShowEvent* aTailEvent,
                                    AccShowEvent* aThisEvent);
--- a/accessible/src/base/nsAccessibilityService.h
+++ b/accessible/src/base/nsAccessibilityService.h
@@ -468,17 +468,17 @@ static const char kEventTypeNames[][40] 
   "minimize start",                          // EVENT_MINIMIZE_START
   "minimize end",                            // EVENT_MINIMIZE_END
   "document load complete",                  // EVENT_DOCUMENT_LOAD_COMPLETE
   "document reload",                         // EVENT_DOCUMENT_RELOAD
   "document load stopped",                   // EVENT_DOCUMENT_LOAD_STOPPED
   "document attributes changed",             // EVENT_DOCUMENT_ATTRIBUTES_CHANGED
   "document content changed",                // EVENT_DOCUMENT_CONTENT_CHANGED
   "property changed",                        // EVENT_PROPERTY_CHANGED
-  "selection changed",                       // EVENT_SELECTION_CHANGED
+  "page changed",                           // EVENT_PAGE_CHANGED
   "text attribute changed",                  // EVENT_TEXT_ATTRIBUTE_CHANGED
   "text caret moved",                        // EVENT_TEXT_CARET_MOVED
   "text changed",                            // EVENT_TEXT_CHANGED
   "text inserted",                           // EVENT_TEXT_INSERTED
   "text removed",                            // EVENT_TEXT_REMOVED
   "text updated",                            // EVENT_TEXT_UPDATED
   "text selection changed",                  // EVENT_TEXT_SELECTION_CHANGED
   "visible data changed",                    // EVENT_VISIBLE_DATA_CHANGED
@@ -509,17 +509,16 @@ static const char kEventTypeNames[][40] 
   "hyperlink number of anchors changed",     // EVENT_HYPERLINK_NUMBER_OF_ANCHORS_CHANGED
   "hyperlink selected link changed",         // EVENT_HYPERLINK_SELECTED_LINK_CHANGED
   "hypertext link activated",                // EVENT_HYPERTEXT_LINK_ACTIVATED
   "hypertext link selected",                 // EVENT_HYPERTEXT_LINK_SELECTED
   "hyperlink start index changed",           // EVENT_HYPERLINK_START_INDEX_CHANGED
   "hypertext changed",                       // EVENT_HYPERTEXT_CHANGED
   "hypertext links count changed",           // EVENT_HYPERTEXT_NLINKS_CHANGED
   "object attribute changed",                // EVENT_OBJECT_ATTRIBUTE_CHANGED
-  "page changed"                             // EVENT_PAGE_CHANGED
 };
 
 /**
  * Map nsIAccessibleRelation constants to strings. Used by
  * nsIAccessibleRetrieval::getStringRelationType() method.
  */
 static const char kRelationTypeNames[][20] = {
   "unknown",             // RELATION_NUL
--- a/accessible/src/base/nsDocAccessible.cpp
+++ b/accessible/src/base/nsDocAccessible.cpp
@@ -1037,42 +1037,33 @@ nsDocAccessible::AttributeChangedImpl(ns
   if (aAttribute == nsGkAtoms::aria_busy) {
     bool isOn = aContent->AttrValueIs(aNameSpaceID, aAttribute,
                                         nsGkAtoms::_true, eCaseMatters);
     nsRefPtr<AccEvent> event = new AccStateChangeEvent(aContent, states::BUSY, isOn);
     FireDelayedAccessibleEvent(event);
     return;
   }
 
-  if (aAttribute == nsGkAtoms::selected ||
+  // ARIA or XUL selection
+  if ((aContent->IsXUL() && aAttribute == nsGkAtoms::selected) ||
       aAttribute == nsGkAtoms::aria_selected) {
-    // ARIA or XUL selection
+    nsAccessible* item = GetAccessible(aContent);
+    nsAccessible* widget =
+      nsAccUtils::GetSelectableContainer(item, item->State());
+    if (widget) {
+      AccSelChangeEvent::SelChangeType selChangeType =
+        aContent->AttrValueIs(aNameSpaceID, aAttribute,
+                              nsGkAtoms::_true, eCaseMatters) ?
+          AccSelChangeEvent::eSelectionAdd : AccSelChangeEvent::eSelectionRemove;
 
-    nsAccessible *multiSelect =
-      nsAccUtils::GetMultiSelectableContainer(aContent);
-    // XXX: Multi selects are handled here only (bug 414302).
-    if (multiSelect) {
-      // Need to find the right event to use here, SELECTION_WITHIN would
-      // seem right but we had started using it for something else
-      FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_SELECTION_WITHIN,
-                                 multiSelect->GetNode(),
-                                 AccEvent::eAllowDupes);
-
-      static nsIContent::AttrValuesArray strings[] =
-        {&nsGkAtoms::_empty, &nsGkAtoms::_false, nsnull};
-      if (aContent->FindAttrValueIn(kNameSpaceID_None, aAttribute,
-                                    strings, eCaseMatters) >= 0) {
-        FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_SELECTION_REMOVE,
-                                   aContent);
-        return;
-      }
-
-      FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_SELECTION_ADD,
-                                 aContent);
+      nsRefPtr<AccEvent> event =
+        new AccSelChangeEvent(widget, item, selChangeType);
+      FireDelayedAccessibleEvent(event);
     }
+    return;
   }
 
   if (aAttribute == nsGkAtoms::contenteditable) {
     nsRefPtr<AccEvent> editableChangeEvent =
       new AccStateChangeEvent(aContent, states::EDITABLE);
     FireDelayedAccessibleEvent(editableChangeEvent);
     return;
   }
@@ -1204,17 +1195,28 @@ void nsDocAccessible::ContentAppended(ns
 {
 }
 
 void nsDocAccessible::ContentStateChanged(nsIDocument* aDocument,
                                           nsIContent* aContent,
                                           nsEventStates aStateMask)
 {
   if (aStateMask.HasState(NS_EVENT_STATE_CHECKED)) {
-    nsHTMLSelectOptionAccessible::SelectionChangedIfOption(aContent);
+    nsAccessible* item = GetAccessible(aContent);
+    if (item) {
+      nsAccessible* widget = item->ContainerWidget();
+      if (widget && widget->IsSelect()) {
+        AccSelChangeEvent::SelChangeType selChangeType =
+          aContent->AsElement()->State().HasState(NS_EVENT_STATE_CHECKED) ?
+            AccSelChangeEvent::eSelectionAdd : AccSelChangeEvent::eSelectionRemove;
+        nsRefPtr<AccEvent> event = new AccSelChangeEvent(widget, item,
+                                                         selChangeType);
+        FireDelayedAccessibleEvent(event);
+      }
+    }
   }
 
   if (aStateMask.HasState(NS_EVENT_STATE_INVALID)) {
     nsRefPtr<AccEvent> event =
       new AccStateChangeEvent(aContent, states::INVALID, true);
     FireDelayedAccessibleEvent(event);
    }
 }
--- a/accessible/src/base/nsRootAccessible.cpp
+++ b/accessible/src/base/nsRootAccessible.cpp
@@ -468,16 +468,18 @@ nsRootAccessible::ProcessDOMEvent(nsIDOM
 
     nsRefPtr<AccEvent> accEvent =
       new AccStateChangeEvent(accessible, states::EXPANDED, isEnabled);
     nsEventShell::FireEvent(accEvent);
     return;
   }
 
   if (treeItemAccessible && eventType.EqualsLiteral("select")) {
+    // XXX: We shouldn't be based on DOM select event which doesn't provide us
+    // any context info. We should integrate into nsTreeSelection instead.
     // If multiselect tree, we should fire selectionadd or selection removed
     if (FocusMgr()->HasDOMFocus(targetNode)) {
       nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSel =
         do_QueryInterface(targetNode);
       nsAutoString selType;
       multiSel->GetSelType(selType);
       if (selType.IsEmpty() || !selType.EqualsLiteral("single")) {
         // XXX: We need to fire EVENT_SELECTION_ADD and EVENT_SELECTION_REMOVE
--- a/accessible/src/html/nsHTMLSelectAccessible.cpp
+++ b/accessible/src/html/nsHTMLSelectAccessible.cpp
@@ -411,69 +411,17 @@ nsHTMLSelectOptionAccessible::SetSelecte
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsHTMLSelectOptionAccessible: Widgets
 
 nsAccessible*
 nsHTMLSelectOptionAccessible::ContainerWidget() const
 {
-  if (mParent && mParent->IsListControl()) {
-    nsAccessible* grandParent = mParent->Parent();
-    if (grandParent && grandParent->IsCombobox())
-      return grandParent;
-
-    return mParent;
-  }
-  return nsnull;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// nsHTMLSelectOptionAccessible: static methods
-
-void
-nsHTMLSelectOptionAccessible::SelectionChangedIfOption(nsIContent *aPossibleOptionNode)
-{
-  if (!aPossibleOptionNode ||
-      aPossibleOptionNode->Tag() != nsGkAtoms::option ||
-      !aPossibleOptionNode->IsHTML()) {
-    return;
-  }
-
-  nsAccessible *multiSelect =
-    nsAccUtils::GetMultiSelectableContainer(aPossibleOptionNode);
-  if (!multiSelect)
-    return;
-
-  nsAccessible *option = GetAccService()->GetAccessible(aPossibleOptionNode);
-  if (!option)
-    return;
-
-
-  nsRefPtr<AccEvent> selWithinEvent =
-    new AccEvent(nsIAccessibleEvent::EVENT_SELECTION_WITHIN, multiSelect);
-
-  if (!selWithinEvent)
-    return;
-
-  option->GetDocAccessible()->FireDelayedAccessibleEvent(selWithinEvent);
-
-  PRUint64 state = option->State();
-  PRUint32 eventType;
-  if (state & states::SELECTED) {
-    eventType = nsIAccessibleEvent::EVENT_SELECTION_ADD;
-  }
-  else {
-    eventType = nsIAccessibleEvent::EVENT_SELECTION_REMOVE;
-  }
-
-  nsRefPtr<AccEvent> selAddRemoveEvent = new AccEvent(eventType, option);
-
-  if (selAddRemoveEvent)
-    option->GetDocAccessible()->FireDelayedAccessibleEvent(selAddRemoveEvent);
+  return mParent && mParent->IsListControl() ? mParent : nsnull;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsHTMLSelectOptionAccessible: private methods
 
 nsIContent*
 nsHTMLSelectOptionAccessible::GetSelectState(PRUint64* aState)
 {
@@ -733,36 +681,32 @@ nsHTMLComboboxAccessible::AreItemsOperab
 {
   nsIComboboxControlFrame* comboboxFrame = do_QueryFrame(GetFrame());
   return comboboxFrame && comboboxFrame->IsDroppedDown();
 }
 
 nsAccessible*
 nsHTMLComboboxAccessible::CurrentItem()
 {
-  // No current item for collapsed combobox.
-  return SelectedOption(true);
+  return AreItemsOperable() ? mListAccessible->CurrentItem() : nsnull;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsHTMLComboboxAccessible: protected
 
 nsAccessible*
-nsHTMLComboboxAccessible::SelectedOption(bool aIgnoreIfCollapsed) const
+nsHTMLComboboxAccessible::SelectedOption() const
 {
   nsIFrame* frame = GetFrame();
   nsIComboboxControlFrame* comboboxFrame = do_QueryFrame(frame);
-  if (comboboxFrame) {
-    if (aIgnoreIfCollapsed && !comboboxFrame->IsDroppedDown())
-      return nsnull;
+  if (!comboboxFrame)
+    return nsnull;
 
-    frame = comboboxFrame->GetDropDown();
-  }
-
-  nsIListControlFrame* listControlFrame = do_QueryFrame(frame);
+  nsIListControlFrame* listControlFrame =
+    do_QueryFrame(comboboxFrame->GetDropDown());
   if (listControlFrame) {
     nsCOMPtr<nsIContent> activeOptionNode = listControlFrame->GetCurrentOption();
     if (activeOptionNode) {
       nsDocAccessible* document = GetDocAccessible();
       if (document)
         return document->GetAccessible(activeOptionNode);
     }
   }
@@ -853,8 +797,24 @@ void nsHTMLComboboxListAccessible::GetBo
   if (!frame) {
     *aBoundingFrame = nsnull;
     return;
   }
 
   *aBoundingFrame = frame->GetParent();
   aBounds = (*aBoundingFrame)->GetRect();
 }
+
+////////////////////////////////////////////////////////////////////////////////
+// nsHTMLComboboxListAccessible: Widgets
+
+bool
+nsHTMLComboboxListAccessible::IsActiveWidget() const
+{
+  return mParent && mParent->IsActiveWidget();
+}
+
+bool
+nsHTMLComboboxListAccessible::AreItemsOperable() const
+{
+  return mParent && mParent->AreItemsOperable();
+}
+
--- a/accessible/src/html/nsHTMLSelectAccessible.h
+++ b/accessible/src/html/nsHTMLSelectAccessible.h
@@ -125,18 +125,16 @@ public:
                                           PRInt32 *aSetSize);
 
   // ActionAccessible
   virtual PRUint8 ActionCount();
 
   // Widgets
   virtual nsAccessible* ContainerWidget() const;
 
-  static void SelectionChangedIfOption(nsIContent *aPossibleOption);
-
 protected:
   // nsAccessible
   virtual nsIFrame* GetBoundsFrame();
 
 private:
   
   /**
    * Get Select element's accessible state
@@ -214,17 +212,17 @@ public:
 
 protected:
   // nsAccessible
   virtual void CacheChildren();
 
   /**
    * Return selected option.
    */
-  nsAccessible* SelectedOption(bool aIgnoreIfCollapsed = false) const;
+  nsAccessible* SelectedOption() const;
 
 private:
   nsRefPtr<nsHTMLComboboxListAccessible> mListAccessible;
 };
 
 /*
  * A class that represents the window that lives to the right
  * of the drop down button inside the Select. This is the window
@@ -241,11 +239,15 @@ public:
 
   // nsAccessNode
   virtual nsIFrame* GetFrame() const;
   virtual bool IsPrimaryForNode() const;
 
   // nsAccessible
   virtual PRUint64 NativeState();
   virtual void GetBoundsRect(nsRect& aBounds, nsIFrame** aBoundingFrame);
+
+  // Widgets
+  virtual bool IsActiveWidget() const;
+  virtual bool AreItemsOperable() const;
 };
 
 #endif
--- a/accessible/src/mac/mozAccessible.mm
+++ b/accessible/src/mac/mozAccessible.mm
@@ -185,19 +185,17 @@ GetNativeFromGeckoAccessible(nsIAccessib
   if (!generalAttributes) {
     // standard attributes that are shared and supported by all generic elements.
     generalAttributes = [[NSArray alloc] initWithObjects:  NSAccessibilityChildrenAttribute, 
                                                            NSAccessibilityParentAttribute,
                                                            NSAccessibilityRoleAttribute,
                                                            NSAccessibilityTitleAttribute,
                                                            NSAccessibilityValueAttribute,
                                                            NSAccessibilitySubroleAttribute,
-#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
                                                            NSAccessibilityRoleDescriptionAttribute,
-#endif
                                                            NSAccessibilityPositionAttribute,
                                                            NSAccessibilityEnabledAttribute,
                                                            NSAccessibilitySizeAttribute,
                                                            NSAccessibilityWindowAttribute,
                                                            NSAccessibilityFocusedAttribute,
                                                            NSAccessibilityHelpAttribute,
                                                            NSAccessibilityTitleUIElementAttribute,
                                                            kTopLevelUIElementAttribute,
@@ -231,20 +229,18 @@ GetNativeFromGeckoAccessible(nsIAccessib
   if ([attribute isEqualToString:NSAccessibilityPositionAttribute]) 
     return [self position];
   if ([attribute isEqualToString:NSAccessibilitySubroleAttribute])
     return [self subrole];
   if ([attribute isEqualToString:NSAccessibilityEnabledAttribute])
     return [NSNumber numberWithBool:[self isEnabled]];
   if ([attribute isEqualToString:NSAccessibilityValueAttribute])
     return [self value];
-#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
   if ([attribute isEqualToString:NSAccessibilityRoleDescriptionAttribute])
     return NSAccessibilityRoleDescription([self role], nil);
-#endif
   if ([attribute isEqualToString: (NSString*) kInstanceDescriptionAttribute])
     return [self customDescription];
   if ([attribute isEqualToString:NSAccessibilityFocusedAttribute])
     return [NSNumber numberWithBool:[self isFocused]];
   if ([attribute isEqualToString:NSAccessibilitySizeAttribute])
     return [self size];
   if ([attribute isEqualToString:NSAccessibilityWindowAttribute])
     return [self window];
--- a/accessible/src/mac/mozActionElements.mm
+++ b/accessible/src/mac/mozActionElements.mm
@@ -221,34 +221,30 @@ enum CheckboxValue {
 }
 
 - (NSArray *)accessibilityActionNames
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
 
   if ([self isEnabled]) {
     return [NSArray arrayWithObjects:NSAccessibilityPressAction,
-#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
                                      NSAccessibilityShowMenuAction,
-#endif
                                      nil];
   }
   return nil;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
 }
 
 - (NSString *)accessibilityActionDescription:(NSString *)action
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
 
-#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
   if ([action isEqualToString:NSAccessibilityShowMenuAction])
     return @"show menu";
-#endif
   return [super accessibilityActionDescription:action];
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
 }
 
 - (void)accessibilityPerformAction:(NSString *)action
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
--- a/accessible/src/mac/mozTextAccessible.mm
+++ b/accessible/src/mac/mozTextAccessible.mm
@@ -44,19 +44,17 @@ extern const NSString *kTopLevelUIElemen
   static NSArray *supportedAttributes = nil;
   if (!supportedAttributes) {
     // standard attributes that are shared and supported by all generic elements.
     supportedAttributes = [[NSArray alloc] initWithObjects:NSAccessibilityParentAttribute, // required
                                                            NSAccessibilityRoleAttribute,   // required
                                                            NSAccessibilityTitleAttribute,
                                                            NSAccessibilityValueAttribute, // required
                                                            NSAccessibilitySubroleAttribute,
-#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
                                                            NSAccessibilityRoleDescriptionAttribute,
-#endif
                                                            NSAccessibilityPositionAttribute, // required
                                                            NSAccessibilitySizeAttribute, // required
                                                            NSAccessibilityWindowAttribute, // required
                                                            NSAccessibilityFocusedAttribute, // required
                                                            NSAccessibilityEnabledAttribute, // required
                                                            kTopLevelUIElementAttribute, // required (on OS X 10.4+)
                                                            kInstanceDescriptionAttribute, // required (on OS X 10.4+)
                                                            /* text-specific attributes */
@@ -244,19 +242,17 @@ extern const NSString *kTopLevelUIElemen
   static NSArray *supportedAttributes = nil;
   if (!supportedAttributes) {
     // standard attributes that are shared and supported by all generic elements.
     supportedAttributes = [[NSArray alloc] initWithObjects:NSAccessibilityParentAttribute, // required
                                                            NSAccessibilityRoleAttribute,   // required
                                                            NSAccessibilityTitleAttribute,
                                                            NSAccessibilityValueAttribute, // required
                                                            NSAccessibilityHelpAttribute,
-#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
                                                            NSAccessibilityRoleDescriptionAttribute,
-#endif
                                                            NSAccessibilityPositionAttribute, // required
                                                            NSAccessibilitySizeAttribute, // required
                                                            NSAccessibilityWindowAttribute, // required
                                                            NSAccessibilityFocusedAttribute, // required
                                                            NSAccessibilityEnabledAttribute, // required
                                                            NSAccessibilityChildrenAttribute, // required
                                                            NSAccessibilityHelpAttribute,
                                                            // NSAccessibilityExpandedAttribute, // required
@@ -276,52 +272,46 @@ extern const NSString *kTopLevelUIElemen
 }
 
 - (NSArray *)accessibilityActionNames
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
 
   if ([self isEnabled]) {
     return [NSArray arrayWithObjects:NSAccessibilityConfirmAction,
-#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
                                      NSAccessibilityShowMenuAction,
-#endif
                                      nil];
   }
   return nil;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
 }
 
 - (NSString *)accessibilityActionDescription:(NSString *)action
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
 
-#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
   if ([action isEqualToString:NSAccessibilityShowMenuAction])
     return @"show menu";
-#endif
   if ([action isEqualToString:NSAccessibilityConfirmAction])
     return @"confirm";
     
   return [super accessibilityActionDescription:action];
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
 }
 
 - (void)accessibilityPerformAction:(NSString *)action
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
   // both the ShowMenu and Click action do the same thing.
   if ([self isEnabled]) {
-#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
     if ([action isEqualToString:NSAccessibilityShowMenuAction])
       [self showMenu];
-#endif
     if ([action isEqualToString:NSAccessibilityConfirmAction])
       [self confirm];
   }
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
 - (void)showMenu
--- a/accessible/src/msaa/nsEventMap.h
+++ b/accessible/src/msaa/nsEventMap.h
@@ -85,17 +85,17 @@ static const PRUint32 gWinEventMap[] = {
   kEVENT_WIN_UNKNOWN,                                // nsIAccessibleEvent::EVENT_MINIMIZE_START
   kEVENT_WIN_UNKNOWN,                                // nsIAccessibleEvent::EVENT_MINIMIZE_END
   IA2_EVENT_DOCUMENT_LOAD_COMPLETE,                  // nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_COMPLETE
   IA2_EVENT_DOCUMENT_RELOAD,                         // nsIAccessibleEvent::EVENT_DOCUMENT_RELOAD
   IA2_EVENT_DOCUMENT_LOAD_STOPPED,                   // nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_STOPPED
   IA2_EVENT_DOCUMENT_ATTRIBUTE_CHANGED,              // nsIAccessibleEvent::EVENT_DOCUMENT_ATTRIBUTES_CHANGED
   IA2_EVENT_DOCUMENT_CONTENT_CHANGED,                // nsIAccessibleEvent::EVENT_DOCUMENT_CONTENT_CHANGED
   kEVENT_WIN_UNKNOWN,                                // nsIAccessibleEvent::EVENT_PROPERTY_CHANGED
-  kEVENT_WIN_UNKNOWN,                                // nsIAccessibleEvent::EVENT_SELECTION_CHANGED
+  IA2_EVENT_PAGE_CHANGED,                            // nsIAccessibleEvent::IA2_EVENT_PAGE_CHANGED
   IA2_EVENT_TEXT_ATTRIBUTE_CHANGED,                  // nsIAccessibleEvent::EVENT_TEXT_ATTRIBUTE_CHANGED
   IA2_EVENT_TEXT_CARET_MOVED,                        // nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED
   IA2_EVENT_TEXT_CHANGED,                            // nsIAccessibleEvent::EVENT_TEXT_CHANGED
   IA2_EVENT_TEXT_INSERTED,                           // nsIAccessibleEvent::EVENT_TEXT_INSERTED
   IA2_EVENT_TEXT_REMOVED,                            // nsIAccessibleEvent::EVENT_TEXT_REMOVED
   IA2_EVENT_TEXT_UPDATED,                            // nsIAccessibleEvent::EVENT_TEXT_UPDATED
   IA2_EVENT_TEXT_SELECTION_CHANGED,                  // nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED
   IA2_EVENT_VISIBLE_DATA_CHANGED,                    // nsIAccessibleEvent::EVENT_VISIBLE_DATA_CHANGED
@@ -126,12 +126,11 @@ static const PRUint32 gWinEventMap[] = {
   IA2_EVENT_HYPERLINK_NUMBER_OF_ANCHORS_CHANGED,     // nsIAccessibleEvent::EVENT_HYPERLINK_NUMBER_OF_ANCHORS_CHANGED
   IA2_EVENT_HYPERLINK_SELECTED_LINK_CHANGED,         // nsIAccessibleEvent::EVENT_HYPERLINK_SELECTED_LINK_CHANGED
   IA2_EVENT_HYPERTEXT_LINK_ACTIVATED,                // nsIAccessibleEvent::EVENT_HYPERTEXT_LINK_ACTIVATED
   IA2_EVENT_HYPERTEXT_LINK_SELECTED,                 // nsIAccessibleEvent::EVENT_HYPERTEXT_LINK_SELECTED
   IA2_EVENT_HYPERLINK_START_INDEX_CHANGED,           // nsIAccessibleEvent::EVENT_HYPERLINK_START_INDEX_CHANGED
   IA2_EVENT_HYPERTEXT_CHANGED,                       // nsIAccessibleEvent::EVENT_HYPERTEXT_CHANGED
   IA2_EVENT_HYPERTEXT_NLINKS_CHANGED,                // nsIAccessibleEvent::EVENT_HYPERTEXT_NLINKS_CHANGED
   IA2_EVENT_OBJECT_ATTRIBUTE_CHANGED,                // nsIAccessibleEvent::EVENT_OBJECT_ATTRIBUTE_CHANGED
-  IA2_EVENT_PAGE_CHANGED,                            // nsIAccessibleEvent::EVENT_PAGE_CHANGED
   kEVENT_LAST_ENTRY                                  // nsIAccessibleEvent::EVENT_LAST_ENTRY
 };
 
--- a/accessible/tests/mochitest/events.js
+++ b/accessible/tests/mochitest/events.js
@@ -10,17 +10,19 @@ const EVENT_FOCUS = nsIAccessibleEvent.E
 const EVENT_NAME_CHANGE = nsIAccessibleEvent.EVENT_NAME_CHANGE;
 const EVENT_MENU_START = nsIAccessibleEvent.EVENT_MENU_START;
 const EVENT_MENU_END = nsIAccessibleEvent.EVENT_MENU_END;
 const EVENT_MENUPOPUP_START = nsIAccessibleEvent.EVENT_MENUPOPUP_START;
 const EVENT_MENUPOPUP_END = nsIAccessibleEvent.EVENT_MENUPOPUP_END;
 const EVENT_OBJECT_ATTRIBUTE_CHANGED = nsIAccessibleEvent.EVENT_OBJECT_ATTRIBUTE_CHANGED;
 const EVENT_REORDER = nsIAccessibleEvent.EVENT_REORDER;
 const EVENT_SCROLLING_START = nsIAccessibleEvent.EVENT_SCROLLING_START;
+const EVENT_SELECTION = nsIAccessibleEvent.EVENT_SELECTION;
 const EVENT_SELECTION_ADD = nsIAccessibleEvent.EVENT_SELECTION_ADD;
+const EVENT_SELECTION_REMOVE = nsIAccessibleEvent.EVENT_SELECTION_REMOVE;
 const EVENT_SELECTION_WITHIN = nsIAccessibleEvent.EVENT_SELECTION_WITHIN;
 const EVENT_SHOW = nsIAccessibleEvent.EVENT_SHOW;
 const EVENT_STATE_CHANGE = nsIAccessibleEvent.EVENT_STATE_CHANGE;
 const EVENT_TEXT_ATTRIBUTE_CHANGED = nsIAccessibleEvent.EVENT_TEXT_ATTRIBUTE_CHANGED;
 const EVENT_TEXT_CARET_MOVED = nsIAccessibleEvent.EVENT_TEXT_CARET_MOVED;
 const EVENT_TEXT_INSERTED = nsIAccessibleEvent.EVENT_TEXT_INSERTED;
 const EVENT_TEXT_REMOVED = nsIAccessibleEvent.EVENT_TEXT_REMOVED;
 const EVENT_TEXT_SELECTION_CHANGED = nsIAccessibleEvent.EVENT_TEXT_SELECTION_CHANGED;
--- a/accessible/tests/mochitest/events/Makefile.in
+++ b/accessible/tests/mochitest/events/Makefile.in
@@ -77,17 +77,19 @@ include $(topsrcdir)/config/rules.mk
 		test_focus_name.html \
 		test_focus_selects.html \
 		test_focus_tabbox.xul \
 		test_focus_tree.xul \
 		test_menu.xul \
 		test_mutation.html \
 		test_mutation.xhtml \
 		test_scroll.xul \
+		test_selection_aria.html \
 		test_selection.html \
+		test_selection.xul \
 		test_statechange.html \
 		test_text_alg.html \
 		test_text.html \
 		test_textattrchange.html \
 		test_tree.xul \
 		test_valuechange.html \
 		$(NULL)
 
--- a/accessible/tests/mochitest/events/test_selection.html
+++ b/accessible/tests/mochitest/events/test_selection.html
@@ -17,78 +17,97 @@
           src="../events.js"></script>
   <script type="application/javascript"
           src="../states.js"></script>
 
   <script type="application/javascript">
     ////////////////////////////////////////////////////////////////////////////
     // Invokers
 
-    function addSelection(aNode, aOption)
-    {
-      this.DOMNode = aNode;
-      this.optionNode = aOption;
-
-      this.eventSeq = [
-        new invokerChecker(EVENT_SELECTION_WITHIN, getAccessible(this.DOMNode)),
-        new invokerChecker(EVENT_SELECTION_ADD, getAccessible(this.optionNode))
-      ];
-
-      this.invoke = function addselection_invoke() {
-        synthesizeMouse(this.optionNode, 1, 1, {});
-      };
-
-      this.getID = function addselection_getID() {
-        return prettyName(this.optionNode) + " added to selection";
-      };
-    }
-
     ////////////////////////////////////////////////////////////////////////////
     // Do tests
 
-    var gQueue = null;
+    //gA11yEventDumpToConsole = true; // debuggin
 
-    //var gA11yEventDumpID = "eventdump"; // debug stuff
-
+    var gQueue = null;
     function doTests()
     {
       gQueue = new eventQueue();
 
-      var select = document.getElementById("toppings");
-      var option = document.getElementById("onions");
-      gQueue.push(new addSelection(select, option));
+      // open combobox
+      gQueue.push(new synthClick("combobox",
+                                 new invokerChecker(EVENT_FOCUS, "cb1_item1")));
+      gQueue.push(new synthDownKey("cb1_item1",
+                                   new invokerChecker(EVENT_SELECTION, "cb1_item2")));
+
+      // closed combobox
+      gQueue.push(new synthEscapeKey("combobox",
+                                     new invokerChecker(EVENT_FOCUS, "combobox")));
+      gQueue.push(new synthDownKey("cb1_item2",
+                                   new invokerChecker(EVENT_SELECTION, "cb1_item3")));
+
+      // listbox
+      gQueue.push(new synthClick("lb1_item1",
+                                 new invokerChecker(EVENT_SELECTION, "lb1_item1")));
+      gQueue.push(new synthDownKey("lb1_item1",
+                                   new invokerChecker(EVENT_SELECTION, "lb1_item2")));
+
+      // multiselectable listbox
+      gQueue.push(new synthClick("lb2_item1",
+                                 new invokerChecker(EVENT_SELECTION, "lb2_item1")));
+      gQueue.push(new synthDownKey("lb2_item1",
+                                   new invokerChecker(EVENT_SELECTION_ADD, "lb2_item2"),
+                                   { shiftKey: true }));
+      gQueue.push(new synthUpKey("lb2_item2",
+                                 new invokerChecker(EVENT_SELECTION_REMOVE, "lb2_item2"),
+                                 { shiftKey: true }));
+      gQueue.push(new synthKey("lb2_item1", " ", { ctrlKey: true },
+                               new invokerChecker(EVENT_SELECTION_REMOVE, "lb2_item1")));
 
       gQueue.invoke(); // Will call SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTests);
   </script>
 </head>
 
 <body>
 
   <a target="_blank"
-     href="https://bugzilla.mozilla.org/show_bug.cgi?id=569653"
-     title="Make selection events async">
-    Mozilla Bug 569653
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=414302"
+     title="Incorrect selection events in HTML, XUL and ARIA">
+    Mozilla Bug 414302
   </a>
 
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 
-  <p>Pizza</p>
-  <select id="toppings" name="toppings" multiple size=5>
-    <option value="mushrooms">mushrooms
-    <option value="greenpeppers">green peppers
-    <option value="onions" id="onions">onions
-    <option value="tomatoes">tomatoes
-    <option value="olives">olives
+  <select id="combobox">
+    <option id="cb1_item1" value="mushrooms">mushrooms
+    <option id="cb1_item2" value="greenpeppers">green peppers
+    <option id="cb1_item3" value="onions" id="onions">onions
+    <option id="cb1_item4" value="tomatoes">tomatoes
+    <option id="cb1_item5" value="olives">olives
   </select>
 
-  <div id="testContainer">
-    <iframe id="iframe"></iframe>
-  </div>
+  <select id="listbox" size=5>
+    <option id="lb1_item1" value="mushrooms">mushrooms
+    <option id="lb1_item2" value="greenpeppers">green peppers
+    <option id="lb1_item3" value="onions" id="onions">onions
+    <option id="lb1_item4" value="tomatoes">tomatoes
+    <option id="lb1_item5" value="olives">olives
+  </select>
+
+  <p>Pizza</p>
+  <select id="listbox2" multiple size=5>
+    <option id="lb2_item1" value="mushrooms">mushrooms
+    <option id="lb2_item2" value="greenpeppers">green peppers
+    <option id="lb2_item3" value="onions" id="onions">onions
+    <option id="lb2_item4" value="tomatoes">tomatoes
+    <option id="lb2_item5" value="olives">olives
+  </select>
+
   <div id="eventdump"></div>
 </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_selection.xul
@@ -0,0 +1,244 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+                 type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+        title="Selection event tests">
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/MochiKit/packed.js" />
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+
+  <script type="application/javascript"
+          src="../common.js" />
+  <script type="application/javascript"
+          src="../states.js" />
+  <script type="application/javascript"
+          src="../events.js" />
+
+  <script type="application/javascript">
+    function advanceTab(aTabsID, aDirection, aNextTabID)
+    {
+      this.eventSeq = [
+        new invokerChecker(EVENT_SELECTION, aNextTabID)
+      ];
+
+      this.invoke = function advanceTab_invoke()
+      {
+        getNode(aTabsID).advanceSelectedTab(aDirection, true);
+      }
+
+      this.getID = function synthFocus_getID()
+      {
+        return "advanceTab on " + prettyName(aTabsID) + " to " + prettyName(aNextTabID);
+      }
+    }
+
+    function select4FirstItems(aID)
+    {
+      this.listboxNode = getNode(aID);
+      this.eventSeq = [
+        new invokerChecker(EVENT_SELECTION_ADD, this.listboxNode.getItemAtIndex(0)),
+        new invokerChecker(EVENT_SELECTION_ADD, this.listboxNode.getItemAtIndex(1)),
+        new invokerChecker(EVENT_SELECTION_ADD, this.listboxNode.getItemAtIndex(2)),
+        new invokerChecker(EVENT_SELECTION_ADD, this.listboxNode.getItemAtIndex(3))
+      ];
+
+      this.invoke = function select4FirstItems_invoke()
+      {
+        synthesizeKey("VK_DOWN", { shiftKey: true }); // selects two items
+        synthesizeKey("VK_DOWN", { shiftKey: true });
+        synthesizeKey("VK_DOWN", { shiftKey: true });
+      }
+
+      this.getID = function select4FirstItems_getID()
+      {
+        return "select 4 first items for " + prettyName(aID);
+      }
+    }
+
+    function unselect4FirstItems(aID)
+    {
+      this.listboxNode = getNode(aID);
+      this.eventSeq = [
+        new invokerChecker(EVENT_SELECTION_REMOVE, this.listboxNode.getItemAtIndex(3)),
+        new invokerChecker(EVENT_SELECTION_REMOVE, this.listboxNode.getItemAtIndex(2)),
+        new invokerChecker(EVENT_SELECTION_REMOVE, this.listboxNode.getItemAtIndex(1)),
+        new invokerChecker(EVENT_SELECTION_REMOVE, this.listboxNode.getItemAtIndex(0))
+      ];
+
+      this.invoke = function unselect4FirstItems_invoke()
+      {
+        synthesizeKey("VK_UP", { shiftKey: true });
+        synthesizeKey("VK_UP", { shiftKey: true });
+        synthesizeKey("VK_UP", { shiftKey: true });
+        synthesizeKey(" ", { ctrlKey: true }); // unselect first item
+      }
+
+      this.getID = function unselect4FirstItems_getID()
+      {
+        return "unselect 4 first items for " + prettyName(aID);
+      }
+    }
+
+    function selectAllItems(aID)
+    {
+      this.listboxNode = getNode(aID);
+      this.eventSeq = [
+        new invokerChecker(EVENT_SELECTION_WITHIN, getAccessible(this.listboxNode))
+      ];
+
+      this.invoke = function selectAllItems_invoke()
+      {
+        synthesizeKey("VK_END", { shiftKey: true });
+      }
+
+      this.getID = function selectAllItems_getID()
+      {
+        return "select all items for " + prettyName(aID);
+      }
+    }
+
+    function unselectAllItemsButFirst(aID)
+    {
+      this.listboxNode = getNode(aID);
+      this.eventSeq = [
+        new invokerChecker(EVENT_SELECTION_WITHIN, getAccessible(this.listboxNode))
+      ];
+
+      this.invoke = function unselectAllItemsButFirst_invoke()
+      {
+        synthesizeKey("VK_HOME", { shiftKey: true });
+      }
+
+      this.getID = function unselectAllItemsButFirst_getID()
+      {
+        return "unselect all items for " + prettyName(aID);
+      }
+    }
+
+    function unselectSelectItem(aID)
+    {
+      this.listboxNode = getNode(aID);
+      this.eventSeq = [
+        new invokerChecker(EVENT_SELECTION_REMOVE, this.listboxNode.getItemAtIndex(0)),
+        new invokerChecker(EVENT_SELECTION_ADD, this.listboxNode.getItemAtIndex(0))
+      ];
+
+      this.invoke = function unselectSelectItem_invoke()
+      {
+        synthesizeKey(" ", { ctrlKey: true }); // select item
+        synthesizeKey(" ", { ctrlKey: true }); // unselect item
+      }
+
+      this.getID = function unselectSelectItem_getID()
+      {
+        return "unselect and then select first item for " + prettyName(aID);
+      }
+    }
+
+    /**
+     * Do tests.
+     */
+    var gQueue = null;
+
+    //gA11yEventDumpToConsole = true; // debuggin
+
+    function doTests()
+    {
+      gQueue = new eventQueue();
+
+      //////////////////////////////////////////////////////////////////////////
+      // tabbox
+      gQueue.push(new advanceTab("tabs", 1, "tab3"));
+
+      //////////////////////////////////////////////////////////////////////////
+      // listbox
+      gQueue.push(new synthClick("lb1_item1",
+                                 new invokerChecker(EVENT_SELECTION, "lb1_item1")));
+      gQueue.push(new synthDownKey("lb1_item1",
+                                   new invokerChecker(EVENT_SELECTION, "lb1_item2")));
+
+      //////////////////////////////////////////////////////////////////////////
+      // multiselectable listbox
+      gQueue.push(new synthClick("lb2_item1",
+                                 new invokerChecker(EVENT_SELECTION, "lb2_item1")));
+      gQueue.push(new synthDownKey("lb2_item1",
+                                   new invokerChecker(EVENT_SELECTION_ADD, "lb2_item2"),
+                                   { shiftKey: true }));
+      gQueue.push(new synthUpKey("lb2_item2",
+                                 new invokerChecker(EVENT_SELECTION_REMOVE, "lb2_item2"),
+                                 { shiftKey: true }));
+      gQueue.push(new synthKey("lb2_item1", " ", { ctrlKey: true },
+                               new invokerChecker(EVENT_SELECTION_REMOVE, "lb2_item1")));
+
+      //////////////////////////////////////////////////////////////////////////
+      // selection event coalescence
+
+      // fire 4 selection_add events
+      gQueue.push(new select4FirstItems("listbox2"));
+      // fire 4 selection_remove events
+      gQueue.push(new unselect4FirstItems("listbox2"));
+      // fire selection_within event
+      gQueue.push(new selectAllItems("listbox2"));
+      // fire selection_within event
+      gQueue.push(new unselectAllItemsButFirst("listbox2"));
+      // fire selection_remove/add events
+      gQueue.push(new unselectSelectItem("listbox2"));
+
+      gQueue.invoke(); // Will call SimpleTest.finish();
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addA11yLoadEvent(doTests);
+  </script>
+
+  <hbox flex="1" style="overflow: auto;">
+    <body xmlns="http://www.w3.org/1999/xhtml">
+      <a target="_blank"
+         href="https://bugzilla.mozilla.org/show_bug.cgi?id=414302"
+         title="Incorrect selection events in HTML, XUL and ARIA">
+        Mozilla Bug 414302
+      </a>
+      <p id="display"></p>
+      <div id="content" style="display: none"></div>
+      <pre id="test">
+      </pre>
+    </body>
+
+    <tabbox id="tabbox" selectedIndex="1">
+      <tabs id="tabs">
+        <tab id="tab1" label="tab1"/>
+        <tab id="tab2" label="tab2"/>
+        <tab id="tab3" label="tab3"/>
+        <tab id="tab4" label="tab4"/>
+      </tabs>
+      <tabpanels>
+        <tabpanel><!-- tabpanel First elements go here --></tabpanel>
+        <tabpanel><button id="b1" label="b1"/></tabpanel>
+        <tabpanel><button id="b2" label="b2"/></tabpanel>
+        <tabpanel></tabpanel>
+      </tabpanels>
+    </tabbox>
+
+    <listbox id="listbox">
+      <listitem id="lb1_item1" label="item1"/>
+      <listitem id="lb1_item2" label="item2"/>
+    </listbox>
+
+    <listbox id="listbox2" seltype="multiple">
+      <listitem id="lb2_item1" label="item1"/>
+      <listitem id="lb2_item2" label="item2"/>
+      <listitem id="lb2_item3" label="item3"/>
+      <listitem id="lb2_item4" label="item4"/>
+      <listitem id="lb2_item5" label="item5"/>
+      <listitem id="lb2_item6" label="item6"/>
+      <listitem id="lb2_item7" label="item7"/>
+    </listbox>
+
+  </hbox>
+</window>
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_selection_aria.html
@@ -0,0 +1,112 @@
+<html>
+
+<head>
+  <title>ARIA selection event testing</title>
+
+  <link rel="stylesheet" type="text/css"
+        href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+  <script type="application/javascript"
+          src="../common.js"></script>
+  <script type="application/javascript"
+          src="../events.js"></script>
+  <script type="application/javascript"
+          src="../states.js"></script>
+
+  <script type="application/javascript">
+    ////////////////////////////////////////////////////////////////////////////
+    // Invokers
+
+    function selectTreeItem(aTreeID, aItemID)
+    {
+      this.treeNode = getNode(aTreeID);
+      this.itemNode = getNode(aItemID);
+
+      this.eventSeq = [
+        new invokerChecker(EVENT_SELECTION, aItemID)
+      ];
+
+      this.invoke = function selectTreeItem_invoke() {
+        var itemNode = this.treeNode.querySelector("*[aria-selected='true']");
+        if (itemNode)
+          itemNode.removeAttribute("aria-selected", "true");
+
+        this.itemNode.setAttribute("aria-selected", "true");
+      }
+
+      this.getID = function selectTreeItem_getID()
+      {
+        return "selectTreeItem " + prettyName(aItemID);
+      }
+    }
+
+    ////////////////////////////////////////////////////////////////////////////
+    // Do tests
+
+    var gQueue = null;
+
+    //var gA11yEventDumpID = "eventdump"; // debug stuff
+
+    function doTests()
+    {
+      gQueue = new eventQueue();
+
+      gQueue.push(new selectTreeItem("tree", "treeitem1"));
+      gQueue.push(new selectTreeItem("tree", "treeitem1a"));
+      gQueue.push(new selectTreeItem("tree", "treeitem1a1"));
+
+      gQueue.push(new selectTreeItem("tree2", "tree2item1"));
+      gQueue.push(new selectTreeItem("tree2", "tree2item1a"));
+      gQueue.push(new selectTreeItem("tree2", "tree2item1a1"));
+
+      gQueue.invoke(); // Will call SimpleTest.finish();
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addA11yLoadEvent(doTests);
+  </script>
+</head>
+
+<body>
+
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=569653"
+     title="Make selection events async">
+    Mozilla Bug 569653
+  </a>
+
+  <p id="display"></p>
+  <div id="content" style="display: none"></div>
+  <pre id="test">
+  </pre>
+
+  <div id="tree" role="tree">
+    <div id="treeitem1" role="treeitem">Canada
+      <div id="treeitem1a" role="treeitem">- Ontario
+        <div id="treeitem1a1" role="treeitem">-- Toronto</div>
+      </div>
+      <div id="treeitem1b" role="treeitem">- Manitoba</div>
+    </div>
+    <div id="treeitem2" role="treeitem">Germany</div>
+    <div id="treeitem3" role="treeitem">Russia</div>
+  </div>
+
+  <div id="tree2" role="tree" aria-multiselectable="true">
+    <div id="tree2item1" role="treeitem">Canada
+      <div id="tree2item1a" role="treeitem">- Ontario
+        <div id="tree2item1a1" role="treeitem">-- Toronto</div>
+      </div>
+      <div id="tree2item1b" role="treeitem">- Manitoba</div>
+    </div>
+    <div id="tree2item2" role="treeitem">Germany</div>
+    <div id="tree2item3" role="treeitem">Russia</div>
+  </div>
+
+  <div id="eventdump"></div>
+</body>
+</html>
--- a/browser/config/mozconfigs/macosx32/debug
+++ b/browser/config/mozconfigs/macosx32/debug
@@ -1,12 +1,9 @@
-# Don't use the standard mozconfig. We don't want universal for a debug build. 
-#. $topsrcdir/build/macosx/universal/mozconfig
-
-ac_add_options --with-macos-sdk=/Developer/SDKs/MacOSX10.5.sdk
+. $topsrcdir/build/macosx/mozconfig.leopard
 ac_add_options --enable-debug
 ac_add_options --enable-trace-malloc
 
 # Enable parallel compiling
 mk_add_options MOZ_MAKE_FLAGS="-j4"
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -130,16 +130,17 @@
 @BINPATH@/components/content_html.xpt
 @BINPATH@/components/content_xslt.xpt
 @BINPATH@/components/content_xtf.xpt
 @BINPATH@/components/cookie.xpt
 @BINPATH@/components/directory.xpt
 @BINPATH@/components/docshell.xpt
 @BINPATH@/components/dom.xpt
 @BINPATH@/components/dom_base.xpt
+@BINPATH@/components/dom_battery.xpt
 @BINPATH@/components/dom_canvas.xpt
 @BINPATH@/components/dom_core.xpt
 @BINPATH@/components/dom_css.xpt
 @BINPATH@/components/dom_events.xpt
 @BINPATH@/components/dom_geolocation.xpt
 @BINPATH@/components/dom_notification.xpt
 @BINPATH@/components/dom_html.xpt
 @BINPATH@/components/dom_indexeddb.xpt
--- a/build/automation.py.in
+++ b/build/automation.py.in
@@ -579,29 +579,36 @@ user_pref("camino.use_system_proxy_setti
 
     # Configure automatic certificate and bind custom certificates, client authentication
     locations = self.readLocations()
     locations.pop(0)
     for loc in locations:
       if loc.scheme == "https" and "nocert" not in loc.options:
         customCertRE = re.compile("^cert=(?P<nickname>[0-9a-zA-Z_ ]+)")
         clientAuthRE = re.compile("^clientauth=(?P<clientauth>[a-z]+)")
+        redirRE      = re.compile("^redir=(?P<redirhost>[0-9a-zA-Z_ .]+)")
         for option in loc.options:
           match = customCertRE.match(option)
           if match:
             customcert = match.group("nickname");
             sslTunnelConfig.write("listen:%s:%s:%s:%s\n" %
                       (loc.host, loc.port, self.sslPort, customcert))
 
           match = clientAuthRE.match(option)
           if match:
             clientauth = match.group("clientauth");
             sslTunnelConfig.write("clientauth:%s:%s:%s:%s\n" %
                       (loc.host, loc.port, self.sslPort, clientauth))
 
+          match = redirRE.match(option)
+          if match:
+            redirhost = match.group("redirhost")
+            sslTunnelConfig.write("redirhost:%s:%s:%s:%s\n" %
+                      (loc.host, loc.port, self.sslPort, redirhost))
+
     sslTunnelConfig.close()
 
     # Pre-create the certification database for the profile
     env = self.environment(xrePath = xrePath)
     certutil = os.path.join(utilityPath, "certutil" + self.BIN_SUFFIX)
     pk12util = os.path.join(utilityPath, "pk12util" + self.BIN_SUFFIX)
 
     status = self.Process([certutil, "-N", "-d", profileDir, "-f", pwfilePath], env = env).wait()
@@ -734,62 +741,62 @@ user_pref("camino.use_system_proxy_setti
         # and re-raise any others
         if err.errno == errno.ESRCH or err.errno == errno.ECHILD:
           return False
         raise
 
     def killPid(self, pid):
       os.kill(pid, signal.SIGKILL)
 
-    def dumpScreen(self, utilityPath):
-      self.haveDumpedScreen = True;
+  def dumpScreen(self, utilityPath):
+    self.haveDumpedScreen = True;
 
-      # Need to figure out what tool and whether it write to a file or stdout
-      if self.UNIXISH:
-        utility = [os.path.join(utilityPath, "screentopng")]
-        imgoutput = 'stdout'
-      elif self.IS_MAC:
-        utility = ['/usr/sbin/screencapture', '-C', '-x', '-t', 'png']
-        imgoutput = 'file'
-      elif self.IS_WIN32:
-        self.log.info("If you fixed bug 589668, you'd get a screenshot here")
-        return
+    # Need to figure out what tool and whether it write to a file or stdout
+    if self.UNIXISH:
+      utility = [os.path.join(utilityPath, "screentopng")]
+      imgoutput = 'stdout'
+    elif self.IS_MAC:
+      utility = ['/usr/sbin/screencapture', '-C', '-x', '-t', 'png']
+      imgoutput = 'file'
+    elif self.IS_WIN32:
+      self.log.info("If you fixed bug 589668, you'd get a screenshot here")
+      return
 
-      # Run the capture correctly for the type of capture
-      try:
-        if imgoutput == 'file':
-          tmpfd, imgfilename = tempfile.mkstemp(prefix='mozilla-test-fail_')
-          os.close(tmpfd)
-          dumper = self.Process(utility + [imgfilename])
-        elif imgoutput == 'stdout':
-          dumper = self.Process(utility, bufsize=-1,
-                                stdout=subprocess.PIPE, close_fds=True)
-      except OSError, err:
-        self.log.info("Failed to start %s for screenshot: %s",
-                      utility[0], err.strerror)
-        return
+    # Run the capture correctly for the type of capture
+    try:
+      if imgoutput == 'file':
+        tmpfd, imgfilename = tempfile.mkstemp(prefix='mozilla-test-fail_')
+        os.close(tmpfd)
+        dumper = self.Process(utility + [imgfilename])
+      elif imgoutput == 'stdout':
+        dumper = self.Process(utility, bufsize=-1,
+                              stdout=subprocess.PIPE, close_fds=True)
+    except OSError, err:
+      self.log.info("Failed to start %s for screenshot: %s",
+                    utility[0], err.strerror)
+      return
 
-      # Check whether the capture utility ran successfully
-      dumper_out, dumper_err = dumper.communicate()
-      if dumper.returncode != 0:
-        self.log.info("%s exited with code %d", utility, dumper.returncode)
-        return
+    # Check whether the capture utility ran successfully
+    dumper_out, dumper_err = dumper.communicate()
+    if dumper.returncode != 0:
+      self.log.info("%s exited with code %d", utility, dumper.returncode)
+      return
 
-      try:
-        if imgoutput == 'stdout':
-          image = dumper_out
-        elif imgoutput == 'file':
-          with open(imgfilename) as imgfile:
-            image = imgfile.read()
-      except IOError, err:
-          self.log.info("Failed to read image from %s", imgoutput)
+    try:
+      if imgoutput == 'stdout':
+        image = dumper_out
+      elif imgoutput == 'file':
+        with open(imgfilename) as imgfile:
+          image = imgfile.read()
+    except IOError, err:
+        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)
+    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 self.CRASHREPORTER and not debuggerInfo:
       if self.UNIXISH:
--- a/build/pgo/server-locations.txt
+++ b/build/pgo/server-locations.txt
@@ -47,17 +47,17 @@
 # and a comma-separated list of options (if indeed any options are needed).
 #
 # The format of an origin is, referring to RFC 2396, a scheme (either "http" or
 # "https"), followed by "://", followed by a host, followed by ":", followed by
 # a port number.  The colon and port number must be present even if the port
 # number is the default for the protocol.
 #
 # Unrecognized options are ignored.  Recognized options are "primary" and
-# "privileged", "nocert", "cert=some_cert_nickname". 
+# "privileged", "nocert", "cert=some_cert_nickname", "redir=hostname".
 #
 # "primary" denotes a location which is the canonical location of
 # the server; this location is the one assumed for requests which don't
 # otherwise identify a particular origin (e.g. HTTP/1.0 requests).  
 #
 # "privileged" denotes a location which should have the ability to request 
 # elevated privileges; the default is no privileges.
 #
@@ -66,16 +66,22 @@
 #
 # "cert=nickname" tells the pgo server to use a particular certificate
 # for this host. The certificate is referenced by its nickname that must
 # not contain any spaces. The certificate  key files (PKCS12 modules)
 # for custom certification are loaded from build/pgo/ssltunnel/certs
 # directory. When new certificate is added to this dir pgo/ssltunnel
 # must be builded then.
 #
+# "redir=hostname" tells the pgo server is only used for https://
+# hosts while processing the CONNECT tunnel request. It responds
+# to the CONNECT with a 302 and redirection to the hostname instead
+# of connecting to the real back end and replying with a 200. This
+# mode exists primarily to ensure we don't allow a proxy to do that.
+#
 
 #
 # This is the primary location from which tests run.
 #
 http://mochi.test:8888   primary,privileged
 
 #
 # These are a common set of prefixes scattered across one TLD with two ports and
@@ -170,8 +176,15 @@ https://sub.sectest1.example.org:443
 #
 # Used while testing the url-classifier
 #
 http://malware.example.com:80
 
 # Bug 483437, 484111
 https://www.bank1.com:443           privileged,cert=escapeattack1
 https://www.bank2.com:443           privileged,cert=escapeattack2
+
+#
+# CONNECT for redirproxy results in a 302 redirect to
+# test1.example.com
+#
+https://redirproxy.example.com:443          privileged,redir=test1.example.com
+
--- a/caps/src/nsPrincipal.cpp
+++ b/caps/src/nsPrincipal.cpp
@@ -202,30 +202,46 @@ nsPrincipal::GetOrigin(char **aOrigin)
 
   // chrome: URLs don't have a meaningful origin, so make
   // sure we just get the full spec for them.
   // XXX this should be removed in favor of the solution in
   // bug 160042.
   bool isChrome;
   nsresult rv = origin->SchemeIs("chrome", &isChrome);
   if (NS_SUCCEEDED(rv) && !isChrome) {
-    rv = origin->GetHostPort(hostPort);
+    rv = origin->GetAsciiHost(hostPort);
+    // Some implementations return an empty string, treat it as no support
+    // for asciiHost by that implementation.
+    if (hostPort.IsEmpty())
+      rv = NS_ERROR_FAILURE;
+  }
+
+  PRInt32 port;
+  if (NS_SUCCEEDED(rv) && !isChrome) {
+    rv = origin->GetPort(&port);
   }
 
   if (NS_SUCCEEDED(rv) && !isChrome) {
+    if (port != -1) {
+      hostPort.AppendLiteral(":");
+      hostPort.AppendInt(port, 10);
+    }
+
     nsCAutoString scheme;
     rv = origin->GetScheme(scheme);
     NS_ENSURE_SUCCESS(rv, rv);
     *aOrigin = ToNewCString(scheme + NS_LITERAL_CSTRING("://") + hostPort);
   }
   else {
-    // Some URIs (e.g., nsSimpleURI) don't support host. Just
+    // Some URIs (e.g., nsSimpleURI) don't support asciiHost. Just
     // get the full spec.
     nsCAutoString spec;
-    rv = origin->GetSpec(spec);
+    // XXX nsMozIconURI and nsJARURI don't implement this correctly, they
+    // both fall back to GetSpec.  That needs to be fixed.
+    rv = origin->GetAsciiSpec(spec);
     NS_ENSURE_SUCCESS(rv, rv);
     *aOrigin = ToNewCString(spec);
   }
 
   return *aOrigin ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
 }
 
 NS_IMETHODIMP
--- a/configure.in
+++ b/configure.in
@@ -4261,17 +4261,17 @@ dnl = If NSS was not detected in the sys
 dnl = use the one in the source tree (mozilla/security/nss)
 dnl ========================================================
 
 MOZ_ARG_WITH_BOOL(system-nss,
 [  --with-system-nss       Use system installed NSS],
     _USE_SYSTEM_NSS=1 )
 
 if test -n "$_USE_SYSTEM_NSS"; then
-    AM_PATH_NSS(3.12.10, [MOZ_NATIVE_NSS=1], [AC_MSG_ERROR([you don't have NSS installed or your version is too old])])
+    AM_PATH_NSS(3.13.1, [MOZ_NATIVE_NSS=1], [AC_MSG_ERROR([you don't have NSS installed or your version is too old])])
 fi
 
 if test -n "$MOZ_NATIVE_NSS"; then
    NSS_LIBS="$NSS_LIBS -lcrmf"
 else
    NSS_CFLAGS='-I$(LIBXUL_DIST)/include/nss'
    NSS_DEP_LIBS="\
         \$(LIBXUL_DIST)/lib/\$(LIB_PREFIX)crmf.\$(LIB_SUFFIX) \
--- a/content/base/public/nsINode.h
+++ b/content/base/public/nsINode.h
@@ -46,16 +46,23 @@
 #include "nsCOMPtr.h"
 #include "nsWrapperCache.h"
 #include "nsIProgrammingLanguage.h" // for ::JAVASCRIPT
 #include "nsDOMError.h"
 #include "nsDOMString.h"
 #include "jspubtd.h"
 #include "nsDOMMemoryReporter.h"
 
+// Including 'windows.h' will #define GetClassInfo to something else.
+#ifdef XP_WIN
+#ifdef GetClassInfo
+#undef GetClassInfo
+#endif
+#endif
+
 class nsIContent;
 class nsIDocument;
 class nsIDOMEvent;
 class nsIDOMNode;
 class nsIDOMElement;
 class nsIDOMNodeList;
 class nsINodeList;
 class nsIPresShell;
--- a/content/base/src/nsAttrAndChildArray.cpp
+++ b/content/base/src/nsAttrAndChildArray.cpp
@@ -214,32 +214,41 @@ nsAttrAndChildArray::InsertChildAt(nsICo
   SetChildCount(childCount + 1);
   
   return NS_OK;
 }
 
 void
 nsAttrAndChildArray::RemoveChildAt(PRUint32 aPos)
 {
+  // Just store the return value of TakeChildAt in an nsCOMPtr to
+  // trigger a release.
+  nsCOMPtr<nsIContent> child = TakeChildAt(aPos);
+}
+
+already_AddRefed<nsIContent>
+nsAttrAndChildArray::TakeChildAt(PRUint32 aPos)
+{
   NS_ASSERTION(aPos < ChildCount(), "out-of-bounds");
 
   PRUint32 childCount = ChildCount();
   void** pos = mImpl->mBuffer + AttrSlotsSize() + aPos;
   nsIContent* child = static_cast<nsIContent*>(*pos);
   if (child->mPreviousSibling) {
     child->mPreviousSibling->mNextSibling = child->mNextSibling;
   }
   if (child->mNextSibling) {
     child->mNextSibling->mPreviousSibling = child->mPreviousSibling;
   }
   child->mPreviousSibling = child->mNextSibling = nsnull;
 
-  NS_RELEASE(child);
   memmove(pos, pos + 1, (childCount - aPos - 1) * sizeof(nsIContent*));
   SetChildCount(childCount - 1);
+
+  return child;
 }
 
 PRInt32
 nsAttrAndChildArray::IndexOfChild(nsINode* aPossibleChild) const
 {
   if (!mImpl) {
     return -1;
   }
--- a/content/base/src/nsAttrAndChildArray.h
+++ b/content/base/src/nsAttrAndChildArray.h
@@ -90,16 +90,19 @@ public:
   nsIContent* GetSafeChildAt(PRUint32 aPos) const;
   nsIContent * const * GetChildArray(PRUint32* aChildCount) const;
   nsresult AppendChild(nsIContent* aChild)
   {
     return InsertChildAt(aChild, ChildCount());
   }
   nsresult InsertChildAt(nsIContent* aChild, PRUint32 aPos);
   void RemoveChildAt(PRUint32 aPos);
+  // Like RemoveChildAt but hands the reference to the child being
+  // removed back to the caller instead of just releasing it.
+  already_AddRefed<nsIContent> TakeChildAt(PRUint32 aPos);
   PRInt32 IndexOfChild(nsINode* aPossibleChild) const;
 
   PRUint32 AttrCount() const;
   const nsAttrValue* GetAttr(nsIAtom* aLocalName, PRInt32 aNamespaceID = kNameSpaceID_None) const;
   const nsAttrValue* AttrAt(PRUint32 aPos) const;
   nsresult SetAttr(nsIAtom* aLocalName, const nsAString& aValue);
   nsresult SetAndTakeAttr(nsIAtom* aLocalName, nsAttrValue& aValue);
   nsresult SetAndTakeAttr(nsINodeInfo* aName, nsAttrValue& aValue);
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -5808,17 +5808,19 @@ nsContentUtils::SetUpChannelOwner(nsIPri
 bool
 nsContentUtils::IsFullScreenApiEnabled()
 {
   return sIsFullScreenApiEnabled;
 }
 
 bool nsContentUtils::IsRequestFullScreenAllowed()
 {
-  return !sTrustedFullScreenOnly || nsEventStateManager::IsHandlingUserInput();
+  return !sTrustedFullScreenOnly ||
+         nsEventStateManager::IsHandlingUserInput() ||
+         IsCallerChrome();
 }
 
 bool
 nsContentUtils::IsFullScreenKeyInputRestricted()
 {
   return sFullScreenKeyInputRestricted;
 }
 
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -8601,16 +8601,24 @@ nsDocument::GetMozFullScreen(bool *aFull
 }
 
 NS_IMETHODIMP
 nsDocument::GetMozFullScreenEnabled(bool *aFullScreen)
 {
   NS_ENSURE_ARG_POINTER(aFullScreen);
   *aFullScreen = false;
 
+  if (nsContentUtils::IsCallerChrome() &&
+      nsContentUtils::IsFullScreenApiEnabled()) {
+    // Chrome code can always use the full-screen API, provided it's not
+    // explicitly disabled.
+    *aFullScreen = true;
+    return NS_OK;
+  }
+
   if (!nsContentUtils::IsFullScreenApiEnabled() ||
       nsContentUtils::HasPluginWithUncontrolledEventDispatch(this) ||
       !IsVisible()) {
     return NS_OK;
   }
 
   // Ensure that all ancestor <iframe> elements have the mozallowfullscreen
   // boolean attribute set.
--- a/content/base/src/nsGenericElement.cpp
+++ b/content/base/src/nsGenericElement.cpp
@@ -4229,20 +4229,28 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
     PRUint32 childCount = tmp->mAttrsAndChildren.ChildCount();
     if (childCount) {
       // Don't allow script to run while we're unbinding everything.
       nsAutoScriptBlocker scriptBlocker;
       while (childCount-- > 0) {
         // Once we have XPCOMGC we shouldn't need to call UnbindFromTree.
         // We could probably do a non-deep unbind here when IsInDoc is false
         // for better performance.
-        tmp->mAttrsAndChildren.ChildAt(childCount)->UnbindFromTree();
-        tmp->mAttrsAndChildren.RemoveChildAt(childCount);
+
+        // Hold a strong ref to the node when we remove it, because we may be
+        // the last reference to it.  We need to call TakeChildAt() and
+        // update mFirstChild before calling UnbindFromTree, since this last
+        // can notify various observers and they should really see consistent
+        // tree state.
+        nsCOMPtr<nsIContent> child = tmp->mAttrsAndChildren.TakeChildAt(childCount);
+        if (childCount == 0) {
+          tmp->mFirstChild = nsnull;
+        }
+        child->UnbindFromTree();
       }
-      tmp->mFirstChild = nsnull;
     }
   }  
 
   // Unlink any DOM slots of interest.
   {
     nsDOMSlots *slots = tmp->GetExistingDOMSlots();
     if (slots) {
       slots->Unlink(tmp->IsXUL());
--- a/content/base/test/chrome/Makefile.in
+++ b/content/base/test/chrome/Makefile.in
@@ -65,15 +65,16 @@ include $(topsrcdir)/config/rules.mk
     test_bug635835.xul \
     test_fileconstructor.xul \
     fileconstructor_file.png \
     test_bug339494.xul \
     test_bug357450.xul \
     test_bug571390.xul \
     test_bug574596.html \
     test_bug683852.xul \
+    test_bug599295.html \
     $(NULL)
 
 libs:: $(_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
 
 libs:: $(_CHROME_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/content/base/test/chrome/test_bug599295.html
@@ -0,0 +1,81 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=599295
+-->
+<head>
+  <title>Test for Bug 599295</title>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript"  src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+  <script type="application/javascript"  src="chrome://mochikit/content/tests/SimpleTest/ChromeUtils.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=599295">Mozilla Bug 599295</a>
+<style type="text/css">
+#link1 a { -moz-user-select:none; }
+</style>
+<div id="link1"><a href="http://www.mozilla.org/">link1</a></div>
+<div id="link2"><a href="http://www.mozilla.org/">link2</a></div>
+<p id="display"></p>
+<div id="content" style="display: none">
+  
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 599295 **/
+
+/* Do not allow a response to a CONNECT method, used to establish an
+   SSL tunnel over an HTTP proxy, to contain a redirect */
+
+const BinaryInputStream = 
+    Components.Constructor("@mozilla.org/binaryinputstream;1",
+                           "nsIBinaryInputStream",
+                           "setInputStream");
+var listener = {
+ _httpstatus : 0,
+
+ onStartRequest: function(request, context) {
+   request.QueryInterface(Components.interfaces.nsIHttpChannel);
+   _httpstatus = request.responseStatus;
+ },
+
+ onDataAvailable: function(request, context, stream, offset, count) {
+   new BinaryInputStream(stream).readByteArray(count);
+ },
+
+ onStopRequest: function(request, context, status) {
+  /* testing here that the redirect was not followed. If it was followed
+     we would see a http status of 200 and status of NS_OK */
+
+   is(_httpstatus, 302, "http status 302");
+   is(status, Components.results.NS_ERROR_CONNECTION_REFUSED, "raised refused");
+   SimpleTest.finish();
+  }
+};
+
+function runTest() {
+  var ios = Components.classes["@mozilla.org/network/io-service;1"].
+            getService(Components.interfaces.nsIIOService);
+  var uri = ios.newURI("https://redirproxy.example.com/test", "",  null);
+  var channel = ios.newChannelFromURI(uri);
+
+  /* Previously, necko would allow a 302 as part of a CONNECT response
+     if the LOAD_DOCUMENT_URI flag was set and the original document
+     URI had not yet been changed. */
+ 
+  channel.loadFlags |= Components.interfaces.nsIChannel.LOAD_DOCUMENT_URI;
+  channel.QueryInterface(Components.interfaces.nsIHttpChannelInternal);
+  channel.documentURI = uri;
+  channel.asyncOpen(listener, null);
+}
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(runTest);
+
+</script>
+</pre>
+</body>
+</html>
+
--- a/content/canvas/src/WebGLContext.cpp
+++ b/content/canvas/src/WebGLContext.cpp
@@ -603,32 +603,38 @@ WebGLContext::SetDimensions(PRInt32 widt
         format.red = 5;
         format.green = 6;
         format.blue = 5;
 
         format.alpha = 0;
         format.minAlpha = 0;
     }
 
-    if (mOptions.antialias) {
-        PRUint32 msaaLevel = Preferences::GetUint("webgl.msaa-level", 2);
-        format.samples = msaaLevel*msaaLevel;
+    bool forceMSAA =
+        Preferences::GetBool("webgl.msaa-force", false);
+
+    PRInt32 status;
+    nsCOMPtr<nsIGfxInfo> gfxInfo = do_GetService("@mozilla.org/gfx/info;1");
+    if (mOptions.antialias && 
+        NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_WEBGL_MSAA, &status))) {
+        if (status == nsIGfxInfo::FEATURE_NO_INFO || forceMSAA) {
+            PRUint32 msaaLevel = Preferences::GetUint("webgl.msaa-level", 2);
+            format.samples = msaaLevel*msaaLevel;
+        }
     }
 
     if (PR_GetEnv("MOZ_WEBGL_PREFER_EGL")) {
         preferEGL = true;
     }
 
     // Ask GfxInfo about what we should use
     bool useOpenGL = true;
     bool useANGLE = true;
 
-    nsCOMPtr<nsIGfxInfo> gfxInfo = do_GetService("@mozilla.org/gfx/info;1");
     if (gfxInfo && !forceEnabled) {
-        PRInt32 status;
         if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_WEBGL_OPENGL, &status))) {
             if (status != nsIGfxInfo::FEATURE_NO_INFO) {
                 useOpenGL = false;
             }
         }
         if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_WEBGL_ANGLE, &status))) {
             if (status != nsIGfxInfo::FEATURE_NO_INFO) {
                 useANGLE = false;
--- a/content/canvas/src/WebGLContext.h
+++ b/content/canvas/src/WebGLContext.h
@@ -728,16 +728,17 @@ public:
     // console logging helpers
     static void LogMessage(const char *fmt, ...);
     static void LogMessage(const char *fmt, va_list ap);
     void LogMessageIfVerbose(const char *fmt, ...);
     void LogMessageIfVerbose(const char *fmt, va_list ap);
 
     friend class WebGLTexture;
     friend class WebGLFramebuffer;
+    friend class WebGLProgram;
 };
 
 // this class is a mixin for the named type wrappers, and is used
 // by WebGLObjectRefPtr to tell the object who holds references, so that
 // we can zero them out appropriately when the object is deleted, because
 // it will be unbound in the GL.
 //
 // PreallocatedOwnersCapacity is the preallocated capacity for the array of refptrs to owners.
@@ -1416,31 +1417,53 @@ class WebGLShader :
     public WebGLContextBoundObject
 {
 public:
     NS_DECLARE_STATIC_IID_ACCESSOR(WEBGLSHADER_PRIVATE_IID)
 
     WebGLShader(WebGLContext *context, WebGLuint name, WebGLenum stype) :
         WebGLContextBoundObject(context),
         mName(name), mDeleted(false), mType(stype),
-        mNeedsTranslation(true), mAttachCount(0)
+        mNeedsTranslation(true), mAttachCount(0),
+        mDeletePending(false)
     { }
 
+    void DetachedFromProgram() {
+        DecrementAttachCount();
+        if (mDeletePending && AttachCount() <= 0) {
+            DeleteWhenNotAttached();
+        }
+    }
+
+    void DeleteWhenNotAttached() {
+        if (mDeleted)
+            return;
+
+        if (AttachCount() > 0) {
+            mDeletePending = true;
+            return;
+        }
+
+        Delete();
+    }
+
     void Delete() {
         if (mDeleted)
             return;
+
         ZeroOwners();
         mDeleted = true;
+        mDeletePending = false;
     }
 
-    bool Deleted() { return mDeleted && mAttachCount == 0; }
+    bool Deleted() { return mDeleted; }
     WebGLuint GLName() { return mName; }
     WebGLenum ShaderType() { return mType; }
 
-    PRUint32 AttachCount() { return mAttachCount; }
+    PRInt32 AttachCount() { return mAttachCount; }
     void IncrementAttachCount() { mAttachCount++; }
     void DecrementAttachCount() { mAttachCount--; }
 
     void SetSource(const nsAString& src) {
         // XXX do some quick gzip here maybe -- getting this will be very rare
         mSource.Assign(src);
     }
 
@@ -1464,17 +1487,18 @@ public:
     NS_DECL_NSIWEBGLSHADER
 protected:
     WebGLuint mName;
     bool mDeleted;
     WebGLenum mType;
     nsString mSource;
     nsCString mTranslationLog;
     bool mNeedsTranslation;
-    PRUint32 mAttachCount;
+    PRInt32 mAttachCount;
+    bool mDeletePending;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(WebGLShader, WEBGLSHADER_PRIVATE_IID)
 
 #define WEBGLPROGRAM_PRIVATE_IID \
     {0xb3084a5b, 0xa5b4, 0x4ee0, {0xa0, 0xf0, 0xfb, 0xdd, 0x64, 0xaf, 0x8e, 0x82}}
 class WebGLProgram :
     public nsIWebGLProgram,
@@ -1491,32 +1515,55 @@ public:
         mName(name), mDeleted(false), mDeletePending(false),
         mLinkStatus(false), mGeneration(0),
         mUniformMaxNameLength(0), mAttribMaxNameLength(0),
         mUniformCount(0), mAttribCount(0)
     {
         mMapUniformLocations.Init();
     }
 
+    void DeleteWhenNotCurrent() {
+        if (mDeleted)
+            return;
+
+        if (mContext->mCurrentProgram == this) {
+            mDeletePending = true;
+            return;
+        }
+
+        Delete();
+    }
+
     void Delete() {
         if (mDeleted)
             return;
+
+        DetachShaders();
         ZeroOwners();
         mDeleted = true;
+        mDeletePending = false;
     }
 
     void DetachShaders() {
         for (PRUint32 i = 0; i < mAttachedShaders.Length(); ++i) {
-            if (mAttachedShaders[i])
-                mAttachedShaders[i]->DecrementAttachCount();
+            WebGLShader* shader = mAttachedShaders[i];
+            if (shader)
+                shader->DetachedFromProgram();
         }
         mAttachedShaders.Clear();
     }
 
-    bool Deleted() { return mDeleted && !mDeletePending; }
+    void NoLongerCurrent() {
+        if (mDeletePending) {
+            DetachShaders();
+            DeleteWhenNotCurrent();
+        }
+    }
+
+    bool Deleted() { return mDeleted; }
     void SetDeletePending() { mDeletePending = true; }
     void ClearDeletePending() { mDeletePending = false; }
     bool HasDeletePending() { return mDeletePending; }
 
     WebGLuint GLName() { return mName; }
     const nsTArray<nsRefPtr<WebGLShader> >& AttachedShaders() const { return mAttachedShaders; }
     bool LinkStatus() { return mLinkStatus; }
     PRUint32 Generation() const { return mGeneration.value(); }
@@ -1533,17 +1580,17 @@ public:
         mAttachedShaders.AppendElement(shader);
         shader->IncrementAttachCount();
         return true;
     }
 
     // return true if the shader was found and removed
     bool DetachShader(WebGLShader *shader) {
         if (mAttachedShaders.RemoveElement(shader)) {
-            shader->DecrementAttachCount();
+            shader->DetachedFromProgram();
             return true;
         }
         return false;
     }
 
     bool HasAttachedShaderOfType(GLenum shaderType) {
         for (PRUint32 i = 0; i < mAttachedShaders.Length(); ++i) {
             if (mAttachedShaders[i] && mAttachedShaders[i]->ShaderType() == shaderType) {
--- a/content/canvas/src/WebGLContextGL.cpp
+++ b/content/canvas/src/WebGLContextGL.cpp
@@ -1260,23 +1260,17 @@ WebGLContext::DeleteProgram(nsIWebGLProg
 
     if (isNull || isDeleted)
         return NS_OK;
 
     MakeContextCurrent();
 
     gl->fDeleteProgram(progname);
 
-    if (prog == mCurrentProgram) {
-        prog->SetDeletePending();
-    } else {
-        prog->DetachShaders();
-    }
-
-    prog->Delete();
+    prog->DeleteWhenNotCurrent();
     mMapPrograms.Remove(progname);
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
 WebGLContext::DeleteShader(nsIWebGLShader *sobj)
 {
@@ -1290,17 +1284,17 @@ WebGLContext::DeleteShader(nsIWebGLShade
         return NS_OK;
 
     if (isNull || isDeleted)
         return NS_OK;
 
     MakeContextCurrent();
 
     gl->fDeleteShader(shadername);
-    shader->Delete();
+    shader->DeleteWhenNotAttached();
     mMapShaders.Remove(shadername);
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
 WebGLContext::DetachShader(nsIWebGLProgram *pobj, nsIWebGLShader *shobj)
 {
@@ -1319,16 +1313,18 @@ WebGLContext::DetachShader(nsIWebGLProgr
     // deleted shader, since it's still a shader
     if (!program->DetachShader(shader))
         return ErrorInvalidOperation("DetachShader: shader is not attached");
 
     MakeContextCurrent();
 
     gl->fDetachShader(progname, shadername);
 
+    shader->DetachedFromProgram();
+
     return NS_OK;
 }
 
 NS_IMETHODIMP
 WebGLContext::DepthFunc(WebGLenum func)
 {
     if (mContextLost)
         return NS_OK;
@@ -2631,16 +2627,22 @@ WebGLContext::GetProgramParameter(nsIWeb
         {
             GLint i = 0;
             gl->fGetProgramiv(progname, pname, &i);
             wrval->SetAsInt32(i);
         }
             break;
         case LOCAL_GL_DELETE_STATUS:
         case LOCAL_GL_LINK_STATUS:
+        {
+            GLint i = 0;
+            gl->fGetProgramiv(progname, pname, &i);
+            wrval->SetAsBool(bool(i));
+        }
+            break;
         case LOCAL_GL_VALIDATE_STATUS:
         {
             GLint i = 0;
 #ifdef XP_MACOSX
             // See comment in ValidateProgram below.
             i = 1;
 #else
             gl->fGetProgramiv(progname, pname, &i);
@@ -3009,18 +3011,21 @@ WebGLContext::GetVertexAttrib(WebGLuint 
 
     MakeContextCurrent();
 
     switch (pname) {
         case LOCAL_GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING:
             wrval->SetAsISupports(mAttribBuffers[index].buf);
             break;
 
+        case LOCAL_GL_VERTEX_ATTRIB_ARRAY_STRIDE:
+            wrval->SetAsInt32(mAttribBuffers[index].stride);
+            break;
+
         case LOCAL_GL_VERTEX_ATTRIB_ARRAY_SIZE:
-        case LOCAL_GL_VERTEX_ATTRIB_ARRAY_STRIDE:
         case LOCAL_GL_VERTEX_ATTRIB_ARRAY_TYPE:
         {
             GLint i = 0;
             gl->fGetVertexAttribiv(index, pname, &i);
             wrval->SetAsInt32(i);
         }
             break;
 
@@ -4321,23 +4326,22 @@ WebGLContext::UseProgram(nsIWebGLProgram
 
     MakeContextCurrent();
 
     if (prog && !prog->LinkStatus())
         return ErrorInvalidOperation("UseProgram: program was not linked successfully");
 
     gl->fUseProgram(progname);
 
-    if (mCurrentProgram && mCurrentProgram->HasDeletePending()) {
-        mCurrentProgram->DetachShaders();
-        mCurrentProgram->ClearDeletePending();
-    }
-
+    WebGLProgram* previous = mCurrentProgram;
     mCurrentProgram = prog;
 
+    if (previous)
+        previous->NoLongerCurrent();
+
     return NS_OK;
 }
 
 NS_IMETHODIMP
 WebGLContext::ValidateProgram(nsIWebGLProgram *pobj)
 {
     if (mContextLost)
         return NS_OK;
@@ -4563,17 +4567,16 @@ WebGLContext::GetShaderParameter(nsIWebG
         case LOCAL_GL_DELETE_STATUS:
         case LOCAL_GL_COMPILE_STATUS:
         {
             GLint i = 0;
             gl->fGetShaderiv(shadername, pname, &i);
             wrval->SetAsBool(bool(i));
         }
             break;
-
         default:
             return NS_ERROR_NOT_IMPLEMENTED;
     }
 
     *retval = wrval.forget().get();
 
     return NS_OK;
 }
--- a/content/canvas/src/nsCanvasRenderingContext2DAzure.cpp
+++ b/content/canvas/src/nsCanvasRenderingContext2DAzure.cpp
@@ -110,25 +110,22 @@
 
 #include <algorithm>
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/ipc/PDocumentRendererParent.h"
 #include "mozilla/dom/PBrowserParent.h"
 #include "mozilla/ipc/DocumentRendererParent.h"
 
 #include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/PathHelpers.h"
 
 #ifdef XP_WIN
 #include "gfxWindowsPlatform.h"
 #endif
 
-#ifndef M_PI
-#define M_PI 3.14159265358979323846
-#endif
-
 // windows.h (included by chromium code) defines this, in its infinite wisdom
 #undef DrawText
 
 using namespace mozilla;
 using namespace mozilla::CanvasUtils;
 using namespace mozilla::css;
 using namespace mozilla::dom;
 using namespace mozilla::gfx;
@@ -435,16 +432,19 @@ public:
   // nsIDOMCanvasRenderingContext2D interface
   NS_DECL_NSIDOMCANVASRENDERINGCONTEXT2D
 
   enum Style {
     STYLE_STROKE = 0,
     STYLE_FILL,
     STYLE_MAX
   };
+  
+  nsresult LineTo(const Point& aPoint);
+  nsresult BezierTo(const Point& aCP1, const Point& aCP2, const Point& aCP3);
 
 protected:
   nsresult InitializeWithTarget(DrawTarget *surface, PRInt32 width, PRInt32 height);
 
   /**
     * The number of living nsCanvasRenderingContexts.  When this goes down to
     * 0, we free the premultiply and unpremultiply tables, if they exist.
     */
@@ -983,33 +983,32 @@ NS_INTERFACE_MAP_END
 // Initialize our static variables.
 PRUint32 nsCanvasRenderingContext2DAzure::sNumLivingContexts = 0;
 PRUint8 (*nsCanvasRenderingContext2DAzure::sUnpremultiplyTable)[256] = nsnull;
 PRUint8 (*nsCanvasRenderingContext2DAzure::sPremultiplyTable)[256] = nsnull;
 
 nsresult
 NS_NewCanvasRenderingContext2DAzure(nsIDOMCanvasRenderingContext2D** aResult)
 {
-#ifndef XP_WIN
-  return NS_ERROR_NOT_AVAILABLE;
-#else
-
+#ifdef XP_WIN
   if (gfxWindowsPlatform::GetPlatform()->GetRenderMode() !=
       gfxWindowsPlatform::RENDER_DIRECT2D ||
       !gfxWindowsPlatform::GetPlatform()->DWriteEnabled()) {
     return NS_ERROR_NOT_AVAILABLE;
   }
+#elif !defined(XP_MACOSX) && !defined(ANDROID)
+  return NS_ERROR_NOT_AVAILABLE;
+#endif
 
   nsRefPtr<nsIDOMCanvasRenderingContext2D> ctx = new nsCanvasRenderingContext2DAzure();
   if (!ctx)
     return NS_ERROR_OUT_OF_MEMORY;
 
   *aResult = ctx.forget().get();
   return NS_OK;
-#endif
 }
 
 nsCanvasRenderingContext2DAzure::nsCanvasRenderingContext2DAzure()
   : mValid(false), mZero(false), mOpaque(false), mResetLayer(true)
   , mIPC(false)
   , mCanvasElement(nsnull)
   , mIsEntireFrameInvalid(false)
   , mPredictManyRedrawCalls(false), mPathTransformWillUpdate(false)
@@ -1248,17 +1247,17 @@ nsCanvasRenderingContext2DAzure::SetDime
     if (ownerDoc) {
       layerManager =
         nsContentUtils::PersistentLayerManagerForDocument(ownerDoc);
     }
 
     if (layerManager) {
       target = layerManager->CreateDrawTarget(size, format);
     } else {
-      target = Factory::CreateDrawTarget(BACKEND_DIRECT2D, size, format);
+      target = gfxPlatform::GetPlatform()->CreateOffscreenDrawTarget(size, format);
     }
   }
 
   if (target) {
     if (gCanvasAzureMemoryReporter == nsnull) {
         gCanvasAzureMemoryReporter = new NS_MEMORY_REPORTER_NAME(CanvasAzureMemory);
       NS_RegisterMemoryReporter(gCanvasAzureMemoryReporter);
     }
@@ -1288,17 +1287,17 @@ nsCanvasRenderingContext2DAzure::Initial
 
   mResetLayer = true;
 
   /* Create dummy surfaces here - target can be null when a canvas was created
    * that is too large to support.
    */
   if (!target)
   {
-    mTarget = Factory::CreateDrawTarget(BACKEND_DIRECT2D, IntSize(1, 1), FORMAT_B8G8R8A8);
+    mTarget = gfxPlatform::GetPlatform()->CreateOffscreenDrawTarget(IntSize(1, 1), FORMAT_B8G8R8A8);
   } else {
     mValid = true;
   }
 
   // set up the initial canvas defaults
   mStyleStack.Clear();
   mPathBuilder = nsnull;
   mPath = nsnull;
@@ -2310,20 +2309,26 @@ nsCanvasRenderingContext2DAzure::MoveTo(
 NS_IMETHODIMP
 nsCanvasRenderingContext2DAzure::LineTo(float x, float y)
 {
   if (!FloatValidate(x,y))
       return NS_OK;
 
   EnsureWritablePath();
     
+  return LineTo(Point(x, y));;
+}
+  
+nsresult 
+nsCanvasRenderingContext2DAzure::LineTo(const Point& aPoint)
+{
   if (mPathBuilder) {
-    mPathBuilder->LineTo(Point(x, y));
+    mPathBuilder->LineTo(aPoint);
   } else {
-    mDSPathBuilder->LineTo(mTarget->GetTransform() * Point(x, y));
+    mDSPathBuilder->LineTo(mTarget->GetTransform() * aPoint);
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2DAzure::QuadraticCurveTo(float cpx, float cpy, float x, float y)
 {
@@ -2349,23 +2354,31 @@ nsCanvasRenderingContext2DAzure::BezierC
                                               float x, float y)
 {
   if (!FloatValidate(cp1x, cp1y, cp2x, cp2y, x, y)) {
     return NS_OK;
   }
 
   EnsureWritablePath();
 
+  return BezierTo(Point(cp1x, cp1y), Point(cp2x, cp2y), Point(x, y));
+}
+
+nsresult
+nsCanvasRenderingContext2DAzure::BezierTo(const Point& aCP1,
+                                          const Point& aCP2,
+                                          const Point& aCP3)
+{
   if (mPathBuilder) {
-    mPathBuilder->BezierTo(Point(cp1x, cp1y), Point(cp2x, cp2y), Point(x, y));
+    mPathBuilder->BezierTo(aCP1, aCP2, aCP3);
   } else {
     Matrix transform = mTarget->GetTransform();
-    mDSPathBuilder->BezierTo(transform * Point(cp1x, cp1y),
-                              transform * Point(cp2x, cp2y),
-                              transform * Point(x, y));
+    mDSPathBuilder->BezierTo(transform * aCP1,
+                              transform * aCP2,
+                              transform * aCP3);
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2DAzure::ArcTo(float x1, float y1, float x2, float y2, float radius)
 {
@@ -2456,93 +2469,17 @@ nsCanvasRenderingContext2DAzure::Arc(flo
     return NS_OK;
   }
 
   if (r < 0.0)
       return NS_ERROR_DOM_INDEX_SIZE_ERR;
 
   EnsureWritablePath();
 
-  // We convert to Bezier curve here, since we need to be able to write in
-  // device space, but a transformed arc is no longer representable by an arc.
-
-  Point startPoint(x + cos(startAngle) * r, y + sin(startAngle) * r);
-
-  if (mPathBuilder) {
-    mPathBuilder->LineTo(startPoint);
-  } else {
-    mDSPathBuilder->LineTo(mTarget->GetTransform() * startPoint);
-  }
-
-  // Clockwise we always sweep from the smaller to the larger angle, ccw
-  // it's vice versa.
-  if (!ccw && (endAngle < startAngle)) {
-    Float correction = ceil((startAngle - endAngle) / (2.0f * M_PI));
-    endAngle += correction * 2.0f * M_PI;
-  } else if (ccw && (startAngle < endAngle)) {
-    Float correction = ceil((endAngle - startAngle) / (2.0f * M_PI));
-    startAngle += correction * 2.0f * M_PI;
-  }
-
-  // Sweeping more than 2 * pi is a full circle.
-  if (!ccw && (endAngle - startAngle > 2 * M_PI)) {
-    endAngle = startAngle + 2.0f * M_PI;
-  } else if (ccw && (startAngle - endAngle > 2.0f * M_PI)) {
-    endAngle = startAngle - 2.0f * M_PI;
-  }
-
-  // Calculate the total arc we're going to sweep.
-  Float arcSweepLeft = abs(endAngle - startAngle);
-
-  Float sweepDirection = ccw ? -1.0f : 1.0f;
-
-  Float currentStartAngle = startAngle;
-
-  while (arcSweepLeft > 0) {
-    // We guarantee here the current point is the start point of the next
-    // curve segment.
-    Float currentEndAngle;
-
-    if (arcSweepLeft > M_PI / 2.0f) {
-      currentEndAngle = currentStartAngle + M_PI / 2.0f * sweepDirection;
-    } else {
-      currentEndAngle = currentStartAngle + arcSweepLeft * sweepDirection;
-    }
-
-    Point currentStartPoint(x + cos(currentStartAngle) * r,
-                              y + sin(currentStartAngle) * r);
-    Point currentEndPoint(x + cos(currentEndAngle) * r,
-                            y + sin(currentEndAngle) * r);
-
-    // Calculate kappa constant for partial curve. The sign of angle in the
-    // tangent will actually ensure this is negative for a counter clockwise
-    // sweep, so changing signs later isn't needed.
-    Float kappa = (4.0f / 3.0f) * tan((currentEndAngle - currentStartAngle) / 4.0f) * r;
-
-    Point tangentStart(-sin(currentStartAngle), cos(currentStartAngle));
-    Point cp1 = currentStartPoint;
-    cp1 += tangentStart * kappa;
-
-    Point revTangentEnd(sin(currentEndAngle), -cos(currentEndAngle));
-    Point cp2 = currentEndPoint;
-    cp2 += revTangentEnd * kappa;
-
-    if (mPathBuilder) {
-      mPathBuilder->BezierTo(cp1, cp2, currentEndPoint);
-    } else {
-      mDSPathBuilder->BezierTo(mTarget->GetTransform() * cp1,
-                               mTarget->GetTransform() * cp2,
-                               mTarget->GetTransform() * currentEndPoint);
-    }
-
-    arcSweepLeft -= M_PI / 2.0f;
-    currentStartAngle = currentEndAngle;
-  }
-
-
+  ArcToBezier(this, Point(x, y), r, startAngle, endAngle, ccw);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2DAzure::Rect(float x, float y, float w, float h)
 {
   if (!FloatValidate(x, y, w, h)) {
     return NS_OK;
--- a/content/canvas/test/webgl/README.mozilla
+++ b/content/canvas/test/webgl/README.mozilla
@@ -1,9 +1,9 @@
-This is a local copy of the WebGL conformance suite, SVN revision 15892
+This is a local copy of the WebGL conformance suite, SVN revision 15981
 
 The canonical location for this testsuite is:
 
   https://cvs.khronos.org/svn/repos/registry/trunk/public/webgl/sdk/tests
 
 All files and directories in this directory, with the exceptions listed below, come from
 upstream and should not be modified without corresponding upstream fixes and/or a
 patch file in this directory. The exceptions (the Mozilla-specific files) are:
--- a/content/canvas/test/webgl/conformance/attribs/gl-enable-vertex-attrib.html
+++ b/content/canvas/test/webgl/conformance/attribs/gl-enable-vertex-attrib.html
@@ -28,18 +28,18 @@
 <script id="fshader" type="x-shader/x-fragment">
     void main()
     {
         gl_FragColor = vec4(1.0,0.0,0.0,1.0);
     }
 </script>
 
 <script>
+description("tests that turning on attribs that have no buffer bound fails to draw");
 gl = initWebGL("example", "vshader", "fshader", [ "vPosition"], [ 0, 0, 0, 1 ], 1);
-gl.viewport(0, 0, 50, 50);
 
 var vertexObject = gl.createBuffer();
 gl.bindBuffer(gl.ARRAY_BUFFER, vertexObject);
 gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ 0,0.5,0, -0.5,-0.5,0, 0.5,-0.5,0 ]), gl.STATIC_DRAW);
 gl.enableVertexAttribArray(0);
 gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0);
 
 gl.enableVertexAttribArray(3);
--- a/content/canvas/test/webgl/conformance/attribs/gl-vertexattribpointer-offsets.html
+++ b/content/canvas/test/webgl/conformance/attribs/gl-vertexattribpointer-offsets.html
@@ -22,17 +22,17 @@ OF LIABILITY, WHETHER IN CONTRACT, STRIC
 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 -->
 
 <!DOCTYPE html>
 <html>
   <head>
 <meta charset="utf-8">
-    <title>Rendering Test</title>
+    <title>vertexattribpointer offsets test</title>
     <link rel="stylesheet" href="../../resources/js-test-style.css"/>
     <script src="../../resources/js-test-pre.js"></script>
     <script src="../resources/webgl-test.js"> </script>
     <script src="../resources/webgl-test-utils.js"> </script>
 </head>
 <body>
 <canvas id="example" width="50" height="50">
 There is supposed to be an example drawing here, but it's not important.
@@ -69,16 +69,17 @@ There is supposed to be an example drawi
             testPassed("drawing is correct");
         }
 
         function init()
         {
             if (window.initNonKhronosFramework) {
                 window.initNonKhronosFramework(false);
             }
+            description("test vertexattribpointer offsets work");
 
             wtu = WebGLTestUtils;
             gl = initWebGL("example", "vshader", "fshader", [ "vPosition"], [ 0, 0, 0, 1 ], 1);
 
             var tests = [
               { data: new Float32Array([ 0, 1, 0, 1, 0, 0, 0, 0, 0 ]),
                 type: gl.FLOAT,
                 componentSize: 4,
--- a/content/canvas/test/webgl/conformance/context/resource-sharing-test.html
+++ b/content/canvas/test/webgl/conformance/context/resource-sharing-test.html
@@ -14,17 +14,17 @@ found in the LICENSE file.
     <script src="../../debug/webgl-debug.js"> </script>
 </head>
 <body>
 <canvas id="example1" width="2" height="2" style="width: 40px; height: 40px;"></canvas>
 <canvas id="example2" width="2" height="2" style="width: 40px; height: 40px;"></canvas>
 <div id="description"></div>
 <div id="console"></div>
 <script>
-debug("Tests that resources can not be shared.");
+description("Tests that resources can not be shared.");
 debug("");
 
 var gl1 = create3DContext(document.getElementById("example1"));
 var gl2 = create3DContext(document.getElementById("example2"));
 assertMsg(gl1 && gl2,
           "Got 3d context.");
 
 var vertexObject = gl1.createBuffer();
--- a/content/canvas/test/webgl/conformance/glsl/00_test_list.txt
+++ b/content/canvas/test/webgl/conformance/glsl/00_test_list.txt
@@ -1,6 +1,8 @@
 functions/00_test_list.txt
 implicit/00_test_list.txt
 misc/00_test_list.txt
 reserved/00_test_list.txt
+# commented out for version 1.0.1 of the conforamnce tests.
+#samplers/00_test_list.txt
 variables/00_test_list.txt
 
--- a/content/canvas/test/webgl/conformance/glsl/misc/00_test_list.txt
+++ b/content/canvas/test/webgl/conformance/glsl/misc/00_test_list.txt
@@ -1,19 +1,22 @@
+attrib-location-length-limits.html
+embedded-struct-definitions-forbidden.html
 #glsl-2types-of-textures-on-same-unit.html
 glsl-function-nodes.html
 glsl-long-variable-names.html
 non-ascii-comments.vert.html
 non-ascii.vert.html
 shader-with-256-character-identifier.frag.html
 shader-with-257-character-identifier.frag.html
 shader-with-_webgl-identifier.vert.html
 shader-with-arbitrary-indexing.frag.html
 shader-with-arbitrary-indexing.vert.html
 shader-with-attrib-array.vert.html
+shader-with-attrib-struct.vert.html
 shader-with-clipvertex.vert.html
 shader-with-default-precision.frag.html
 shader-with-default-precision.vert.html
 shader-with-define-line-continuation.frag.html
 shader-with-dfdx-no-ext.frag.html
 shader-with-dfdx.frag.html
 shader-with-error-directive.html
 shader-with-explicit-int-cast.vert.html
@@ -44,8 +47,11 @@ shader-with-vec3-return-value.frag.html
 shader-with-vec4-return-value.frag.html
 shader-with-version-100.frag.html
 shader-with-version-100.vert.html
 shader-with-version-120.vert.html
 shader-with-version-130.vert.html
 shader-with-webgl-identifier.vert.html
 shader-without-precision.frag.html
 shared.html
+struct-nesting-exceeds-maximum.html
+struct-nesting-under-maximum.html
+uniform-location-length-limits.html
new file mode 100644
--- /dev/null
+++ b/content/canvas/test/webgl/conformance/glsl/misc/attrib-location-length-limits.html
@@ -0,0 +1,81 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>WebGL attrib location length tests</title>
+<link rel="stylesheet" href="../../../resources/js-test-style.css"/>
+<script src="../../../resources/js-test-pre.js"></script>
+<script src="../../resources/webgl-test.js"> </script>
+<script src="../../resources/webgl-test-utils.js"> </script>
+</head>
+<body>
+<canvas id="example" width="50" height="50">
+There is supposed to be an example drawing here, but it's not important.
+</canvas>
+<div id="description">Verify limits on the lengths of attribute locations per WebGL spec, "Maximum Uniform and Attribute Location Lengths".</div>
+<div id="console"></div>
+<script id="goodVertexShader" type="x-shader/x-vertex">
+// A vertex shader where the needed attrib location is exactly 256 characters.
+attribute vec4 vPosition0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456;
+
+void main()
+{
+    gl_Position = vPosition0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456;
+}
+</script>
+<script id="badVertexShader" type="x-shader/x-vertex">
+// A vertex shader where the needed attrib location is 257 characters.
+attribute vec4 vPosition01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567;
+
+void main()
+{
+    gl_Position = vPosition01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567;
+}
+</script>
+<script id="fragmentShader" type="x-shader/x-fragment">
+precision mediump float;
+
+void main() {
+    gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
+}
+</script>
+<script>
+if (window.initNonKhronosFramework) {
+    window.initNonKhronosFramework(false);
+}
+description("test attrib location length limit");
+
+var wtu = WebGLTestUtils;
+var gl = wtu.create3DContext(document.getElementById("example"));
+
+debug("Test attrib location underneath the length limit");
+var program = wtu.loadProgramFromScript(gl, "goodVertexShader", "fragmentShader");
+shouldBe('gl.getProgramParameter(program, gl.LINK_STATUS)', 'true');
+var attribLoc = gl.getAttribLocation(program, "vPosition0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456");
+if (attribLoc == -1) {
+    testFailed("attrib location was -1, should not be");
+} else {
+    testPassed("attrib location should not be -1");
+}
+wtu.glErrorShouldBe(gl, gl.NONE);
+
+debug("Test attrib location over the length limit");
+debug("Shader compilation should fail");
+shouldBe('wtu.loadShaderFromScript(gl, "badVertexShader", gl.VERTEX_SHADER, function (err) {})', 'null');
+wtu.glErrorShouldBe(gl, gl.NONE);
+
+debug("Attempt to bind too-long attrib location should produce error");
+program = gl.createProgram();
+gl.bindAttribLocation(program, 0, "vPosition01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567");
+wtu.glErrorShouldBe(gl, gl.INVALID_VALUE);
+
+debug("Attempt to fetch too-long attrib location should produce error");
+program = wtu.loadStandardProgram(gl);
+shouldBe('gl.getAttribLocation(program, "vPosition01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567")', '-1');
+wtu.glErrorShouldBe(gl, gl.INVALID_VALUE);
+
+successfullyParsed = true;
+</script>
+<script src="../../../resources/js-test-post.js"></script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/canvas/test/webgl/conformance/glsl/misc/embedded-struct-definitions-forbidden.html
@@ -0,0 +1,38 @@
+<!--
+Copyright (c) 2011 The Chromium Authors. All rights reserved.
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+-->
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>WebGL GLSL Conformance Tests</title>
+<link rel="stylesheet" href="../../../resources/js-test-style.css"/>
+<script src="../../../resources/js-test-pre.js"></script>
+<script src="../../resources/webgl-test-utils.js"></script>
+<script src="../../resources/glsl-conformance-test.js"></script>
+</head>
+<body>
+<div id="description"></div>
+<div id="console"></div>
+<script id="vertexShader" type="text/something-not-javascript">
+// embedded structure definitions are forbidden per GLSL ES section 4.1.8, "Structures", and should fail
+struct nesting1 {
+  struct nesting2 {
+    vec4 vector;
+  } field2;
+};
+
+uniform nesting1 uniform1;
+void main()
+{
+    gl_Position = uniform1.field2.vector;
+}
+</script>
+<script>
+GLSLConformanceTester.runTest();
+successfullyParsed = true;
+</script>
+</body>
+</html>
--- a/content/canvas/test/webgl/conformance/glsl/misc/glsl-2types-of-textures-on-same-unit.html
+++ b/content/canvas/test/webgl/conformance/glsl/misc/glsl-2types-of-textures-on-same-unit.html
@@ -44,20 +44,20 @@ void main()
 
   <script>
 function init()
 {
   if (window.initNonKhronosFramework) {
       window.initNonKhronosFramework(false);
   }
 
-  debug("Tests that using 2 types of textures on the same texture unit");
-  debug("and referencing them both in the same program fails as per");
-  debug("OpenGL ES 2.0.24 spec section 2.10.4, Samplers subsection.");
-  debug("");
+  description(
+    "Tests that using 2 types of textures on the same texture unit" +
+    "and referencing them both in the same program fails as per" +
+    "OpenGL ES 2.0.24 spec section 2.10.4, Samplers subsection.");
 
   var canvas2d = document.getElementById("canvas2d");
   var ctx2d = canvas2d.getContext("2d");
 
   gl = initWebGL("example", "vshader", "fshader", [ "vPosition", "texCoord0"],
                  [ 0, 0, 0, 1 ], 1);
 
   gl.disable(gl.DEPTH_TEST);
--- a/content/canvas/test/webgl/conformance/glsl/misc/glsl-function-nodes.html
+++ b/content/canvas/test/webgl/conformance/glsl/misc/glsl-function-nodes.html
@@ -107,25 +107,28 @@ function compareRendering(buffer1, buffe
 }
 
 function init()
 {
     if (window.initNonKhronosFramework) {
         window.initNonKhronosFramework(false);
     }
 
+    description("tests function nodes");
+
     var bufFunction = new Uint8Array(width * height * 4);
     var bufMacro = new Uint8Array(width * height * 4);
 
     if (drawAndRead("canvasFunction", "vshaderFunction", bufFunction) == false ||
         drawAndRead("canvasMacro", "vshaderMacro", bufMacro) == false) {
         testFailed("Setup failed");
     } else {
         if (compareRendering(bufFunction, bufMacro, 4) == false)
-            testFailed("Rendering results are different");
+            testFailedRender("Rendering results are different", bufMacro,
+                             bufFunction, width, height);
         else
             testPassed("Rendering results are the same");
     }
 }
 
 init();
 successfullyParsed = true;
 </script>
--- a/content/canvas/test/webgl/conformance/glsl/misc/glsl-long-variable-names.html
+++ b/content/canvas/test/webgl/conformance/glsl/misc/glsl-long-variable-names.html
@@ -6,17 +6,17 @@
     <link rel="stylesheet" href="../../../resources/js-test-style.css"/>
     <script src="../../../resources/js-test-pre.js"></script>
     <script src="../../resources/webgl-test.js"> </script>
 </head>
 <body>
     <canvas id="example" width="50" height="50">
     There is supposed to be an example drawing here, but it's not important.
     </canvas>
-    <div id="description">Verify that shader long variable names works fine if they are within 256 characters.</div>
+    <div id="description"></div>
     <div id="console"></div>
     <script id="vshader" type="x-shader/x-vertex">
         attribute vec4 vPosition0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456;
         varying float alpha01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890;
         void main()
         {
             alpha01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890 = 1.0;
             gl_Position = vPosition0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456;
@@ -48,16 +48,18 @@
         {
             testPassed("drawing is correct");
         }
 
         if (window.initNonKhronosFramework) {
             window.initNonKhronosFramework(false);
         }
 
+        description("Verify that shader long variable names works fine if they are within 256 characters.");
+
         gl = initWebGL("example", "vshader", "fshader", [ "vPosition0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456"], [ 0, 0, 0, 1 ], 1);
 
         var prog = gl.getParameter(gl.CURRENT_PROGRAM);
         shouldBeNonNull(prog);
         var redLoc = gl.getUniformLocation(prog, "color01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567[0]");
         shouldBeNonNull(redLoc);
         var greenLoc = gl.getUniformLocation(prog, "color01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567[1]");
         shouldBeNonNull(greenLoc);
new file mode 100644
--- /dev/null
+++ b/content/canvas/test/webgl/conformance/glsl/misc/shader-with-attrib-struct.vert.html
@@ -0,0 +1,38 @@
+<!--
+Copyright (c) 2011 The Chromium Authors. All rights reserved.
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+-->
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>WebGL GLSL Conformance Tests</title>
+<link rel="stylesheet" href="../../../resources/js-test-style.css"/>
+<script src="../../../resources/js-test-pre.js"></script>
+<script src="../../resources/webgl-test-utils.js"></script>
+<script src="../../resources/glsl-conformance-test.js"></script>
+</head>
+<body>
+<div id="description"></div>
+<div id="console"></div>
+<script id="vertexShader" type="text/something-not-javascript">
+// vertex shader that uses attribute struct should fail per GLSL ES section 4.4.3, "Attribute", p. 30
+struct UserType {
+    attribute vec4 position;
+};
+
+attribute UserType userAttr;
+void main()
+{
+    gl_Position = userAttr.position;
+}
+</script>
+<script>
+GLSLConformanceTester.runTest();
+successfullyParsed = true;
+</script>
+</body>
+</html>
+
+
new file mode 100644
--- /dev/null
+++ b/content/canvas/test/webgl/conformance/glsl/misc/struct-nesting-exceeds-maximum.html
@@ -0,0 +1,48 @@
+<!--
+Copyright (c) 2011 The Chromium Authors. All rights reserved.
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+-->
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>WebGL GLSL Conformance Tests</title>
+<link rel="stylesheet" href="../../../resources/js-test-style.css"/>
+<script src="../../../resources/js-test-pre.js"></script>
+<script src="../../resources/webgl-test-utils.js"></script>
+<script src="../../resources/glsl-conformance-test.js"></script>
+</head>
+<body>
+<div id="description"></div>
+<div id="console"></div>
+<script id="vertexShader" type="text/something-not-javascript">
+// shader with too-deep struct nesting should fail per WebGL spec
+struct nesting4 {
+  vec4 vector;
+};
+
+struct nesting3 {
+  nesting4 field4;
+};
+
+struct nesting2 {
+  nesting3 field3;
+};
+
+struct nesting1 {
+  nesting2 field2;
+};
+
+uniform nesting1 uniform1;
+void main()
+{
+    gl_Position = uniform1.field2.field3.field4.vector;
+}
+</script>
+<script>
+GLSLConformanceTester.runTest();
+successfullyParsed = true;
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/canvas/test/webgl/conformance/glsl/misc/struct-nesting-under-maximum.html
@@ -0,0 +1,44 @@
+<!--
+Copyright (c) 2011 The Chromium Authors. All rights reserved.
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+-->
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>WebGL GLSL Conformance Tests</title>
+<link rel="stylesheet" href="../../../resources/js-test-style.css"/>
+<script src="../../../resources/js-test-pre.js"></script>
+<script src="../../resources/webgl-test-utils.js"></script>
+<script src="../../resources/glsl-conformance-test.js"></script>
+</head>
+<body>
+<div id="description"></div>
+<div id="console"></div>
+<script id="vertexShader" type="text/something-not-javascript">
+// shader with struct nesting less than maximum in WebGL spec should succeed
+struct nesting3 {
+  vec4 vector;
+};
+
+struct nesting2 {
+  nesting3 field3;
+};
+
+struct nesting1 {
+  nesting2 field2;
+};
+
+uniform nesting1 uniform1;
+void main()
+{
+    gl_Position = uniform1.field2.field3.vector;
+}
+</script>
+<script>
+GLSLConformanceTester.runTest();
+successfullyParsed = true;
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/canvas/test/webgl/conformance/glsl/misc/uniform-location-length-limits.html
@@ -0,0 +1,84 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>WebGL uniform location length tests</title>
+<link rel="stylesheet" href="../../../resources/js-test-style.css"/>
+<script src="../../../resources/js-test-pre.js"></script>
+<script src="../../resources/webgl-test.js"> </script>
+<script src="../../resources/webgl-test-utils.js"> </script>
+</head>
+<body>
+<canvas id="example" width="50" height="50">
+There is supposed to be an example drawing here, but it's not important.
+</canvas>
+<div id="description">Verify limits on the lengths of uniform locations per WebGL spec, "Maximum Uniform and Attribute Location Lengths".</div>
+<div id="console"></div>
+<script id="goodVertexShader" type="x-shader/x-vertex">
+// A vertex shader where the needed uniform location is exactly 256 characters.
+struct Nesting2 {
+    vec4 identifier62CharactersLong_01234567890123456789012345678901234;
+};
+
+struct Nesting1 {
+    Nesting2 identifier64CharactersLong_0123456789012345678901234567890123456;
+};
+
+uniform Nesting1 identifier128CharactersLong_0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789;
+
+void main() {
+    gl_Position = identifier128CharactersLong_0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789.identifier64CharactersLong_0123456789012345678901234567890123456.identifier62CharactersLong_01234567890123456789012345678901234;
+}
+</script>
+<script id="badVertexShader" type="x-shader/x-vertex">
+// A vertex shader where the needed uniform location is 257 characters.
+struct Nesting2 {
+    vec4 identifier63CharactersLong_012345678901234567890123456789012345;
+};
+
+struct Nesting1 {
+    Nesting2 identifier64CharactersLong_0123456789012345678901234567890123456;
+};
+
+uniform Nesting1 identifier128CharactersLong_0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789;
+
+void main() {
+    Nesting2 temp = identifier128CharactersLong_0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789.identifier64CharactersLong_0123456789012345678901234567890123456;
+    gl_Position = temp.identifier63CharactersLong_012345678901234567890123456789012345;
+}
+</script>
+<script id="fragmentShader" type="x-shader/x-fragment">
+precision mediump float;
+
+void main() {
+    gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
+}
+</script>
+<script>
+if (window.initNonKhronosFramework) {
+    window.initNonKhronosFramework(false);
+}
+
+var wtu = WebGLTestUtils;
+var gl = wtu.create3DContext(document.getElementById("example"));
+
+debug("Test uniform location underneath the length limit");
+var program = wtu.loadProgramFromScript(gl, "goodVertexShader", "fragmentShader");
+shouldBe('gl.getProgramParameter(program, gl.LINK_STATUS)', 'true');
+var uniformLoc = gl.getUniformLocation(program, "identifier128CharactersLong_0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789.identifier64CharactersLong_0123456789012345678901234567890123456.identifier62CharactersLong_01234567890123456789012345678901234");
+shouldBeNonNull('uniformLoc');
+wtu.glErrorShouldBe(gl, gl.NONE);
+
+debug("Test uniform location over the length limit");
+program = wtu.loadProgramFromScript(gl, "badVertexShader", "fragmentShader");
+wtu.glErrorShouldBe(gl, gl.NONE);
+shouldBe('gl.getProgramParameter(program, gl.LINK_STATUS)', 'true');
+var uniformLoc = gl.getUniformLocation(program, "identifier128CharactersLong_0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789.identifier64CharactersLong_0123456789012345678901234567890123456.identifier63CharactersLong_012345678901234567890123456789012345");
+wtu.glErrorShouldBe(gl, gl.INVALID_VALUE);
+shouldBeNull('uniformLoc');
+
+successfullyParsed = true;
+</script>
+<script src="../../../resources/js-test-post.js"></script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/canvas/test/webgl/conformance/glsl/samplers/00_test_list.txt
@@ -0,0 +1,4 @@
+glsl-function-texture2d-bias.html
+glsl-function-texture2dlod.html
+glsl-function-texture2dproj.html
+
new file mode 100644
--- /dev/null
+++ b/content/canvas/test/webgl/conformance/glsl/samplers/glsl-function-texture2d-bias.html
@@ -0,0 +1,104 @@
+<!--
+Copyright (c) 2011 The Chromium Authors. All rights reserved.
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+ -->
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>WebGL texture2D GLSL conformance test.</title>
+<link rel="stylesheet" href="../../../resources/js-test-style.css"/>
+<script src="../../../resources/js-test-pre.js"></script>
+<script src="../../resources/webgl-test.js"> </script>
+<script src="../../resources/webgl-test-utils.js"> </script>
+</head>
+<body>
+<canvas id="example" width="256" height="256" style="width: 16px; height: 16px;"></canvas>
+<div id="description"></div>
+<div id="console"></div>
+<script id="vshader2d" type="x-shader/x-vertex">
+attribute vec4 vPosition;
+attribute vec2 texCoord0;
+varying vec2 texCoord;
+void main() {
+    gl_Position = vPosition;
+    texCoord = texCoord0;
+}
+</script>
+<script id="fshader2d" type="x-shader/x-vertex">
+precision mediump float;
+uniform sampler2D tex;
+uniform float bias;
+varying vec2 texCoord;
+void main() {
+    gl_FragData[0] = texture2D(tex, texCoord, bias);
+}
+</script>
+<script>
+description("tests GLSL texture2D function with bias");
+
+var wtu = WebGLTestUtils;
+var canvas = document.getElementById("example");
+
+shouldBe("canvas.width", "256");
+shouldBe("canvas.height", "256");
+
+var gl = wtu.create3DContext(canvas);
+var program = wtu.setupProgram(
+    gl,
+    [wtu.loadShaderFromScript(gl, 'vshader2d', gl.VERTEX_SHADER),
+     wtu.loadShaderFromScript(gl, 'fshader2d', gl.FRAGMENT_SHADER)],
+    ['vPosition', 'texCoord0'], [0, 1]);
+wtu.setupUnitQuad(gl, 0, 1);
+
+var colors = [
+  {name: 'red', color:[255, 0, 0, 255]},
+  {name: 'green', color:[0, 255, 0, 255]},
+  {name: 'blue', color:[0, 0, 255, 255]},
+  {name: 'yellow', color:[255, 255, 0, 255]},
+  {name: 'magenta', color:[255, 0, 255, 255]},
+  {name: 'cyan', color:[0, 255, 255, 255]},
+  {name: 'pink', color:[255, 128, 128, 255]},
+  {name: 'gray', color:[128, 128, 128, 255]},
+  {name: 'light green', color:[128, 255, 128, 255]},
+];
+
+shouldBe("colors.length", "9");
+
+var tex = gl.createTexture();
+gl.bindTexture(gl.TEXTURE_2D, tex);
+gl.texParameteri(
+    gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_NEAREST);
+gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
+gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
+gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
+
+for (var ii = 0; ii < colors.length; ++ii) {
+  var color = colors[ii];
+  var size = Math.pow(2, colors.length - ii - 1);
+  wtu.fillTexture(gl, tex, size, size, color.color, ii);
+}
+
+var loc = gl.getUniformLocation(program, "bias");
+
+for (var ii = 0; ii < colors.length; ++ii) {
+  gl.uniform1f(loc, ii);
+  var color = colors[ii];
+  wtu.drawQuad(gl);
+  wtu.checkCanvas(
+      gl, color.color,
+      "256x256 texture drawn to 256x256 dest with bias = " + ii +
+      " should be " + color.name);
+}
+glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors.");
+
+successfullyParsed = true;
+
+</script>
+<script src="../../../resources/js-test-post.js"></script>
+
+</body>
+</html>
+
+
new file mode 100644
--- /dev/null
+++ b/content/canvas/test/webgl/conformance/glsl/samplers/glsl-function-texture2dlod.html
@@ -0,0 +1,104 @@
+<!--
+Copyright (c) 2011 The Chromium Authors. All rights reserved.
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+ -->
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>WebGL texture2D GLSL conformance test.</title>
+<link rel="stylesheet" href="../../../resources/js-test-style.css"/>
+<script src="../../../resources/js-test-pre.js"></script>
+<script src="../../resources/webgl-test.js"> </script>
+<script src="../../resources/webgl-test-utils.js"> </script>
+</head>
+<body>
+<canvas id="example" width="256" height="256" style="width: 16px; height: 16px;"></canvas>
+<div id="description"></div>
+<div id="console"></div>
+<script id="vshader2d" type="x-shader/x-vertex">
+attribute vec4 vPosition;
+attribute vec2 texCoord0;
+varying vec4 color;
+uniform sampler2D tex;
+uniform float lod;
+void main() {
+    gl_Position = vPosition;
+    color = texture2DLod(tex, texCoord0, lod);
+}
+</script>
+<script id="fshader2d" type="x-shader/x-vertex">
+precision mediump float;
+varying vec4 color;
+void main() {
+    gl_FragData[0] = color;
+}
+</script>
+<script>
+description("tests GLSL texture2DLod function");
+
+var wtu = WebGLTestUtils;
+var canvas = document.getElementById("example");
+
+shouldBe("canvas.width", "256");
+shouldBe("canvas.height", "256");
+
+var gl = wtu.create3DContext(canvas);
+var program = wtu.setupProgram(
+    gl,
+    [wtu.loadShaderFromScript(gl, 'vshader2d', gl.VERTEX_SHADER),
+     wtu.loadShaderFromScript(gl, 'fshader2d', gl.FRAGMENT_SHADER)],
+    ['vPosition', 'texCoord0'], [0, 1]);
+wtu.setupUnitQuad(gl, 0, 1);
+
+var colors = [
+  {name: 'red', color:[255, 0, 0, 255]},
+  {name: 'green', color:[0, 255, 0, 255]},
+  {name: 'blue', color:[0, 0, 255, 255]},
+  {name: 'yellow', color:[255, 255, 0, 255]},
+  {name: 'magenta', color:[255, 0, 255, 255]},
+  {name: 'cyan', color:[0, 255, 255, 255]},
+  {name: 'pink', color:[255, 128, 128, 255]},
+  {name: 'gray', color:[128, 128, 128, 255]},
+  {name: 'light green', color:[128, 255, 128, 255]},
+];
+
+shouldBe("colors.length", "9");
+
+var tex = gl.createTexture();
+gl.bindTexture(gl.TEXTURE_2D, tex);
+gl.texParameteri(
+    gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST_MIPMAP_LINEAR);
+gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
+gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
+gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
+
+for (var ii = 0; ii < colors.length; ++ii) {
+  var color = colors[ii];
+  var size = Math.pow(2, colors.length - ii - 1);
+  wtu.fillTexture(gl, tex, size, size, color.color, ii);
+}
+
+var loc = gl.getUniformLocation(program, "lod");
+
+for (var ii = 0; ii < colors.length; ++ii) {
+  gl.uniform1f(loc, ii);
+  var color = colors[ii];
+  wtu.drawQuad(gl);
+  wtu.checkCanvas(
+      gl, color.color,
+      "256x256 texture drawn to 256x256 dest with lod = " + ii +
+      " should be " + color.name);
+}
+glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors.");
+
+successfullyParsed = true;
+
+</script>
+<script src="../../../resources/js-test-post.js"></script>
+
+</body>
+</html>
+
+
new file mode 100644
--- /dev/null
+++ b/content/canvas/test/webgl/conformance/glsl/samplers/glsl-function-texture2dproj.html
@@ -0,0 +1,120 @@
+<!--
+Copyright (c) 2011 The Chromium Authors. All rights reserved.
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+ -->
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>WebGL texture2D GLSL conformance test.</title>
+<link rel="stylesheet" href="../../../resources/js-test-style.css"/>
+<script src="../../../resources/js-test-pre.js"></script>
+<script src="../../resources/webgl-test.js"> </script>
+<script src="../../resources/webgl-test-utils.js"> </script>
+</head>
+<body>
+<canvas id="example" width="32" height="32"></canvas>
+<div id="description"></div>
+<div id="console"></div>
+<script id="vshader0" type="x-shader/x-vertex">
+attribute vec4 vPosition;
+attribute vec2 texCoord0;
+varying vec2 texCoord;
+void main() {
+    gl_Position = vPosition;
+    texCoord = texCoord0;
+}
+</script>
+<script id="fshader0" type="x-shader/x-vertex">
+precision mediump float;
+uniform sampler2D tex;
+uniform float divisor;
+varying vec2 texCoord;
+void main() {
+    gl_FragData[0] = texture2DProj(tex, vec3(texCoord, divisor));
+}
+</script>
+<script id="vshader1" type="x-shader/x-vertex">
+attribute vec4 vPosition;
+attribute vec2 texCoord0;
+varying vec2 texCoord;
+void main() {
+    gl_Position = vPosition;
+    texCoord = texCoord0;
+}
+</script>
+<script id="fshader1" type="x-shader/x-vertex">
+precision mediump float;
+uniform sampler2D tex;
+uniform float divisor;
+varying vec2 texCoord;
+void main() {
+    gl_FragData[0] = texture2DProj(tex, vec4(texCoord, 123.0, divisor));
+}
+</script>
+<script>
+description("tests GLSL texture2DProj function with");
+
+var wtu = WebGLTestUtils;
+var canvas = document.getElementById("example");
+
+var gl = wtu.create3DContext(canvas, {antialias: false});
+
+wtu.setupUnitQuad(gl, 0, 1);
+var tex = gl.createTexture();
+gl.bindTexture(gl.TEXTURE_2D, tex);
+gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
+gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
+gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
+gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
+
+var c = document.createElement("canvas");
+c.width = 16;
+c.height = 16;
+var ctx = c.getContext("2d");
+ctx.fillStyle = "rgb(0,255,0)";
+ctx.fillRect(0, 0, 16, 16);
+ctx.fillStyle = "rgb(0,0,255)";
+ctx.fillRect(0, 0, 8, 8);
+ctx.fillRect(8, 8, 8, 8);
+
+gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, c);
+
+for (var ss = 0; ss < 2; ++ss) {
+  debug("");
+  debug(ss ? "testing vec4 version" : "testing vec3 version");
+  var program = wtu.setupProgram(
+	  gl,
+	  [wtu.loadShaderFromScript(gl, 'vshader' + ss, gl.VERTEX_SHADER),
+	   wtu.loadShaderFromScript(gl, 'fshader' + ss, gl.FRAGMENT_SHADER)],
+	  ['vPosition', 'texCoord0'], [0, 1]);
+  gl.useProgram(program);
+  var loc = gl.getUniformLocation(program, "divisor");
+
+  for (var ii = 0; ii < 3; ++ii) {
+	var denominator = Math.pow(2, ii);
+	gl.uniform1f(loc, 1 / denominator);
+	wtu.drawQuad(gl);
+	var size = 16 / denominator;
+	for (var yy = 0; yy < 32; yy += size) {
+	  for (var xx = 0; xx < 32; xx += size) {
+		var odd = (xx / size + yy / size) % 2;
+		var color = odd ? [0, 255, 0, 255] : [0, 0, 255, 255];
+		var msg = "" + xx + ", " + yy + ", " + size + ", " + size + " should be " + (odd ? "green" : "blue");
+		wtu.checkCanvasRect(gl, xx, yy, size, size, color, msg);
+	  }
+	}
+  }
+}
+glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors.");
+
+successfullyParsed = true;
+
+</script>
+<script src="../../../resources/js-test-post.js"></script>
+
+</body>
+</html>
+
+
--- a/content/canvas/test/webgl/conformance/glsl/variables/gl-fragcoord.html
+++ b/content/canvas/test/webgl/conformance/glsl/variables/gl-fragcoord.html
@@ -40,16 +40,18 @@ found in the LICENSE file.
 
   <script>
     function init()
     {
       if (window.initNonKhronosFramework) {
         window.initNonKhronosFramework(false);
       }
 
+      description("tests gl_FragCoord");
+
       wtu = WebGLTestUtils;
       gl = initWebGL("example", "vshader", "fshader", [ "vPosition"], [ 0, 0, 0, 1 ], 1);
 
       var vertexObject = gl.createBuffer();
       gl.bindBuffer(gl.ARRAY_BUFFER, vertexObject);
       gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(
         [-1, -1, -1,  1, -1,  0, -1,  1,  0,
          -1,  1,  0,  1, -1,  0,  1,  1,  1]),
--- a/content/canvas/test/webgl/conformance/glsl/variables/gl-frontfacing.html
+++ b/content/canvas/test/webgl/conformance/glsl/variables/gl-frontfacing.html
@@ -40,16 +40,18 @@ found in the LICENSE file.
 
   <script>
     function init()
     {
       if (window.initNonKhronosFramework) {
         window.initNonKhronosFramework(false);
       }
 
+      description("tests gl_FrontFacing");
+
       wtu = WebGLTestUtils;
       gl = initWebGL("example", "vshader", "fshader", [ "vPosition"], [ 0, 0, 0, 1 ], 1);
 
       var gridRes = 4;
       wtu.setupQuad(gl, gridRes, 0, true);
 
       gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
       gl.drawElements(gl.TRIANGLES, gridRes * gridRes * 6, gl.UNSIGNED_SHORT, 0);
--- a/content/canvas/test/webgl/conformance/limits/gl-max-texture-dimensions.html
+++ b/content/canvas/test/webgl/conformance/limits/gl-max-texture-dimensions.html
@@ -33,16 +33,17 @@ precision mediump float;
 uniform samplerCube tex;
 varying vec2 texCoord;
 void main()
 {
     gl_FragColor = textureCube(tex, normalize(vec3(texCoord, 1)));
 }
 </script>
 <script>
+description(document.title);
 var wtu = WebGLTestUtils;
 var canvas = document.getElementById("example");
 var gl = wtu.create3DContext(canvas);
 var program = wtu.setupTexturedQuad(gl);
 
 // Note: It seems like a reasonable assuption that a 1xN texture size should
 // work. Even 1 by 128k is only 512k
 var maxSize = gl.getParameter(gl.MAX_TEXTURE_SIZE);
--- a/content/canvas/test/webgl/conformance/limits/gl-min-attribs.html
+++ b/content/canvas/test/webgl/conformance/limits/gl-min-attribs.html
@@ -38,16 +38,17 @@ void main()
 precision mediump float;
 varying vec4 color;
 void main()
 {
     gl_FragColor = color;
 }
 </script>
 <script>
+description(document.title);
 var wtu = WebGLTestUtils;
 var canvas = document.getElementById("example");
 var gl = wtu.create3DContext(canvas);
 var program = wtu.setupTexturedQuad(gl);
 
 var program = wtu.setupProgram(
     gl,
     [wtu.loadShaderFromScript(gl, 'vshader', gl.VERTEX_SHADER),
--- a/content/canvas/test/webgl/conformance/limits/gl-min-textures.html
+++ b/content/canvas/test/webgl/conformance/limits/gl-min-textures.html
@@ -23,27 +23,28 @@ void main()
 {
     gl_Position = vPosition;
 }
 </script>
 
 <script id="fshader" type="x-shader/x-fragment">
 #define NUM_TEXTURES 8 // See spec
 precision mediump float;
-uniform sampler2D uni[8];
+uniform sampler2D uni[NUM_TEXTURES];
 void main()
 {
     vec4 c = vec4(0,0,0,0);
     for (int ii = 0; ii < NUM_TEXTURES; ++ii) {
       c += texture2D(uni[ii], vec2(0.5, 0.5));
     }
     gl_FragColor = c;
 }
 </script>
 <script>
+description(document.title);
 var wtu = WebGLTestUtils;
 var canvas = document.getElementById("example");
 var gl = wtu.create3DContext(canvas);
 var program = wtu.setupTexturedQuad(gl);
 
 //------------------------------------------------------------------------------
 var program = wtu.setupProgram(
     gl,
@@ -57,17 +58,17 @@ for (var ii = 0; ii < 8; ++ii) {
   var tex = gl.createTexture();
   wtu.fillTexture(gl, tex, 1, 1, [32, 16, 8, ii * 9], 0);
   gl.uniform1i(loc, ii);
 }
 
 wtu.drawQuad(gl);
 glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup.");
 wtu.checkCanvas(gl, [255, 128, 64, 252],
-                "Should render using all texture units");
+                "Should render using all texture units", 1);
 
 successfullyParsed = true;
 
 </script>
 <script src="../../resources/js-test-post.js"></script>
 
 </body>
 </html>
--- a/content/canvas/test/webgl/conformance/limits/gl-min-uniforms.html
+++ b/content/canvas/test/webgl/conformance/limits/gl-min-uniforms.html
@@ -58,16 +58,17 @@ void main()
     vec4 c = vec4(0,0,0,0);
     for (int ii = 0; ii < NUM_UNIFORMS; ++ii) {
        c += uni[ii];
     }
     gl_FragColor = vec4(c.r, c.g, c.b, c.a / 120.0);
 }
 </script>
 <script>
+description(document.title);
 var wtu = WebGLTestUtils;
 var canvas = document.getElementById("example");
 var gl = wtu.create3DContext(canvas);
 var program = wtu.setupTexturedQuad(gl);
 
 //------------------------------------------------------------------------------
 var program = wtu.setupProgram(
     gl,
--- a/content/canvas/test/webgl/conformance/misc/instanceof-test.html
+++ b/content/canvas/test/webgl/conformance/misc/instanceof-test.html
@@ -1,91 +1,92 @@
-<!--
-Copyright (c) 2011 The Chromium Authors. All rights reserved.
-Use of this source code is governed by a BSD-style license that can be
-found in the LICENSE file.
- -->
-<!DOCTYPE html>
-<html>
-<head>
-<meta charset="utf-8">
-<title>WebGL instanceof test.</title>
-<link rel="stylesheet" href="../../resources/js-test-style.css"/>
-<script src="../../resources/js-test-pre.js"></script>
-<script src="../resources/webgl-test.js"> </script>
-<script src="../resources/webgl-test-utils.js"> </script>
-</head>
-<body>
-<canvas id="canvas" width="2" height="2" style="width: 40px; height: 40px;"></canvas>
-<div id="description"></div>
-<div id="console"></div>
-<script id="vshader" type="x-shader/x-vertex">
-attribute vec4 vPosition;
-varying vec2 texCoord;
-void main()
-{
-    gl_Position = vPosition;
-}
-</script>
-
-<script id="fshader" type="x-shader/x-fragment">
-precision mediump float;
-uniform vec4 color;
-void main()
-{
-    gl_FragColor = color;
-}
-</script>
-<script>
-var wtu = WebGLTestUtils;
-debug("Tests that instanceof works on WebGL objects.");
-debug("");
-var gl = create3DContext(document.getElementById("canvas"));
-shouldBeTrue('gl instanceof WebGLRenderingContext');
-shouldBeTrue('gl.createBuffer() instanceof WebGLBuffer');
-shouldBeTrue('gl.createFramebuffer() instanceof WebGLFramebuffer');
-shouldBeTrue('gl.createProgram() instanceof WebGLProgram');
-shouldBeTrue('gl.createRenderbuffer() instanceof WebGLRenderbuffer');
-shouldBeTrue('gl.createShader(gl.VERTEX_SHADER) instanceof WebGLShader');
-shouldBeTrue('gl.createTexture() instanceof WebGLTexture');
-
-var program = wtu.setupProgram(
-    gl,
-    [wtu.loadShaderFromScript(gl, 'vshader', gl.VERTEX_SHADER),
-     wtu.loadShaderFromScript(gl, 'fshader', gl.FRAGMENT_SHADER)],
-    ['vPosition'], [0]);
-
-shouldBeTrue('gl.getUniformLocation(program, "color") instanceof WebGLUniformLocation');
-shouldBeTrue('gl.getActiveAttrib(program, 0) instanceof WebGLActiveInfo');
-shouldBeTrue('gl.getActiveUniform(program, 0) instanceof WebGLActiveInfo');
-
-debug("");
-debug("Tests that those WebGL objects can not be constructed through new operator");
-debug("");
-
-function shouldThrowWithNew(objectType, objectName)
-{
-    try {
-        new objectType;
-        testFailed('new ' + objectName + ' did not throw');
-    } catch (e) {
-        testPassed('new ' + objectName + ' threw an error');
-    }
-}
-
-shouldThrowWithNew(WebGLRenderingContext, 'WebGLRenderingContext');
-shouldThrowWithNew(WebGLActiveInfo, 'WebGLActiveInfo');
-shouldThrowWithNew(WebGLBuffer, 'WebGLBuffer');
-shouldThrowWithNew(WebGLFramebuffer, 'WebGLFramebuffer');
-shouldThrowWithNew(WebGLProgram, 'WebGLProgram');
-shouldThrowWithNew(WebGLRenderbuffer, 'WebGLRenderbuffer');
-shouldThrowWithNew(WebGLShader, 'WebGLShader');
-shouldThrowWithNew(WebGLTexture, 'WebGLTexture');
-shouldThrowWithNew(WebGLUniformLocation, 'WebGLUniformLocation');
-
-successfullyParsed = true;
-</script>
-<script src="../../resources/js-test-post.js"></script>
-
-</body>
-</html>
-
-
+<!--
+Copyright (c) 2011 The Chromium Authors. All rights reserved.
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+ -->
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>WebGL instanceof test.</title>
+<link rel="stylesheet" href="../../resources/js-test-style.css"/>
+<script src="../../resources/js-test-pre.js"></script>
+<script src="../resources/webgl-test.js"> </script>
+<script src="../resources/webgl-test-utils.js"> </script>
+</head>
+<body>
+<canvas id="canvas" width="2" height="2" style="width: 40px; height: 40px;"></canvas>
+<div id="description"></div>
+<div id="console"></div>
+<script id="vshader" type="x-shader/x-vertex">
+attribute vec4 vPosition;
+varying vec2 texCoord;
+void main()
+{
+    gl_Position = vPosition;
+}
+</script>
+
+<script id="fshader" type="x-shader/x-fragment">
+precision mediump float;
+uniform vec4 color;
+void main()
+{
+    gl_FragColor = color;
+}
+</script>
+<script>
+var wtu = WebGLTestUtils;
+description(document.title);
+debug("Tests that instanceof works on WebGL objects.");
+debug("");
+var gl = create3DContext(document.getElementById("canvas"));
+shouldBeTrue('gl instanceof WebGLRenderingContext');
+shouldBeTrue('gl.createBuffer() instanceof WebGLBuffer');
+shouldBeTrue('gl.createFramebuffer() instanceof WebGLFramebuffer');
+shouldBeTrue('gl.createProgram() instanceof WebGLProgram');
+shouldBeTrue('gl.createRenderbuffer() instanceof WebGLRenderbuffer');
+shouldBeTrue('gl.createShader(gl.VERTEX_SHADER) instanceof WebGLShader');
+shouldBeTrue('gl.createTexture() instanceof WebGLTexture');
+
+var program = wtu.setupProgram(
+    gl,
+    [wtu.loadShaderFromScript(gl, 'vshader', gl.VERTEX_SHADER),
+     wtu.loadShaderFromScript(gl, 'fshader', gl.FRAGMENT_SHADER)],
+    ['vPosition'], [0]);
+
+shouldBeTrue('gl.getUniformLocation(program, "color") instanceof WebGLUniformLocation');
+shouldBeTrue('gl.getActiveAttrib(program, 0) instanceof WebGLActiveInfo');
+shouldBeTrue('gl.getActiveUniform(program, 0) instanceof WebGLActiveInfo');
+
+debug("");
+debug("Tests that those WebGL objects can not be constructed through new operator");
+debug("");
+
+function shouldThrowWithNew(objectType, objectName)
+{
+    try {
+        new objectType;
+        testFailed('new ' + objectName + ' did not throw');
+    } catch (e) {
+        testPassed('new ' + objectName + ' threw an error');
+    }
+}
+
+shouldThrowWithNew(WebGLRenderingContext, 'WebGLRenderingContext');
+shouldThrowWithNew(WebGLActiveInfo, 'WebGLActiveInfo');
+shouldThrowWithNew(WebGLBuffer, 'WebGLBuffer');
+shouldThrowWithNew(WebGLFramebuffer, 'WebGLFramebuffer');
+shouldThrowWithNew(WebGLProgram, 'WebGLProgram');
+shouldThrowWithNew(WebGLRenderbuffer, 'WebGLRenderbuffer');
+shouldThrowWithNew(WebGLShader, 'WebGLShader');
+shouldThrowWithNew(WebGLTexture, 'WebGLTexture');
+shouldThrowWithNew(WebGLUniformLocation, 'WebGLUniformLocation');
+
+successfullyParsed = true;
+</script>
+<script src="../../resources/js-test-post.js"></script>
+
+</body>
+</html>
+
+
--- a/content/canvas/test/webgl/conformance/misc/uninitialized-test.html
+++ b/content/canvas/test/webgl/conformance/misc/uninitialized-test.html
@@ -56,17 +56,19 @@ function checkNonZeroPixels(texture, tex
     gl.readPixels(0, 0, texWidth, texHeight, gl.RGBA, gl.UNSIGNED_BYTE, data);
 
     var k = 0;
     for (var y = 0; y < texHeight; ++y) {
         for (var x = 0; x < texWidth; ++x) {
             var index = (y * texWidth + x) * 4;
             if (x >= skipX && x < skipX + skipWidth && y >= skipY && y < skipY + skipHeight) {
                 if (data[index] != skipR || data[index + 1] != skipG || data[index + 2] != skipB || data[index + 3] != skipA) {
-                    testFailed("non-zero pixel values are wrong");
+                    testFailed("non-zero pixel values are wrong at (" + x + ", " + y + "), data was (" +
+                               data[index] + "," + data[index + 1] + "," + data[index + 2] + "," + data[index + 3] +
+                               ") should have been (" + skipR + "," + skipG + "," + skipB + "," + skipA + ")");
                     return;
                 }
             } else {
                 for (var i = 0; i < 4; ++i) {
                     if (data[index + i] != 0)
                         k++;
                 }
             }
--- a/content/canvas/test/webgl/conformance/renderbuffers/framebuffer-object-attachment.html
+++ b/content/canvas/test/webgl/conformance/renderbuffers/framebuffer-object-attachment.html
@@ -5,23 +5,24 @@ found in the LICENSE file.
  -->
 <!DOCTYPE html>
 <html>
 <head>
 <meta charset="utf-8">
 <link rel="stylesheet" href="../../resources/js-test-style.css"/>
 <script src="../../resources/js-test-pre.js"></script>
 <script src="../resources/webgl-test.js"></script>
+<script src="../resources/webgl-test-utils.js"></script>
 </head>
 <body>
 <div id="description"></div>
 <div id="console"></div>
 
 <script>
-
+var wtu = WebGLTestUtils;
 var gl;
 var fbo;
 var depthBuffer;
 var stencilBuffer;
 var depthStencilBuffer;
 var colorBuffer;
 var width;
 var height;
@@ -106,16 +107,17 @@ function testDepthStencilRenderbuffer()
 }
 
 description("Test framebuffer object attachment behaviors");
 
 for (width = 0; width <= 2; width += 2)
 {
     for (height = 0; height <= 2; height += 2)
     {
+        debug("");
         debug("Dimensions " + width + " x " + height);
 
         debug("Create renderbuffers");
         shouldBeNonNull("gl = create3DContext()");
         shouldBeNonNull("colorBuffer = gl.createRenderbuffer()");
         gl.bindRenderbuffer(gl.RENDERBUFFER, colorBuffer);
         gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA4, width, height);
         glErrorShouldBe(gl, gl.NO_ERROR);
@@ -173,14 +175,217 @@ for (width = 0; width <= 2; width += 2)
         debug("Attach color renderbuffer with internalformat == RGB565");
         testColorRenderbuffer(gl.RGB565);
 
         debug("Create and attach depthStencil renderbuffer");
         testDepthStencilRenderbuffer();
     }
 }
 
+// Determine if we can attach both color and depth or color and depth_stencil
+var depthFormat;
+var depthAttachment;
+
+function checkValidColorDepthCombination() {
+    shouldBeNonNull("fbo = gl.createFramebuffer()");
+    gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
+    shouldBeNonNull("colorBuffer = gl.createRenderbuffer()");
+    gl.bindRenderbuffer(gl.RENDERBUFFER, colorBuffer);
+    gl.framebufferRenderbuffer(
+        gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, colorBuffer);
+    gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA4, 16, 16);
+
+    shouldBeNonNull("depthBuffer = gl.createRenderbuffer()");
+    gl.bindRenderbuffer(gl.RENDERBUFFER, depthBuffer);
+
+    return tryDepth(gl.DEPTH_COMPONENT16, gl.DEPTH_ATTACHMENT) || tryDepth(gl.DEPTH_STENCIL, gl.DEPTH_STENCIL_ATTACHMENT);
+
+    function tryDepth(try_format, try_attachment) {
+        if (depthAttachment) {
+            // If we've tried once unattach the old one.
+            gl.framebufferRenderbuffer(
+                gl.FRAMEBUFFER, depthAttachment, gl.RENDERBUFFER, null);
+        }
+        depthFormat = try_format;
+        depthAttachment = try_attachment;
+        gl.framebufferRenderbuffer(
+            gl.FRAMEBUFFER, depthAttachment, gl.RENDERBUFFER, depthBuffer);
+        gl.renderbufferStorage(gl.RENDERBUFFER, depthFormat, 16, 16);
+        glErrorShouldBe(gl, gl.NO_ERROR);
+        return gl.checkFramebufferStatus(gl.FRAMEBUFFER) == gl.FRAMEBUFFER_COMPLETE;
+    }
+}
+
+if (checkValidColorDepthCombination()) {
+    testFramebufferIncompleteDimensions();
+    testFramebufferIncompleteAttachment();
+    testFramebufferIncompleteMissingAttachment();
+    testUsingIncompleteFramebuffer();
+}
+
+function CheckFramebuffer(expected) {
+    var actual = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
+    var msg = "gl.checkFramebufferStatus(gl.FRAMEBUFFER) should be " + wtu.glEnumToString(gl, expected) + " was " + wtu.glEnumToString(gl, actual);
+    if (expected != gl.FRAMEBUFFER_COMPLETE) {
+        msg += " or FRAMEBUFFER_UNSUPPORTED";
+    }
+    if (actual == expected ||
+        (expected != gl.FRAMEBUFFER_COMPLETE &&
+         actual == gl.FRAMBUFFER_UNSUPPORTED)) {
+        testPassed(msg);
+    } else {
+        testFailed(msg);
+    }
+}
+
+function testUsingIncompleteFramebuffer() {
+    debug("");
+    debug("Test drawing or reading from an incomplete framebuffer");
+    var program = wtu.setupTexturedQuad(gl);
+    var tex = gl.createTexture();
+    wtu.fillTexture(gl, tex, 1, 1, [0,255,0,255]);
+
+    shouldBeNonNull("fbo = gl.createFramebuffer()");
+    gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
+    shouldBeNonNull("colorBuffer = gl.createRenderbuffer()");
+    gl.bindRenderbuffer(gl.RENDERBUFFER, colorBuffer);
+    gl.framebufferRenderbuffer(
+        gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, colorBuffer);
+    gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA4, 16, 16);
+
+    shouldBeNonNull("depthBuffer = gl.createRenderbuffer()");
+    gl.bindRenderbuffer(gl.RENDERBUFFER, depthBuffer);
+    gl.framebufferRenderbuffer(
+        gl.FRAMEBUFFER, depthAttachment, gl.RENDERBUFFER, depthBuffer);
+    gl.renderbufferStorage(gl.RENDERBUFFER, depthFormat, 16, 16);
+    glErrorShouldBe(gl, gl.NO_ERROR);
+    CheckFramebuffer(gl.FRAMEBUFFER_COMPLETE);
+
+    // We pick this combination because it works on desktop OpenGL but should not work on OpenGL ES 2.0
+    gl.renderbufferStorage(gl.RENDERBUFFER, depthFormat, 32, 16);
+    CheckFramebuffer(gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS);
+    debug("");
+    debug("Drawing or reading from an incomplete framebuffer should generate INVALID_FRAMEBUFFER_OPERATION");
+    testRenderingAndReading();
+
+    shouldBeNonNull("fbo = gl.createFramebuffer()");
+    gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
+    CheckFramebuffer(gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT);
+    debug("");
+    debug("Drawing or reading from an incomplete framebuffer should generate INVALID_FRAMEBUFFER_OPERATION");
+    testRenderingAndReading();
+
+    function testRenderingAndReading() {
+        glErrorShouldBe(gl, gl.NO_ERROR);
+        wtu.drawQuad(gl);
+        glErrorShouldBe(gl, gl.INVALID_FRAMEBUFFER_OPERATION, "drawArrays with incomplete framebuffer");
+        gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(4));
+        glErrorShouldBe(gl, gl.INVALID_FRAMEBUFFER_OPERATION, "readPixels from incomplete framebuffer");
+        gl.copyTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, 0, 0, 1, 1);
+        glErrorShouldBe(gl, gl.INVALID_FRAMEBUFFER_OPERATION, "copyTexImage2D from incomplete framebuffer");
+        gl.copyTexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 0, 0, 1, 1, 0);
+        glErrorShouldBe(gl, gl.INVALID_FRAMEBUFFER_OPERATION, "copyTexSubImage2D from incomplete framebuffer");
+        gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
+        glErrorShouldBe(gl, gl.INVALID_FRAMEBUFFER_OPERATION, "clear with incomplete framebuffer");
+    }
+}
+
+function testFramebufferIncompleteAttachment() {
+    shouldBeNonNull("fbo = gl.createFramebuffer()");
+    gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
+    shouldBeNonNull("colorBuffer = gl.createRenderbuffer()");
+    gl.bindRenderbuffer(gl.RENDERBUFFER, colorBuffer);
+    gl.framebufferRenderbuffer(
+        gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, colorBuffer);
+    gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA4, 16, 16);
+    CheckFramebuffer(gl.FRAMEBUFFER_COMPLETE);
+
+    debug("");
+    debug("Wrong storage type for type of attachment be FRAMEBUFFER_INCOMPLETE_ATTACHMENT (OpenGL ES 2.0 4.4.5)");
+    gl.renderbufferStorage(gl.RENDERBUFFER, depthFormat, 16, 16);
+    CheckFramebuffer(gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT);
+
+    gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA4, 16, 16);
+    CheckFramebuffer(gl.FRAMEBUFFER_COMPLETE);
+
+    debug("");
+    debug("0 size attachment should be FRAMEBUFFER_INCOMPLETE_ATTACHMENT (OpenGL ES 2.0 4.4.5)");
+    gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA4, 0, 0);
+    CheckFramebuffer(gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT);
+
+    glErrorShouldBe(gl, gl.NO_ERROR);
+}
+
+function testFramebufferIncompleteMissingAttachment() {
+    debug("");
+    debug("No attachments should be INCOMPLETE_FRAMEBUFFER_MISSING_ATTACHMENT (OpenGL ES 2.0 4.4.5)");
+    shouldBeNonNull("fbo = gl.createFramebuffer()");
+    gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
+    CheckFramebuffer(gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT);
+
+    shouldBeNonNull("colorBuffer = gl.createRenderbuffer()");
+    gl.bindRenderbuffer(gl.RENDERBUFFER, colorBuffer);
+    gl.framebufferRenderbuffer(
+        gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, colorBuffer);
+    gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA4, 16, 16);
+    CheckFramebuffer(gl.FRAMEBUFFER_COMPLETE);
+
+    gl.framebufferRenderbuffer(
+        gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, null);
+    CheckFramebuffer(gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT);
+
+    glErrorShouldBe(gl, gl.NO_ERROR);
+}
+
+function testFramebufferIncompleteDimensions() {
+    debug("");
+    debug("Attachments of different sizes should be FRAMEBUFFER_INCOMPLETE_DIMENSIONS (OpenGL ES 2.0 4.4.5)");
+
+    shouldBeNonNull("fbo = gl.createFramebuffer()");
+    gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
+    shouldBeNonNull("colorBuffer = gl.createRenderbuffer()");
+    gl.bindRenderbuffer(gl.RENDERBUFFER, colorBuffer);
+    gl.framebufferRenderbuffer(
+        gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, colorBuffer);
+    gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA4, 16, 16);
+
+    shouldBeNonNull("depthBuffer = gl.createRenderbuffer()");
+    gl.bindRenderbuffer(gl.RENDERBUFFER, depthBuffer);
+    gl.framebufferRenderbuffer(
+        gl.FRAMEBUFFER, depthAttachment, gl.RENDERBUFFER, depthBuffer);
+    gl.renderbufferStorage(gl.RENDERBUFFER, depthFormat, 16, 16);
+    glErrorShouldBe(gl, gl.NO_ERROR);
+    CheckFramebuffer(gl.FRAMEBUFFER_COMPLETE);
+
+    gl.renderbufferStorage(gl.RENDERBUFFER, depthFormat, 32, 16);
+    CheckFramebuffer(gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS);
+    gl.renderbufferStorage(gl.RENDERBUFFER, depthFormat, 16, 16);
+    CheckFramebuffer(gl.FRAMEBUFFER_COMPLETE);
+    gl.bindRenderbuffer(gl.RENDERBUFFER, colorBuffer);
+    gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA4, 16, 32);
+    CheckFramebuffer(gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS);
+    gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA4, 16, 16);
+    CheckFramebuffer(gl.FRAMEBUFFER_COMPLETE);
+    glErrorShouldBe(gl, gl.NO_ERROR);
+
+    var tex = gl.createTexture();
+    gl.bindTexture(gl.TEXTURE_2D, tex);
+    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 16, 16, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
+    gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0);
+    glErrorShouldBe(gl, gl.NO_ERROR);
+    if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) {
+        return;
+    }
+
+    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 32, 16, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
+    CheckFramebuffer(gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS);
+    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 16, 16, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
+    CheckFramebuffer(gl.FRAMEBUFFER_COMPLETE);
+
+    glErrorShouldBe(gl, gl.NO_ERROR);
+}
+
 successfullyParsed = true;
 </script>
 
 <script src="../../resources/js-test-post.js"></script>
 </body>
 </html>
--- a/content/canvas/test/webgl/conformance/renderbuffers/framebuffer-test.html
+++ b/content/canvas/test/webgl/conformance/renderbuffers/framebuffer-test.html
@@ -6,16 +6,17 @@ found in the LICENSE file.
 <!DOCTYPE html>
 <html>
 <head>
 <meta charset="utf-8">
 <title>WebGL Framebuffer Test</title>
 <link rel="stylesheet" href="../../resources/js-test-style.css"/>
 <script src="../../resources/js-test-pre.js"></script>
 <script src="../resources/webgl-test.js"></script>
+<script src="../../resources/desktop-gl-constants.js"></script>
 </head>
 <body>
 <div id="description"></div>
 <div id="console"></div>
 <canvas id="canvas" width="2" height="2"> </canvas>
 <script>
 description("This tests framebuffer/renderbuffer-related functions");
 
@@ -92,17 +93,17 @@ if (!gl) {
   gl.bindTexture(gl.TEXTURE_2D, fbtex);
   gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, canvas.width, canvas.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
   var fb = gl.createFramebuffer();
 
   gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
   glErrorShouldBe(gl, gl.NO_ERROR,
             "binding a newly created framebuffer should succeed.");
 
-  var target = 0x8CA8; // GL_READ_FRAMEBUFFER
+  var target = desktopGL.READ_FRAMEBUFFER
   gl.getFramebufferAttachmentParameter(
      target,
      gl.COLOR_ATTACHMENT0,
      gl.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE);
   glErrorShouldBe(gl, gl.INVALID_ENUM,
             "calling getFramebufferAttachmentParameter with target = READ_FRAMEBUFFER should generate INVALID_ENUM.");
   assertMsg(gl.checkFramebufferStatus(target) == 0,
             "calling checkFramebufferStatus with target = READ_FRAMEBUFFER should return 0.");
@@ -118,27 +119,27 @@ if (!gl) {
             "calling getFramebufferAttachmentParameter with target = READ_FRAMEBUFFER should generate INVALID_ENUM.");
   gl.framebufferTexture2D(target, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, fbtex, 0);
   glErrorShouldBe(gl, gl.INVALID_ENUM,
             "calling framebufferTexImage2D with target = READ_FRAMEBUFFER should generate INVALID_ENUM.");
   gl.framebufferRenderbuffer(target, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, rb);
   glErrorShouldBe(gl, gl.INVALID_ENUM,
             "calling framebufferRenderbuffer with target = READ_FRAMEBUFFER should generate INVALID_ENUM.");
 
-  var attachment = 0x8CE1; // GL_COLOR_ATTACHMENT1
+  var attachment = desktopGL.COLOR_ATTACHMENT1
   gl.framebufferTexture2D(gl.FRAMEBUFFER, attachment, gl.TEXTURE_2D, fbtex, 0);
   glErrorShouldBe(gl, gl.INVALID_ENUM,
             "calling framebufferTexImage2D with attachment = COLOR_ATTACHMENT1 should generate INVALID_ENUM.");
   gl.framebufferRenderbuffer(gl.FRAMEBUFFER, attachment, gl.RENDERBUFFER, rb);
   glErrorShouldBe(gl, gl.INVALID_ENUM,
             "calling framebufferRenderbuffer with attachment = COLOR_ATTACHMENT1 should generate INVALID_ENUM.");
 
   gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER,
                                        gl.COLOR_ATTACHMENT0,
-                                       0x8210); // GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING
+                                       desktopGL.FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING);
   glErrorShouldBe(gl, gl.INVALID_ENUM,
             "calling getFramebufferAttachmentParameter with pname = GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING should generate INVALID_ENUM.");
 
   gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, fbtex, 0);
   glErrorShouldBe(gl, gl.NO_ERROR,
             "attaching a texture to a framebuffer should succeed.");
 
   gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, null, 0);
--- a/content/canvas/test/webgl/conformance/rendering/gl-drawelements.html
+++ b/content/canvas/test/webgl/conformance/rendering/gl-drawelements.html
@@ -34,16 +34,18 @@ found in the LICENSE file.
 
     <script>
         function init()
         {
             if (window.initNonKhronosFramework) {
                 window.initNonKhronosFramework(false);
             }
 
+            description(document.title);
+
             function checkDrawElements(mode, count, type, expect, msg) {
               gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
               gl.drawElements(mode, count, type, 0);
               glErrorShouldBe(gl, expect, msg);
             }
 
             gl = initWebGL("example", "vshader", "fshader", [ "vPosition"], [ 0, 0, 0, 1 ], 1);
 
--- a/content/canvas/test/webgl/conformance/rendering/triangle.html
+++ b/content/canvas/test/webgl/conformance/rendering/triangle.html
@@ -67,16 +67,18 @@ There is supposed to be an example drawi
         }
 
         function init()
         {
             if (window.initNonKhronosFramework) {
                 window.initNonKhronosFramework(false);
             }
 
+            description(document.title);
+
             gl = initWebGL("example", "vshader", "fshader", [ "vPosition"], [ 0, 0, 0, 1 ], 1);
 
             var vertexObject = gl.createBuffer();
             gl.bindBuffer(gl.ARRAY_BUFFER, vertexObject);
             gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ 0,0.5,0, -0.5,-0.5,0, 0.5,-0.5,0 ]), gl.STATIC_DRAW);
             gl.enableVertexAttribArray(0);
             gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0);
 
--- a/content/canvas/test/webgl/conformance/resources/webgl-test-utils.js
+++ b/content/canvas/test/webgl/conformance/resources/webgl-test-utils.js
@@ -389,21 +389,29 @@ var drawQuad = function(gl, opt_color) {
 var checkCanvasRect = function(gl, x, y, width, height, color, msg, errorRange) {
   errorRange = errorRange || 0;
   var buf = new Uint8Array(width * height * 4);
   gl.readPixels(x, y, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buf);
   for (var i = 0; i < width * height; ++i) {
     var offset = i * 4;
     for (var j = 0; j < color.length; ++j) {
       if (Math.abs(buf[offset + j] - color[j]) > errorRange) {
-        testFailed(msg);
         var was = buf[offset + 0].toString();
         for (j = 1; j < color.length; ++j) {
           was += "," + buf[offset + j];
         }
+
+        var cv = document.createElement('canvas');
+        cv.height = height;
+        cv.width = width;
+        var ctx = cv.getContext('2d');
+        ctx.fillStyle="rgba(" + color[0] + ", " + color[1] + ", " + color[2] + ", 255)";
+        ctx.fillRect(0, 0, width, height);
+        testFailedRender(msg, ctx, buf, width, height);
+
         debug('at (' + (i % width) + ', ' + Math.floor(i / width) +
               ') expected: ' + color + ' was ' + was);
         return;
       }
     }
   }
   testPassed(msg);
 };
@@ -586,18 +594,16 @@ var linkProgram = function(gl, program, 
   var linked = gl.getProgramParameter(program, gl.LINK_STATUS);
   if (!linked) {
     // something went wrong with the link
     var error = gl.getProgramInfoLog (program);
 
     testFailed("Error in program linking:" + error);
 
     gl.deleteProgram(program);
-    //gl.deleteProgram(fragmentShader);
-    //gl.deleteProgram(vertexShader);
   }
 };
 
 /**
  * Sets up WebGL with shaders.
  * @param {string} canvasName The id of the canvas.
  * @param {string} vshader The id of the script tag that contains the vertex
  *     shader source.
--- a/content/canvas/test/webgl/conformance/textures/gl-pixelstorei.html
+++ b/content/canvas/test/webgl/conformance/textures/gl-pixelstorei.html
@@ -41,22 +41,21 @@ function fail(x,y, name, buf, shouldBe) 
   testFailed(reason);
 }
 
 function pass(name) {
   testPassed("drawing is correct in " + name);
 }
 
 function init() {
+  description("This test checks that drawImage and readPixels are not effected by gl.Pixelstorei(gl.PACK_ALIGNMENT) and visa versa");
+
   debug("There should be 5 red triangles on 5 black squares above");
   debug("");
 
-  debug("This test checks that drawImage and readPixels are not effected by gl.Pixelstorei(gl.PACK_ALIGNMENT) and visa versa");
-  debug("");
-
   var canvas3d = document.getElementById("example");
   gl = initWebGL("example", "vshader", "fshader", [ "vPosition"], [ 0, 0, 0, 1 ], 1);
 
   var vertexObject = gl.createBuffer();
   gl.bindBuffer(gl.ARRAY_BUFFER, vertexObject);
   gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ 0,0.5,0, -0.5,-0.5,0, 0.5,-0.5,0 ]), gl.STATIC_DRAW);
   gl.enableVertexAttribArray(0);
   gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0);
--- a/content/canvas/test/webgl/conformance/textures/texparameter-test.html
+++ b/content/canvas/test/webgl/conformance/textures/texparameter-test.html
@@ -42,17 +42,17 @@ void main()
 
 <script>
 function init()
 {
   if (window.initNonKhronosFramework) {
     window.initNonKhronosFramework(false);
   }
 
-  debug("Tests TexParameter works as expected");
+  description("Tests TexParameter works as expected");
   debug("");
 
   var canvas2d = document.getElementById("canvas2d");
   var ctx2d = canvas2d.getContext("2d");
 
   gl = initWebGL("example", "vshader", "fshader", [ "vPosition", "texCoord0"],
                  [ 0, 0, 0, 1 ], 1);
 
--- a/content/canvas/test/webgl/conformance/textures/texture-active-bind-2.html
+++ b/content/canvas/test/webgl/conformance/textures/texture-active-bind-2.html
@@ -50,20 +50,20 @@ void main()
 
 <script>
 function init()
 {
   if (window.initNonKhronosFramework) {
     window.initNonKhronosFramework(false);
   }
 
-  debug("Tests that binding both TEXTURE_2D and TEXTURE_CUBE_MAP to the same");
-  debug("active texture unit works as long as they are not used");
-  debug("simultaneously in the same shader program.");
-  debug("");
+  description(
+      "Tests that binding both TEXTURE_2D and TEXTURE_CUBE_MAP to the same" +
+      "active texture unit works as long as they are not used" +
+      "simultaneously in the same shader program.");
 
   var canvas2d = document.getElementById("canvas2d");
   var ctx2d = canvas2d.getContext("2d");
   ctx2d.globalCompositeOperation = "copy";
 
   gl = initWebGL("example", "vshader", "fshader2d", ["vPosition", "texCoord0"],
                  [ 0, 0, 0, 1 ], 1);
 
--- a/content/canvas/test/webgl/conformance/textures/texture-active-bind.html
+++ b/content/canvas/test/webgl/conformance/textures/texture-active-bind.html
@@ -33,19 +33,19 @@ void main()
 var gl;
 
 function init()
 {
   if (window.initNonKhronosFramework) {
     window.initNonKhronosFramework(false);
   }
 
-  debug("Tests that glActiveTexture and glBindTexture work as expected");
-  debug("Specifically texture targets are per active texture unit.");
-  debug("");
+  description(
+      "Tests that glActiveTexture and glBindTexture work as expected" +
+      "Specifically texture targets are per active texture unit.");
 
   var canvas2d = document.getElementById("canvas2d");
   var ctx2d = canvas2d.getContext("2d");
 
   var wtu = WebGLTestUtils;
   var canvas = document.getElementById("example");
   gl = wtu.create3DContext(canvas);
   var program = wtu.setupProgram(
--- a/content/canvas/test/webgl/conformance/textures/texture-complete.html
+++ b/content/canvas/test/webgl/conformance/textures/texture-complete.html
@@ -20,19 +20,19 @@ found in the LICENSE file.
 <div id="console"></div>
 <script>
 function init()
 {
   if (window.initNonKhronosFramework) {
     window.initNonKhronosFramework(false);
   }
 
-  debug("Checks that a texture that is not -texture-complete- does not draw if"+
-        " filtering needs mips");
-  debug("");
+  description(
+      "Checks that a texture that is not -texture-complete- does not draw if"+
+      " filtering needs mips");
 
   var canvas2d = document.getElementById("canvas2d");
   var ctx2d = canvas2d.getContext("2d");
   ctx2d.fillStyle = "rgba(0,192,128,1)";
   ctx2d.fillRect(0, 0, 16, 16);
 
   var wtu = WebGLTestUtils;
   var canvas = document.getElementById("example");
--- a/content/canvas/test/webgl/conformance/textures/texture-mips.html
+++ b/content/canvas/test/webgl/conformance/textures/texture-mips.html
@@ -42,18 +42,17 @@ void main()
 var canvas;
 var wtu = WebGLTestUtils;
 function init()
 {
   if (window.initNonKhronosFramework) {
     window.initNonKhronosFramework(false);
   }
 
-  debug("Checks mip issues");
-  debug("");
+  description("Checks mip issues");
 
   canvas = document.getElementById("example");
   shouldBe("canvas.width", "2");
   shouldBe("canvas.height", "2");
 
   gl = wtu.create3DContext(canvas);
   wtu.setupUnitQuad(gl, 0, 1);
   var program = wtu.setupProgram(
--- a/content/canvas/test/webgl/conformance/textures/texture-npot.html
+++ b/content/canvas/test/webgl/conformance/textures/texture-npot.html
@@ -33,16 +33,17 @@ precision mediump float;
 uniform samplerCube tex;
 varying vec2 texCoord;
 void main()
 {
     gl_FragColor = textureCube(tex, normalize(vec3(texCoord, 1)));
 }
 </script>
 <script>
+description(document.title);
 var wtu = WebGLTestUtils;
 var canvas = document.getElementById("example");
 var gl = wtu.create3DContext(canvas);
 var program = wtu.setupTexturedQuad(gl);
 
 glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup.");
 
 var tex = gl.createTexture();
--- a/content/canvas/test/webgl/conformance/uniforms/uniform-samplers-test.html
+++ b/content/canvas/test/webgl/conformance/uniforms/uniform-samplers-test.html
@@ -20,19 +20,19 @@ found in the LICENSE file.
 
   <script>
 function init()
 {
   if (window.initNonKhronosFramework) {
     window.initNonKhronosFramework(false);
   }
 
-  debug("Tests that only Uniform1i and Uniform1iv can be used to set");
-  debug("sampler uniforms.");
-  debug("");
+  description(
+      "Tests that only Uniform1i and Uniform1iv can be used to set" +
+      "sampler uniforms.");
 
   var canvas2d = document.getElementById("canvas2d");
 
   var wtu = WebGLTestUtils;
   var canvas = document.getElementById("example");
   gl = wtu.create3DContext(canvas);
   var program = wtu.setupTexturedQuad(gl);
 
--- a/content/canvas/test/webgl/failing_tests_linux.txt
+++ b/content/canvas/test/webgl/failing_tests_linux.txt
@@ -3,8 +3,11 @@ conformance/glsl/misc/glsl-long-variable
 conformance/glsl/misc/shader-with-256-character-identifier.frag.html
 conformance/glsl/misc/shader-with-long-line.html
 conformance/misc/uninitialized-test.html
 conformance/programs/gl-get-active-attribute.html
 conformance/textures/texture-mips.html
 conformance/uniforms/gl-uniform-bool.html
 conformance/more/conformance/quickCheckAPI-S_V.html
 conformance/more/functions/uniformfArrayLen1.html
+conformance/glsl/misc/attrib-location-length-limits.html
+conformance/glsl/misc/uniform-location-length-limits.html
+conformance/renderbuffers/framebuffer-object-attachment.html
--- a/content/canvas/test/webgl/failing_tests_mac.txt
+++ b/content/canvas/test/webgl/failing_tests_mac.txt
@@ -1,12 +1,13 @@
 conformance/context/premultiplyalpha-test.html
 conformance/glsl/misc/glsl-function-nodes.html
 conformance/glsl/misc/glsl-long-variable-names.html
 conformance/glsl/misc/shader-with-256-character-identifier.frag.html
 conformance/glsl/misc/shader-with-long-line.html
-conformance/programs/program-test.html
-conformance/state/gl-object-get-calls.html
 conformance/textures/texture-mips.html
 conformance/textures/texture-npot.html
 conformance/more/conformance/quickCheckAPI-S_V.html
 conformance/more/functions/uniformfBadArgs.html
 conformance/more/functions/uniformiBadArgs.html
+conformance/glsl/misc/attrib-location-length-limits.html
+conformance/glsl/misc/uniform-location-length-limits.html
+conformance/renderbuffers/framebuffer-object-attachment.html
--- a/content/canvas/test/webgl/failing_tests_windows.txt
+++ b/content/canvas/test/webgl/failing_tests_windows.txt
@@ -2,8 +2,12 @@ conformance/context/premultiplyalpha-tes
 conformance/glsl/functions/glsl-function-atan.html
 conformance/glsl/functions/glsl-function-atan-xy.html
 conformance/glsl/functions/glsl-function-mod-gentype.html
 conformance/glsl/misc/glsl-long-variable-names.html
 conformance/glsl/misc/shader-with-256-character-identifier.frag.html
 conformance/glsl/misc/shader-with-long-line.html
 conformance/more/conformance/quickCheckAPI-S_V.html
 conformance/more/functions/uniformfArrayLen1.html
+conformance/glsl/misc/attrib-location-length-limits.html
+conformance/glsl/misc/struct-nesting-under-maximum.html
+conformance/glsl/misc/uniform-location-length-limits.html
+conformance/renderbuffers/framebuffer-object-attachment.html
new file mode 100644
--- /dev/null
+++ b/content/canvas/test/webgl/log-more-info-about-test-failures.patch
@@ -0,0 +1,181 @@
+# HG changeset patch
+# Parent 2dbd71999fe8a8da476ab6cc97716f7b7c294fb8
+# User Doug Sherk <dsherk@mozilla.com>
+Bug 693703: added additional logging information for mochitests, incl. image reference differences r=bjacob
+
+Added some code to print to dump output of WebGL mochitest failures. Also added
+special code to handle incorrect reference images. It will now provide the user
+with a way to compare the reference and actual drawings.
+
+diff --git a/content/canvas/test/webgl/conformance/glsl/misc/glsl-function-nodes.html b/content/canvas/test/webgl/conformance/glsl/misc/glsl-function-nodes.html
+--- a/content/canvas/test/webgl/conformance/glsl/misc/glsl-function-nodes.html
++++ b/content/canvas/test/webgl/conformance/glsl/misc/glsl-function-nodes.html
+@@ -115,17 +115,18 @@ function init()
+     var bufFunction = new Uint8Array(width * height * 4);
+     var bufMacro = new Uint8Array(width * height * 4);
+ 
+     if (drawAndRead("canvasFunction", "vshaderFunction", bufFunction) == false ||
+         drawAndRead("canvasMacro", "vshaderMacro", bufMacro) == false) {
+         testFailed("Setup failed");
+     } else {
+         if (compareRendering(bufFunction, bufMacro, 4) == false)
+-            testFailed("Rendering results are different");
++            testFailedRender("Rendering results are different", bufMacro,
++                             bufFunction, width, height);
+         else
+             testPassed("Rendering results are the same");
+     }
+ }
+ 
+ init();
+ successfullyParsed = true;
+ </script>
+diff --git a/content/canvas/test/webgl/conformance/misc/uninitialized-test.html b/content/canvas/test/webgl/conformance/misc/uninitialized-test.html
+--- a/content/canvas/test/webgl/conformance/misc/uninitialized-test.html
++++ b/content/canvas/test/webgl/conformance/misc/uninitialized-test.html
+@@ -56,17 +56,19 @@ function checkNonZeroPixels(texture, tex
+     gl.readPixels(0, 0, texWidth, texHeight, gl.RGBA, gl.UNSIGNED_BYTE, data);
+ 
+     var k = 0;
+     for (var y = 0; y < texHeight; ++y) {
+         for (var x = 0; x < texWidth; ++x) {
+             var index = (y * texWidth + x) * 4;
+             if (x >= skipX && x < skipX + skipWidth && y >= skipY && y < skipY + skipHeight) {
+                 if (data[index] != skipR || data[index + 1] != skipG || data[index + 2] != skipB || data[index + 3] != skipA) {
+-                    testFailed("non-zero pixel values are wrong");
++                    testFailed("non-zero pixel values are wrong at (" + x + ", " + y + "), data was (" +
++                               data[index] + "," + data[index + 1] + "," + data[index + 2] + "," + data[index + 3] +
++                               ") should have been (" + skipR + "," + skipG + "," + skipB + "," + skipA + ")");
+                     return;
+                 }
+             } else {
+                 for (var i = 0; i < 4; ++i) {
+                     if (data[index + i] != 0)
+                         k++;
+                 }
+             }
+diff --git a/content/canvas/test/webgl/conformance/resources/webgl-test-utils.js b/content/canvas/test/webgl/conformance/resources/webgl-test-utils.js
+--- a/content/canvas/test/webgl/conformance/resources/webgl-test-utils.js
++++ b/content/canvas/test/webgl/conformance/resources/webgl-test-utils.js
+@@ -389,21 +389,29 @@ var drawQuad = function(gl, opt_color) {
+ var checkCanvasRect = function(gl, x, y, width, height, color, msg, errorRange) {
+   errorRange = errorRange || 0;
+   var buf = new Uint8Array(width * height * 4);
+   gl.readPixels(x, y, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buf);
+   for (var i = 0; i < width * height; ++i) {
+     var offset = i * 4;
+     for (var j = 0; j < color.length; ++j) {
+       if (Math.abs(buf[offset + j] - color[j]) > errorRange) {
+-        testFailed(msg);
+         var was = buf[offset + 0].toString();
+         for (j = 1; j < color.length; ++j) {
+           was += "," + buf[offset + j];
+         }
++
++        var cv = document.createElement('canvas');
++        cv.height = height;
++        cv.width = width;
++        var ctx = cv.getContext('2d');
++        ctx.fillStyle="rgba(" + color[0] + ", " + color[1] + ", " + color[2] + ", 255)";
++        ctx.fillRect(0, 0, width, height);
++        testFailedRender(msg, ctx, buf, width, height);
++
+         debug('at (' + (i % width) + ', ' + Math.floor(i / width) +
+               ') expected: ' + color + ' was ' + was);
+         return;
+       }
+     }
+   }
+   testPassed(msg);
+ };
+diff --git a/content/canvas/test/webgl/resources/js-test-pre.js b/content/canvas/test/webgl/resources/js-test-pre.js
+--- a/content/canvas/test/webgl/resources/js-test-pre.js
++++ b/content/canvas/test/webgl/resources/js-test-pre.js
+@@ -81,16 +81,87 @@ function testPassed(msg)
+     reportTestResultsToHarness(true, msg);
+     debug('<span><span class="pass">PASS</span> ' + escapeHTML(msg) + '</span>');
+ }
+ 
+ function testFailed(msg)
+ {
+     reportTestResultsToHarness(false, msg);
+     debug('<span><span class="fail">FAIL</span> ' + escapeHTML(msg) + '</span>');
++    dump('FAIL: ' + msg + '\n');
++
++    var stack = (new Error).stack.split('\n');
++    if (!stack.length) {
++        return;
++    }
++
++    dump('STACK TRACE: \n');
++
++    stack.pop();
++    var index = 0, frame, messages = new Array();
++    // Match all .html files and print out the line in them.
++    while (stack.length && index != -1) {
++        frame = stack.pop();
++        index = frame.indexOf(".html:");
++        if (index != -1) {
++            messages.unshift(frame);
++        }
++    }
++
++    // Print out the first stack frame in JS and then stop.
++    if (stack.length) {
++        messages.unshift(stack.pop());
++    }
++
++    for (message in messages) {
++        dump(messages[message] + '\n');
++    }
++}
++
++function testFailedRender(msg, ref, test, width, height) 
++{
++    var refData;
++    if (typeof ref.getImageData == 'function') {
++        refData = ref.canvas.toDataURL();
++    } else {
++        refData = arrayToURLData(ref, width, height);
++    }
++
++    var testData;
++    if (typeof test.getImageData == 'function') {
++        testData = test.canvas.toDataURL();
++    } else {
++        testData = arrayToURLData(test, width, height);
++    }
++    
++    testFailed(msg);
++
++    var data = 'REFTEST TEST-UNEXPECTED-FAIL | ' + msg + ' | image comparison (==)\n' +
++               'REFTEST   IMAGE 1 (TEST): ' + testData + '\n' +
++               'REFTEST   IMAGE 2 (REFERENCE): ' + refData;
++    dump('FAIL: ' + data + '\n');
++    dump('To view the differences between these image renderings, go to the following link: https://hg.mozilla.org/mozilla-central/raw-file/tip/layout/tools/reftest/reftest-analyzer.xhtml#log=' +
++    encodeURIComponent(encodeURIComponent(data)) + '\n');
++}
++
++function arrayToURLData(buf, width, height)
++{
++    var cv = document.createElement('canvas');
++    cv.height = height;
++    cv.width = width;
++    var ctx = cv.getContext('2d');
++    var imgd = ctx.getImageData(0, 0, width, height);
++    for (i = 0; i < height * width; ++i) {
++        offset = i * 4;
++        for (j = 0; j < 4; j++) {
++            imgd.data[offset + j] = buf[offset + j];
++        }
++    }
++    ctx.putImageData(imgd, 0, 0);
++    return cv.toDataURL();
+ }
+ 
+ function areArraysEqual(_a, _b)
+ {
+     try {
+         if (_a.length !== _b.length)
+             return false;
+         for (var i = 0; i < _a.length; i++)
--- a/content/canvas/test/webgl/resources/js-test-pre.js
+++ b/content/canvas/test/webgl/resources/js-test-pre.js
@@ -81,16 +81,87 @@ function testPassed(msg)
     reportTestResultsToHarness(true, msg);
     debug('<span><span class="pass">PASS</span> ' + escapeHTML(msg) + '</span>');
 }
 
 function testFailed(msg)
 {
     reportTestResultsToHarness(false, msg);
     debug('<span><span class="fail">FAIL</span> ' + escapeHTML(msg) + '</span>');
+    dump('FAIL: ' + msg + '\n');
+
+    var stack = (new Error).stack.split('\n');
+    if (!stack.length) {
+        return;
+    }
+
+    dump('STACK TRACE: \n');
+
+    stack.pop();
+    var index = 0, frame, messages = new Array();
+    // Match all .html files and print out the line in them.
+    while (stack.length && index != -1) {
+        frame = stack.pop();
+        index = frame.indexOf(".html:");
+        if (index != -1) {
+            messages.unshift(frame);
+        }
+    }
+
+    // Print out the first stack frame in JS and then stop.
+    if (stack.length) {
+        messages.unshift(stack.pop());
+    }
+
+    for (message in messages) {
+        dump(messages[message] + '\n');
+    }
+}
+
+function testFailedRender(msg, ref, test, width, height) 
+{
+    var refData;
+    if (typeof ref.getImageData == 'function') {
+        refData = ref.canvas.toDataURL();
+    } else {
+        refData = arrayToURLData(ref, width, height);
+    }
+
+    var testData;
+    if (typeof test.getImageData == 'function') {
+        testData = test.canvas.toDataURL();
+    } else {
+        testData = arrayToURLData(test, width, height);
+    }
+    
+    testFailed(msg);
+
+    var data = 'REFTEST TEST-UNEXPECTED-FAIL | ' + msg + ' | image comparison (==)\n' +
+               'REFTEST   IMAGE 1 (TEST): ' + testData + '\n' +
+               'REFTEST   IMAGE 2 (REFERENCE): ' + refData;
+    dump('FAIL: ' + data + '\n');
+    dump('To view the differences between these image renderings, go to the following link: https://hg.mozilla.org/mozilla-central/raw-file/tip/layout/tools/reftest/reftest-analyzer.xhtml#log=' +
+    encodeURIComponent(encodeURIComponent(data)) + '\n');
+}
+
+function arrayToURLData(buf, width, height)
+{
+    var cv = document.createElement('canvas');
+    cv.height = height;
+    cv.width = width;
+    var ctx = cv.getContext('2d');
+    var imgd = ctx.getImageData(0, 0, width, height);
+    for (i = 0; i < height * width; ++i) {
+        offset = i * 4;
+        for (j = 0; j < 4; j++) {
+            imgd.data[offset + j] = buf[offset + j];
+        }
+    }
+    ctx.putImageData(imgd, 0, 0);
+    return cv.toDataURL();
 }
 
 function areArraysEqual(_a, _b)
 {
     try {
         if (_a.length !== _b.length)
             return false;
         for (var i = 0; i < _a.length; i++)
--- a/content/canvas/test/webgl/test_webgl_conformance_test_suite.html
+++ b/content/canvas/test/webgl/test_webgl_conformance_test_suite.html
@@ -206,20 +206,24 @@ function start() {
     this.currentPage = new Page(this, url, this.resultElem);
     this.resultElem.appendChild(this.currentPage.elem);
     ++this.totalPages;
     this.pagesByURL[url] = this.currentPage;
   };
 
   Reporter.prototype.startPage = function(url) {
     dump('WebGL mochitest: starting page ' + url + '\n');
-    if (kIsLinux) {
-      dump('Calling garbageCollect()\n');
-      SpecialPowers.DOMWindowUtils.garbageCollect();
-    }
+
+    // Calling garbageCollect before each test page fixes intermittent failures with
+    // out-of-memory errors, often failing to create a WebGL context.
+    // The explanation is that the JS engine keeps unreferenced WebGL contexts around
+    // for too long before GCing (bug 617453), so that during this mochitest dozens of unreferenced
+    // WebGL contexts can accumulate at a given time.
+    SpecialPowers.DOMWindowUtils.garbageCollect();
+
     var page = this.pagesByURL[url];
     this.currentPage = page;
     statusTextNode.textContent = 'Running URL: ' + url;
     expectedtofailTextNode.textContent = testsExpectedToFail.length +
                                          ' test pages are expected to fail out of ' +
                                          this.totalPages;
     ignoredtestsTextNode.textContent = testsToIgnore.length +
                                          ' test pages have their results ignored';
--- a/content/canvas/test/webgl/webgl-conformance-tests.html
+++ b/content/canvas/test/webgl/webgl-conformance-tests.html
@@ -289,16 +289,22 @@ function start() {
       this.contextInfo["VERSION"] = ctx.getParameter(ctx.VERSION);
       this.contextInfo["RENDERER"] = ctx.getParameter(ctx.RENDERER);
       this.contextInfo["RED_BITS"] = ctx.getParameter(ctx.RED_BITS);
       this.contextInfo["GREEN_BITS"] = ctx.getParameter(ctx.GREEN_BITS);
       this.contextInfo["BLUE_BITS"] = ctx.getParameter(ctx.BLUE_BITS);
       this.contextInfo["ALPHA_BITS"] = ctx.getParameter(ctx.ALPHA_BITS);
       this.contextInfo["DEPTH_BITS"] = ctx.getParameter(ctx.DEPTH_BITS);
       this.contextInfo["STENCIL_BITS"] = ctx.getParameter(ctx.STENCIL_BITS);
+
+      var ext = ctx.getExtension("WEBGL_debug_renderer_info");
+      if (ext) {
+        this.contextInfo["UNMASKED_VENDOR"] = ctx.getParameter(ext.UNMASKED_VENDOR_WEBGL);
+        this.contextInfo["UNMASKED_RENDERER"] = ctx.getParameter(ext.UNMASKED_RENDERER_WEBGL);
+      }
     }
   };
 
   Reporter.prototype.runTest = function(url) {
     var page = this.pagesByURL[url];
     page.startPage();
     this.currentPage = page;
     this.iframe.src = url;
@@ -359,16 +365,18 @@ function start() {
       tx += "WebGL Conformance Test Results\n";
       tx += "Version " + CONFORMANCE_TEST_VERSION + "\n";
       tx += "\n";
       tx += "-------------------\n\n";
       tx += "User Agent: " + (navigator.userAgent ? navigator.userAgent : "(navigator.userAgent is null)") + "\n";
       tx += "WebGL VENDOR: " + this.contextInfo["VENDOR"] + "\n";
       tx += "WebGL VERSION: " + this.contextInfo["VERSION"] + "\n";
       tx += "WebGL RENDERER: " + this.contextInfo["RENDERER"] + "\n";
+      tx += "Unmasked VENDOR: " + this.contextInfo["UNMASKED_VENDOR"] + "\n";
+      tx += "Unmasked RENDERER: " + this.contextInfo["UNMASKED_RENDERER"] + "\n";
       tx += "WebGL R/G/B/A/Depth/Stencil bits (default config): " + this.contextInfo["RED_BITS"] + "/" + this.contextInfo["GREEN_BITS"] + "/" + this.contextInfo["BLUE_BITS"] + "/" + this.contextInfo["ALPHA_BITS"] + "/" + this.contextInfo["DEPTH_BITS"] + "/" + this.contextInfo["STENCIL_BITS"] + "\n";
       tx += "\n";
       tx += "-------------------\n\n";
       tx += "Test Summary (" + totalTests + " total tests):\n";
       tx += "Tests PASSED: " + totalSuccessful + "\n";
       tx += "Tests FAILED: " + (totalTests - totalSuccessful) + "\n";
       tx += "Tests TIMED OUT: " + totalTimeouts + "\n";
       tx += "\n";
--- a/content/svg/content/src/SVGPathSegListSMILType.cpp
+++ b/content/svg/content/src/SVGPathSegListSMILType.cpp
@@ -90,16 +90,31 @@ SVGPathSegListSMILType::IsEqual(const ns
 {
   NS_PRECONDITION(aLeft.mType == aRight.mType, "Incompatible SMIL types");
   NS_PRECONDITION(aLeft.mType == this, "Unexpected type for SMIL value");
 
   return *static_cast<const SVGPathDataAndOwner*>(aLeft.mU.mPtr) ==
          *static_cast<const SVGPathDataAndOwner*>(aRight.mU.mPtr);
 }
 
+static bool
+ArcFlagsDiffer(SVGPathDataAndOwner::const_iterator aPathData1,
+               SVGPathDataAndOwner::const_iterator aPathData2)
+{
+  NS_ABORT_IF_FALSE
+    (SVGPathSegUtils::IsArcType(SVGPathSegUtils::DecodeType(aPathData1[0])),
+                                "ArcFlagsDiffer called with non-arc segment");
+  NS_ABORT_IF_FALSE
+    (SVGPathSegUtils::IsArcType(SVGPathSegUtils::DecodeType(aPathData2[0])),
+                                "ArcFlagsDiffer called with non-arc segment");
+
+  return aPathData1[LARGE_ARC_FLAG_IDX] != aPathData2[LARGE_ARC_FLAG_IDX] ||
+         aPathData1[SWEEP_FLAG_IDX]     != aPathData2[SWEEP_FLAG_IDX];
+}
+
 enum PathInterpolationResult {
   eCannotInterpolate,
   eRequiresConversion,
   eCanInterpolate
 };
 
 static PathInterpolationResult
 CanInterpolate(const SVGPathDataAndOwner& aStart,
@@ -119,16 +134,22 @@ CanInterpolate(const SVGPathDataAndOwner
   SVGPathDataAndOwner::const_iterator pEnd = aEnd.begin();
   SVGPathDataAndOwner::const_iterator pStartDataEnd = aStart.end();
   SVGPathDataAndOwner::const_iterator pEndDataEnd = aEnd.end();
 
   while (pStart < pStartDataEnd && pEnd < pEndDataEnd) {
     PRUint32 startType = SVGPathSegUtils::DecodeType(*pStart);
     PRUint32 endType = SVGPathSegUtils::DecodeType(*pEnd);
 
+    if (SVGPathSegUtils::IsArcType(startType) &&
+        SVGPathSegUtils::IsArcType(endType) &&
+        ArcFlagsDiffer(pStart, pEnd)) {
+      return eCannotInterpolate;
+    }
+
     if (startType != endType) {
       if (!SVGPathSegUtils::SameTypeModuloRelativeness(startType, endType)) {
         return eCannotInterpolate;
       }
 
       result = eRequiresConversion;
     }
 
@@ -189,32 +210,35 @@ AddWeightedPathSegs(double aCoeff1,
 {
   NS_ABORT_IF_FALSE(aSeg2, "2nd segment must be non-null");
   NS_ABORT_IF_FALSE(aResultSeg, "result segment must be non-null");
 
   PRUint32 segType = SVGPathSegUtils::DecodeType(aSeg2[0]);
   NS_ABORT_IF_FALSE(!aSeg1 || SVGPathSegUtils::DecodeType(*aSeg1) == segType,
                     "unexpected segment type");
 
+  // FIRST: Directly copy the arguments that don't make sense to add.
   aResultSeg[0] = aSeg2[0];  // encoded segment type
 
-  // FIRST: Add all the arguments.
+  bool isArcType = SVGPathSegUtils::IsArcType(segType);
+  if (isArcType) {
+    // Copy boolean arc flags.
+    NS_ABORT_IF_FALSE(!aSeg1 || !ArcFlagsDiffer(aSeg1, aSeg2),
+                      "Expecting arc flags to match");
+    aResultSeg[LARGE_ARC_FLAG_IDX] = aSeg2[LARGE_ARC_FLAG_IDX];
+    aResultSeg[SWEEP_FLAG_IDX]     = aSeg2[SWEEP_FLAG_IDX];
+  }
+
+  // SECOND: Add the arguments that are supposed to be added.
   // (The 1's below are to account for segment type)
   PRUint32 numArgs = SVGPathSegUtils::ArgCountForType(segType);
   for (PRUint32 i = 1; i < 1 + numArgs; ++i) {
-    aResultSeg[i] = (aSeg1 ? aCoeff1 * aSeg1[i] : 0.0) + aCoeff2 * aSeg2[i];
-  }
-
-  // SECOND: ensure non-zero flags become 1.
-  if (SVGPathSegUtils::IsArcType(segType)) {
-    if (aResultSeg[LARGE_ARC_FLAG_IDX] != 0.0f) {
-      aResultSeg[LARGE_ARC_FLAG_IDX] = 1.0f;
-    }
-    if (aResultSeg[SWEEP_FLAG_IDX] != 0.0f) {
-      aResultSeg[SWEEP_FLAG_IDX] = 1.0f;
+     // Need to skip arc flags for arc-type segments. (already handled them)
+    if (!(isArcType && (i == LARGE_ARC_FLAG_IDX || i == SWEEP_FLAG_IDX))) {
+      aResultSeg[i] = (aSeg1 ? aCoeff1 * aSeg1[i] : 0.0) + aCoeff2 * aSeg2[i];
     }
   }
 
   // FINALLY: Shift iterators forward. ("1+" is to include seg-type)
   if (aSeg1) {
     aSeg1 += 1 + numArgs;
   }
   aSeg2 += 1 + numArgs;
--- a/content/svg/content/test/test_pathAnimInterpolation.xhtml
+++ b/content/svg/content/test/test_pathAnimInterpolation.xhtml
@@ -133,20 +133,20 @@ var gSuffixes = {
   LL: [[10, 20], [30, 40], [20, 30], [30, 50]],
   ll: [[10, 20], [30, 40], [20, 30], [30, 50]],
   CC: [[10, 20, 30, 40, 50, 60], [70, 80, 90, 100, 110, 120],
        [40, 50, 60, 70, 80, 90], [50, 70, 90, 110, 130, 150]],
   cc: [[10, 20, 30, 40, 50, 60], [70, 80, 90, 100, 110, 120],
        [40, 50, 60, 70, 80, 90], [50, 70, 90, 110, 130, 150]],
   QQ: [[10, 20, 30, 40], [50, 60, 70, 80], [30, 40, 50, 60], [40, 60, 80, 100]],
   qq: [[10, 20, 30, 40], [50, 60, 70, 80], [30, 40, 50, 60], [40, 60, 80, 100]],
-  AA: [[10, 20, 30, 1, 0, 40, 50], [60, 70, 80, 0, 0, 90, 100],
-       [35, 45, 55, 1, 0, 65, 75], [45, 65, 85, 1, 0, 105, 125]],
-  aa: [[10, 20, 30, 0, 1, 40, 50], [60, 70, 80, 0, 0, 90, 100],
-       [35, 45, 55, 0, 1, 65, 75], [45, 65, 85, 0, 1, 105, 125]],
+  AA: [[10, 20, 30, 0, 0, 40, 50], [60, 70, 80, 0, 0, 90, 100],
+       [35, 45, 55, 0, 0, 65, 75], [45, 65, 85, 0, 0, 105, 125]],
+  aa: [[10, 20, 30, 0, 0, 40, 50], [60, 70, 80, 0, 0, 90, 100],
+       [35, 45, 55, 0, 0, 65, 75], [45, 65, 85, 0, 0, 105, 125]],
   HH: [[10], [20], [15], [25]],
   hh: [[10], [20], [15], [25]],
   VV: [[10], [20], [15], [25]],
   vv: [[10], [20], [15], [25]],
   SS: [[10, 20, 30, 40], [50, 60, 70, 80], [30, 40, 50, 60], [40, 60, 80, 100]],
   ss: [[10, 20, 30, 40], [50, 60, 70, 80], [30, 40, 50, 60], [40, 60, 80, 100]],
   TT: [[10, 20], [30, 40], [20, 30], [30, 50]],
   tt: [[10, 20], [30, 40], [20, 30], [30, 50]],
@@ -281,16 +281,27 @@ function run()
 
       for each (let prefixEntry in gPrefixes) {
         let [prefixLength, prefix] = prefixEntry;
         addTest(prefixLength, prefix, fromType, fromArguments,
 	        toType, toArguments, toType, expectedArguments, additive);
       }
     }
 
+    // Test that differences in arc flag parameters cause the
+    // interpolation/addition not to occur.
+    addTest(1, "M100,100",
+            "A", [10, 20, 30, 0, 0, 40, 50],
+            "a", [60, 70, 80, 0, 1, 90, 100],
+	    "a", [60, 70, 80, 0, 1, 90, 100], additive);
+    addTest(1, "M100,100",
+            "A", [10, 20, 30, 0, 0, 40, 50],
+            "a", [60, 70, 80, 1, 0, 90, 100],
+	    "a", [60, 70, 80, 1, 0, 90, 100], additive);
+
     // Test all pairs of segment types that cannot be interpolated between.
     for each (let fromType in gTypes) {
       let fromArguments = generatePathSegmentArguments(fromType, 0);
       for each (let toType in gTypes) {
         if (!isValidInterpolation(fromType, toType)) {
           let toArguments = generatePathSegmentArguments(toType, 1000);
           addTest(1, "M100,100", fromType, fromArguments,
 	          toType, toArguments, toType, toArguments, additive);
--- a/dom/Makefile.in
+++ b/dom/Makefile.in
@@ -67,16 +67,17 @@ DIRS = \
   $(NULL)
 
 ifdef MOZ_SMIL
 DIRS += interfaces/smil
 endif
 
 DIRS += \
   base \
+  battery \
   src \
   locales \
   plugins/base \
   plugins/ipc \
   indexedDB \
   system \
   ipc \
   workers \
--- a/dom/base/Makefile.in
+++ b/dom/base/Makefile.in
@@ -117,16 +117,17 @@ CPPSRCS =			\
 	nsScriptNameSpaceManager.cpp \
 	nsDOMScriptObjectFactory.cpp \
 	nsQueryContentEventResult.cpp \
 	nsContentPermissionHelper.cpp \
 	nsStructuredCloneContainer.cpp \
 	nsDOMNavigationTiming.cpp \
 	nsPerformance.cpp	\
 	nsDOMMemoryReporter.cpp \
+	Navigator.cpp \
 	$(NULL)
 
 include $(topsrcdir)/dom/dom-config.mk
 
 ifdef MOZ_JSDEBUGGER
 DEFINES += -DMOZ_JSDEBUGGER
 endif
 
new file mode 100644
--- /dev/null
+++ b/dom/base/Navigator.cpp
@@ -0,0 +1,891 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=2 et tw=78: */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Travis Bogard <travis@netscape.com>
+ *   Brendan Eich <brendan@mozilla.org>
+ *   David Hyatt (hyatt@netscape.com)
+ *   Dan Rosen <dr@netscape.com>
+ *   Vidur Apparao <vidur@netscape.com>
+ *   Johnny Stenback <jst@netscape.com>
+ *   Mark Hammond <mhammond@skippinet.com.au>
+ *   Ryan Jones <sciguyryan@gmail.com>
+ *   Jeff Walden <jwalden+code@mit.edu>
+ *   Ben Bucksch <ben.bucksch  beonex.com>
+ *   Emanuele Costa <emanuele.costa@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+// Needs to be first.
+#include "base/basictypes.h"
+
+#include "Navigator.h"
+#include "nsIXULAppInfo.h"
+#include "nsPluginArray.h"
+#include "nsMimeTypeArray.h"
+#include "nsDesktopNotification.h"
+#include "nsGeolocation.h"
+#include "nsIHttpProtocolHandler.h"
+#include "nsICachingChannel.h"
+#include "nsIDocShell.h"
+#include "nsIWebContentHandlerRegistrar.h"
+#include "nsICookiePermission.h"
+#include "nsIScriptSecurityManager.h"
+#include "nsIJSContextStack.h"
+#include "nsCharSeparatedTokenizer.h"
+#include "nsContentUtils.h"
+#include "nsUnicharUtils.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/Telemetry.h"
+#include "BatteryManager.h"
+
+// This should not be in the namespace.
+DOMCI_DATA(Navigator, mozilla::dom::Navigator)
+
+namespace mozilla {
+namespace dom {
+
+static const char sJSStackContractID[] = "@mozilla.org/js/xpc/ContextStack;1";
+bool Navigator::sDoNotTrackEnabled = false;
+
+/* static */
+void
+Navigator::Init()
+{
+  Preferences::AddBoolVarCache(&sDoNotTrackEnabled,
+                               "privacy.donottrackheader.enabled",
+                               false);
+}
+
+Navigator::Navigator(nsIDocShell* aDocShell)
+  : mDocShell(aDocShell)
+{
+}
+
+Navigator::~Navigator()
+{
+  if (mMimeTypes) {
+    mMimeTypes->Invalidate();
+  }
+
+  if (mPlugins) {
+    mPlugins->Invalidate();
+  }
+
+  if (mBatteryManager) {
+    mBatteryManager->Shutdown();
+  }
+}
+
+NS_INTERFACE_MAP_BEGIN(Navigator)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMNavigator)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMNavigator)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMClientInformation)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMNavigatorGeolocation)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMNavigatorBattery)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMNavigatorDesktopNotification)
+  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(Navigator)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_ADDREF(Navigator)
+NS_IMPL_RELEASE(Navigator)
+
+void
+Navigator::SetDocShell(nsIDocShell* aDocShell)
+{
+  mDocShell = aDocShell;
+
+  if (mPlugins) {
+    mPlugins->SetDocShell(aDocShell);
+  }
+
+  // If there is a page transition, make sure delete the geolocation object.
+  if (mGeolocation) {
+    mGeolocation->Shutdown();
+    mGeolocation = nsnull;
+  }
+
+  if (mNotification) {
+    mNotification->Shutdown();
+    mNotification = nsnull;
+  }
+
+  if (mBatteryManager) {
+    mBatteryManager->Shutdown();
+    mBatteryManager = nsnull;
+  }
+}
+
+//*****************************************************************************
+//    Navigator::nsIDOMNavigator
+//*****************************************************************************
+
+NS_IMETHODIMP
+Navigator::GetUserAgent(nsAString& aUserAgent)
+{
+  return NS_GetNavigatorUserAgent(aUserAgent);
+}
+
+NS_IMETHODIMP
+Navigator::GetAppCodeName(nsAString& aAppCodeName)
+{
+  nsresult rv;
+
+  nsCOMPtr<nsIHttpProtocolHandler>
+    service(do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &rv));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCAutoString appName;
+  rv = service->GetAppName(appName);
+  CopyASCIItoUTF16(appName, aAppCodeName);
+
+  return rv;
+}
+
+NS_IMETHODIMP
+Navigator::GetAppVersion(nsAString& aAppVersion)
+{
+  return NS_GetNavigatorAppVersion(aAppVersion);
+}
+
+NS_IMETHODIMP
+Navigator::GetAppName(nsAString& aAppName)
+{
+  return NS_GetNavigatorAppName(aAppName);
+}
+
+/**
+ * JS property navigator.language, exposed to web content.
+ * Take first value from Accept-Languages (HTTP header), which is
+ * the "content language" freely set by the user in the Pref window.
+ *
+ * Do not use UI language (chosen app locale) here.
+ * See RFC 2616, Section 15.1.4 "Privacy Issues Connected to Accept Headers"
+ *
+ * "en", "en-US" and "i-cherokee" and "" are valid.
+ * Fallback in case of invalid pref should be "" (empty string), to
+ * let site do fallback, e.g. to site's local language.
+ */
+NS_IMETHODIMP
+Navigator::GetLanguage(nsAString& aLanguage)
+{
+  // E.g. "de-de, en-us,en".
+  const nsAdoptingString& acceptLang =
+    Preferences::GetLocalizedString("intl.accept_languages");
+
+  // Take everything before the first "," or ";", without trailing space.
+  nsCharSeparatedTokenizer langTokenizer(acceptLang, ',');
+  const nsSubstring &firstLangPart = langTokenizer.nextToken();
+  nsCharSeparatedTokenizer qTokenizer(firstLangPart, ';');
+  aLanguage.Assign(qTokenizer.nextToken());
+
+  // Checks and fixups:
+  // replace "_" with "-" to avoid POSIX/Windows "en_US" notation.
+  if (aLanguage.Length() > 2 && aLanguage[2] == PRUnichar('_')) {
+    aLanguage.Replace(2, 1, PRUnichar('-')); // TODO replace all
+  }
+
+  // Use uppercase for country part, e.g. "en-US", not "en-us", see BCP47
+  // only uppercase 2-letter country codes, not "zh-Hant", "de-DE-x-goethe".
+  if (aLanguage.Length() <= 2) {
+    return NS_OK;
+  }
+
+  nsCharSeparatedTokenizer localeTokenizer(aLanguage, '-');
+  PRInt32 pos = 0;
+  bool first = true;
+  while (localeTokenizer.hasMoreTokens()) {
+    const nsSubstring& code = localeTokenizer.nextToken();
+
+    if (code.Length() == 2 && !first) {
+      nsAutoString upper(code);
+      ToUpperCase(upper);
+      aLanguage.Replace(pos, code.Length(), upper);
+    }
+
+    pos += code.Length() + 1; // 1 is the separator
+    first = false;
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+Navigator::GetPlatform(nsAString& aPlatform)
+{
+  return NS_GetNavigatorPlatform(aPlatform);
+}
+
+NS_IMETHODIMP
+Navigator::GetOscpu(nsAString& aOSCPU)
+{
+  if (!nsContentUtils::IsCallerTrustedForRead()) {
+    const nsAdoptingString& override =
+      Preferences::GetString("general.oscpu.override");
+
+    if (override) {
+      aOSCPU = override;
+      return NS_OK;
+    }
+  }
+
+  nsresult rv;
+
+  nsCOMPtr<nsIHttpProtocolHandler>
+    service(do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &rv));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCAutoString oscpu;
+  rv = service->GetOscpu(oscpu);
+  CopyASCIItoUTF16(oscpu, aOSCPU);
+
+  return rv;
+}
+
+NS_IMETHODIMP
+Navigator::GetVendor(nsAString& aVendor)
+{
+  aVendor.Truncate();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+Navigator::GetVendorSub(nsAString& aVendorSub)
+{
+  aVendorSub.Truncate();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+Navigator::GetProduct(nsAString& aProduct)
+{
+  nsresult rv;
+
+  nsCOMPtr<nsIHttpProtocolHandler>
+    service(do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &rv));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCAutoString product;
+  rv = service->GetProduct(product);
+  CopyASCIItoUTF16(product, aProduct);
+
+  return rv;
+}
+
+NS_IMETHODIMP
+Navigator::GetProductSub(nsAString& aProductSub)
+{
+  if (!nsContentUtils::IsCallerTrustedForRead()) {
+    const nsAdoptingString& override =
+      Preferences::GetString("general.productSub.override");
+
+    if (override) {
+      aProductSub = override;
+      return NS_OK;
+    }
+
+    // 'general.useragent.productSub' backwards compatible with 1.8 branch.
+    const nsAdoptingString& override2 =
+      Preferences::GetString("general.useragent.productSub");
+
+    if (override2) {
+      aProductSub = override2;
+      return NS_OK;
+    }
+  }
+
+  nsresult rv;
+
+  nsCOMPtr<nsIHttpProtocolHandler>
+    service(do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &rv));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCAutoString productSub;
+  rv = service->GetProductSub(productSub);
+  CopyASCIItoUTF16(productSub, aProductSub);
+
+  return rv;
+}
+
+NS_IMETHODIMP
+Navigator::GetMimeTypes(nsIDOMMimeTypeArray** aMimeTypes)
+{
+  if (!mMimeTypes) {
+    mMimeTypes = new nsMimeTypeArray(this);
+  }
+
+  NS_ADDREF(*aMimeTypes = mMimeTypes);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+Navigator::GetPlugins(nsIDOMPluginArray** aPlugins)
+{
+  if (!mPlugins) {
+    mPlugins = new nsPluginArray(this, mDocShell);
+  }
+
+  NS_ADDREF(*aPlugins = mPlugins);
+
+  return NS_OK;
+}
+
+// Values for the network.cookie.cookieBehavior pref are documented in
+// nsCookieService.cpp.
+#define COOKIE_BEHAVIOR_REJECT 2
+
+NS_IMETHODIMP
+Navigator::GetCookieEnabled(bool* aCookieEnabled)
+{
+  *aCookieEnabled =
+    (Preferences::GetInt("network.cookie.cookieBehavior",
+                         COOKIE_BEHAVIOR_REJECT) != COOKIE_BEHAVIOR_REJECT);
+
+  // Check whether an exception overrides the global cookie behavior
+  // Note that the code for getting the URI here matches that in
+  // nsHTMLDocument::SetCookie.
+  nsCOMPtr<nsIDocument> doc = do_GetInterface(mDocShell);
+  if (!doc) {
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsIURI> codebaseURI;
+  doc->NodePrincipal()->GetURI(getter_AddRefs(codebaseURI));
+
+  if (!codebaseURI) {
+    // Not a codebase, so technically can't set cookies, but let's
+    // just return the default value.
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsICookiePermission> permMgr =
+    do_GetService(NS_COOKIEPERMISSION_CONTRACTID);
+  NS_ENSURE_TRUE(permMgr, NS_OK);
+
+  // Pass null for the channel, just like the cookie service does.
+  nsCookieAccess access;
+  nsresult rv = permMgr->CanAccess(codebaseURI, nsnull, &access);
+  NS_ENSURE_SUCCESS(rv, NS_OK);
+
+  if (access != nsICookiePermission::ACCESS_DEFAULT) {
+    *aCookieEnabled = access != nsICookiePermission::ACCESS_DENY;
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+Navigator::GetOnLine(bool* aOnline)
+{
+  NS_PRECONDITION(aOnline, "Null out param");
+
+  *aOnline = !NS_IsOffline();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+Navigator::GetBuildID(nsAString& aBuildID)
+{
+  if (!nsContentUtils::IsCallerTrustedForRead()) {
+    const nsAdoptingString& override =
+      Preferences::GetString("general.buildID.override");
+
+    if (override) {
+      aBuildID = override;
+      return NS_OK;
+    }
+  }
+
+  nsCOMPtr<nsIXULAppInfo> appInfo =
+    do_GetService("@mozilla.org/xre/app-info;1");
+  if (!appInfo) {
+    return NS_ERROR_NOT_IMPLEMENTED;
+  }
+
+  nsCAutoString buildID;
+  nsresult rv = appInfo->GetAppBuildID(buildID);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  aBuildID.Truncate();
+  AppendASCIItoUTF16(buildID, aBuildID);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+Navigator::GetDoNotTrack(nsAString &aResult)
+{
+  if (sDoNotTrackEnabled) {
+    aResult.AssignLiteral("yes");
+  } else {
+    aResult.AssignLiteral("unspecified");
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+Navigator::JavaEnabled(bool* aReturn)
+{
+  Telemetry::AutoTimer<Telemetry::CHECK_JAVA_ENABLED> telemetryTimer;
+  // Return true if we have a handler for "application/x-java-vm",
+  // otherwise return false.
+  *aReturn = false;
+
+  if (!mMimeTypes) {
+    mMimeTypes = new nsMimeTypeArray(this);
+  }
+
+  RefreshMIMEArray();
+
+  PRUint32 count;
+  mMimeTypes->GetLength(&count);
+  for (PRUint32 i = 0; i < count; i++) {
+    nsresult rv;
+    nsIDOMMimeType* type = mMimeTypes->GetItemAt(i, &rv);
+
+    if (NS_FAILED(rv) || !type) {
+      continue;
+    }
+
+    nsAutoString mimeString;
+    if (NS_FAILED(type->GetType(mimeString))) {
+      continue;
+    }
+
+    if (mimeString.EqualsLiteral("application/x-java-vm")) {
+      *aReturn = true;
+      break;
+    }
+  }
+
+  return NS_OK;
+}
+
+void
+Navigator::LoadingNewDocument()
+{
+  // Release these so that they will be recreated for the
+  // new document (if requested).  The plugins or mime types
+  // arrays may have changed.  See bug 150087.
+  if (mMimeTypes) {
+    mMimeTypes->Invalidate();
+    mMimeTypes = nsnull;
+  }
+
+  if (mPlugins) {
+    mPlugins->Invalidate();
+    mPlugins = nsnull;
+  }
+
+  if (mGeolocation) {
+    mGeolocation->Shutdown();
+    mGeolocation = nsnull;
+  }
+
+  if (mNotification) {
+    mNotification->Shutdown();
+    mNotification = nsnull;
+  }
+
+  if (mBatteryManager) {
+    mBatteryManager->Shutdown();
+    mBatteryManager = nsnull;
+  }
+}
+
+nsresult
+Navigator::RefreshMIMEArray()
+{
+  if (mMimeTypes) {
+    return mMimeTypes->Refresh();
+  }
+
+  return NS_OK;
+}
+
+bool
+Navigator::HasDesktopNotificationSupport()
+{
+  return Preferences::GetBool("notification.feature.enabled", false);
+}
+
+//*****************************************************************************
+//    Navigator::nsIDOMClientInformation
+//*****************************************************************************
+
+NS_IMETHODIMP
+Navigator::RegisterContentHandler(const nsAString& aMIMEType,
+                                  const nsAString& aURI,
+                                  const nsAString& aTitle)
+{
+  if (!mDocShell) {
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsIWebContentHandlerRegistrar> registrar =
+    do_GetService(NS_WEBCONTENTHANDLERREGISTRAR_CONTRACTID);
+  if (!registrar) {
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsIDOMWindow> contentDOMWindow = do_GetInterface(mDocShell);
+  if (!contentDOMWindow) {
+    return NS_OK;
+  }
+
+  return registrar->RegisterContentHandler(aMIMEType, aURI, aTitle,
+                                           contentDOMWindow);
+}
+
+NS_IMETHODIMP
+Navigator::RegisterProtocolHandler(const nsAString& aProtocol,
+                                   const nsAString& aURI,
+                                   const nsAString& aTitle)
+{
+  if (!mDocShell) {
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsIWebContentHandlerRegistrar> registrar =
+    do_GetService(NS_WEBCONTENTHANDLERREGISTRAR_CONTRACTID);
+  if (!registrar) {
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsIDOMWindow> contentDOMWindow = do_GetInterface(mDocShell);
+  if (!contentDOMWindow) {
+    return NS_OK;
+  }
+
+  return registrar->RegisterProtocolHandler(aProtocol, aURI, aTitle,
+                                            contentDOMWindow);
+}
+
+NS_IMETHODIMP
+Navigator::MozIsLocallyAvailable(const nsAString &aURI,
+                                 bool aWhenOffline,
+                                 bool* aIsAvailable)
+{
+  nsCOMPtr<nsIURI> uri;
+  nsresult rv = NS_NewURI(getter_AddRefs(uri), aURI);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // This method of checking the cache will only work for http/https urls.
+  bool match;
+  rv = uri->SchemeIs("http", &match);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (!match) {
+    rv = uri->SchemeIs("https", &match);
+    NS_ENSURE_SUCCESS(rv, rv);
+    if (!match) {
+      return NS_ERROR_DOM_BAD_URI;
+    }
+  }
+
+  // Same origin check.
+  nsCOMPtr<nsIJSContextStack> stack = do_GetService(sJSStackContractID);
+  NS_ENSURE_TRUE(stack, NS_ERROR_FAILURE);
+
+  JSContext* cx = nsnull;
+  rv = stack->Peek(&cx);
+  NS_ENSURE_TRUE(cx, NS_ERROR_FAILURE);
+
+  rv = nsContentUtils::GetSecurityManager()->CheckSameOrigin(cx, uri);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // These load flags cause an error to be thrown if there is no
+  // valid cache entry, and skip the load if there is.
+  // If the cache is busy, assume that it is not yet available rather
+  // than waiting for it to become available.
+  PRUint32 loadFlags = nsIChannel::INHIBIT_CACHING |
+                       nsICachingChannel::LOAD_NO_NETWORK_IO |
+                       nsICachingChannel::LOAD_ONLY_IF_MODIFIED |
+                       nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE_IF_BUSY;
+
+  if (aWhenOffline) {
+    loadFlags |= nsICachingChannel::LOAD_CHECK_OFFLINE_CACHE |
+                 nsICachingChannel::LOAD_ONLY_FROM_CACHE |
+                 nsIRequest::LOAD_FROM_CACHE;
+  }
+
+  nsCOMPtr<nsIChannel> channel;
+  rv = NS_NewChannel(getter_AddRefs(channel), uri,
+                     nsnull, nsnull, nsnull, loadFlags);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIInputStream> stream;
+  rv = channel->Open(getter_AddRefs(stream));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  stream->Close();
+
+  nsresult status;
+  rv = channel->GetStatus(&status);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (NS_FAILED(status)) {
+    *aIsAvailable = false;
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel);
+  return httpChannel->GetRequestSucceeded(aIsAvailable);
+}
+
+//*****************************************************************************
+//    Navigator::nsIDOMNavigatorGeolocation
+//*****************************************************************************
+
+NS_IMETHODIMP Navigator::GetGeolocation(nsIDOMGeoGeolocation** _retval)
+{
+  NS_ENSURE_ARG_POINTER(_retval);
+  *_retval = nsnull;
+
+  if (!Preferences::GetBool("geo.enabled", true)) {
+    return NS_OK;
+  }
+
+  if (mGeolocation) {
+    NS_ADDREF(*_retval = mGeolocation);
+    return NS_OK;
+  }
+
+  if (!mDocShell) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsCOMPtr<nsIDOMWindow> contentDOMWindow = do_GetInterface(mDocShell);
+  if (!contentDOMWindow) {
+    return NS_ERROR_FAILURE;
+  }
+
+  mGeolocation = new nsGeolocation();
+  if (!mGeolocation) {
+    return NS_ERROR_FAILURE;
+  }
+
+  if (NS_FAILED(mGeolocation->Init(contentDOMWindow))) {
+    mGeolocation = nsnull;
+    return NS_ERROR_FAILURE;
+  }
+
+  NS_ADDREF(*_retval = mGeolocation);
+  return NS_OK;
+}
+
+//*****************************************************************************
+//    Navigator::nsIDOMNavigatorDesktopNotification
+//*****************************************************************************
+
+NS_IMETHODIMP Navigator::GetMozNotification(nsIDOMDesktopNotificationCenter** aRetVal)
+{
+  NS_ENSURE_ARG_POINTER(aRetVal);
+  *aRetVal = nsnull;
+
+  if (mNotification) {
+    NS_ADDREF(*aRetVal = mNotification);
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsPIDOMWindow> window = do_GetInterface(mDocShell);
+  NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
+
+  nsCOMPtr<nsIDocument> document = do_GetInterface(mDocShell);
+  NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
+
+  nsIScriptGlobalObject* sgo = document->GetScopeObject();
+  NS_ENSURE_TRUE(sgo, NS_ERROR_FAILURE);
+
+  nsIScriptContext* scx = sgo->GetContext();
+  NS_ENSURE_TRUE(scx, NS_ERROR_FAILURE);
+
+  mNotification = new nsDesktopNotificationCenter(window->GetCurrentInnerWindow(),
+                                                  scx);
+
+  NS_ADDREF(*aRetVal = mNotification);
+  return NS_OK;
+}
+
+//*****************************************************************************
+//    Navigator::nsIDOMNavigatorBattery
+//*****************************************************************************
+
+NS_IMETHODIMP
+Navigator::GetMozBattery(nsIDOMBatteryManager** aBattery)
+{
+  if (!mBatteryManager) {
+    mBatteryManager = new battery::BatteryManager();
+    mBatteryManager->Init();
+  }
+
+  NS_ADDREF(*aBattery = mBatteryManager);
+
+  return NS_OK;
+}
+
+PRInt64
+Navigator::SizeOf() const
+{
+  PRInt64 size = sizeof(*this);
+
+  // TODO: add SizeOf() to nsMimeTypeArray, bug 674113.
+  size += mMimeTypes ? sizeof(*mMimeTypes.get()) : 0;
+  // TODO: add SizeOf() to nsPluginArray, bug 674114.
+  size += mPlugins ? sizeof(*mPlugins.get()) : 0;
+  // TODO: add SizeOf() to nsGeolocation, bug 674115.
+  size += mGeolocation ? sizeof(*mGeolocation.get()) : 0;
+  // TODO: add SizeOf() to nsDesktopNotificationCenter, bug 674116.
+  size += mNotification ? sizeof(*mNotification.get()) : 0;
+
+  return size;
+}
+
+} // namespace dom
+} // namespace mozilla
+
+nsresult
+NS_GetNavigatorUserAgent(nsAString& aUserAgent)
+{
+  nsresult rv;
+
+  nsCOMPtr<nsIHttpProtocolHandler>
+    service(do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &rv));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCAutoString ua;
+  rv = service->GetUserAgent(ua);
+  CopyASCIItoUTF16(ua, aUserAgent);
+
+  return rv;
+}
+
+nsresult
+NS_GetNavigatorPlatform(nsAString& aPlatform)
+{
+  if (!nsContentUtils::IsCallerTrustedForRead()) {
+    const nsAdoptingString& override =
+      mozilla::Preferences::GetString("general.platform.override");
+
+    if (override) {
+      aPlatform = override;
+      return NS_OK;
+    }
+  }
+
+  nsresult rv;
+
+  nsCOMPtr<nsIHttpProtocolHandler>
+    service(do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &rv));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Sorry for the #if platform ugliness, but Communicator is likewise
+  // hardcoded and we are seeking backward compatibility here (bug 47080).
+#if defined(_WIN64)
+  aPlatform.AssignLiteral("Win64");
+#elif defined(WIN32)
+  aPlatform.AssignLiteral("Win32");
+#elif defined(XP_MACOSX) && defined(__ppc__)
+  aPlatform.AssignLiteral("MacPPC");
+#elif defined(XP_MACOSX) && defined(__i386__)
+  aPlatform.AssignLiteral("MacIntel");
+#elif defined(XP_MACOSX) && defined(__x86_64__)
+  aPlatform.AssignLiteral("MacIntel");
+#elif defined(XP_OS2)
+  aPlatform.AssignLiteral("OS/2");
+#else
+  // XXX Communicator uses compiled-in build-time string defines
+  // to indicate the platform it was compiled *for*, not what it is
+  // currently running *on* which is what this does.
+  nsCAutoString plat;
+  rv = service->GetOscpu(plat);
+  CopyASCIItoUTF16(plat, aPlatform);
+#endif
+
+  return rv;
+}
+nsresult
+NS_GetNavigatorAppVersion(nsAString& aAppVersion)
+{
+  if (!nsContentUtils::IsCallerTrustedForRead()) {
+    const nsAdoptingString& override =
+      mozilla::Preferences::GetString("general.appversion.override");
+
+    if (override) {
+      aAppVersion = override;
+      return NS_OK;
+    }
+  }
+
+  nsresult rv;
+
+  nsCOMPtr<nsIHttpProtocolHandler>
+    service(do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &rv));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCAutoString str;
+  rv = service->GetAppVersion(str);
+  CopyASCIItoUTF16(str, aAppVersion);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  aAppVersion.AppendLiteral(" (");
+
+  rv = service->GetPlatform(str);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  AppendASCIItoUTF16(str, aAppVersion);
+  aAppVersion.Append(PRUnichar(')'));
+
+  return rv;
+}
+
+nsresult
+NS_GetNavigatorAppName(nsAString& aAppName)
+{
+  if (!nsContentUtils::IsCallerTrustedForRead()) {
+    const nsAdoptingString& override =
+      mozilla::Preferences::GetString("general.appname.override");
+
+    if (override) {
+      aAppName = override;
+      return NS_OK;
+    }
+  }
+
+  aAppName.AssignLiteral("Netscape");
+  return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/dom/base/Navigator.h
@@ -0,0 +1,121 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 sw=2 et tw=80: */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Travis Bogard <travis@netscape.com>
+ *   Dan Rosen <dr@netscape.com>
+ *   Vidur Apparao <vidur@netscape.com>
+ *   Johnny Stenback <jst@netscape.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef mozilla_dom_Navigator_h
+#define mozilla_dom_Navigator_h
+
+#include "nsIDOMNavigator.h"
+#include "nsIDOMNavigatorGeolocation.h"
+#include "nsIDOMNavigatorDesktopNotification.h"
+#include "nsIDOMClientInformation.h"
+#include "nsIDOMNavigatorBattery.h"
+#include "nsAutoPtr.h"
+
+class nsPluginArray;
+class nsMimeTypeArray;
+class nsGeolocation;
+class nsDesktopNotificationCenter;
+class nsIDocShell;
+
+//*****************************************************************************
+// Navigator: Script "navigator" object
+//*****************************************************************************
+
+namespace mozilla {
+namespace dom {
+
+namespace battery {
+class BatteryManager;
+} // namespace battery
+
+class Navigator : public nsIDOMNavigator,
+                  public nsIDOMClientInformation,
+                  public nsIDOMNavigatorGeolocation,
+                  public nsIDOMNavigatorDesktopNotification,
+                  public nsIDOMNavigatorBattery
+{
+public:
+  Navigator(nsIDocShell *aDocShell);
+  virtual ~Navigator();
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIDOMNAVIGATOR
+  NS_DECL_NSIDOMCLIENTINFORMATION
+  NS_DECL_NSIDOMNAVIGATORGEOLOCATION
+  NS_DECL_NSIDOMNAVIGATORDESKTOPNOTIFICATION
+  NS_DECL_NSIDOMNAVIGATORBATTERY
+
+  static void Init();
+
+  void SetDocShell(nsIDocShell *aDocShell);
+  nsIDocShell *GetDocShell()
+  {
+    return mDocShell;
+  }
+
+  void LoadingNewDocument();
+  nsresult RefreshMIMEArray();
+
+  static bool HasDesktopNotificationSupport();
+
+  PRInt64 SizeOf() const;
+
+private:
+  static bool sDoNotTrackEnabled;
+
+  nsRefPtr<nsMimeTypeArray> mMimeTypes;
+  nsRefPtr<nsPluginArray> mPlugins;
+  nsRefPtr<nsGeolocation> mGeolocation;
+  nsRefPtr<nsDesktopNotificationCenter> mNotification;
+  nsRefPtr<battery::BatteryManager> mBatteryManager;
+  nsIDocShell* mDocShell; // weak reference
+};
+
+} // namespace dom
+} // namespace mozilla
+
+nsresult NS_GetNavigatorUserAgent(nsAString& aUserAgent);
+nsresult NS_GetNavigatorPlatform(nsAString& aPlatform);
+nsresult NS_GetNavigatorAppVersion(nsAString& aAppVersion);
+nsresult NS_GetNavigatorAppName(nsAString& aAppName);
+
+#endif // mozilla_dom_Navigator_h
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -465,16 +465,21 @@
 #include "nsIDOMFileException.h"
 #include "nsIDOMFileError.h"
 #include "nsIDOMFormData.h"
 
 #include "nsIDOMDOMStringMap.h"
 
 #include "nsIDOMDesktopNotification.h"
 #include "nsIDOMNavigatorDesktopNotification.h"
+#include "nsIDOMNavigatorGeolocation.h"
+#include "Navigator.h"
+
+#include "nsPluginArray.h"
+#include "nsMimeTypeArray.h"
 
 // Simple gestures include
 #include "nsIDOMSimpleGestureEvent.h"
 #include "nsIDOMMozTouchEvent.h"
 
 #include "nsIEventListenerService.h"
 #include "nsIFrameMessageManager.h"
 #include "mozilla/dom/Element.h"
@@ -501,16 +506,18 @@
 #include "nsIDOMMediaQueryList.h"
 
 #include "nsDOMTouchEvent.h"
 #include "nsIDOMCustomEvent.h"
 
 #include "nsWrapperCacheInlines.h"
 #include "dombindings.h"
 
+#include "nsIDOMBatteryManager.h"
+
 using namespace mozilla;
 using namespace mozilla::dom;
 
 static NS_DEFINE_CID(kDOMSOF_CID, NS_DOM_SCRIPT_OBJECT_FACTORY_CID);
 
 static const char kDOMStringBundleURL[] =
   "chrome://global/locale/dom/dom.properties";
 
@@ -1375,17 +1382,20 @@ static nsDOMClassInfoData sClassInfoData
   NS_DEFINE_CLASSINFO_DATA(GeoPositionCoords, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(GeoPositionAddress, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(GeoPositionError, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
-  
+
+  NS_DEFINE_CLASSINFO_DATA(BatteryManager, nsDOMGenericSH,
+                           DOM_DEFAULT_SCRIPTABLE_FLAGS)
+
   NS_DEFINE_CLASSINFO_DATA(CSSFontFaceRule, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(CSSFontFaceStyleDecl, nsCSSStyleDeclSH,
                            ARRAY_SCRIPTABLE_FLAGS)
 
 #if defined(MOZ_MEDIA)
   NS_DEFINE_CLASSINFO_DATA(HTMLVideoElement, nsElementSH,
                            ELEMENT_SCRIPTABLE_FLAGS)
@@ -2273,18 +2283,19 @@ nsDOMClassInfo::Init()
   DOM_CLASSINFO_MAP_BEGIN(Location, nsIDOMLocation)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMLocation)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(Navigator, nsIDOMNavigator)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMNavigator)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMNavigatorGeolocation)
     DOM_CLASSINFO_MAP_CONDITIONAL_ENTRY(nsIDOMNavigatorDesktopNotification,
-                                        nsNavigator::HasDesktopNotificationSupport())
+                                        Navigator::HasDesktopNotificationSupport())
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMClientInformation)
+    DOM_CLASSINFO_MAP_ENTRY(nsIDOMNavigatorBattery)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(Plugin, nsIDOMPlugin)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMPlugin)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(PluginArray, nsIDOMPluginArray)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMPluginArray)
@@ -3854,16 +3865,21 @@ nsDOMClassInfo::Init()
   DOM_CLASSINFO_MAP_BEGIN(GeoPositionAddress, nsIDOMGeoPositionAddress)
      DOM_CLASSINFO_MAP_ENTRY(nsIDOMGeoPositionAddress)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(GeoPositionError, nsIDOMGeoPositionError)
      DOM_CLASSINFO_MAP_ENTRY(nsIDOMGeoPositionError)
   DOM_CLASSINFO_MAP_END
 
+  DOM_CLASSINFO_MAP_BEGIN(BatteryManager, nsIDOMBatteryManager)
+     DOM_CLASSINFO_MAP_ENTRY(nsIDOMBatteryManager)
+     DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget)
+  DOM_CLASSINFO_MAP_END
+
   DOM_CLASSINFO_MAP_BEGIN(CSSFontFaceRule, nsIDOMCSSFontFaceRule)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMCSSFontFaceRule)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN_NO_CLASS_IF(CSSFontFaceStyleDecl,
                                       nsIDOMCSSStyleDeclaration)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMCSSStyleDeclaration)
   DOM_CLASSINFO_MAP_END
@@ -7126,17 +7142,17 @@ nsNavigatorSH::PreCreate(nsISupports *na
   nsCOMPtr<nsIDOMNavigator> safeNav(do_QueryInterface(nativeObj));
   if (!safeNav) {
     // Oops, this wasn't really a navigator object. This can happen if someone
     // tries to use our scriptable helper as a real object and tries to wrap
     // it, see bug 319296.
     return NS_OK;
   }
 
-  nsNavigator *nav = (nsNavigator *)safeNav.get();
+  Navigator *nav = static_cast<Navigator*>(safeNav.get());
   nsIDocShell *ds = nav->GetDocShell();
   if (!ds) {
     NS_WARNING("Refusing to create a navigator in the wrong scope");
     return NS_ERROR_UNEXPECTED;
   }
 
   nsCOMPtr<nsIScriptGlobalObject> sgo = do_GetInterface(ds);
 
--- a/dom/base/nsDOMClassInfoClasses.h
+++ b/dom/base/nsDOMClassInfoClasses.h
@@ -423,16 +423,18 @@ DOMCI_CLASS(MessageEvent)
 
 // Geolocation
 DOMCI_CLASS(GeoGeolocation)
 DOMCI_CLASS(GeoPosition)
 DOMCI_CLASS(GeoPositionCoords)
 DOMCI_CLASS(GeoPositionAddress)
 DOMCI_CLASS(GeoPositionError)
 
+DOMCI_CLASS(BatteryManager)
+
 // @font-face in CSS
 DOMCI_CLASS(CSSFontFaceRule)
 DOMCI_CLASS(CSSFontFaceStyleDecl)
 
 #if defined(MOZ_MEDIA)
 // WhatWG Video Element
 DOMCI_CLASS(HTMLVideoElement)
 DOMCI_CLASS(HTMLSourceElement)
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -49,16 +49,17 @@
 
 #include "base/basictypes.h"
 
 /* This must occur *after* base/basictypes.h to avoid typedefs conflicts. */
 #include "mozilla/Util.h"
 
 // Local Includes
 #include "nsGlobalWindow.h"
+#include "Navigator.h"
 #include "nsScreen.h"
 #include "nsHistory.h"
 #include "nsPerformance.h"
 #include "nsDOMNavigationTiming.h"
 #include "nsBarProps.h"
 #include "nsDOMStorage.h"
 #include "nsDOMOfflineResourceList.h"
 #include "nsDOMError.h"
@@ -272,17 +273,16 @@ static PRInt32              gRefCnt     
 static PRInt32              gOpenPopupSpamCount        = 0;
 static PopupControlState    gPopupControlState         = openAbused;
 static PRInt32              gRunningTimeoutDepth       = 0;
 static bool                 gMouseDown                 = false;
 static bool                 gDragServiceDisabled       = false;
 static FILE                *gDumpFile                  = nsnull;
 static PRUint64             gNextWindowID              = 0;
 static PRUint32             gSerialCounter             = 0;
-static bool                 gDoNotTrackEnabled         = false;
 
 #ifdef DEBUG_jst
 PRInt32 gTimeoutCnt                                    = 0;
 #endif
 
 #if !(defined(NS_DEBUG) || defined(MOZ_ENABLE_JS_DUMP))
 static bool                 gDOMWindowDumpEnabled      = false;
 #endif
@@ -957,20 +957,16 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalW
 /* static */
 void
 nsGlobalWindow::Init()
 {
   CallGetService(NS_ENTROPYCOLLECTOR_CONTRACTID, &gEntropyCollector);
   NS_ASSERTION(gEntropyCollector,
                "gEntropyCollector should have been initialized!");
 
-  mozilla::Preferences::AddBoolVarCache(&gDoNotTrackEnabled,
-                                        "privacy.donottrackheader.enabled",
-                                        false);
-
 #ifdef PR_LOGGING
   gDOMLeakPRLog = PR_NewLogModule("DOMLeak");
   NS_ASSERTION(gDOMLeakPRLog, "gDOMLeakPRLog should have been initialized!");
 #endif
 
   sWindowsById = new WindowByIdTable();
   // There are two reasons to have Init() failing: if we were not able to
   // alloc the memory or if the size we want to init is too high. None of them
@@ -1732,25 +1728,25 @@ nsGlobalWindow::GetPopupControlState() c
 class WindowStateHolder : public nsISupports
 {
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(WINDOWSTATEHOLDER_IID)
   NS_DECL_ISUPPORTS
 
   WindowStateHolder(nsGlobalWindow *aWindow,
                     nsIXPConnectJSObjectHolder *aHolder,
-                    nsNavigator *aNavigator,
+                    Navigator *aNavigator,
                     nsIXPConnectJSObjectHolder *aOuterProto,
                     nsIXPConnectJSObjectHolder *aOuterRealProto);
 
   nsGlobalWindow* GetInnerWindow() { return mInnerWindow; }
   nsIXPConnectJSObjectHolder *GetInnerWindowHolder()
   { return mInnerWindowHolder; }
 
-  nsNavigator* GetNavigator() { return mNavigator; }
+  Navigator* GetNavigator() { return mNavigator; }
   nsIXPConnectJSObjectHolder* GetOuterProto() { return mOuterProto; }
   nsIXPConnectJSObjectHolder* GetOuterRealProto() { return mOuterRealProto; }
 
   void DidRestoreWindow()
   {
     mInnerWindow = nsnull;
 
     mInnerWindowHolder = nsnull;
@@ -1761,26 +1757,26 @@ public:
 
 protected:
   ~WindowStateHolder();
 
   nsGlobalWindow *mInnerWindow;
   // We hold onto this to make sure the inner window doesn't go away. The outer
   // window ends up recalculating it anyway.
   nsCOMPtr<nsIXPConnectJSObjectHolder> mInnerWindowHolder;
-  nsRefPtr<nsNavigator> mNavigator;
+  nsRefPtr<Navigator> mNavigator;
   nsCOMPtr<nsIXPConnectJSObjectHolder> mOuterProto;
   nsCOMPtr<nsIXPConnectJSObjectHolder> mOuterRealProto;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(WindowStateHolder, WINDOWSTATEHOLDER_IID)
 
 WindowStateHolder::WindowStateHolder(nsGlobalWindow *aWindow,
                                      nsIXPConnectJSObjectHolder *aHolder,
-                                     nsNavigator *aNavigator,
+                                     Navigator *aNavigator,
                                      nsIXPConnectJSObjectHolder *aOuterProto,
                                      nsIXPConnectJSObjectHolder *aOuterRealProto)
   : mInnerWindow(aWindow),
     mNavigator(aNavigator),
     mOuterProto(aOuterProto),
     mOuterRealProto(aOuterRealProto)
 {
   NS_PRECONDITION(aWindow, "null window");
@@ -3004,20 +3000,17 @@ nsGlobalWindow::GetSelf(nsIDOMWindow** a
 NS_IMETHODIMP
 nsGlobalWindow::GetNavigator(nsIDOMNavigator** aNavigator)
 {
   FORWARD_TO_OUTER(GetNavigator, (aNavigator), NS_ERROR_NOT_INITIALIZED);
 
   *aNavigator = nsnull;
 
   if (!mNavigator) {
-    mNavigator = new nsNavigator(mDocShell);
-    if (!mNavigator) {
-      return NS_ERROR_OUT_OF_MEMORY;
-    }
+    mNavigator = new Navigator(mDocShell);
   }
 
   NS_ADDREF(*aNavigator = mNavigator);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -9361,35 +9354,37 @@ nsGlobalWindow::RunTimeout(nsTimeout *aT
       // Compute time to next timeout for interval timer.
       // Make sure nextInterval is at least DOMMinTimeoutValue().
       TimeDuration nextInterval =
         TimeDuration::FromMilliseconds(NS_MAX(timeout->mInterval,
                                               PRUint32(DOMMinTimeoutValue())));
 
       // If we're running pending timeouts because they've been temporarily
       // disabled (!aTimeout), set the next interval to be relative to "now",
-      // and not to when the timeout that was pending should have fired.  Also
-      // check if the next interval timeout is overdue.  If so, then restart
-      // the interval from now.
+      // and not to when the timeout that was pending should have fired.
       TimeStamp firingTime;
-      if (!aTimeout || timeout->mWhen + nextInterval <= now)
+      if (!aTimeout)
         firingTime = now + nextInterval;
       else
         firingTime = timeout->mWhen + nextInterval;
 
-      TimeDuration delay = firingTime - TimeStamp::Now();
+      TimeStamp currentNow = TimeStamp::Now();
+      TimeDuration delay = firingTime - currentNow;
 
       // And make sure delay is nonnegative; that might happen if the timer
-      // thread is firing our timers somewhat early.
+      // thread is firing our timers somewhat early or if they're taking a long
+      // time to run the callback.
       if (delay < TimeDuration(0)) {
         delay = TimeDuration(0);
       }
 
       if (timeout->mTimer) {
-        timeout->mWhen = firingTime;
+        timeout->mWhen = currentNow + delay; // firingTime unless delay got
+                                             // clamped, in which case it's
+                                             // currentNow.
 
         // Reschedule the OS timer. Don't bother returning any error
         // codes if this fails since the callers of this method
         // doesn't care about them nobody who cares about them
         // anyways.
 
         // Make sure to cast the unsigned PR_USEC_PER_MSEC to signed
         // PRTime to make the division do the right thing on 64-bit
@@ -10699,758 +10694,16 @@ NS_NewScriptGlobalObject(bool aIsChrome,
 
   NS_ENSURE_TRUE(global, NS_ERROR_OUT_OF_MEMORY);
 
   NS_ADDREF(*aResult = global);
 
   return NS_OK;
 }
 
-//*****************************************************************************
-//***    nsNavigator: Object Management
-//*****************************************************************************
-
-nsNavigator::nsNavigator(nsIDocShell *aDocShell)
-  : mDocShell(aDocShell)
-{
-}
-
-nsNavigator::~nsNavigator()
-{
-  if (mMimeTypes)
-    mMimeTypes->Invalidate();
-  if (mPlugins)
-    mPlugins->Invalidate();
-}
-
-//*****************************************************************************
-//    nsNavigator::nsISupports
-//*****************************************************************************
-
-
-DOMCI_DATA(Navigator, nsNavigator)
-
-// QueryInterface implementation for nsNavigator
-NS_INTERFACE_MAP_BEGIN(nsNavigator)
-  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMNavigator)
-  NS_INTERFACE_MAP_ENTRY(nsIDOMNavigator)
-  NS_INTERFACE_MAP_ENTRY(nsIDOMClientInformation)
-  NS_INTERFACE_MAP_ENTRY(nsIDOMNavigatorGeolocation)
-  NS_INTERFACE_MAP_ENTRY(nsIDOMNavigatorDesktopNotification)
-  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(Navigator)
-NS_INTERFACE_MAP_END
-
-
-NS_IMPL_ADDREF(nsNavigator)
-NS_IMPL_RELEASE(nsNavigator)
-
-
-void
-nsNavigator::SetDocShell(nsIDocShell *aDocShell)
-{
-  mDocShell = aDocShell;
-  if (mPlugins)
-    mPlugins->SetDocShell(aDocShell);
-
-  // if there is a page transition, make sure delete the geolocation object
-  if (mGeolocation)
-  {
-    mGeolocation->Shutdown();
-    mGeolocation = nsnull;
-  }
-
-  if (mNotification)
-  {
-    mNotification->Shutdown();
-    mNotification = nsnull;
-  }
-}
-
-//*****************************************************************************
-//    nsNavigator::nsIDOMNavigator
-//*****************************************************************************
-
-nsresult
-NS_GetNavigatorUserAgent(nsAString& aUserAgent)
-{
-  nsresult rv;
-  nsCOMPtr<nsIHttpProtocolHandler>
-    service(do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &rv));
-  if (NS_SUCCEEDED(rv)) {
-    nsCAutoString ua;
-    rv = service->GetUserAgent(ua);
-    CopyASCIItoUTF16(ua, aUserAgent);
-  }
-
-  return rv;
-}
-
-nsresult
-NS_GetNavigatorPlatform(nsAString& aPlatform)
-{
-  if (!nsContentUtils::IsCallerTrustedForRead()) {
-    const nsAdoptingString& override =
-      Preferences::GetString("general.platform.override");
-
-    if (override) {
-      aPlatform = override;
-      return NS_OK;
-    }
-  }
-
-  nsresult rv;
-  nsCOMPtr<nsIHttpProtocolHandler>
-    service(do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &rv));
-  if (NS_SUCCEEDED(rv)) {
-    // sorry for the #if platform ugliness, but Communicator is
-    // likewise hardcoded and we're seeking backward compatibility
-    // here (bug 47080)
-#if defined(_WIN64)
-    aPlatform.AssignLiteral("Win64");
-#elif defined(WIN32)
-    aPlatform.AssignLiteral("Win32");
-#elif defined(XP_MACOSX) && defined(__ppc__)
-    aPlatform.AssignLiteral("MacPPC");
-#elif defined(XP_MACOSX) && defined(__i386__)
-    aPlatform.AssignLiteral("MacIntel");
-#elif defined(XP_MACOSX) && defined(__x86_64__)
-    aPlatform.AssignLiteral("MacIntel");
-#elif defined(XP_OS2)
-    aPlatform.AssignLiteral("OS/2");
-#else
-    // XXX Communicator uses compiled-in build-time string defines
-    // to indicate the platform it was compiled *for*, not what it is
-    // currently running *on* which is what this does.
-    nsCAutoString plat;
-    rv = service->GetOscpu(plat);
-    CopyASCIItoUTF16(plat, aPlatform);
-#endif
-  }
-
-  return rv;
-}
-nsresult
-NS_GetNavigatorAppVersion(nsAString& aAppVersion)
-{
-  if (!nsContentUtils::IsCallerTrustedForRead()) {
-    const nsAdoptingString& override =
-      Preferences::GetString("general.appversion.override");
-
-    if (override) {
-      aAppVersion = override;
-      return NS_OK;
-    }
-  }
-
-  nsresult rv;
-  nsCOMPtr<nsIHttpProtocolHandler>
-    service(do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &rv));
-  if (NS_SUCCEEDED(rv)) {
-    nsCAutoString str;
-    rv = service->GetAppVersion(str);
-    CopyASCIItoUTF16(str, aAppVersion);
-    if (NS_FAILED(rv))
-      return rv;
-
-    aAppVersion.AppendLiteral(" (");
-
-    rv = service->GetPlatform(str);
-    if (NS_FAILED(rv))
-      return rv;
-
-    AppendASCIItoUTF16(str, aAppVersion);
-
-    aAppVersion.Append(PRUnichar(')'));
-  }
-
-  return rv;
-}
-
-nsresult
-NS_GetNavigatorAppName(nsAString& aAppName)
-{
-  if (!nsContentUtils::IsCallerTrustedForRead()) {
-    const nsAdoptingString& override =
-      Preferences::GetString("general.appname.override");
-
-    if (override) {
-      aAppName = override;
-      return NS_OK;
-    }
-  }
-
-  aAppName.AssignLiteral("Netscape");
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsNavigator::GetUserAgent(nsAString& aUserAgent)
-{
-  return NS_GetNavigatorUserAgent(aUserAgent);
-}
-
-NS_IMETHODIMP
-nsNavigator::GetAppCodeName(nsAString& aAppCodeName)
-{
-  nsresult rv;
-  nsCOMPtr<nsIHttpProtocolHandler>
-    service(do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &rv));
-  if (NS_SUCCEEDED(rv)) {
-    nsCAutoString appName;
-    rv = service->GetAppName(appName);
-    CopyASCIItoUTF16(appName, aAppCodeName);
-  }
-
-  return rv;
-}
-
-NS_IMETHODIMP
-nsNavigator::GetAppVersion(nsAString& aAppVersion)
-{
-  return NS_GetNavigatorAppVersion(aAppVersion);
-}
-
-NS_IMETHODIMP
-nsNavigator::GetAppName(nsAString& aAppName)
-{
-  return NS_GetNavigatorAppName(aAppName);
-}
-
-/**
- * JS property navigator.language, exposed to web content.
- * Take first value from Accept-Languages (HTTP header), which is
- * the "content language" freely set by the user in the Pref window.
- *
- * Do not use UI language (chosen app locale) here.
- * See RFC 2616, Section 15.1.4 "Privacy Issues Connected to Accept Headers"
- *
- * "en", "en-US" and "i-cherokee" and "" are valid.
- * Fallback in case of invalid pref should be "" (empty string), to
- * let site do fallback, e.g. to site's local language.
- */
-NS_IMETHODIMP
-nsNavigator::GetLanguage(nsAString& aLanguage)
-{
-  // e.g. "de-de, en-us,en"
-  const nsAdoptingString& acceptLang =
-    Preferences::GetLocalizedString("intl.accept_languages");
-  // take everything before the first "," or ";", without trailing space
-  nsCharSeparatedTokenizer langTokenizer(acceptLang, ',');
-  const nsSubstring &firstLangPart = langTokenizer.nextToken();
-  nsCharSeparatedTokenizer qTokenizer(firstLangPart, ';');
-  aLanguage.Assign(qTokenizer.nextToken());
-
-  // checks and fixups
-  // replace "_" with "-", to avoid POSIX/Windows "en_US" notation
-  if (aLanguage.Length() > 2 && aLanguage[2] == PRUnichar('_'))
-    aLanguage.Replace(2, 1, PRUnichar('-')); // TODO replace all
-  // use uppercase for country part, e.g. "en-US", not "en-us", see BCP47
-  // only uppercase 2-letter country codes, not "zh-Hant", "de-DE-x-goethe"
-  if (aLanguage.Length() > 2)
-  {
-    nsCharSeparatedTokenizer localeTokenizer(aLanguage, '-');
-    PRInt32 pos = 0;
-    bool first = true;
-    while (localeTokenizer.hasMoreTokens())
-    {
-      const nsSubstring &code = localeTokenizer.nextToken();
-      if (code.Length() == 2 && !first)
-      {
-        nsAutoString upper(code);
-        ::ToUpperCase(upper);
-        aLanguage.Replace(pos, code.Length(), upper);
-      }
-      pos += code.Length() + 1; // 1 is the separator
-      if (first)
-        first = false;
-    }
-  }
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsNavigator::GetPlatform(nsAString& aPlatform)
-{
-  return NS_GetNavigatorPlatform(aPlatform);
-}
-
-NS_IMETHODIMP
-nsNavigator::GetOscpu(nsAString& aOSCPU)
-{
-  if (!nsContentUtils::IsCallerTrustedForRead()) {
-    const nsAdoptingString& override =
-      Preferences::GetString("general.oscpu.override");
-
-    if (override) {
-      aOSCPU = override;
-      return NS_OK;
-    }
-  }
-
-  nsresult rv;
-  nsCOMPtr<nsIHttpProtocolHandler>
-    service(do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &rv));
-  if (NS_SUCCEEDED(rv)) {
-    nsCAutoString oscpu;
-    rv = service->GetOscpu(oscpu);
-    CopyASCIItoUTF16(oscpu, aOSCPU);
-  }
-
-  return rv;
-}
-
-NS_IMETHODIMP
-nsNavigator::GetVendor(nsAString& aVendor)
-{
-  aVendor.Truncate();
-  return NS_OK;
-}
-
-
-NS_IMETHODIMP
-nsNavigator::GetVendorSub(nsAString& aVendorSub)
-{
-  aVendorSub.Truncate();
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsNavigator::GetProduct(nsAString& aProduct)
-{
-  nsresult rv;
-  nsCOMPtr<nsIHttpProtocolHandler>
-    service(do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &rv));
-  if (NS_SUCCEEDED(rv)) {
-    nsCAutoString product;
-    rv = service->GetProduct(product);
-    CopyASCIItoUTF16(product, aProduct);
-  }
-
-  return rv;
-}
-
-NS_IMETHODIMP
-nsNavigator::GetProductSub(nsAString& aProductSub)
-{
-  if (!nsContentUtils::IsCallerTrustedForRead()) {
-    const nsAdoptingString& override =
-      Preferences::GetString("general.productSub.override");
-
-    if (override) {
-      aProductSub = override;
-      return NS_OK;
-    }
-
-    // 'general.useragent.productSub' backwards compatible with 1.8 branch.
-    const nsAdoptingString& override2 =
-      Preferences::GetString("general.useragent.productSub");
-
-    if (override2) {
-      aProductSub = override2;
-      return NS_OK;
-    }
-  }
-
-  nsresult rv;
-  nsCOMPtr<nsIHttpProtocolHandler>
-    service(do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &rv));
-  if (NS_SUCCEEDED(rv)) {
-    nsCAutoString productSub;
-    rv = service->GetProductSub(productSub);
-    CopyASCIItoUTF16(productSub, aProductSub);
-  }
-
-  return rv;
-}
-
-NS_IMETHODIMP
-nsNavigator::GetMimeTypes(nsIDOMMimeTypeArray **aMimeTypes)
-{
-  if (!mMimeTypes) {
-    mMimeTypes = new nsMimeTypeArray(this);
-    if (!mMimeTypes) {
-      return NS_ERROR_OUT_OF_MEMORY;
-    }
-  }
-
-  NS_ADDREF(*aMimeTypes = mMimeTypes);
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsNavigator::GetPlugins(nsIDOMPluginArray **aPlugins)
-{
-  if (!mPlugins) {
-    mPlugins = new nsPluginArray(this, mDocShell);
-    if (!mPlugins) {
-      return NS_ERROR_OUT_OF_MEMORY;
-    }
-  }
-
-  NS_ADDREF(*aPlugins = mPlugins);
-
-  return NS_OK;
-}
-
-// values for the network.cookie.cookieBehavior pref are documented in
-// nsCookieService.cpp.
-#define COOKIE_BEHAVIOR_REJECT 2
-
-NS_IMETHODIMP
-nsNavigator::GetCookieEnabled(bool *aCookieEnabled)
-{
-  *aCookieEnabled =
-    (Preferences::GetInt("network.cookie.cookieBehavior",
-                         COOKIE_BEHAVIOR_REJECT) != COOKIE_BEHAVIOR_REJECT);
-
-  // Check whether an exception overrides the global cookie behavior
-  // Note that the code for getting the URI here matches that in
-  // nsHTMLDocument::SetCookie.
-  nsCOMPtr<nsIDocument> doc = do_GetInterface(mDocShell);
-  if (!doc) {
-    return NS_OK;
-  }
-
-  nsCOMPtr<nsIURI> codebaseURI;
-  doc->NodePrincipal()->GetURI(getter_AddRefs(codebaseURI));
-
-  if (!codebaseURI) {
-    // Not a codebase, so technically can't set cookies, but let's
-    // just return the default value.
-    return NS_OK;
-  }
-  
-  nsCOMPtr<nsICookiePermission> permMgr =
-    do_GetService(NS_COOKIEPERMISSION_CONTRACTID);
-  NS_ENSURE_TRUE(permMgr, NS_OK);
-
-  // Pass null for the channel, just like the cookie service does
-  nsCookieAccess access;
-  nsresult rv = permMgr->CanAccess(codebaseURI, nsnull, &access);
-  NS_ENSURE_SUCCESS(rv, NS_OK);
-
-  if (access != nsICookiePermission::ACCESS_DEFAULT) {
-    *aCookieEnabled = access != nsICookiePermission::ACCESS_DENY;
-  }
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsNavigator::GetOnLine(bool* aOnline)
-{
-  NS_PRECONDITION(aOnline, "Null out param");
-  
-  *aOnline = !NS_IsOffline();
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsNavigator::GetBuildID(nsAString& aBuildID)
-{
-  if (!nsContentUtils::IsCallerTrustedForRead()) {
-    const nsAdoptingString& override =
-      Preferences::GetString("general.buildID.override");
-
-    if (override) {
-      aBuildID = override;
-      return NS_OK;
-    }
-  }
-
-  nsCOMPtr<nsIXULAppInfo> appInfo =
-    do_GetService("@mozilla.org/xre/app-info;1");
-  if (!appInfo)
-    return NS_ERROR_NOT_IMPLEMENTED;
-
-  nsCAutoString buildID;
-  nsresult rv = appInfo->GetAppBuildID(buildID);
-  if (NS_FAILED(rv))
-    return rv;
-
-  aBuildID.Truncate();
-  AppendASCIItoUTF16(buildID, aBuildID);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsNavigator::GetDoNotTrack(nsAString &aResult)
-{
-  if (gDoNotTrackEnabled) {
-    aResult.AssignLiteral("yes");
-  }
-  else {
-    aResult.AssignLiteral("unspecified");
-  }
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsNavigator::JavaEnabled(bool *aReturn)
-{
-  Telemetry::AutoTimer<Telemetry::CHECK_JAVA_ENABLED> telemetryTimer;
-  // Return true if we have a handler for "application/x-java-vm",
-  // otherwise return false.
-  *aReturn = false;
-
-  if (!mMimeTypes) {
-    mMimeTypes = new nsMimeTypeArray(this);
-    if (!mMimeTypes)
-      return NS_ERROR_OUT_OF_MEMORY;
-  }
-
-  RefreshMIMEArray();
-
-  PRUint32 count;
-  mMimeTypes->GetLength(&count);
-  for (PRUint32 i = 0; i < count; i++) {
-    nsresult rv;
-    nsIDOMMimeType* type = mMimeTypes->GetItemAt(i, &rv);
-    nsAutoString mimeString;
-    if (type && NS_SUCCEEDED(type->GetType(mimeString))) {
-      if (mimeString.EqualsLiteral("application/x-java-vm")) {
-        *aReturn = true;
-        break;
-      }
-    }
-  }
-
-  return NS_OK;
-}
-
-void
-nsNavigator::LoadingNewDocument()
-{
-  // Release these so that they will be recreated for the
-  // new document (if requested).  The plugins or mime types
-  // arrays may have changed.  See bug 150087.
-  if (mMimeTypes) {
-    mMimeTypes->Invalidate();
-    mMimeTypes = nsnull;
-  }
-
-  if (mPlugins) {
-    mPlugins->Invalidate();
-    mPlugins = nsnull;
-  }
-
-  if (mGeolocation)
-  {
-    mGeolocation->Shutdown();
-    mGeolocation = nsnull;
-  }
-
-  if (mNotification)
-  {
-    mNotification->Shutdown();
-    mNotification = nsnull;
-  }
-
-}
-
-nsresult
-nsNavigator::RefreshMIMEArray()
-{
-  nsresult rv = NS_OK;
-  if (mMimeTypes)
-    rv = mMimeTypes->Refresh();
-  return rv;
-}
-
-bool
-nsNavigator::HasDesktopNotificationSupport()
-{
-  return Preferences::GetBool("notification.feature.enabled", false);
-}
-
-//*****************************************************************************
-//    nsNavigator::nsIDOMClientInformation
-//*****************************************************************************
-
-NS_IMETHODIMP
-nsNavigator::RegisterContentHandler(const nsAString& aMIMEType, 
-                                    const nsAString& aURI, 
-                                    const nsAString& aTitle)
-{
-  nsCOMPtr<nsIWebContentHandlerRegistrar> registrar = 
-    do_GetService(NS_WEBCONTENTHANDLERREGISTRAR_CONTRACTID);
-  if (registrar && mDocShell) {
-    nsCOMPtr<nsIDOMWindow> contentDOMWindow(do_GetInterface(mDocShell));
-    if (contentDOMWindow)
-      return registrar->RegisterContentHandler(aMIMEType, aURI, aTitle,
-                                               contentDOMWindow);
-  }
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsNavigator::RegisterProtocolHandler(const nsAString& aProtocol, 
-                                     const nsAString& aURI, 
-                                     const nsAString& aTitle)
-{
-  nsCOMPtr<nsIWebContentHandlerRegistrar> registrar = 
-    do_GetService(NS_WEBCONTENTHANDLERREGISTRAR_CONTRACTID);
-  if (registrar && mDocShell) {
-    nsCOMPtr<nsIDOMWindow> contentDOMWindow(do_GetInterface(mDocShell));
-    if (contentDOMWindow)
-      return registrar->RegisterProtocolHandler(aProtocol, aURI, aTitle,
-                                                contentDOMWindow);
-  }
-
-  return NS_OK;
-}
-
-
-NS_IMETHODIMP
-nsNavigator::MozIsLocallyAvailable(const nsAString &aURI,
-                                   bool aWhenOffline,
-                                   bool *aIsAvailable)
-{
-  nsCOMPtr<nsIURI> uri;
-  nsresult rv = NS_NewURI(getter_AddRefs(uri), aURI);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // This method of checking the cache will only work for http/https urls
-  bool match;
-  rv = uri->SchemeIs("http", &match);
-  NS_ENSURE_SUCCESS(rv, rv);
-  if (!match) {
-    rv = uri->SchemeIs("https", &match);
-    NS_ENSURE_SUCCESS(rv, rv);
-    if (!match) return NS_ERROR_DOM_BAD_URI;
-  }
-
-  // Same origin check
-  nsCOMPtr<nsIJSContextStack> stack = do_GetService(sJSStackContractID);
-  NS_ENSURE_TRUE(stack, NS_ERROR_FAILURE);
-
-  JSContext *cx = nsnull;
-  rv = stack->Peek(&cx);
-  NS_ENSURE_TRUE(cx, NS_ERROR_FAILURE);
-
-  rv = nsContentUtils::GetSecurityManager()->CheckSameOrigin(cx, uri);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // these load flags cause an error to be thrown if there is no
-  // valid cache entry, and skip the load if there is.
-  // if the cache is busy, assume that it is not yet available rather
-  // than waiting for it to become available.
-  PRUint32 loadFlags = nsIChannel::INHIBIT_CACHING |
-                       nsICachingChannel::LOAD_NO_NETWORK_IO |
-                       nsICachingChannel::LOAD_ONLY_IF_MODIFIED |
-                       nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE_IF_BUSY;
-
-  if (aWhenOffline) {
-    loadFlags |= nsICachingChannel::LOAD_CHECK_OFFLINE_CACHE |
-                 nsICachingChannel::LOAD_ONLY_FROM_CACHE |
-                 nsIRequest::LOAD_FROM_CACHE;
-  }
-
-  nsCOMPtr<nsIChannel> channel;
-  rv = NS_NewChannel(getter_AddRefs(channel), uri,
-                     nsnull, nsnull, nsnull, loadFlags);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsCOMPtr<nsIInputStream> stream;
-  rv = channel->Open(getter_AddRefs(stream));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  stream->Close();
-
-  nsresult status;
-  rv = channel->GetStatus(&status);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  if (NS_SUCCEEDED(status)) {
-    nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel);
-    rv = httpChannel->GetRequestSucceeded(aIsAvailable);
-    NS_ENSURE_SUCCESS(rv, rv);
-  } else {
-    *aIsAvailable = false;
-  }
-
-  return NS_OK;
-}
-
-//*****************************************************************************
-//    nsNavigator::nsIDOMNavigatorGeolocation
-//*****************************************************************************
-
-NS_IMETHODIMP nsNavigator::GetGeolocation(nsIDOMGeoGeolocation **_retval)
-{
-  NS_ENSURE_ARG_POINTER(_retval);
-  *_retval = nsnull;
-
-  if (!Preferences::GetBool("geo.enabled", true))
-    return NS_OK;
-
-  if (mGeolocation) {
-    NS_ADDREF(*_retval = mGeolocation);
-    return NS_OK;
-  }
-
-  if (!mDocShell)
-    return NS_ERROR_FAILURE;
-
-  nsCOMPtr<nsIDOMWindow> contentDOMWindow(do_GetInterface(mDocShell));
-  if (!contentDOMWindow)
-    return NS_ERROR_FAILURE;
-    
-  mGeolocation = new nsGeolocation();
-  if (!mGeolocation)
-    return NS_ERROR_FAILURE;
-  
-  if (NS_FAILED(mGeolocation->Init(contentDOMWindow))) {
-    mGeolocation = nsnull;
-    return NS_ERROR_FAILURE;
-  }
-
-  NS_ADDREF(*_retval = mGeolocation);    
-  return NS_OK; 
-}
-
-
-//*****************************************************************************
-//    nsNavigator::nsIDOMNavigatorDesktopNotification
-//*****************************************************************************
-
-NS_IMETHODIMP nsNavigator::GetMozNotification(nsIDOMDesktopNotificationCenter **aRetVal)
-{
-  NS_ENSURE_ARG_POINTER(aRetVal);
-  *aRetVal = nsnull;
-
-  if (mNotification) {
-    NS_ADDREF(*aRetVal = mNotification);
-    return NS_OK;
-  }
-
-  nsCOMPtr<nsPIDOMWindow> window(do_GetInterface(mDocShell));
-  NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
-    
-  nsCOMPtr<nsIDocument> document = do_GetInterface(mDocShell);
-  NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
-
-  nsIScriptGlobalObject *sgo = document->GetScopeObject();
-  NS_ENSURE_TRUE(sgo, NS_ERROR_FAILURE);
-
-  nsIScriptContext *scx = sgo->GetContext();
-  NS_ENSURE_TRUE(scx, NS_ERROR_FAILURE);
-
-  mNotification = new nsDesktopNotificationCenter(window->GetCurrentInnerWindow(),
-                                                  scx);
-  if (!mNotification) {
-    return NS_ERROR_FAILURE;
-  }
-
-  NS_ADDREF(*aRetVal = mNotification);    
-  return NS_OK; 
-}
-
 #define EVENT(name_, id_, type_, struct_)                                    \
   NS_IMETHODIMP nsGlobalWindow::GetOn##name_(JSContext *cx,                  \
                                              jsval *vp) {                    \
     nsEventListenerManager *elm = GetListenerManager(false);              \
     if (elm) {                                                               \
       elm->GetJSEventListener(nsGkAtoms::on##name_, vp);                     \
     } else {                                                                 \
       *vp = JSVAL_NULL;                                                      \
@@ -11472,25 +10725,8 @@ NS_IMETHODIMP nsNavigator::GetMozNotific
   }
 #define WINDOW_ONLY_EVENT EVENT
 #define TOUCH_EVENT EVENT
 #include "nsEventNameList.h"
 #undef TOUCH_EVENT
 #undef WINDOW_ONLY_EVENT
 #undef EVENT
 
-PRInt64
-nsNavigator::SizeOf() const
-{
-  PRInt64 size = sizeof(*this);
-
-  // TODO: add SizeOf() to nsMimeTypeArray, bug 674113.
-  size += mMimeTypes ? sizeof(*mMimeTypes.get()) : 0;
-  // TODO: add SizeOf() to nsPluginArray, bug 674114.
-  size += mPlugins ? sizeof(*mPlugins.get()) : 0;
-  // TODO: add SizeOf() to nsGeolocation, bug 674115.
-  size += mGeolocation ? sizeof(*mGeolocation.get()) : 0;
-  // TODO: add SizeOf() to nsDesktopNotificationCenter, bug 674116.
-  size += mNotification ? sizeof(*mNotification.get()) : 0;
-
-  return size;
-}
-
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -56,21 +56,17 @@
 #include "nsDOMScriptObjectHolder.h"
 
 // Interfaces Needed
 #include "nsDOMWindowList.h"
 #include "nsIBaseWindow.h"
 #include "nsIBrowserDOMWindow.h"
 #include "nsIDocShellTreeOwner.h"
 #include "nsIDocShellTreeItem.h"
-#include "nsIDOMClientInformation.h"
 #include "nsIDOMEventTarget.h"
-#include "nsIDOMNavigator.h"
-#include "nsIDOMNavigatorGeolocation.h"
-#include "nsIDOMNavigatorDesktopNotification.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIDOMJSWindow.h"
 #include "nsIDOMChromeWindow.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIScriptContext.h"
 #include "nsIScriptObjectPrincipal.h"
 #include "nsIScriptTimeoutHandler.h"
@@ -80,18 +76,16 @@
 #include "nsIDOMModalContentWindow.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsEventListenerManager.h"
 #include "nsIDOMDocument.h"
 #ifndef MOZ_DISABLE_DOMCRYPTO
 #include "nsIDOMCrypto.h"
 #endif
 #include "nsIPrincipal.h"
-#include "nsPluginArray.h"
-#include "nsMimeTypeArray.h"
 #include "nsIXPCScriptable.h"
 #include "nsPoint.h"
 #include "nsSize.h"
 #include "nsRect.h"
 #include "mozFlushType.h"
 #include "prclist.h"
 #include "nsIDOMStorageObsolete.h"
 #include "nsIDOMStorageList.h"
@@ -125,37 +119,40 @@ class nsIDOMBarProp;
 class nsIDocument;
 class nsPresContext;
 class nsIDOMEvent;
 class nsIScrollableFrame;
 class nsIControllers;
 
 class nsBarProp;
 class nsLocation;
-class nsNavigator;
 class nsScreen;
 class nsHistory;
 class nsPerformance;
 class nsIDocShellLoadInfo;
 class WindowStateHolder;
 class nsGlobalWindowObserver;
 class nsGlobalWindow;
 class nsDummyJavaPluginOwner;
 class PostMessageEvent;
 class nsRunnable;
 
 class nsDOMOfflineResourceList;
-class nsGeolocation;
-class nsDesktopNotificationCenter;
 class nsDOMMozURLProperty;
 
 #ifdef MOZ_DISABLE_DOMCRYPTO
 class nsIDOMCrypto;
 #endif
 
+namespace mozilla {
+namespace dom {
+class Navigator;
+} // namespace dom
+} // namespace mozilla
+
 extern nsresult
 NS_CreateJSTimeoutHandler(nsGlobalWindow *aWindow,
                           bool *aIsInterval,
                           PRInt32 *aInterval,
                           nsIScriptTimeoutHandler **aRet);
 
 /*
  * Timeout struct that holds information about each script
@@ -285,16 +282,17 @@ class nsGlobalWindow : public nsPIDOMWin
                        public nsITouchEventReceiver,
                        public nsIInlineEventHandlers
 {
 public:
   friend class nsDOMMozURLProperty;
 
   typedef mozilla::TimeStamp TimeStamp;
   typedef mozilla::TimeDuration TimeDuration;
+  typedef mozilla::dom::Navigator Navigator;
   typedef nsDataHashtable<nsUint64HashKey, nsGlobalWindow*> WindowByIdTable;
 
   // public methods
   nsPIDOMWindow* GetPrivateParent();
   // callback for close event
   void ReallyCloseWindow();
 
   // nsISupports
@@ -901,17 +899,17 @@ protected:
   bool                   mNotifiedIDDestroyed : 1;
 
   nsCOMPtr<nsIScriptContext>    mContext;
   nsWeakPtr                     mOpener;
   nsCOMPtr<nsIControllers>      mControllers;
   nsCOMPtr<nsIArray>            mArguments;
   nsCOMPtr<nsIArray>            mArgumentsLast;
   nsCOMPtr<nsIPrincipal>        mArgumentsOrigin;
-  nsRefPtr<nsNavigator>         mNavigator;
+  nsRefPtr<Navigator>           mNavigator;
   nsRefPtr<nsScreen>            mScreen;
   nsRefPtr<nsPerformance>       mPerformance;
   nsRefPtr<nsDOMWindowList>     mFrames;
   nsRefPtr<nsBarProp>           mMenubar;
   nsRefPtr<nsBarProp>           mToolbar;
   nsRefPtr<nsBarProp>           mLocationbar;
   nsRefPtr<nsBarProp>           mPersonalbar;
   nsRefPtr<nsBarProp>           mStatusbar;
@@ -1068,60 +1066,14 @@ public:
   virtual NS_HIDDEN_(nsresult) SetNewDocument(nsIDocument *aDocument,
                                               nsISupports *aState,
                                               bool aForceReuseInnerWindow);
 
 protected:
   nsCOMPtr<nsIVariant> mReturnValue;
 };
 
-
-//*****************************************************************************
-// nsNavigator: Script "navigator" object
-//*****************************************************************************
-
-class nsNavigator : public nsIDOMNavigator,
-                    public nsIDOMClientInformation,
-                    public nsIDOMNavigatorGeolocation,
-                    public nsIDOMNavigatorDesktopNotification
-{
-public:
-  nsNavigator(nsIDocShell *aDocShell);
-  virtual ~nsNavigator();
-
-  NS_DECL_ISUPPORTS
-  NS_DECL_NSIDOMNAVIGATOR
-  NS_DECL_NSIDOMCLIENTINFORMATION
-  NS_DECL_NSIDOMNAVIGATORGEOLOCATION
-  NS_DECL_NSIDOMNAVIGATORDESKTOPNOTIFICATION
-  
-  void SetDocShell(nsIDocShell *aDocShell);
-  nsIDocShell *GetDocShell()
-  {
-    return mDocShell;
-  }
-
-  void LoadingNewDocument();
-  nsresult RefreshMIMEArray();
-
-  static bool HasDesktopNotificationSupport();
-
-  PRInt64 SizeOf() const;
-
-protected:
-  nsRefPtr<nsMimeTypeArray> mMimeTypes;
-  nsRefPtr<nsPluginArray> mPlugins;
-  nsRefPtr<nsGeolocation> mGeolocation;
-  nsRefPtr<nsDesktopNotificationCenter> mNotification;
-  nsIDocShell* mDocShell; // weak reference
-};
-
-nsresult NS_GetNavigatorUserAgent(nsAString& aUserAgent);
-nsresult NS_GetNavigatorPlatform(nsAString& aPlatform);
-nsresult NS_GetNavigatorAppVersion(nsAString& aAppVersion);
-nsresult NS_GetNavigatorAppName(nsAString& aAppName);
-
 /* factory function */
 nsresult
 NS_NewScriptGlobalObject(bool aIsChrome, bool aIsModalContentWindow,
                          nsIScriptGlobalObject **aResult);
 
 #endif /* nsGlobalWindow_h___ */
--- a/dom/base/nsPluginArray.cpp
+++ b/dom/base/nsPluginArray.cpp
@@ -33,29 +33,32 @@
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsPluginArray.h"
 #include "nsMimeTypeArray.h"
-#include "nsGlobalWindow.h"
+#include "Navigator.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIDOMNavigator.h"
 #include "nsIDOMMimeType.h"
 #include "nsIPluginHost.h"
 #include "nsIDocShell.h"
 #include "nsIWebNavigation.h"
 #include "nsDOMClassInfoID.h"
 #include "nsPluginError.h"
 #include "nsContentUtils.h"
 #include "nsPluginHost.h"
 
-nsPluginArray::nsPluginArray(nsNavigator* navigator,
+using namespace mozilla;
+using namespace mozilla::dom;
+
+nsPluginArray::nsPluginArray(Navigator* navigator,
                              nsIDocShell *aDocShell)
 {
   nsresult rv;
   mNavigator = navigator; // don't ADDREF here, needed for parent of script object.
   mPluginHost = do_GetService(MOZ_PLUGIN_HOST_CONTRACTID, &rv);
   mPluginCount = 0;
   mPluginArray = nsnull;
   mDocShell = aDocShell;
--- a/dom/base/nsPluginArray.h
+++ b/dom/base/nsPluginArray.h
@@ -39,25 +39,30 @@
 #define nsPluginArray_h___
 
 #include "nsCOMPtr.h"
 #include "nsIDOMPluginArray.h"
 #include "nsIDOMPlugin.h"
 #include "nsIPluginHost.h"
 #include "nsIURL.h"
 
-class nsNavigator;
+namespace mozilla {
+namespace dom {
+class Navigator;
+} // namespace dom
+} // namespace mozilla
+
 class nsIDocShell;
 
-// NB: Due to weak references, nsNavigator has intimate knowledge of our
+// NB: Due to weak references, Navigator has intimate knowledge of our
 // internals.
 class nsPluginArray : public nsIDOMPluginArray
 {
 public:
-  nsPluginArray(nsNavigator* navigator, nsIDocShell *aDocShell);
+  nsPluginArray(mozilla::dom::Navigator* navigator, nsIDocShell *aDocShell);
   virtual ~nsPluginArray();
 
   NS_DECL_ISUPPORTS
 
   // nsIDOMPluginArray
   NS_DECL_NSIDOMPLUGINARRAY
 
   nsresult GetPluginHost(nsIPluginHost** aPluginHost);
@@ -86,17 +91,17 @@ private:
   nsresult GetPlugins();
   bool AllowPlugins();
 
 public:
   void SetDocShell(nsIDocShell *aDocShell);
   void Invalidate();
 
 protected:
-  nsNavigator* mNavigator;
+  mozilla::dom::Navigator* mNavigator;
   nsCOMPtr<nsIPluginHost> mPluginHost;
   PRUint32 mPluginCount;
   nsIDOMPlugin** mPluginArray;
   nsIDocShell* mDocShell; // weak reference
 };
 
 class nsPluginElement : public nsIDOMPlugin
 {
new file mode 100644
--- /dev/null
+++ b/dom/battery/BatteryManager.cpp
@@ -0,0 +1,196 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * 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):
+ *   Mounir Lamouri <mounir.lamouri@mozilla.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "mozilla/Hal.h"
+#include "BatteryManager.h"
+#include "nsIDOMClassInfo.h"
+#include "Constants.h"
+#include "nsDOMEvent.h"
+
+/**
+ * We have to use macros here because our leak analysis tool things we are
+ * leaking strings when we have |static const nsString|. Sad :(
+ */
+#define LEVELCHANGE_EVENT_NAME    NS_LITERAL_STRING("levelchange")
+#define CHARGINGCHANGE_EVENT_NAME NS_LITERAL_STRING("chargingchange")
+
+DOMCI_DATA(BatteryManager, mozilla::dom::battery::BatteryManager)
+
+namespace mozilla {
+namespace dom {
+namespace battery {
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(BatteryManager)
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(BatteryManager,
+                                                  nsDOMEventTargetHelper)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnLevelChangeListener)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnChargingChangeListener)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(BatteryManager,
+                                                nsDOMEventTargetHelper)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnLevelChangeListener)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnChargingChangeListener)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(BatteryManager)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMBatteryManager)
+  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(BatteryManager)
+NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
+
+NS_IMPL_ADDREF_INHERITED(BatteryManager, nsDOMEventTargetHelper)
+NS_IMPL_RELEASE_INHERITED(BatteryManager, nsDOMEventTargetHelper)
+
+BatteryManager::BatteryManager()
+  : mLevel(kDefaultLevel)
+  , mCharging(kDefaultCharging)
+{
+}
+
+BatteryManager::~BatteryManager()
+{
+  if (mListenerManager) {
+    mListenerManager->Disconnect();
+  }
+}
+
+void
+BatteryManager::Init()
+{
+  hal::RegisterBatteryObserver(this);
+
+  hal::BatteryInformation* batteryInfo = new hal::BatteryInformation();
+  hal::GetCurrentBatteryInformation(batteryInfo);
+
+  UpdateFromBatteryInfo(*batteryInfo);
+
+  delete batteryInfo;
+}
+
+void
+BatteryManager::Shutdown()
+{
+  hal::UnregisterBatteryObserver(this);
+}
+
+NS_IMETHODIMP
+BatteryManager::GetCharging(bool* aCharging)
+{
+  *aCharging = mCharging;
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+BatteryManager::GetLevel(float* aLevel)
+{
+  *aLevel = mLevel;
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+BatteryManager::GetOnlevelchange(nsIDOMEventListener** aOnlevelchange)
+{
+  return GetInnerEventListener(mOnLevelChangeListener, aOnlevelchange);
+}
+
+NS_IMETHODIMP
+BatteryManager::SetOnlevelchange(nsIDOMEventListener* aOnlevelchange)
+{
+  return RemoveAddEventListener(LEVELCHANGE_EVENT_NAME, mOnLevelChangeListener,
+                                aOnlevelchange);
+}
+
+NS_IMETHODIMP
+BatteryManager::GetOnchargingchange(nsIDOMEventListener** aOnchargingchange)
+{
+  return GetInnerEventListener(mOnChargingChangeListener, aOnchargingchange);
+}
+
+NS_IMETHODIMP
+BatteryManager::SetOnchargingchange(nsIDOMEventListener* aOnchargingchange)
+{
+  return RemoveAddEventListener(CHARGINGCHANGE_EVENT_NAME,
+                                mOnChargingChangeListener, aOnchargingchange);
+}
+
+nsresult
+BatteryManager::DispatchTrustedEventToSelf(const nsAString& aEventName)
+{
+  nsRefPtr<nsDOMEvent> event = new nsDOMEvent(nsnull, nsnull);
+  nsresult rv = event->InitEvent(aEventName, false, false);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = event->SetTrusted(PR_TRUE);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  bool dummy;
+  rv = DispatchEvent(event, &dummy);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+void
+BatteryManager::UpdateFromBatteryInfo(const hal::BatteryInformation& aBatteryInfo)
+{
+  mLevel = aBatteryInfo.level();
+  mCharging = aBatteryInfo.charging();
+}
+
+void
+BatteryManager::Notify(const hal::BatteryInformation& aBatteryInfo)
+{
+  float previousLevel = mLevel;
+  bool previousCharging = mCharging;
+
+  UpdateFromBatteryInfo(aBatteryInfo);
+
+  if (previousCharging != mCharging) {
+    DispatchTrustedEventToSelf(CHARGINGCHANGE_EVENT_NAME);
+  }
+
+  if (previousLevel != mLevel) {
+    DispatchTrustedEventToSelf(LEVELCHANGE_EVENT_NAME);
+  }
+}
+
+} // namespace battery
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/battery/BatteryManager.h
@@ -0,0 +1,100 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * 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):
+ *   Mounir Lamouri <mounir.lamouri@mozilla.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef mozilla_dom_battery_BatteryManager_h
+#define mozilla_dom_battery_BatteryManager_h
+
+#include "nsIDOMBatteryManager.h"
+#include "nsDOMEventTargetHelper.h"
+#include "nsCycleCollectionParticipant.h"
+#include "mozilla/Observer.h"
+#include "Types.h"
+
+namespace mozilla {
+
+namespace hal {
+class BatteryInformation;
+} // namespace hal
+
+namespace dom {
+namespace battery {
+
+class BatteryManager : public nsIDOMBatteryManager
+                     , public nsDOMEventTargetHelper
+                     , public BatteryObserver
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIDOMBATTERYMANAGER
+  NS_FORWARD_NSIDOMEVENTTARGET(nsDOMEventTargetHelper::)
+
+  BatteryManager();
+  virtual ~BatteryManager();
+
+  void Init();
+  void Shutdown();
+
+  // For IObserver.
+  void Notify(const hal::BatteryInformation& aBatteryInfo);
+
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(BatteryManager,
+                                           nsDOMEventTargetHelper)
+
+private:
+  /**
+   * Dispatch a trusted non-cancellable and non-bubbling event to itself.
+   */
+  nsresult DispatchTrustedEventToSelf(const nsAString& aEventName);
+
+  /**
+   * Update the battery information stored in the battery manager object using
+   * a battery information object.
+   */
+  void UpdateFromBatteryInfo(const hal::BatteryInformation& aBatteryInfo);
+
+  float mLevel;
+  bool  mCharging;
+
+  nsRefPtr<nsDOMEventListenerWrapper> mOnLevelChangeListener;
+  nsRefPtr<nsDOMEventListenerWrapper> mOnChargingChangeListener;
+};
+
+} // namespace battery
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_battery_BatteryManager_h
new file mode 100644
--- /dev/null
+++ b/dom/battery/Constants.h
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * 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):
+ *   Mounir Lamouri <mounir.lamouri@mozilla.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef mozilla_dom_battery_Constants_h__
+#define mozilla_dom_battery_Constants_h__
+
+/**
+ * A set of constants that might need to be used by battery backends.
+ * It's not part of BatteryManager.h to prevent those backends to include it.
+ */
+namespace mozilla {
+namespace dom {
+namespace battery {
+
+  static const float kDefaultLevel    = 1.0f;
+  static const bool  kDefaultCharging = true;
+
+} // namespace battery
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_battery_Constants_h__
new file mode 100644
--- /dev/null
+++ b/dom/battery/Makefile.in
@@ -0,0 +1,77 @@
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is mozilla.org build system.
+#
+# 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):
+#   Mounir Lamouri <mounir.lamouri@mozilla.com> (Original Author)
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+DEPTH            = ../..
+topsrcdir        = @top_srcdir@
+srcdir           = @srcdir@
+VPATH            = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+LIBRARY_NAME     = dom_battery_s
+XPIDL_MODULE     = dom_battery
+LIBXUL_LIBRARY   = 1
+FORCE_STATIC_LIB = 1
+
+include $(topsrcdir)/dom/dom-config.mk
+
+EXPORTS_NAMESPACES = mozilla/dom/battery
+
+EXPORTS_mozilla/dom/battery = \
+  Constants.h \
+  Types.h \
+  $(NULL)
+
+CPPSRCS = \
+  BatteryManager.cpp \
+  $(NULL)
+
+LOCAL_INCLUDES = \
+  -I$(topsrcdir)/content/events/src \
+  $(NULL)
+
+XPIDLSRCS = \
+  nsIDOMBatteryManager.idl \
+  nsIDOMNavigatorBattery.idl \
+  $(NULL)
+
+ifdef ENABLE_TESTS
+DIRS += test
+endif
+
+include $(topsrcdir)/config/config.mk
+include $(topsrcdir)/ipc/chromium/chromium-config.mk
+include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/dom/battery/Types.h
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * 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):
+ *   Mounir Lamouri <mounir.lamouri@mozilla.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef mozilla_dom_battery_Types_h
+#define mozilla_dom_battery_Types_h
+
+namespace mozilla {
+namespace hal {
+class BatteryInformation;
+} // namespace hal
+
+template <class T>
+class Observer;
+
+typedef Observer<hal::BatteryInformation> BatteryObserver;
+
+} // namespace mozilla
+
+#endif // mozilla_dom_battery_Types_h
+
new file mode 100644
--- /dev/null
+++ b/dom/battery/nsIDOMBatteryManager.idl
@@ -0,0 +1,49 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org.
+ *
+ * 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):
+ *  Mounir Lamouri <mounir.lamouri@mozilla.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsIDOMEventTarget.idl"
+
+interface nsIDOMEventListener;
+
+[scriptable, function, uuid(6ac8bdb2-f005-469b-a55e-398e23ef3c95)]
+interface nsIDOMBatteryManager : nsIDOMEventTarget
+{
+  readonly attribute float      level;
+  readonly attribute boolean    charging;
+
+  attribute nsIDOMEventListener onlevelchange;
+  attribute nsIDOMEventListener onchargingchange;
+};
new file mode 100644
--- /dev/null
+++ b/dom/battery/nsIDOMNavigatorBattery.idl
@@ -0,0 +1,45 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org.
+ *
+ * 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):
+ *  Mounir Lamouri <mounir.lamouri@mozilla.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsISupports.idl"
+
+interface nsIDOMBatteryManager;
+
+[scriptable, uuid(a19eedd7-6c26-4676-bd34-7ca74ca5f565)]
+interface nsIDOMNavigatorBattery : nsISupports
+{
+  readonly attribute nsIDOMBatteryManager mozBattery;
+};
new file mode 100644
--- /dev/null
+++ b/dom/battery/test/Makefile.in
@@ -0,0 +1,62 @@
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is mozilla.org build system.
+#
+# 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):
+#   Mounir Lamouri <mounir.lamouri@mozilla.com> (Original Author)
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+DEPTH            = ../../..
+topsrcdir        = @top_srcdir@
+srcdir           = @srcdir@
+VPATH            = @srcdir@
+
+relativesrcdir   = dom/battery/test
+
+include $(DEPTH)/config/autoconf.mk
+
+DIRS = \
+  $(NULL)
+
+include $(topsrcdir)/config/rules.mk
+
+_TEST_FILES = \
+  test_battery_basics.html \
+  $(NULL)
+
+_CHROME_TEST_FILES = \
+  $(NULL)
+
+libs:: $(_TEST_FILES)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
+
+#libs:: $(_CHROME_TEST_FILES)
+#	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/dom/battery/test/test_battery_basics.html
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for Battery API</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Battery API **/
+
+ok('mozBattery' in navigator, "navigator.mozBattery should exist");
+
+var battery = navigator.mozBattery;
+is(battery.level, 1.0, "Default battery level should be 1.0");
+is(battery.charging, true, "Default charging value should be true");
+
+</script>
+</pre>
+</body>
+</html>
--- a/dom/dom-config.mk
+++ b/dom/dom-config.mk
@@ -1,10 +1,11 @@
 DOM_SRCDIRS = \
   dom/base \
+  dom/battery \
   dom/src/events \
   dom/src/storage \
   dom/src/offline \
   dom/src/geolocation \
   dom/src/notification \
   dom/workers \
   content/xbl/src \
   content/xul/document/src \
--- a/dom/indexedDB/AsyncConnectionHelper.cpp
+++ b/dom/indexedDB/AsyncConnectionHelper.cpp
@@ -554,19 +554,17 @@ NS_IMPL_QUERY_INTERFACE1(TransactionPool
 
 NS_IMETHODIMP
 TransactionPoolEventTarget::Dispatch(nsIRunnable* aRunnable,
                                      PRUint32 aFlags)
 {
   NS_ASSERTION(aRunnable, "Null pointer!");
   NS_ASSERTION(aFlags == NS_DISPATCH_NORMAL, "Unsupported!");
 
-  TransactionThreadPool* pool = TransactionThreadPool::Get();
-  NS_ASSERTION(pool, "This should never be null!");
-
+  TransactionThreadPool* pool = TransactionThreadPool::GetOrCreate();
   return pool->Dispatch(mTransaction, aRunnable, false, nsnull);
 }
 
 NS_IMETHODIMP
 TransactionPoolEventTarget::IsOnCurrentThread(bool* aResult)
 {
   *aResult = false;
   return NS_OK;
--- a/dom/indexedDB/CheckPermissionsHelper.cpp
+++ b/dom/indexedDB/CheckPermissionsHelper.cpp
@@ -201,13 +201,13 @@ CheckPermissionsHelper::Observe(nsISuppo
 
   nsresult rv;
   mPromptResult = nsDependentString(aData).ToInteger(&rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
   NS_ASSERTION(mgr, "This should never be null!");
 
-  rv = mgr->WaitForOpenAllowed(mName, mASCIIOrigin, this);
+  rv = NS_DispatchToCurrentThread(this);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
--- a/dom/indexedDB/CheckPermissionsHelper.h
+++ b/dom/indexedDB/CheckPermissionsHelper.h
@@ -59,35 +59,31 @@ class CheckPermissionsHelper : public ns
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIRUNNABLE
   NS_DECL_NSIINTERFACEREQUESTOR
   NS_DECL_NSIOBSERVER
 
   CheckPermissionsHelper(OpenDatabaseHelper* aHelper,
                          nsIDOMWindow* aWindow,
-                         const nsAString& aName,
                          const nsACString& aASCIIOrigin)
   : mHelper(aHelper),
     mWindow(aWindow),
-    mName(aName),
     mASCIIOrigin(aASCIIOrigin),
     mHasPrompted(false),
     mPromptResult(0)
   {
     NS_ASSERTION(aHelper, "Null pointer!");
     NS_ASSERTION(aWindow, "Null pointer!");
-    NS_ASSERTION(!aName.IsEmpty(), "Empty name!");
     NS_ASSERTION(!aASCIIOrigin.IsEmpty(), "Empty origin!");
   }
 
 private:
   nsRefPtr<OpenDatabaseHelper> mHelper;
   nsCOMPtr<nsIDOMWindow> mWindow;
-  nsString mName;
   nsCString mASCIIOrigin;
   bool mHasPrompted;
   PRUint32 mPromptResult;
 };
 
 END_INDEXEDDB_NAMESPACE
 
 #endif // mozilla_dom_indexeddb_checkpermissionshelper_h__
--- a/dom/indexedDB/DatabaseInfo.cpp
+++ b/dom/indexedDB/DatabaseInfo.cpp
@@ -56,17 +56,17 @@ struct DatabaseInfoHash
     NS_ASSERTION(aInfo, "Null pointer!");
     info = aInfo;
   }
 
   nsAutoPtr<DatabaseInfo> info;
   nsAutoPtr<ObjectStoreInfoHash> objectStoreHash;
 };
 
-typedef nsClassHashtable<nsUint32HashKey, DatabaseInfoHash>
+typedef nsClassHashtable<nsISupportsHashKey, DatabaseInfoHash>
         DatabaseHash;
 
 DatabaseHash* gDatabaseHash = nsnull;
 
 PLDHashOperator
 EnumerateObjectStoreNames(const nsAString& aKey,
                           ObjectStoreInfo* aData,
                           void* aUserArg)
@@ -78,18 +78,17 @@ EnumerateObjectStoreNames(const nsAStrin
   }
   return PL_DHASH_NEXT;
 }
 
 }
 
 #ifdef NS_BUILD_REFCNT_LOGGING
 DatabaseInfo::DatabaseInfo()
-: id(0),
-  nextObjectStoreId(1),
+: nextObjectStoreId(1),
   nextIndexId(1),
   runningVersionChange(false)
 {
   MOZ_COUNT_CTOR(DatabaseInfo);
 }
 
 DatabaseInfo::~DatabaseInfo()
 {
@@ -130,17 +129,17 @@ IndexUpdateInfo::IndexUpdateInfo()
 IndexUpdateInfo::~IndexUpdateInfo()
 {
   MOZ_COUNT_DTOR(IndexUpdateInfo);
 }
 #endif /* NS_BUILD_REFCNT_LOGGING */
 
 // static
 bool
-DatabaseInfo::Get(PRUint32 aId,
+DatabaseInfo::Get(nsIAtom* aId,
                   DatabaseInfo** aInfo)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(aId, "Bad id!");
 
   if (gDatabaseHash) {
     DatabaseInfoHash* hash;
     if (gDatabaseHash->Get(aId, &hash)) {
@@ -182,17 +181,17 @@ DatabaseInfo::Put(DatabaseInfo* aInfo)
   }
 
   hash.forget();
   return true;
 }
 
 // static
 void
-DatabaseInfo::Remove(PRUint32 aId)
+DatabaseInfo::Remove(nsIAtom* aId)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(Get(aId, nsnull), "Don't know anything about this one!");
 
   if (gDatabaseHash) {
     gDatabaseHash->Remove(aId);
 
     if (!gDatabaseHash->Count()) {
@@ -236,17 +235,17 @@ DatabaseInfo::ContainsStoreName(const ns
   return gDatabaseHash &&
          gDatabaseHash->Get(id, &hash) &&
          hash->objectStoreHash &&
          hash->objectStoreHash->Get(aName, &info);
 }
 
 // static
 bool
-ObjectStoreInfo::Get(PRUint32 aDatabaseId,
+ObjectStoreInfo::Get(nsIAtom* aDatabaseId,
                      const nsAString& aName,
                      ObjectStoreInfo** aInfo)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(!aName.IsEmpty(), "Empty object store name!");
 
   if (gDatabaseHash) {
     DatabaseInfoHash* hash;
@@ -292,17 +291,17 @@ ObjectStoreInfo::Put(ObjectStoreInfo* aI
     return false;
   }
 
   return !!hash->objectStoreHash->Put(aInfo->name, aInfo);
 }
 
 // static
 void
-ObjectStoreInfo::Remove(PRUint32 aDatabaseId,
+ObjectStoreInfo::Remove(nsIAtom* aDatabaseId,
                         const nsAString& aName)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(Get(aDatabaseId, aName, nsnull), "Don't know about this one!");
 
   if (gDatabaseHash) {
     DatabaseInfoHash* hash;
     if (gDatabaseHash->Get(aDatabaseId, &hash) && hash->objectStoreHash) {
--- a/dom/indexedDB/DatabaseInfo.h
+++ b/dom/indexedDB/DatabaseInfo.h
@@ -38,44 +38,45 @@
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef mozilla_dom_indexeddb_databaseinfo_h__
 #define mozilla_dom_indexeddb_databaseinfo_h__
 
 // Only meant to be included in IndexedDB source files, not exported.
 #include "IndexedDatabase.h"
 
+#include "Key.h"
 #include "IDBObjectStore.h"
 
 BEGIN_INDEXEDDB_NAMESPACE
 
 struct DatabaseInfo
 {
 #ifdef NS_BUILD_REFCNT_LOGGING
   DatabaseInfo();
   ~DatabaseInfo();
 #else
   DatabaseInfo()
-  : id(0), nextObjectStoreId(1), nextIndexId(1), runningVersionChange(false)
+  : nextObjectStoreId(1), nextIndexId(1), runningVersionChange(false)
   { }
 #endif
 
-  static bool Get(PRUint32 aId,
+  static bool Get(nsIAtom* aId,
                   DatabaseInfo** aInfo);
 
   static bool Put(DatabaseInfo* aInfo);
 
-  static void Remove(PRUint32 aId);
+  static void Remove(nsIAtom* aId);
 
   bool GetObjectStoreNames(nsTArray<nsString>& aNames);
   bool ContainsStoreName(const nsAString& aName);
 
   nsString name;
   PRUint64 version;
-  PRUint32 id;
+  nsIAtom* id;
   nsString filePath;
   PRInt64 nextObjectStoreId;
   PRInt64 nextIndexId;
   bool runningVersionChange;
 
   nsAutoRefCnt referenceCount;
 };
 
@@ -101,30 +102,30 @@ struct ObjectStoreInfo
 #ifdef NS_BUILD_REFCNT_LOGGING
   ObjectStoreInfo();
   ~ObjectStoreInfo();
 #else
   ObjectStoreInfo()
   : id(0), autoIncrement(false), databaseId(0) { }
 #endif
 
-  static bool Get(PRUint32 aDatabaseId,
+  static bool Get(nsIAtom* aDatabaseId,
                   const nsAString& aName,
                   ObjectStoreInfo** aInfo);
 
   static bool Put(ObjectStoreInfo* aInfo);
 
-  static void Remove(PRUint32 aDatabaseId,
+  static void Remove(nsIAtom* aDatabaseId,
                      const nsAString& aName);
 
   nsString name;
   PRInt64 id;
   nsString keyPath;
   bool autoIncrement;
-  PRUint32 databaseId;
+  nsIAtom* databaseId;
   nsTArray<IndexInfo> indexes;
 };
 
 struct IndexUpdateInfo
 {
 #ifdef NS_BUILD_REFCNT_LOGGING
   IndexUpdateInfo();
   ~IndexUpdateInfo();
--- a/dom/indexedDB/IDBCursor.cpp
+++ b/dom/indexedDB/IDBCursor.cpp
@@ -34,18 +34,16 @@
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "IDBCursor.h"
 
-#include "nsIVariant.h"
-
 #include "jscntxt.h"
 #include "mozilla/storage.h"
 #include "nsComponentManagerUtils.h"
 #include "nsContentUtils.h"
 #include "nsDOMClassInfoID.h"
 #include "nsEventDispatcher.h"
 #include "nsJSUtils.h"
 #include "nsThreadUtils.h"
@@ -270,18 +268,20 @@ IDBCursor::CreateCommon(IDBRequest* aReq
   cursor->mRangeKey = aRangeKey;
 
   return cursor.forget();
 }
 
 IDBCursor::IDBCursor()
 : mType(OBJECTSTORE),
   mDirection(nsIIDBCursor::NEXT),
+  mCachedKey(JSVAL_VOID),
   mCachedPrimaryKey(JSVAL_VOID),
   mCachedValue(JSVAL_VOID),
+  mHaveCachedKey(false),
   mHaveCachedPrimaryKey(false),
   mHaveCachedValue(false),
   mRooted(false),
   mContinueCalled(false),
   mHaveValue(true)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 }
@@ -306,37 +306,45 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(
                                                        nsIDOMEventTarget)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mObjectStore)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mIndex)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOwner)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mScriptContext)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(IDBCursor)
+  NS_ASSERTION(tmp->mHaveCachedKey || JSVAL_IS_VOID(tmp->mCachedKey),
+               "Should have a cached key");
   NS_ASSERTION(tmp->mHaveCachedPrimaryKey ||
                JSVAL_IS_VOID(tmp->mCachedPrimaryKey),
                "Should have a cached primary key");
   NS_ASSERTION(tmp->mHaveCachedValue || JSVAL_IS_VOID(tmp->mCachedValue),
                "Should have a cached value");
-  if (JSVAL_IS_GCTHING(tmp->mCachedValue)) {
-    void *gcThing = JSVAL_TO_GCTHING(tmp->mCachedValue);
-    NS_IMPL_CYCLE_COLLECTION_TRACE_JS_CALLBACK(gcThing, "mCachedValue")
+  if (JSVAL_IS_GCTHING(tmp->mCachedKey)) {
+    void *gcThing = JSVAL_TO_GCTHING(tmp->mCachedKey);
+    NS_IMPL_CYCLE_COLLECTION_TRACE_JS_CALLBACK(gcThing, "mCachedKey")
   }
   if (JSVAL_IS_GCTHING(tmp->mCachedPrimaryKey)) {
     void *gcThing = JSVAL_TO_GCTHING(tmp->mCachedPrimaryKey);
     NS_IMPL_CYCLE_COLLECTION_TRACE_JS_CALLBACK(gcThing, "mCachedPrimaryKey")
   }
+  if (JSVAL_IS_GCTHING(tmp->mCachedValue)) {
+    void *gcThing = JSVAL_TO_GCTHING(tmp->mCachedValue);
+    NS_IMPL_CYCLE_COLLECTION_TRACE_JS_CALLBACK(gcThing, "mCachedValue")
+  }
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBCursor)
   // Don't unlink mObjectStore, mIndex, or mTransaction!
   if (tmp->mRooted) {
     NS_DROP_JS_OBJECTS(tmp, IDBCursor);
+    tmp->mCachedKey = JSVAL_VOID;
     tmp->mCachedPrimaryKey = JSVAL_VOID;
     tmp->mCachedValue = JSVAL_VOID;
+    tmp->mHaveCachedKey = false;
     tmp->mHaveCachedPrimaryKey = false;
     tmp->mHaveCachedValue = false;
     tmp->mRooted = false;
     tmp->mHaveValue = false;
   }
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mRequest)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOwner)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mScriptContext)
@@ -373,53 +381,43 @@ IDBCursor::GetSource(nsISupports** aSour
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   return mType == OBJECTSTORE ?
          CallQueryInterface(mObjectStore, aSource) :
          CallQueryInterface(mIndex, aSource);
 }
 
 NS_IMETHODIMP
-IDBCursor::GetKey(nsIVariant** aKey)
+IDBCursor::GetKey(JSContext* aCx,
+                  jsval* aKey)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
-  if (!mCachedKey) {
-    nsresult rv;
-    nsCOMPtr<nsIWritableVariant> variant =
-      do_CreateInstance(NS_VARIANT_CONTRACTID, &rv);
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-    NS_ASSERTION(!mKey.IsUnset() || !mHaveValue, "Bad key!");
+  NS_ASSERTION(!mKey.IsUnset() || !mHaveValue, "Bad key!");
 
-    if (!mHaveValue) {
-      rv = variant->SetAsVoid();
-    }
-    else if (mKey.IsString()) {
-      rv = variant->SetAsAString(mKey.StringValue());
-    }
-    else if (mKey.IsInt()) {
-      rv = variant->SetAsInt64(mKey.IntValue());
-    }
-    else {
-      NS_NOTREACHED("Huh?!");
-    }
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-    rv = variant->SetWritable(false);
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-    nsIWritableVariant* result;
-    variant.forget(&result);
-
-    mCachedKey = dont_AddRef(static_cast<nsIVariant*>(result));
+  if (!mHaveValue) {
+    *aKey = JSVAL_VOID;
+    return NS_OK;
   }
 
-  nsCOMPtr<nsIVariant> result(mCachedKey);
-  result.forget(aKey);
+  if (!mHaveCachedKey) {
+    if (!mRooted) {
+      NS_HOLD_JS_OBJECTS(this, IDBCursor);
+      mRooted = true;
+    }
+
+    JSAutoRequest ar(aCx);
+
+    nsresult rv = mKey.ToJSVal(aCx, &mCachedKey);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    mHaveCachedKey = true;
+  }
+
+  *aKey = mCachedKey;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 IDBCursor::GetPrimaryKey(JSContext* aCx,
                          jsval* aValue)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
@@ -435,19 +433,19 @@ IDBCursor::GetPrimaryKey(JSContext* aCx,
       mRooted = true;
     }
 
     JSAutoRequest ar(aCx);
 
     NS_ASSERTION(mType == OBJECTSTORE ? !mKey.IsUnset() :
                                         !mObjectKey.IsUnset(), "Bad key!");
 
-    nsresult rv =
-      IDBObjectStore::GetJSValFromKey(mType == OBJECTSTORE ? mKey : mObjectKey,
-                                      aCx, &mCachedPrimaryKey);
+    const Key& key = mType == OBJECTSTORE ? mKey : mObjectKey;
+
+    nsresult rv = key.ToJSVal(aCx, &mCachedPrimaryKey);
     NS_ENSURE_SUCCESS(rv, rv);
 
     mHaveCachedPrimaryKey = true;
   }
 
   *aValue = mCachedPrimaryKey;
   return NS_OK;
 }
@@ -493,17 +491,17 @@ IDBCursor::Continue(const jsval &aKey,
     return NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR;
   }
 
   if (!mHaveValue || mContinueCalled) {
     return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
   }
 
   Key key;
-  nsresult rv = IDBObjectStore::GetKeyFromJSVal(aKey, aCx, key);
+  nsresult rv = key.SetFromJSVal(aCx, aKey);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (!key.IsUnset()) {
     switch (mDirection) {
       case nsIIDBCursor::NEXT:
       case nsIIDBCursor::NEXT_NO_DUPLICATE:
         if (key <= mKey) {
           return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
@@ -583,17 +581,17 @@ IDBCursor::Update(const jsval& aValue,
   NS_ASSERTION(mObjectStore, "This cannot be null!");
   NS_ASSERTION(!mKey.IsUnset() , "Bad key!");
   NS_ASSERTION(mType != INDEXOBJECT || !mObjectKey.IsUnset(), "Bad key!");
 
   nsresult rv;
 
   JSAutoRequest ar(aCx);
 
-  const Key& objectKey = (mType == OBJECTSTORE) ? mKey : mObjectKey;
+  Key& objectKey = (mType == OBJECTSTORE) ? mKey : mObjectKey;
 
   if (!mObjectStore->KeyPath().IsEmpty()) {
     // This has to be an object.
     if (JSVAL_IS_PRIMITIVE(aValue)) {
       return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
     }
 
     // Make sure the object given has the correct keyPath value set on it.
@@ -601,30 +599,30 @@ IDBCursor::Update(const jsval& aValue,
 
     jsval prop;
     JSBool ok = JS_GetUCProperty(aCx, JSVAL_TO_OBJECT(aValue),
                                  reinterpret_cast<const jschar*>(keyPath.get()),
                                  keyPath.Length(), &prop);
     NS_ENSURE_TRUE(ok, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
     Key key;
-    rv = IDBObjectStore::GetKeyFromJSVal(prop, aCx, key);
+    rv = key.SetFromJSVal(aCx, prop);
     if (NS_FAILED(rv)) {
       return rv;
     }
 
     if (key != objectKey) {
       return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
     }
 
     return mObjectStore->Put(aValue, JSVAL_VOID, aCx, 0, _retval);
   }
 
   jsval keyVal;
-  rv = IDBObjectStore::GetJSValFromKey(objectKey, aCx, &keyVal);
+  rv = objectKey.ToJSVal(aCx, &keyVal);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return mObjectStore->Put(aValue, keyVal, aCx, 1, _retval);
 }
 
 NS_IMETHODIMP
 IDBCursor::Delete(JSContext* aCx,
                   nsIIDBRequest** _retval)
@@ -641,20 +639,20 @@ IDBCursor::Delete(JSContext* aCx,
 
   if (!mHaveValue || mType == INDEXKEY) {
     return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
   }
 
   NS_ASSERTION(mObjectStore, "This cannot be null!");
   NS_ASSERTION(!mKey.IsUnset() , "Bad key!");
 
-  const Key& objectKey = (mType == OBJECTSTORE) ? mKey : mObjectKey;
+  Key& objectKey = (mType == OBJECTSTORE) ? mKey : mObjectKey;
 
   jsval key;
-  nsresult rv = IDBObjectStore::GetJSValFromKey(objectKey, aCx, &key);
+  nsresult rv = objectKey.ToJSVal(aCx, &key);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return mObjectStore->Delete(key, aCx, _retval);
 }
 
 nsresult
 ContinueHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
 {
@@ -683,46 +681,47 @@ ContinueHelper::DoDatabaseWork(mozIStora
   rv = stmt->ExecuteStep(&hasResult);
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   if (hasResult) {
     rv = GatherResultsFromStatement(stmt);
     NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
   }
   else {
-    mKey = Key::UNSETKEY;
+    mKey.Unset();
   }
 
   return NS_OK;
 }
 
 nsresult
 ContinueHelper::GetSuccessResult(JSContext* aCx,
                                  jsval* aVal)
 {
   // Remove cached stuff from last time.
-  mCursor->mCachedKey = nsnull;
-  mCursor->mCachedValue = JSVAL_VOID;
+  mCursor->mCachedKey = JSVAL_VOID;
   mCursor->mCachedPrimaryKey = JSVAL_VOID;
+  mCursor->mCachedValue = JSVAL_VOID;
+  mCursor->mHaveCachedKey = false;
+  mCursor->mHaveCachedPrimaryKey = false;
   mCursor->mHaveCachedValue = false;
-  mCursor->mHaveCachedPrimaryKey = false;
   mCursor->mContinueCalled = false;
 
   if (mKey.IsUnset()) {
     mCursor->mHaveValue = false;
     *aVal = JSVAL_VOID;
   }
   else {
     NS_ASSERTION(mCursor->mType == IDBCursor::OBJECTSTORE ||
                  !mObjectKey.IsUnset(), "Bad key!");
 
     // Set new values.
     mCursor->mKey = mKey;
     mCursor->mObjectKey = mObjectKey;
-    mCursor->mContinueToKey = Key::UNSETKEY;
+    mCursor->mContinueToKey.Unset();
 
     mCursor->mCloneBuffer.swap(mCloneBuffer);
     mCloneBuffer.clear();
 
     nsresult rv = WrapNative(aCx, mCursor, aVal);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
@@ -741,65 +740,37 @@ ContinueObjectStoreHelper::BindArguments
   NS_NAMED_LITERAL_CSTRING(currentKeyName, "current_key");
   NS_NAMED_LITERAL_CSTRING(rangeKeyName, "range_key");
 
   // Bind current key.
   const Key& currentKey = mCursor->mContinueToKey.IsUnset() ?
                           mCursor->mKey :
                           mCursor->mContinueToKey;
 
-  if (currentKey.IsString()) {
-    rv = aStatement->BindStringByName(currentKeyName, currentKey.StringValue());
-  }
-  else if (currentKey.IsInt()) {
-    rv = aStatement->BindInt64ByName(currentKeyName, currentKey.IntValue());
-  }
-  else {
-    NS_NOTREACHED("Bad key!");
-  }
-  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+  rv = currentKey.BindToStatement(aStatement, currentKeyName);
+  NS_ENSURE_SUCCESS(rv, rv);
 
   // Bind range key if it is specified.
   const Key& rangeKey = mCursor->mRangeKey;
 
   if (!rangeKey.IsUnset()) {
-    if (rangeKey.IsString()) {
-      rv = aStatement->BindStringByName(rangeKeyName, rangeKey.StringValue());
-    }
-    else if (rangeKey.IsInt()) {
-      rv = aStatement->BindInt64ByName(rangeKeyName, rangeKey.IntValue());
-    }
-    else {
-      NS_NOTREACHED("Bad key!");
-    }
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+    rv = rangeKey.BindToStatement(aStatement, rangeKeyName);
+    NS_ENSURE_SUCCESS(rv, rv);
   }
 
   return NS_OK;
 }
 
 nsresult
 ContinueObjectStoreHelper::GatherResultsFromStatement(
                                                mozIStorageStatement* aStatement)
 {
   // Figure out what kind of key we have next.
-  PRInt32 keyType;
-  nsresult rv = aStatement->GetTypeOfIndex(0, &keyType);
-  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-  if (keyType == mozIStorageStatement::VALUE_TYPE_INTEGER) {
-    mKey = aStatement->AsInt64(0);
-  }
-  else if (keyType == mozIStorageStatement::VALUE_TYPE_TEXT) {
-    rv = aStatement->GetString(0, mKey.ToString());
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-  }
-  else {
-    NS_NOTREACHED("Bad SQLite type!");
-  }
+  nsresult rv = mKey.SetFromStatement(aStatement, 0);
+  NS_ENSURE_SUCCESS(rv, rv);
 
   rv = IDBObjectStore::GetStructuredCloneDataFromStatement(aStatement, 1,
                                                            mCloneBuffer);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
@@ -813,152 +784,62 @@ ContinueIndexHelper::BindArgumentsToStat
 
   NS_NAMED_LITERAL_CSTRING(currentKeyName, "current_key");
 
   // Bind current key.
   const Key& currentKey = mCursor->mContinueToKey.IsUnset() ?
                           mCursor->mKey :
                           mCursor->mContinueToKey;
 
-  if (currentKey.IsString()) {
-    rv = aStatement->BindStringByName(currentKeyName, currentKey.StringValue());
-  }
-  else if (currentKey.IsInt()) {
-    rv = aStatement->BindInt64ByName(currentKeyName, currentKey.IntValue());
-  }
-  else {
-    NS_NOTREACHED("Bad key!");
-  }
-  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+  rv = currentKey.BindToStatement(aStatement, currentKeyName);
+  NS_ENSURE_SUCCESS(rv, rv);
 
   // Bind range key if it is specified.
   if (!mCursor->mRangeKey.IsUnset()) {
     NS_NAMED_LITERAL_CSTRING(rangeKeyName, "range_key");
-    if (mCursor->mRangeKey.IsString()) {
-      rv = aStatement->BindStringByName(rangeKeyName,
-                                        mCursor->mRangeKey.StringValue());
-    }
-    else if (mCursor->mRangeKey.IsInt()) {
-      rv = aStatement->BindInt64ByName(rangeKeyName,
-                                       mCursor->mRangeKey.IntValue());
-    }
-    else {
-      NS_NOTREACHED("Bad key!");
-    }
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+    rv = mCursor->mRangeKey.BindToStatement(aStatement, rangeKeyName);
+    NS_ENSURE_SUCCESS(rv, rv);
   }
 
   // Bind object key if duplicates are allowed and we're not continuing to a
   // specific key.
   if ((mCursor->mDirection == nsIIDBCursor::NEXT ||
        mCursor->mDirection == nsIIDBCursor::PREV) &&
        mCursor->mContinueToKey.IsUnset()) {
     NS_ASSERTION(!mCursor->mObjectKey.IsUnset(), "Bad key!");
 
     NS_NAMED_LITERAL_CSTRING(objectKeyName, "object_key");
-    if (mCursor->mObjectKey.IsString()) {
-      rv = aStatement->BindStringByName(objectKeyName,
-                                        mCursor->mObjectKey.StringValue());
-    }
-    else if (mCursor->mObjectKey.IsInt()) {
-      rv = aStatement->BindInt64ByName(objectKeyName,
-                                       mCursor->mObjectKey.IntValue());
-    }
-    else {
-      NS_NOTREACHED("Bad key!");
-    }
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+    rv = mCursor->mObjectKey.BindToStatement(aStatement, objectKeyName);
+    NS_ENSURE_SUCCESS(rv, rv);
   }
 
   return NS_OK;
 }
 
 nsresult
 ContinueIndexHelper::GatherResultsFromStatement(
                                                mozIStorageStatement* aStatement)
 {
-  PRInt32 keyType;
-  nsresult rv = aStatement->GetTypeOfIndex(0, &keyType);
-  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-  NS_ASSERTION(keyType == mozIStorageStatement::VALUE_TYPE_INTEGER ||
-               keyType == mozIStorageStatement::VALUE_TYPE_TEXT,
-               "Bad key type!");
+  nsresult rv = mKey.SetFromStatement(aStatement, 0);
+  NS_ENSURE_SUCCESS(rv, rv);
 
-  if (keyType == mozIStorageStatement::VALUE_TYPE_INTEGER) {
-    mKey = aStatement->AsInt64(0);
-  }
-  else if (keyType == mozIStorageStatement::VALUE_TYPE_TEXT) {
-    rv = aStatement->GetString(0, mKey.ToString());
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-  }
-  else {
-    NS_NOTREACHED("Bad SQLite type!");
-  }
-
-  rv = aStatement->GetTypeOfIndex(1, &keyType);
-  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-  NS_ASSERTION(keyType == mozIStorageStatement::VALUE_TYPE_INTEGER ||
-               keyType == mozIStorageStatement::VALUE_TYPE_TEXT,
-               "Bad key type!");
-
-  if (keyType == mozIStorageStatement::VALUE_TYPE_INTEGER) {
-    mObjectKey = aStatement->AsInt64(1);
-  }
-  else if (keyType == mozIStorageStatement::VALUE_TYPE_TEXT) {
-    rv = aStatement->GetString(1, mObjectKey.ToString());
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-  }
-  else {
-    NS_NOTREACHED("Bad SQLite type!");
-  }
+  rv = mObjectKey.SetFromStatement(aStatement, 1);
+  NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 nsresult
 ContinueIndexObjectHelper::GatherResultsFromStatement(
                                                mozIStorageStatement* aStatement)
 {
-  PRInt32 keyType;
-  nsresult rv = aStatement->GetTypeOfIndex(0, &keyType);
-  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-  NS_ASSERTION(keyType == mozIStorageStatement::VALUE_TYPE_INTEGER ||
-               keyType == mozIStorageStatement::VALUE_TYPE_TEXT,
-               "Bad key type!");
+  nsresult rv = mKey.SetFromStatement(aStatement, 0);
+  NS_ENSURE_SUCCESS(rv, rv);
 
-  if (keyType == mozIStorageStatement::VALUE_TYPE_INTEGER) {
-    mKey = aStatement->AsInt64(0);
-  }
-  else if (keyType == mozIStorageStatement::VALUE_TYPE_TEXT) {
-    rv = aStatement->GetString(0, mKey.ToString());
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-  }
-  else {
-    NS_NOTREACHED("Bad SQLite type!");
-  }
-
-  rv = aStatement->GetTypeOfIndex(1, &keyType);
-  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-  NS_ASSERTION(keyType == mozIStorageStatement::VALUE_TYPE_INTEGER ||
-               keyType == mozIStorageStatement::VALUE_TYPE_TEXT,
-               "Bad key type!");
-
-  if (keyType == mozIStorageStatement::VALUE_TYPE_INTEGER) {
-    mObjectKey = aStatement->AsInt64(1);
-  }
-  else if (keyType == mozIStorageStatement::VALUE_TYPE_TEXT) {
-    rv = aStatement->GetString(1, mObjectKey.ToString());
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-  }
-  else {
-    NS_NOTREACHED("Bad SQLite type!");
-  }
+  rv = mObjectKey.SetFromStatement(aStatement, 1);
+  NS_ENSURE_SUCCESS(rv, rv);
 
   rv = IDBObjectStore::GetStructuredCloneDataFromStatement(aStatement, 2,
                                                            mCloneBuffer);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
--- a/dom/indexedDB/IDBCursor.h
+++ b/dom/indexedDB/IDBCursor.h
@@ -37,16 +37,17 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef mozilla_dom_indexeddb_idbcursor_h__
 #define mozilla_dom_indexeddb_idbcursor_h__
 
 #include "mozilla/dom/indexedDB/IndexedDatabase.h"
 #include "mozilla/dom/indexedDB/IDBObjectStore.h"
+#include "mozilla/dom/indexedDB/Key.h"
 
 #include "nsIIDBCursorWithValue.h"
 
 #include "nsCycleCollectionParticipant.h"
 
 class nsIRunnable;
 class nsIScriptContext;
 class nsPIDOMWindow;
@@ -145,35 +146,34 @@ protected:
   nsRefPtr<IDBRequest> mRequest;
   nsRefPtr<IDBTransaction> mTransaction;
   nsRefPtr<IDBObjectStore> mObjectStore;
   nsRefPtr<IDBIndex> mIndex;
 
   nsCOMPtr<nsIScriptContext> mScriptContext;
   nsCOMPtr<nsPIDOMWindow> mOwner;
 
-  // Not cycle-collected, this is guaranteed to be primitive!
-  nsCOMPtr<nsIVariant> mCachedKey;
-
   Type mType;
   PRUint16 mDirection;
   nsCString mContinueQuery;
   nsCString mContinueToQuery;
 
   // These are cycle-collected!
+  jsval mCachedKey;
   jsval mCachedPrimaryKey;
   jsval mCachedValue;
 
   Key mRangeKey;
 
   Key mKey;
   Key mObjectKey;
   JSAutoStructuredCloneBuffer mCloneBuffer;
   Key mContinueToKey;
 
+  bool mHaveCachedKey;
   bool mHaveCachedPrimaryKey;
   bool mHaveCachedValue;
   bool mRooted;
   bool mContinueCalled;
   bool mHaveValue;
 };
 
 END_INDEXEDDB_NAMESPACE
--- a/dom/indexedDB/IDBDatabase.cpp
+++ b/dom/indexedDB/IDBDatabase.cpp
@@ -123,119 +123,40 @@ public:
   }
 
 private:
   // In-params.
   PRInt64 mObjectStoreId;
 };
 
 NS_STACK_CLASS
-class AutoFree
-{
-public:
-  AutoFree(void* aPtr) : mPtr(aPtr) { }
-  ~AutoFree() { NS_Free(mPtr); }
-private:
-  void* mPtr;
-};
-
-NS_STACK_CLASS
 class AutoRemoveObjectStore
 {
 public:
-  AutoRemoveObjectStore(PRUint32 aId, const nsAString& aName)
+  AutoRemoveObjectStore(nsIAtom* aId, const nsAString& aName)
   : mId(aId), mName(aName)
   { }
 
   ~AutoRemoveObjectStore()
   {
     if (mId) {
       ObjectStoreInfo::Remove(mId, mName);
     }
   }
 
   void forget()
   {
     mId = 0;
   }
 
 private:
-  PRUint32 mId;
+  nsCOMPtr<nsIAtom> mId;
   nsString mName;
 };
 
-inline
-nsresult
-ConvertVariantToStringArray(nsIVariant* aVariant,
-                            nsTArray<nsString>& aStringArray)
-{
-#ifdef DEBUG
-  PRUint16 type;
-  NS_ASSERTION(NS_SUCCEEDED(aVariant->GetDataType(&type)) &&
-               type == nsIDataType::VTYPE_ARRAY, "Bad arg!");
-#endif
-
-  PRUint16 valueType;
-  nsIID iid;
-  PRUint32 valueCount;
-  void* rawArray;
-
-  nsresult rv = aVariant->GetAsArray(&valueType, &iid, &valueCount, &rawArray);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  AutoFree af(rawArray);
-
-  // Just delete anything that we don't expect and return.
-  if (valueType != nsIDataType::VTYPE_WCHAR_STR) {
-    switch (valueType) {
-      case nsIDataType::VTYPE_ID:
-      case nsIDataType::VTYPE_CHAR_STR: {
-        char** charArray = reinterpret_cast<char**>(rawArray);
-        for (PRUint32 index = 0; index < valueCount; index++) {
-          if (charArray[index]) {
-            NS_Free(charArray[index]);
-          }
-        }
-      } break;
-
-      case nsIDataType::VTYPE_INTERFACE:
-      case nsIDataType::VTYPE_INTERFACE_IS: {
-        nsISupports** supportsArray = reinterpret_cast<nsISupports**>(rawArray);
-        for (PRUint32 index = 0; index < valueCount; index++) {
-          NS_IF_RELEASE(supportsArray[index]);
-        }
-      } break;
-
-      default: {
-        // The other types are primitives that do not need to be freed.
-      }
-    }
-
-    return NS_ERROR_ILLEGAL_VALUE;
-  }
-
-  PRUnichar** strings = reinterpret_cast<PRUnichar**>(rawArray);
-
-  for (PRUint32 index = 0; index < valueCount; index++) {
-    nsString* newString = aStringArray.AppendElement();
-
-    if (!newString) {
-      NS_ERROR("Out of memory?");
-      for (; index < valueCount; index++) {
-        NS_Free(strings[index]);
-      }
-      return NS_ERROR_OUT_OF_MEMORY;
-    }
-
-    newString->Adopt(strings[index], -1);
-  }
-
-  return NS_OK;
-}
-
 } // anonymous namespace
 
 // static
 already_AddRefed<IDBDatabase>
 IDBDatabase::Create(nsIScriptContext* aScriptContext,
                     nsPIDOMWindow* aOwner,
                     DatabaseInfo* aDatabaseInfo,
                     const nsACString& aASCIIOrigin)
@@ -445,20 +366,16 @@ IDBDatabase::ExitSetVersionTransaction()
 {
   DatabaseInfo* dbInfo;
   if (!DatabaseInfo::Get(mDatabaseId, &dbInfo)) {
     NS_ERROR("This should never fail!");
   }
 
   NS_ASSERTION(dbInfo->runningVersionChange, "How did that happen?");
   dbInfo->runningVersionChange = false;
-
-  IndexedDatabaseManager* manager = IndexedDatabaseManager::Get();
-  NS_ASSERTION(manager, "We should always have a manager here");
-  manager->UnblockSetVersionRunnable(this);
 }
 
 void
 IDBDatabase::OnUnlink()
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(!mOwner, "Should have been cleared already!");
 
@@ -693,147 +610,147 @@ IDBDatabase::DeleteObjectStore(const nsA
   nsresult rv = helper->DispatchToTransactionPool();
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   ObjectStoreInfo::Remove(mDatabaseId, aName);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-IDBDatabase::Transaction(nsIVariant* aStoreNames,
+IDBDatabase::Transaction(const jsval& aStoreNames,
                          PRUint16 aMode,
-                         PRUint32 aTimeout,
                          JSContext* aCx,
                          PRUint8 aOptionalArgCount,
                          nsIIDBTransaction** _retval)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   if (IndexedDatabaseManager::IsShuttingDown()) {
     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
   }
 
   if (mClosed) {
     return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
   }
 
+  DatabaseInfo* info;
+  if (!DatabaseInfo::Get(mDatabaseId, &info)) {
+    NS_ERROR("This should never fail!");
+  }
+
+  if (info->runningVersionChange) {
+    return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
+  }
+
   if (aOptionalArgCount) {
     if (aMode != nsIIDBTransaction::READ_WRITE &&
         aMode != nsIIDBTransaction::READ_ONLY) {
       return NS_ERROR_DOM_INDEXEDDB_NON_TRANSIENT_ERR;
     }
   }
   else {
     aMode = nsIIDBTransaction::READ_ONLY;
   }
 
-  if (aOptionalArgCount <= 1) {
-    aTimeout = kDefaultDatabaseTimeoutSeconds;
-  }
-
-  PRUint16 type;
-  nsresult rv = aStoreNames->GetDataType(&type);
-  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-  DatabaseInfo* info;
-  if (!DatabaseInfo::Get(mDatabaseId, &info)) {
-    NS_ERROR("This should never fail!");
-  }
-
-  if (info->runningVersionChange) {
-    return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
-  }
-
+  nsresult rv;
   nsTArray<nsString> storesToOpen;
 
-  switch (type) {
-    case nsIDataType::VTYPE_VOID:
-    case nsIDataType::VTYPE_EMPTY:
-    case nsIDataType::VTYPE_EMPTY_ARRAY: {
-      // Empty, request all object stores
-      if (!info->GetObjectStoreNames(storesToOpen)) {
-        NS_WARNING("Out of memory?");
+  if (!JSVAL_IS_PRIMITIVE(aStoreNames)) {
+    JSObject* obj = JSVAL_TO_OBJECT(aStoreNames);
+
+    // See if this is a JS array.
+    if (JS_IsArrayObject(aCx, obj)) {
+      jsuint length;
+      if (!JS_GetArrayLength(aCx, obj, &length)) {
         return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
       }
-    } break;
 
-    case nsIDataType::VTYPE_WSTRING_SIZE_IS: {
-      // Single name
-      nsString name;
-      rv = aStoreNames->GetAsAString(name);
-      NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-      if (!info->ContainsStoreName(name)) {
-        return NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR;
+      if (!length) {
+        return NS_ERROR_DOM_INVALID_ACCESS_ERR;
       }
 
-      if (!storesToOpen.AppendElement(name)) {
-        NS_WARNING("Out of memory?");
-        return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+      storesToOpen.SetCapacity(length);
+
+      for (jsuint index = 0; index < length; index++) {
+        jsval val;
+        JSString* jsstr;
+        nsDependentJSString str;
+        if (!JS_GetElement(aCx, obj, index, &val) ||
+            !(jsstr = JS_ValueToString(aCx, val)) ||
+            !str.init(aCx, jsstr)) {
+          return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+        }
+
+        storesToOpen.AppendElement(str);
       }
-    } break;
 
-    case nsIDataType::VTYPE_ARRAY: {
-      nsTArray<nsString> names;
-      rv = ConvertVariantToStringArray(aStoreNames, names);
+      NS_ASSERTION(!storesToOpen.IsEmpty(),
+                   "Must have something here or else code below will "
+                   "misbehave!");
+    }
+    else {
+      // Perhaps some kind of wrapped object?
+      nsIXPConnect* xpc = nsContentUtils::XPConnect();
+      NS_ASSERTION(xpc, "This should never be null!");
+
+      nsCOMPtr<nsIXPConnectWrappedNative> wrapper;
+      rv = xpc->GetWrappedNativeOfJSObject(aCx, obj, getter_AddRefs(wrapper));
       NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
-      PRUint32 nameCount = names.Length();
-      for (PRUint32 nameIndex = 0; nameIndex < nameCount; nameIndex++) {
-        nsString& name = names[nameIndex];
+      if (wrapper) {
+        nsISupports* wrappedObject = wrapper->Native();
+        NS_ENSURE_TRUE(wrappedObject, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+
+        // We only accept DOMStringList.
+        nsCOMPtr<nsIDOMDOMStringList> list = do_QueryInterface(wrappedObject);
+        if (list) {
+          PRUint32 length;
+          rv = list->GetLength(&length);
+          NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
-        if (!info->ContainsStoreName(name)) {
-          return NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR;
-        }
+          if (!length) {
+            return NS_ERROR_DOM_INVALID_ACCESS_ERR;
+          }
+
+          storesToOpen.SetCapacity(length);
 
-        if (!storesToOpen.AppendElement(name)) {
-          NS_WARNING("Out of memory?");
-          return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+          for (PRUint32 index = 0; index < length; index++) {
+            nsString* item = storesToOpen.AppendElement();
+            NS_ASSERTION(item, "This should never fail!");
+
+            rv = list->Item(index, *item);
+            NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+          }
+
+          NS_ASSERTION(!storesToOpen.IsEmpty(),
+                       "Must have something here or else code below will "
+                       "misbehave!");
         }
       }
-      NS_ASSERTION(nameCount == storesToOpen.Length(), "Should have bailed!");
-    } break;
-
-    case nsIDataType::VTYPE_INTERFACE:
-    case nsIDataType::VTYPE_INTERFACE_IS: {
-      nsCOMPtr<nsISupports> supports;
-      nsID *iid;
-      rv = aStoreNames->GetAsInterface(&iid, getter_AddRefs(supports));
-      NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-      NS_Free(iid);
-
-      nsCOMPtr<nsIDOMDOMStringList> stringList(do_QueryInterface(supports));
-      if (!stringList) {
-        // We don't support anything other than nsIDOMDOMStringList.
-        return NS_ERROR_DOM_INDEXEDDB_NON_TRANSIENT_ERR;
-      }
+    }
+  }
 
-      PRUint32 stringCount;
-      rv = stringList->GetLength(&stringCount);
-      NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-      for (PRUint32 stringIndex = 0; stringIndex < stringCount; stringIndex++) {
-        nsString name;
-        rv = stringList->Item(stringIndex, name);
-        NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+  // If our list is empty here then the argument must have been an object that
+  // we don't support or a primitive. Either way we convert to a string.
+  if (storesToOpen.IsEmpty()) {
+    JSString* jsstr;
+    nsDependentJSString str;
+    if (!(jsstr = JS_ValueToString(aCx, aStoreNames)) ||
+        !str.init(aCx, jsstr)) {
+      return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+    }
 
-        if (!info->ContainsStoreName(name)) {
-          return NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR;
-        }
+    storesToOpen.AppendElement(str);
+  }
 
-        if (!storesToOpen.AppendElement(name)) {
-          NS_WARNING("Out of memory?");
-          return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
-        }
-      }
-    } break;
-
-    default:
-      return NS_ERROR_DOM_INDEXEDDB_NON_TRANSIENT_ERR;
+  // Now check to make sure the object store names we collected actually exist.
+  for (PRUint32 index = 0; index < storesToOpen.Length(); index++) {
+    if (!info->ContainsStoreName(storesToOpen[index])) {
+      return NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR;
+    }
   }
 
   nsRefPtr<IDBTransaction> transaction =
     IDBTransaction::Create(this, storesToOpen, aMode,
                            kDefaultDatabaseTimeoutSeconds);
   NS_ENSURE_TRUE(transaction, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   transaction.forget(_retval);
--- a/dom/indexedDB/IDBDatabase.h
+++ b/dom/indexedDB/IDBDatabase.h
@@ -78,17 +78,17 @@ public:
   Create(nsIScriptContext* aScriptContext,
          nsPIDOMWindow* aOwner,
          DatabaseInfo* aDatabaseInfo,
          const nsACString& aASCIIOrigin);
 
   // nsIDOMEventTarget
   virtual nsresult PostHandleEvent(nsEventChainPostVisitor& aVisitor);
 
-  PRUint32 Id()
+  nsIAtom* Id()
   {
     return mDatabaseId;
   }
 
   const nsString& Name()
   {
     return mName;
   }
@@ -139,17 +139,17 @@ public:
   void ExitSetVersionTransaction();
 
 private:
   IDBDatabase();
   ~IDBDatabase();
 
   void OnUnlink();
 
-  PRUint32 mDatabaseId;
+  nsCOMPtr<nsIAtom> mDatabaseId;
   nsString mName;
   nsString mFilePath;
   nsCString mASCIIOrigin;
 
   PRInt32 mInvalidated;
   bool mRegistered;
   bool mClosed;
 
--- a/dom/indexedDB/IDBFactory.cpp
+++ b/dom/indexedDB/IDBFactory.cpp
@@ -209,17 +209,17 @@ IDBFactory::GetDirectoryForOrigin(const 
 
   directory.forget(aDirectory);
   return NS_OK;
 }
 
 // static
 nsresult
 IDBFactory::LoadDatabaseInformation(mozIStorageConnection* aConnection,
-                                    PRUint32 aDatabaseId,
+                                    nsIAtom* aDatabaseId,
                                     PRUint64* aVersion,
                                     ObjectStoreInfoArray& aObjectStores)
 {
   NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(aConnection, "Null pointer!");
 
   aObjectStores.Clear();
 
@@ -376,21 +376,22 @@ NS_INTERFACE_MAP_BEGIN(IDBFactory)
 NS_INTERFACE_MAP_END
 
 DOMCI_DATA(IDBFactory, IDBFactory)
 
 NS_IMETHODIMP
 IDBFactory::Open(const nsAString& aName,
                  PRInt64 aVersion,
                  JSContext* aCx,
+                 PRUint8 aOptionalArgCount,
                  nsIIDBOpenDBRequest** _retval)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
-  if (aVersion < 1) {
+  if (aVersion < 1 && aOptionalArgCount) {
     return NS_ERROR_DOM_INDEXEDDB_NON_TRANSIENT_ERR;
   }
 
   if (XRE_GetProcessType() == GeckoProcessType_Content) {
     // Force ContentChild to cache the path from the parent, so that
     // we do not end up in a side thread that asks for the path (which
     // would make ContentChild try to send a message in a thread other
     // than the main one).
@@ -431,20 +432,23 @@ IDBFactory::Open(const nsAString& aName,
 
   nsRefPtr<IDBOpenDBRequest> request =
     IDBOpenDBRequest::Create(context, window);
   NS_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   nsRefPtr<OpenDatabaseHelper> openHelper =
     new OpenDatabaseHelper(request, aName, origin, aVersion);
 
+  rv = openHelper->Init();
+  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+
   nsRefPtr<CheckPermissionsHelper> permissionHelper =
-    new CheckPermissionsHelper(openHelper, window, aName, origin);
+    new CheckPermissionsHelper(openHelper, window, origin);
 
   nsRefPtr<IndexedDatabaseManager> mgr = IndexedDatabaseManager::GetOrCreate();
   NS_ENSURE_TRUE(mgr, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
-  rv = mgr->WaitForOpenAllowed(aName, origin, permissionHelper);
+  rv = mgr->WaitForOpenAllowed(origin, openHelper->Id(), permissionHelper);
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   request.forget(_retval);
   return NS_OK;
 }
--- a/dom/indexedDB/IDBFactory.h
+++ b/dom/indexedDB/IDBFactory.h
@@ -44,16 +44,17 @@
 
 #include "mozIStorageConnection.h"
 #include "nsIIDBFactory.h"
 
 #include "nsIWeakReferenceUtils.h"
 #include "nsXULAppAPI.h"
 
 class nsPIDOMWindow;
+class nsIAtom;
 
 BEGIN_INDEXEDDB_NAMESPACE
 
 struct DatabaseInfo;
 class IDBDatabase;
 struct ObjectStoreInfo;
 
 class IDBFactory : public nsIIDBFactory
@@ -79,17 +80,17 @@ public:
   GetDirectory(nsIFile** aDirectory);
 
   static nsresult
   GetDirectoryForOrigin(const nsACString& aASCIIOrigin,
                         nsIFile** aDirectory);
 
   static nsresult
   LoadDatabaseInformation(mozIStorageConnection* aConnection,
-                          PRUint32 aDatabaseId,
+                          nsIAtom* aDatabaseId,
                           PRUint64* aVersion,
                           ObjectStoreInfoArray& aObjectStores);
 
   static nsresult
   UpdateDatabaseMetadata(DatabaseInfo* aDatabaseInfo,
                          PRUint64 aVersion,
                          ObjectStoreInfoArray& aObjectStores);
 
--- a/dom/indexedDB/IDBIndex.cpp
+++ b/dom/indexedDB/IDBIndex.cpp
@@ -47,16 +47,17 @@
 #include "nsDOMClassInfoID.h"
 #include "nsEventDispatcher.h"
 #include "nsThreadUtils.h"
 #include "mozilla/storage.h"
 
 #include "AsyncConnectionHelper.h"
 #include "IDBCursor.h"
 #include "IDBEvents.h"
+#include "IDBKeyRange.h"
 #include "IDBObjectStore.h"
 #include "IDBTransaction.h"
 #include "DatabaseInfo.h"
 
 USING_INDEXEDDB_NAMESPACE
 
 namespace {
 
@@ -162,102 +163,118 @@ protected:
 };
 
 class OpenKeyCursorHelper : public AsyncConnectionHelper
 {
 public:
   OpenKeyCursorHelper(IDBTransaction* aTransaction,
                       IDBRequest* aRequest,
                       IDBIndex* aIndex,
-                      const Key& aLowerKey,
-                      const Key& aUpperKey,
-                      bool aLowerOpen,
-                      bool aUpperOpen,
+                      IDBKeyRange* aKeyRange,
                       PRUint16 aDirection)
   : AsyncConnectionHelper(aTransaction, aRequest), mIndex(aIndex),
-    mLowerKey(aLowerKey), mUpperKey(aUpperKey), mLowerOpen(aLowerOpen),
-    mUpperOpen(aUpperOpen), mDirection(aDirection)
+    mKeyRange(aKeyRange), mDirection(aDirection)
   { }
 
   nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
   nsresult GetSuccessResult(JSContext* aCx,
                             jsval* aVal);
 
   void ReleaseMainThreadObjects()
   {
     mIndex = nsnull;
+    mKeyRange = nsnull;
     AsyncConnectionHelper::ReleaseMainThreadObjects();
   }
 
 private:
   // In-params.
   nsRefPtr<IDBIndex> mIndex;
-  const Key mLowerKey;
-  const Key mUpperKey;
-  const bool mLowerOpen;
-  const bool mUpperOpen;
+  nsRefPtr<IDBKeyRange> mKeyRange;
   const PRUint16 mDirection;
 
   // Out-params.
   Key mKey;
   Key mObjectKey;
   nsCString mContinueQuery;
   nsCString mContinueToQuery;
   Key mRangeKey;
 };
 
 class OpenCursorHelper : public AsyncConnectionHelper
 {
 public:
   OpenCursorHelper(IDBTransaction* aTransaction,
                    IDBRequest* aRequest,
                    IDBIndex* aIndex,
-                   const Key& aLowerKey,
-                   const Key& aUpperKey,
-                   bool aLowerOpen,
-                   bool aUpperOpen,
+                   IDBKeyRange* aKeyRange,
                    PRUint16 aDirection)
   : AsyncConnectionHelper(aTransaction, aRequest), mIndex(aIndex),
-    mLowerKey(aLowerKey), mUpperKey(aUpperKey), mLowerOpen(aLowerOpen),
-    mUpperOpen(aUpperOpen), mDirection(aDirection)
+    mKeyRange(aKeyRange), mDirection(aDirection)
   { }
 
   ~OpenCursorHelper()
   {
     IDBObjectStore::ClearStructuredCloneBuffer(mCloneBuffer);
   }
 
   nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
   nsresult GetSuccessResult(JSContext* aCx,
                             jsval* aVal);
 
   void ReleaseMainThreadObjects()
   {
     mIndex = nsnull;
+    mKeyRange = nsnull;
     AsyncConnectionHelper::ReleaseMainThreadObjects();
   }
 
 private:
   // In-params.
   nsRefPtr<IDBIndex> mIndex;
-  const Key mLowerKey;
-  const Key mUpperKey;
-  const bool mLowerOpen;
-  const bool mUpperOpen;
+  nsRefPtr<IDBKeyRange> mKeyRange;
   const PRUint16 mDirection;
 
   // Out-params.
   Key mKey;
   Key mObjectKey;
   JSAutoStructuredCloneBuffer mCloneBuffer;
   nsCString mContinueQuery;
   nsCString mContinueToQuery;
   Key mRangeKey;
 };
 
+class CountHelper : public AsyncConnectionHelper
+{
+public:
+  CountHelper(IDBTransaction* aTransaction,
+              IDBRequest* aRequest,
+              IDBIndex* aIndex,
+              IDBKeyRange* aKeyRange)
+  : AsyncConnectionHelper(aTransaction, aRequest), mIndex(aIndex),
+    mKeyRange(aKeyRange), mCount(0)
+  { }
+
+  nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
+  nsresult GetSuccessResult(JSContext* aCx,
+                            jsval* aVal);
+
+  void ReleaseMainThreadObjects()
+  {
+    mIndex = nsnull;
+    mKeyRange = nsnull;
+    AsyncConnectionHelper::ReleaseMainThreadObjects();
+  }
+
+private:
+  nsRefPtr<IDBIndex> mIndex;
+  nsRefPtr<IDBKeyRange> mKeyRange;
+  PRUint64 mCount;
+};
+
 inline
 already_AddRefed<IDBRequest>
 GenerateRequest(IDBIndex* aIndex)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   IDBTransaction* transaction = aIndex->ObjectStore()->Transaction();
   IDBDatabase* database = transaction->Database();
   return IDBRequest::Create(aIndex, database->ScriptContext(),
@@ -266,17 +283,17 @@ GenerateRequest(IDBIndex* aIndex)
 
 } // anonymous namespace
 
 // static
 already_AddRefed<IDBIndex>
 IDBIndex::Create(IDBObjectStore* aObjectStore,
                  const IndexInfo* aIndexInfo)
 {
-  NS_PRECONDITION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(aObjectStore, "Null pointer!");
   NS_ASSERTION(aIndexInfo, "Null pointer!");
 
   IDBDatabase* database = aObjectStore->Transaction()->Database();
 
   nsRefPtr<IDBIndex> index = new IDBIndex();
 
   index->mScriptContext = database->ScriptContext();
@@ -292,22 +309,22 @@ IDBIndex::Create(IDBObjectStore* aObject
   return index.forget();
 }
 
 IDBIndex::IDBIndex()
 : mId(LL_MININT),
   mUnique(false),
   mAutoIncrement(false)
 {
-  NS_PRECONDITION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 }
 
 IDBIndex::~IDBIndex()
 {
-  NS_PRECONDITION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(IDBIndex)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IDBIndex)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mObjectStore)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOwner)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mScriptContext)
@@ -328,193 +345,174 @@ NS_INTERFACE_MAP_END
 NS_IMPL_CYCLE_COLLECTING_ADDREF(IDBIndex)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(IDBIndex)
 
 DOMCI_DATA(IDBIndex, IDBIndex)
 
 NS_IMETHODIMP
 IDBIndex::GetName(nsAString& aName)
 {
-  NS_PRECONDITION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   aName.Assign(mName);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 IDBIndex::GetStoreName(nsAString& aStoreName)
 {
-  NS_PRECONDITION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   return mObjectStore->GetName(aStoreName);
 }
 
 NS_IMETHODIMP
 IDBIndex::GetKeyPath(nsAString& aKeyPath)
 {
-  NS_PRECONDITION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   aKeyPath.Assign(mKeyPath);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 IDBIndex::GetUnique(bool* aUnique)
 {
-  NS_PRECONDITION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   *aUnique = mUnique;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 IDBIndex::GetObjectStore(nsIIDBObjectStore** aObjectStore)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   nsCOMPtr<nsIIDBObjectStore> objectStore(mObjectStore);
   objectStore.forget(aObjectStore);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-IDBIndex::Get(nsIVariant* aKey,
+IDBIndex::Get(const jsval& aKey,
+              JSContext* aCx,
               nsIIDBRequest** _retval)
 {
-  NS_PRECONDITION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   IDBTransaction* transaction = mObjectStore->Transaction();
   if (!transaction->IsOpen()) {
     return NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR;
   }
 
   Key key;
-  nsresult rv = IDBObjectStore::GetKeyFromVariant(aKey, key);
+  nsresult rv = key.SetFromJSVal(aCx, aKey);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
-  if (key.IsUnset()) {
-    return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
-  }
-
   nsRefPtr<IDBRequest> request = GenerateRequest(this);
   NS_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
-  nsRefPtr<GetHelper> helper = new GetHelper(transaction, request, this, key);
+  nsRefPtr<GetHelper> helper =
+    new GetHelper(transaction, request, this, key);
   rv = helper->DispatchToTransactionPool();
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   request.forget(_retval);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-IDBIndex::GetKey(nsIVariant* aKey,
+IDBIndex::GetKey(const jsval& aKey,
+                 JSContext* aCx,
                  nsIIDBRequest** _retval)
 {
-  NS_PRECONDITION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   IDBTransaction* transaction = mObjectStore->Transaction();
   if (!transaction->IsOpen()) {
     return NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR;
   }
 
   Key key;
-  nsresult rv = IDBObjectStore::GetKeyFromVariant(aKey, key);
+  nsresult rv = key.SetFromJSVal(aCx, aKey);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
-  if (key.IsUnset()) {
-    return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
-  }
-
   nsRefPtr<IDBRequest> request = GenerateRequest(this);
   NS_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   nsRefPtr<GetKeyHelper> helper =
     new GetKeyHelper(transaction, request, this, key);
 
   rv = helper->DispatchToTransactionPool();
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   request.forget(_retval);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-IDBIndex::GetAll(nsIVariant* aKey,
+IDBIndex::GetAll(const jsval& aKey,
                  PRUint32 aLimit,
+                 JSContext* aCx,
                  PRUint8 aOptionalArgCount,
                  nsIIDBRequest** _retval)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   IDBTransaction* transaction = mObjectStore->Transaction();
   if (!transaction->IsOpen()) {
     return NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR;
   }
 
-  nsresult rv;
-
   Key key;
-  if (aOptionalArgCount &&
-      NS_FAILED(IDBObjectStore::GetKeyFromVariant(aKey, key))) {
-    PRUint16 type;
-    rv = aKey->GetDataType(&type);
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-    if (type != nsIDataType::VTYPE_EMPTY) {
-      return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
-    }
+  if (aOptionalArgCount && NS_FAILED(key.SetFromJSVal(aCx, aKey))) {
+    return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
   }
 
   if (aOptionalArgCount < 2) {
     aLimit = PR_UINT32_MAX;
   }
 
   nsRefPtr<IDBRequest> request = GenerateRequest(this);
   NS_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   nsRefPtr<GetAllHelper> helper =
     new GetAllHelper(transaction, request, this, key, aLimit);
 
-  rv = helper->DispatchToTransactionPool();
+  nsresult rv = helper->DispatchToTransactionPool();
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   request.forget(_retval);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-IDBIndex::GetAllKeys(nsIVariant* aKey,
+IDBIndex::GetAllKeys(const jsval& aKey,
                      PRUint32 aLimit,
+                     JSContext* aCx,
                      PRUint8 aOptionalArgCount,
                      nsIIDBRequest** _retval)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   IDBTransaction* transaction = mObjectStore->Transaction();
   if (!transaction->IsOpen()) {
     return NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR;
   }
 
   nsresult rv;
 
   Key key;
-  if (aOptionalArgCount &&
-      NS_FAILED(IDBObjectStore::GetKeyFromVariant(aKey, key))) {
-    PRUint16 type;
-    rv = aKey->GetDataType(&type);
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-    if (type != nsIDataType::VTYPE_EMPTY) {
-      return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
-    }
+  if (aOptionalArgCount && NS_FAILED(key.SetFromJSVal(aCx, aKey))) {
+    return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
   }
 
   if (aOptionalArgCount < 2) {
     aLimit = PR_UINT32_MAX;
   }
 
   nsRefPtr<IDBRequest> request = GenerateRequest(this);
   NS_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
@@ -525,144 +523,133 @@ IDBIndex::GetAllKeys(nsIVariant* aKey,
   rv = helper->DispatchToTransactionPool();
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   request.forget(_retval);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-IDBIndex::OpenCursor(nsIIDBKeyRange* aKeyRange,
+IDBIndex::OpenCursor(const jsval& aKey,
                      PRUint16 aDirection,
+                     JSContext* aCx,
                      PRUint8 aOptionalArgCount,
                      nsIIDBRequest** _retval)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   IDBTransaction* transaction = mObjectStore->Transaction();
   if (!transaction->IsOpen()) {
     return NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR;
   }
 
   nsresult rv;
-  Key lowerKey, upperKey;
-  bool lowerOpen = false, upperOpen = false;
 
-  if (aKeyRange) {
-    nsCOMPtr<nsIVariant> variant;
-    rv = aKeyRange->GetLower(getter_AddRefs(variant));
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-    rv = IDBObjectStore::GetKeyFromVariant(variant, lowerKey);
-    if (NS_FAILED(rv)) {
-      return rv;
-    }
-
-    rv = aKeyRange->GetUpper(getter_AddRefs(variant));
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+  nsRefPtr<IDBKeyRange> keyRange;
+  if (aOptionalArgCount) {
+    rv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange));
+    NS_ENSURE_SUCCESS(rv, rv);
 
-    rv = IDBObjectStore::GetKeyFromVariant(variant, upperKey);
-    if (NS_FAILED(rv)) {
-      return rv;
+    if (aOptionalArgCount >= 2) {
+      if (aDirection != nsIIDBCursor::NEXT &&
+          aDirection != nsIIDBCursor::NEXT_NO_DUPLICATE &&
+          aDirection != nsIIDBCursor::PREV &&
+          aDirection != nsIIDBCursor::PREV_NO_DUPLICATE) {
+        return NS_ERROR_DOM_INDEXEDDB_NON_TRANSIENT_ERR;
+      }
     }
-
-    rv = aKeyRange->GetLowerOpen(&lowerOpen);
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-    rv = aKeyRange->GetUpperOpen(&upperOpen);
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-  }
-
-  if (aOptionalArgCount >= 2) {
-    if (aDirection != nsIIDBCursor::NEXT &&
-        aDirection != nsIIDBCursor::NEXT_NO_DUPLICATE &&
-        aDirection != nsIIDBCursor::PREV &&
-        aDirection != nsIIDBCursor::PREV_NO_DUPLICATE) {
-      return NS_ERROR_DOM_INDEXEDDB_NON_TRANSIENT_ERR;
+    else {
+      aDirection = nsIIDBCursor::NEXT;
     }
   }
-  else {
-    aDirection = nsIIDBCursor::NEXT;
-  }
 
   nsRefPtr<IDBRequest> request = GenerateRequest(this);
   NS_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   nsRefPtr<OpenCursorHelper> helper =
-    new OpenCursorHelper(transaction, request, this, lowerKey, upperKey,
-                         lowerOpen, upperOpen, aDirection);
+    new OpenCursorHelper(transaction, request, this, keyRange, aDirection);
 
   rv = helper->DispatchToTransactionPool();
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   request.forget(_retval);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-IDBIndex::OpenKeyCursor(nsIIDBKeyRange* aKeyRange,
+IDBIndex::OpenKeyCursor(const jsval& aKey,
                         PRUint16 aDirection,
+                        JSContext* aCx,
                         PRUint8 aOptionalArgCount,
                         nsIIDBRequest** _retval)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   IDBTransaction* transaction = mObjectStore->Transaction();
   if (!transaction->IsOpen()) {
     return NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR;
   }
 
   nsresult rv;
-  Key lowerKey, upperKey;
-  bool lowerOpen = false, upperOpen = false;
 
-  if (aKeyRange) {
-    nsCOMPtr<nsIVariant> variant;
-    rv = aKeyRange->GetLower(getter_AddRefs(variant));
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-    rv = IDBObjectStore::GetKeyFromVariant(variant, lowerKey);
-    if (NS_FAILED(rv)) {
-      return rv;
-    }
-
-    rv = aKeyRange->GetUpper(getter_AddRefs(variant));
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+  nsRefPtr<IDBKeyRange> keyRange;
+  if (aOptionalArgCount) {
+    rv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange));
+    NS_ENSURE_SUCCESS(rv, rv);
 
-    rv = IDBObjectStore::GetKeyFromVariant(variant, upperKey);
-    if (NS_FAILED(rv)) {
-      return rv;
+    if (aOptionalArgCount >= 2) {
+      if (aDirection != nsIIDBCursor::NEXT &&
+          aDirection != nsIIDBCursor::NEXT_NO_DUPLICATE &&
+          aDirection != nsIIDBCursor::PREV &&
+          aDirection != nsIIDBCursor::PREV_NO_DUPLICATE) {
+        return NS_ERROR_DOM_INDEXEDDB_NON_TRANSIENT_ERR;
+      }
     }
-
-    rv = aKeyRange->GetLowerOpen(&lowerOpen);
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-    rv = aKeyRange->GetUpperOpen(&upperOpen);
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-  }
-
-  if (aOptionalArgCount >= 2) {
-    if (aDirection != nsIIDBCursor::NEXT &&
-        aDirection != nsIIDBCursor::NEXT_NO_DUPLICATE &&
-        aDirection != nsIIDBCursor::PREV &&
-        aDirection != nsIIDBCursor::PREV_NO_DUPLICATE) {
-      return NS_ERROR_DOM_INDEXEDDB_NON_TRANSIENT_ERR;
+    else {
+      aDirection = nsIIDBCursor::NEXT;
     }
   }
-  else {
-    aDirection = nsIIDBCursor::NEXT;
-  }
 
   nsRefPtr<IDBRequest> request = GenerateRequest(this);
   NS_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   nsRefPtr<OpenKeyCursorHelper> helper =
-    new OpenKeyCursorHelper(transaction, request, this, lowerKey, upperKey,
-                            lowerOpen, upperOpen, aDirection);
+    new OpenKeyCursorHelper(transaction, request, this, keyRange, aDirection);
+
+  rv = helper->DispatchToTransactionPool();
+  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+
+  request.forget(_retval);
+  return NS_OK;
+}
 
+NS_IMETHODIMP
+IDBIndex::Count(const jsval& aKey,
+                JSContext* aCx,
+