merge
authorRob Campbell <rcampbell@mozilla.com>
Fri, 16 Jul 2010 11:15:49 -0300
changeset 47806 66ed85eff8efe277fac5e24272a8a1bac8ce7a84
parent 47805 5575b3579d2f63bdb0197cb9fa81c4bb646a05cc (current diff)
parent 47802 8951841a4cdad53edea3f4a2379987b3e19fb3b0 (diff)
child 47807 cdeaa6dff0a787ecdb83605d3782c969e3ec4a8c
push id14426
push userrcampbell@mozilla.com
push dateFri, 16 Jul 2010 14:16:39 +0000
treeherdermozilla-central@cdeaa6dff0a7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone2.0b2pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge
--- a/accessible/src/base/nsAccessible.cpp
+++ b/accessible/src/base/nsAccessible.cpp
@@ -2183,19 +2183,17 @@ nsAccessible::GetRelationByType(PRUint32
     }
 
   case nsIAccessibleRelation::RELATION_DEFAULT_BUTTON:
     {
       if (mContent->IsHTML()) {
         // HTML form controls implements nsIFormControl interface.
         nsCOMPtr<nsIFormControl> control(do_QueryInterface(mContent));
         if (control) {
-          nsCOMPtr<nsIDOMHTMLFormElement> htmlform;
-          control->GetForm(getter_AddRefs(htmlform));
-          nsCOMPtr<nsIForm> form(do_QueryInterface(htmlform));
+          nsCOMPtr<nsIForm> form(do_QueryInterface(control->GetFormElement()));
           if (form) {
             nsCOMPtr<nsIContent> formContent =
               do_QueryInterface(form->GetDefaultSubmitElement());
             return nsRelUtils::AddTargetFromContent(aRelationType, aRelation,
                                                     formContent);
           }
         }
       }
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -3096,16 +3096,17 @@
                  class="tab-icon-image"
                  role="presentation"/>
       <xul:label flex="1"
                  xbl:inherits="value=label,crop,accesskey"
                  class="tab-text"
                  role="presentation"/>
       <xul:toolbarbutton anonid="close-button"
                          tabindex="-1"
+                         clickthrough="never"
                          class="tab-close-button"/>
     </content>
 
     <implementation>
       <property name="pinned" readonly="true">
         <getter>
           return this.getAttribute("pinned") == "true";
         </getter>
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -218,16 +218,21 @@ public:
 
   /**
    * Check whether a caller is trusted to have aCapability.  This also
    * checks for UniversalXPConnect in addition to aCapability.
    */
   static PRBool   IsCallerTrustedForCapability(const char* aCapability);
 
   /**
+   * Returns the parent node of aChild crossing document boundaries.
+   */
+  static nsINode* GetCrossDocParentNode(nsINode* aChild);
+
+  /**
    * Do not ever pass null pointers to this method.  If one of your
    * nsIContents is null, you have to decide for yourself what
    * "IsDescendantOf" really means.
    *
    * @param  aPossibleDescendant node to test for being a descendant of
    *         aPossibleAncestor
    * @param  aPossibleAncestor node to test for being an ancestor of
    *         aPossibleDescendant
--- a/content/base/src/nsContentList.cpp
+++ b/content/base/src/nsContentList.cpp
@@ -165,25 +165,21 @@ void nsBaseContentList::InsertElementAt(
 nsFormContentList::nsFormContentList(nsIDOMHTMLFormElement *aForm,
                                      nsBaseContentList& aContentList)
   : nsBaseContentList()
 {
 
   // move elements that belong to mForm into this content list
 
   PRUint32 i, length = 0;
-  nsCOMPtr<nsIDOMNode> item;
 
   aContentList.GetLength(&length);
 
   for (i = 0; i < length; i++) {
-    aContentList.Item(i, getter_AddRefs(item));
-
-    nsCOMPtr<nsIContent> c(do_QueryInterface(item));
-
+    nsIContent *c = aContentList.GetNodeAt(i);
     if (c && nsContentUtils::BelongsInForm(aForm, c)) {
       AppendElement(c);
     }
   }
 }
 
 // Hashtable for storing nsContentLists
 static PLDHashTable gContentListHashTable;
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -1517,16 +1517,31 @@ nsContentUtils::IsCallerTrustedForRead()
 
 PRBool
 nsContentUtils::IsCallerTrustedForWrite()
 {
   return IsCallerTrustedForCapability("UniversalBrowserWrite");
 }
 
 // static
+nsINode*
+nsContentUtils::GetCrossDocParentNode(nsINode* aChild)
+{
+  NS_PRECONDITION(aChild, "The child is null!");
+
+  nsINode* parent = aChild->GetNodeParent();
+  if (parent || !aChild->IsNodeOfType(nsINode::eDOCUMENT))
+    return parent;
+
+  nsIDocument* doc = static_cast<nsIDocument*>(aChild);
+  nsIDocument* parentDoc = doc->GetParentDocument();
+  return parentDoc ? parentDoc->FindContentForSubDocument(doc) : nsnull;
+}
+
+// static
 PRBool
 nsContentUtils::ContentIsDescendantOf(const nsINode* aPossibleDescendant,
                                       const nsINode* aPossibleAncestor)
 {
   NS_PRECONDITION(aPossibleDescendant, "The possible descendant is null!");
   NS_PRECONDITION(aPossibleAncestor, "The possible ancestor is null!");
 
   do {
@@ -1544,26 +1559,17 @@ nsContentUtils::ContentIsCrossDocDescend
                                               nsINode* aPossibleAncestor)
 {
   NS_PRECONDITION(aPossibleDescendant, "The possible descendant is null!");
   NS_PRECONDITION(aPossibleAncestor, "The possible ancestor is null!");
 
   do {
     if (aPossibleDescendant == aPossibleAncestor)
       return PR_TRUE;
-    nsINode* parent = aPossibleDescendant->GetNodeParent();
-    if (!parent && aPossibleDescendant->IsNodeOfType(nsINode::eDOCUMENT)) {
-      nsIDocument* doc = static_cast<nsIDocument*>(aPossibleDescendant);
-      nsIDocument* parentDoc = doc->GetParentDocument();
-      aPossibleDescendant = parentDoc ?
-                            parentDoc->FindContentForSubDocument(doc) : nsnull;
-    }
-    else {
-      aPossibleDescendant = parent;
-    }
+    aPossibleDescendant = GetCrossDocParentNode(aPossibleDescendant);
   } while (aPossibleDescendant);
 
   return PR_FALSE;
 }
 
 
 // static
 nsresult
@@ -1910,21 +1916,20 @@ static inline void KeyAppendInt(PRInt32 
 
 static inline void KeyAppendAtom(nsIAtom* aAtom, nsACString& aKey)
 {
   NS_PRECONDITION(aAtom, "KeyAppendAtom: aAtom can not be null!\n");
 
   KeyAppendString(nsAtomCString(aAtom), aKey);
 }
 
-static inline PRBool IsAutocompleteOff(nsIDOMElement* aElement)
-{
-  nsAutoString autocomplete;
-  aElement->GetAttribute(NS_LITERAL_STRING("autocomplete"), autocomplete);
-  return autocomplete.LowerCaseEqualsLiteral("off");
+static inline PRBool IsAutocompleteOff(nsIContent* aElement)
+{
+  return aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::autocomplete,
+                               NS_LITERAL_STRING("off"), eIgnoreCase);
 }
 
 /*static*/ nsresult
 nsContentUtils::GenerateStateKey(nsIContent* aContent,
                                  nsIDocument* aDocument,
                                  nsIStatefulFrame::SpecialStateID aID,
                                  nsACString& aKey)
 {
@@ -1943,18 +1948,17 @@ nsContentUtils::GenerateStateKey(nsICont
   // We must have content if we're not using a special state id
   NS_ENSURE_TRUE(aContent, NS_ERROR_FAILURE);
 
   // Don't capture state for anonymous content
   if (aContent->IsInAnonymousSubtree()) {
     return NS_OK;
   }
 
-  nsCOMPtr<nsIDOMElement> element(do_QueryInterface(aContent));
-  if (element && IsAutocompleteOff(element)) {
+  if (IsAutocompleteOff(aContent)) {
     return NS_OK;
   }
 
   nsCOMPtr<nsIHTMLDocument> htmlDocument(do_QueryInterface(aContent->GetCurrentDoc()));
 
   KeyAppendInt(partID, aKey);  // first append a partID
   // Make sure we can't possibly collide with an nsIStatefulFrame
   // special id of some sort
@@ -1990,30 +1994,27 @@ nsContentUtils::GenerateStateKey(nsICont
     nsCOMPtr<nsIFormControl> control(do_QueryInterface(aContent));
     if (control && htmlFormControls && htmlForms) {
 
       // Append the control type
       KeyAppendInt(control->GetType(), aKey);
 
       // If in a form, add form name / index of form / index in form
       PRInt32 index = -1;
-      nsCOMPtr<nsIDOMHTMLFormElement> formElement;
-      control->GetForm(getter_AddRefs(formElement));
+      Element *formElement = control->GetFormElement();
       if (formElement) {
-
         if (IsAutocompleteOff(formElement)) {
           aKey.Truncate();
           return NS_OK;
         }
 
         KeyAppendString(NS_LITERAL_CSTRING("f"), aKey);
 
         // Append the index of the form in the document
-        nsCOMPtr<nsIContent> formContent(do_QueryInterface(formElement));
-        index = htmlForms->IndexOf(formContent, PR_FALSE);
+        index = htmlForms->IndexOf(formElement, PR_FALSE);
         if (index <= -1) {
           //
           // XXX HACK this uses some state that was dumped into the document
           // specifically to fix bug 138892.  What we are trying to do is *guess*
           // which form this control's state is found in, with the highly likely
           // guess that the highest form parsed so far is the one.
           // This code should not be on trunk, only branch.
           //
@@ -2029,17 +2030,17 @@ nsContentUtils::GenerateStateKey(nsICont
           if (index > -1) {
             KeyAppendInt(index, aKey);
             generatedUniqueKey = PR_TRUE;
           }
         }
 
         // Append the form name
         nsAutoString formName;
-        formElement->GetName(formName);
+        formElement->GetAttr(kNameSpaceID_None, nsGkAtoms::name, formName);
         KeyAppendString(formName, aKey);
 
       } else {
 
         KeyAppendString(NS_LITERAL_CSTRING("d"), aKey);
 
         // If not in a form, add index of control in document
         // Less desirable than indexing by form info.
--- a/content/base/src/nsDOMAttributeMap.cpp
+++ b/content/base/src/nsDOMAttributeMap.cpp
@@ -67,21 +67,20 @@ nsDOMAttributeMap::Init()
 {
   return mAttributeCache.Init();
 }
 
 /**
  * Clear map pointer for attributes.
  */
 PLDHashOperator
-RemoveMapRef(nsAttrHashKey::KeyType aKey, nsCOMPtr<nsIDOMNode>& aData, void* aUserArg)
+RemoveMapRef(nsAttrHashKey::KeyType aKey, nsRefPtr<nsDOMAttribute>& aData,
+             void* aUserArg)
 {
-  nsCOMPtr<nsIAttribute> attr(do_QueryInterface(aData));
-  NS_ASSERTION(attr, "non-nsIAttribute somehow made it into the hashmap?!");
-  attr->SetMap(nsnull);
+  aData->SetMap(nsnull);
 
   return PL_DHASH_REMOVE;
 }
 
 nsDOMAttributeMap::~nsDOMAttributeMap()
 {
   mAttributeCache.Enumerate(RemoveMapRef, nsnull);
 }
@@ -96,22 +95,23 @@ nsDOMAttributeMap::DropReference()
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMAttributeMap)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDOMAttributeMap)
   tmp->DropReference();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 
 PLDHashOperator
-TraverseMapEntry(nsAttrHashKey::KeyType aKey, nsCOMPtr<nsIDOMNode>& aData, void* aUserArg)
+TraverseMapEntry(nsAttrHashKey::KeyType aKey, nsRefPtr<nsDOMAttribute>& aData,
+                 void* aUserArg)
 {
   nsCycleCollectionTraversalCallback *cb = 
     static_cast<nsCycleCollectionTraversalCallback*>(aUserArg);
 
-  cb->NoteXPCOMChild(aData.get());
+  cb->NoteXPCOMChild(static_cast<nsINode*>(aData.get()));
 
   return PL_DHASH_NEXT;
 }
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDOMAttributeMap)
   tmp->mAttributeCache.Enumerate(TraverseMapEntry, &cb);
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
@@ -126,22 +126,21 @@ NS_INTERFACE_TABLE_HEAD(nsDOMAttributeMa
   NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(nsDOMAttributeMap)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(NamedNodeMap)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMAttributeMap)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMAttributeMap)
 
 PLDHashOperator
-SetOwnerDocumentFunc(nsAttrHashKey::KeyType aKey, nsCOMPtr<nsIDOMNode>& aData,
+SetOwnerDocumentFunc(nsAttrHashKey::KeyType aKey,
+                     nsRefPtr<nsDOMAttribute>& aData,
                      void* aUserArg)
 {
-  nsCOMPtr<nsIAttribute> attr(do_QueryInterface(aData));
-  NS_ASSERTION(attr, "non-nsIAttribute somehow made it into the hashmap?!");
-  nsresult rv = attr->SetOwnerDocument(static_cast<nsIDocument*>(aUserArg));
+  nsresult rv = aData->SetOwnerDocument(static_cast<nsIDocument*>(aUserArg));
 
   return NS_FAILED(rv) ? PL_DHASH_STOP : PL_DHASH_NEXT;
 }
 
 nsresult
 nsDOMAttributeMap::SetOwnerDocument(nsIDocument* aDocument)
 {
   PRUint32 n = mAttributeCache.Enumerate(SetOwnerDocumentFunc, aDocument);
@@ -149,98 +148,90 @@ nsDOMAttributeMap::SetOwnerDocument(nsID
 
   return NS_OK;
 }
 
 void
 nsDOMAttributeMap::DropAttribute(PRInt32 aNamespaceID, nsIAtom* aLocalName)
 {
   nsAttrKey attr(aNamespaceID, aLocalName);
-  nsIDOMNode *node = mAttributeCache.GetWeak(attr);
+  nsDOMAttribute *node = mAttributeCache.GetWeak(attr);
   if (node) {
-    nsCOMPtr<nsIAttribute> iAttr(do_QueryInterface(node));
-    NS_ASSERTION(iAttr, "non-nsIAttribute somehow made it into the hashmap?!");
-
     // Break link to map
-    iAttr->SetMap(nsnull);
+    node->SetMap(nsnull);
 
     // Remove from cache
     mAttributeCache.Remove(attr);
   }
 }
 
 nsresult
 nsDOMAttributeMap::RemoveAttribute(nsINodeInfo* aNodeInfo, nsIDOMNode** aReturn)
 {
   NS_ASSERTION(aNodeInfo, "RemoveAttribute() called with aNodeInfo == nsnull!");
   NS_ASSERTION(aReturn, "RemoveAttribute() called with aReturn == nsnull");
 
   *aReturn = nsnull;
 
   nsAttrKey attr(aNodeInfo->NamespaceID(), aNodeInfo->NameAtom());
 
-  if (!mAttributeCache.Get(attr, aReturn)) {
+  nsRefPtr<nsDOMAttribute> node;
+  if (!mAttributeCache.Get(attr, getter_AddRefs(node))) {
     nsAutoString value;
     // As we are removing the attribute we need to set the current value in
     // the attribute node.
     mContent->GetAttr(aNodeInfo->NamespaceID(), aNodeInfo->NameAtom(), value);
     nsCOMPtr<nsIDOMNode> newAttr = new nsDOMAttribute(nsnull, aNodeInfo, value);
     if (!newAttr) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
     newAttr.swap(*aReturn);
   }
   else {
-    nsCOMPtr<nsIAttribute> iAttr(do_QueryInterface(*aReturn));
-    NS_ASSERTION(iAttr, "non-nsIAttribute somehow made it into the hashmap?!");
-
     // Break link to map
-    iAttr->SetMap(nsnull);
+    node->SetMap(nsnull);
 
     // Remove from cache
     mAttributeCache.Remove(attr);
+
+    node.forget(aReturn);
   }
 
   return NS_OK;
 }
 
-nsIDOMNode*
+nsDOMAttribute*
 nsDOMAttributeMap::GetAttribute(nsINodeInfo* aNodeInfo)
 {
   NS_ASSERTION(aNodeInfo, "GetAttribute() called with aNodeInfo == nsnull!");
 
   nsAttrKey attr(aNodeInfo->NamespaceID(), aNodeInfo->NameAtom());
 
-  nsIDOMNode* node = mAttributeCache.GetWeak(attr);
+  nsDOMAttribute* node = mAttributeCache.GetWeak(attr);
   if (!node) {
-    nsCOMPtr<nsIDOMNode> newAttr =
+    nsRefPtr<nsDOMAttribute> newAttr =
       new nsDOMAttribute(this, aNodeInfo, EmptyString());
     if (newAttr && mAttributeCache.Put(attr, newAttr)) {
       node = newAttr;
     }
   }
 
   return node;
 }
 
-nsIDOMNode*
+nsDOMAttribute*
 nsDOMAttributeMap::GetNamedItem(const nsAString& aAttrName, nsresult *aResult)
 {
   *aResult = NS_OK;
 
   if (mContent) {
     nsCOMPtr<nsINodeInfo> ni =
       mContent->GetExistingAttrNameFromQName(aAttrName);
     if (ni) {
-      nsIDOMNode* node = GetAttribute(ni);
-      if (node) {
-        return node;
-      }
-
-      *aResult = NS_ERROR_OUT_OF_MEMORY;
+      return GetAttribute(ni);
     }
   }
 
   return nsnull;
 }
 
 NS_IMETHODIMP
 nsDOMAttributeMap::GetNamedItem(const nsAString& aAttrName,
@@ -277,22 +268,23 @@ nsDOMAttributeMap::SetNamedItemInternal(
   nsresult rv = NS_OK;
   *aReturn = nsnull;
   nsCOMPtr<nsIDOMNode> tmpReturn;
 
   if (mContent) {
     // XXX should check same-origin between mContent and aNode however
     // nsContentUtils::CheckSameOrigin can't deal with attributenodes yet
     
-    nsCOMPtr<nsIDOMAttr> attribute(do_QueryInterface(aNode));
     nsCOMPtr<nsIAttribute> iAttribute(do_QueryInterface(aNode));
-    if (!attribute || !iAttribute) {
+    if (!iAttribute) {
       return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
     }
 
+    nsDOMAttribute *attribute = static_cast<nsDOMAttribute*>(iAttribute.get());
+
     // Check that attribute is not owned by somebody else
     nsDOMAttributeMap* owner = iAttribute->GetMap();
     if (owner) {
       if (owner != this) {
         return NS_ERROR_DOM_INUSE_ATTRIBUTE_ERR;
       }
 
       // setting a preexisting attribute is a no-op, just return the same
@@ -384,45 +376,44 @@ nsDOMAttributeMap::RemoveNamedItem(const
   nsresult rv = NS_OK;
 
   if (mContent) {
     nsCOMPtr<nsINodeInfo> ni = mContent->GetExistingAttrNameFromQName(aName);
     if (!ni) {
       return NS_ERROR_DOM_NOT_FOUND_ERR;
     }
 
-    rv = GetAttribute(ni, aReturn);
-    NS_ENSURE_SUCCESS(rv, rv);
+    NS_ADDREF(*aReturn = GetAttribute(ni));
 
     // This removes the attribute node from the attribute map.
     rv = mContent->UnsetAttr(ni->NamespaceID(), ni->NameAtom(), PR_TRUE);
   }
 
   return rv;
 }
 
 
-nsIDOMNode*
+nsDOMAttribute*
 nsDOMAttributeMap::GetItemAt(PRUint32 aIndex, nsresult *aResult)
 {
   *aResult = NS_OK;
 
-  nsIDOMNode* node = nsnull;
+  nsDOMAttribute* node = nsnull;
 
   const nsAttrName* name;
   if (mContent && (name = mContent->GetAttrNameAt(aIndex))) {
     // Don't use the nodeinfo even if one exists since it can
     // have the wrong owner document.
     nsCOMPtr<nsINodeInfo> ni;
     ni = mContent->NodeInfo()->NodeInfoManager()->
       GetNodeInfo(name->LocalName(), name->GetPrefix(), name->NamespaceID());
     if (ni) {
       node = GetAttribute(ni);
     }
-    if (!node) {
+    else {
       *aResult = NS_ERROR_OUT_OF_MEMORY;
     }
   }
 
   return node;
 }
 
 NS_IMETHODIMP
@@ -488,17 +479,23 @@ nsDOMAttributeMap::GetNamedItemNSInterna
 
     if (nameSpaceID == attrNS &&
         nameAtom->Equals(aLocalName)) {
       nsCOMPtr<nsINodeInfo> ni;
       ni = mContent->NodeInfo()->NodeInfoManager()->
         GetNodeInfo(nameAtom, name->GetPrefix(), nameSpaceID);
       NS_ENSURE_TRUE(ni, NS_ERROR_OUT_OF_MEMORY);
 
-      return aRemove ? RemoveAttribute(ni, aReturn) : GetAttribute(ni, aReturn);
+      if (aRemove) {
+        return RemoveAttribute(ni, aReturn);
+      }
+
+      NS_ADDREF(*aReturn = GetAttribute(ni));
+
+      return NS_OK;
     }
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMAttributeMap::RemoveNamedItemNS(const nsAString& aNamespaceURI,
--- a/content/base/src/nsDOMAttributeMap.h
+++ b/content/base/src/nsDOMAttributeMap.h
@@ -40,17 +40,17 @@
  * Implementation of the |attributes| property of DOM Core's nsIDOMNode object.
  */
 
 #ifndef nsDOMAttributeMap_h___
 #define nsDOMAttributeMap_h___
 
 #include "nsIDOMNamedNodeMap.h"
 #include "nsString.h"
-#include "nsInterfaceHashtable.h"
+#include "nsRefPtrHashtable.h"
 #include "nsCycleCollectionParticipant.h"
 #include "prbit.h"
 #include "nsIDOMNode.h"
 
 class nsIAtom;
 class nsIContent;
 class nsDOMAttribute;
 class nsINodeInfo;
@@ -155,28 +155,28 @@ public:
    * Returns the number of attribute nodes currently in the map.
    * Note: this is just the number of cached attribute nodes, not the number of
    * attributes in mContent.
    *
    * @return The number of attribute nodes in the map.
    */
   PRUint32 Count() const;
 
-  typedef nsInterfaceHashtable<nsAttrHashKey, nsIDOMNode> AttrCache;
+  typedef nsRefPtrHashtable<nsAttrHashKey, nsDOMAttribute> AttrCache;
 
   /**
    * Enumerates over the attribute nodess in the map and calls aFunc for each
    * one. If aFunc returns PL_DHASH_STOP we'll stop enumerating at that point.
    *
    * @return The number of attribute nodes that aFunc was called for.
    */
   PRUint32 Enumerate(AttrCache::EnumReadFunction aFunc, void *aUserArg) const;
 
-  nsIDOMNode* GetItemAt(PRUint32 aIndex, nsresult *rv);
-  nsIDOMNode* GetNamedItem(const nsAString& aAttrName, nsresult *rv);
+  nsDOMAttribute* GetItemAt(PRUint32 aIndex, nsresult *rv);
+  nsDOMAttribute* GetNamedItem(const nsAString& aAttrName, nsresult *rv);
 
   static nsDOMAttributeMap* FromSupports(nsISupports* aSupports)
   {
 #ifdef DEBUG
     {
       nsCOMPtr<nsIDOMNamedNodeMap> map_qi = do_QueryInterface(aSupports);
 
       // If this assertion fires the QI implementation for the object in
@@ -212,34 +212,17 @@ private:
    * GetNamedItemNS() implementation taking |aRemove| for GetAttribute(),
    * which is used by RemoveNamedItemNS().
    */
   nsresult GetNamedItemNSInternal(const nsAString& aNamespaceURI,
                                   const nsAString& aLocalName,
                                   nsIDOMNode** aReturn,
                                   PRBool aRemove = PR_FALSE);
 
-  /**
-   * Returns an attribute, either by retrieving it from the cache or by
-   * creating a new one.
-   */
-  nsresult GetAttribute(nsINodeInfo*     aNodeInfo,
-                        nsIDOMNode**     aReturn)
-  {
-    *aReturn = GetAttribute(aNodeInfo);
-    if (!*aReturn) {
-      return NS_ERROR_OUT_OF_MEMORY;
-    }
-
-    NS_ADDREF(*aReturn);
-
-    return NS_OK;
-  }
-
-  nsIDOMNode* GetAttribute(nsINodeInfo*     aNodeInfo);
+  nsDOMAttribute* GetAttribute(nsINodeInfo* aNodeInfo);
 
   /**
    * Remove an attribute, returns the removed node.
    */
   nsresult RemoveAttribute(nsINodeInfo*     aNodeInfo,
                            nsIDOMNode**     aReturn);
 };
 
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -5614,22 +5614,22 @@ nsDocument::SetDocumentURI(const nsAStri
   // Not allowing this yet, need to think about security ramifications first.
   // We use mDocumentURI to get principals for this document.
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 static void BlastSubtreeToPieces(nsINode *aNode);
 
 PLDHashOperator
-BlastFunc(nsAttrHashKey::KeyType aKey, nsIDOMNode *aData, void* aUserArg)
+BlastFunc(nsAttrHashKey::KeyType aKey, nsDOMAttribute *aData, void* aUserArg)
 {
   nsCOMPtr<nsIAttribute> *attr =
     static_cast<nsCOMPtr<nsIAttribute>*>(aUserArg);
 
-  *attr = do_QueryInterface(aData);
+  *attr = aData;
 
   NS_ASSERTION(attr->get(),
                "non-nsIAttribute somehow made it into the hashmap?!");
 
   return PL_DHASH_STOP;
 }
 
 static void
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -198,16 +198,17 @@ GK_ATOM(chromemargin, "chromemargin")
 GK_ATOM(circ, "circ")
 GK_ATOM(circle, "circle")
 GK_ATOM(cite, "cite")
 GK_ATOM(_class, "class")
 GK_ATOM(classid, "classid")
 GK_ATOM(clear, "clear")
 GK_ATOM(click, "click")
 GK_ATOM(clickcount, "clickcount")
+GK_ATOM(clickthrough, "clickthrough")
 GK_ATOM(movetoclick, "movetoclick")
 GK_ATOM(clip, "clip")
 GK_ATOM(close, "close")
 GK_ATOM(closed, "closed")
 GK_ATOM(closemenu, "closemenu")
 GK_ATOM(coalesceduplicatearcs, "coalesceduplicatearcs")
 GK_ATOM(code, "code")
 GK_ATOM(codebase, "codebase")
--- a/content/base/src/nsNodeUtils.h
+++ b/content/base/src/nsNodeUtils.h
@@ -33,24 +33,23 @@
  * 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 nsNodeUtils_h___
 #define nsNodeUtils_h___
 
-#include "nsDOMAttributeMap.h"
-#include "nsIDOMNode.h"
 #include "nsINode.h"
 
 struct CharacterDataChangeInfo;
 struct JSContext;
 struct JSObject;
 class nsIVariant;
+class nsIDOMNode;
 class nsIDOMUserDataHandler;
 template<class E> class nsCOMArray;
 class nsCycleCollectionTraversalCallback;
 
 class nsNodeUtils
 {
 public:
   /**
@@ -245,19 +244,16 @@ public:
   /**
    * Release the UserData and UserDataHandlers for aNode.
    *
    * @param aNode the node to release the UserData and UserDataHandlers for
    */
   static void UnlinkUserData(nsINode *aNode);
 
 private:
-  friend PLDHashOperator
-    AdoptFunc(nsAttrHashKey::KeyType aKey, nsIDOMNode *aData, void* aUserArg);
-
   /**
    * Walks aNode, its attributes and, if aDeep is PR_TRUE, its descendant nodes.
    * If aClone is PR_TRUE the nodes will be cloned. If aNewNodeInfoManager is
    * not null, it is used to create new nodeinfos for the nodes. Also reparents
    * the XPConnect wrappers for the nodes in aNewScope if aCx is not null.
    * aNodesWithProperties will be filled with all the nodes that have
    * properties.
    *
--- a/content/events/src/nsEventStateManager.cpp
+++ b/content/events/src/nsEventStateManager.cpp
@@ -2698,16 +2698,37 @@ nsEventStateManager::DecideGestureEvent(
       }
     } //scrollableFrame
   } //ancestor chain
 
   aEvent->displayPanFeedback = displayPanFeedback;
   aEvent->panDirection = panDirection;
 }
 
+static bool
+NodeAllowsClickThrough(nsINode* aNode)
+{
+  while (aNode) {
+    if (aNode->IsElement() && aNode->AsElement()->IsXUL()) {
+      mozilla::dom::Element* element = aNode->AsElement();
+      static nsIContent::AttrValuesArray strings[] =
+        {&nsGkAtoms::always, &nsGkAtoms::never, nsnull};
+      switch (element->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::clickthrough,
+                                       strings, eCaseMatters)) {
+        case 0:
+          return true;
+        case 1:
+          return false;
+      }
+    }
+    aNode = nsContentUtils::GetCrossDocParentNode(aNode);
+  }
+  return true;
+}
+
 NS_IMETHODIMP
 nsEventStateManager::PostHandleEvent(nsPresContext* aPresContext,
                                      nsEvent *aEvent,
                                      nsIFrame* aTargetFrame,
                                      nsEventStatus* aStatus,
                                      nsIView* aView)
 {
   NS_ENSURE_ARG(aPresContext);
@@ -3161,16 +3182,29 @@ nsEventStateManager::PostHandleEvent(nsP
   case NS_MOUSE_ENTER:
     if (mCurrentTarget) {
       nsCOMPtr<nsIContent> targetContent;
       mCurrentTarget->GetContentForEvent(presContext, aEvent,
                                          getter_AddRefs(targetContent));
       SetContentState(targetContent, NS_EVENT_STATE_HOVER);
     }
     break;
+
+#ifdef XP_MACOSX
+  case NS_MOUSE_ACTIVATE:
+    if (mCurrentTarget) {
+      nsCOMPtr<nsIContent> targetContent;
+      mCurrentTarget->GetContentForEvent(presContext, aEvent,
+                                         getter_AddRefs(targetContent));
+      if (!NodeAllowsClickThrough(targetContent)) {
+        *aStatus = nsEventStatus_eConsumeNoDefault;
+      }
+    }
+    break;
+#endif
   }
 
   //Reset target frame to null to avoid mistargeting after reentrant event
   mCurrentTarget = nsnull;
 
   return ret;
 }
 
--- a/content/html/content/public/nsIFormControl.h
+++ b/content/html/content/public/nsIFormControl.h
@@ -40,16 +40,22 @@
 #include "nsISupports.h"
 class nsIDOMHTMLFormElement;
 class nsPresState;
 class nsIContent;
 class nsString;
 class nsIFormProcessor;
 class nsFormSubmission;
 
+namespace mozilla {
+namespace dom {
+class Element;
+} // namespace dom
+} // namespace mozilla
+
 enum FormControlsTypes {
   NS_FORM_FIELDSET = 1,
   NS_FORM_LABEL,
   NS_FORM_OUTPUT,
   NS_FORM_SELECT,
   NS_FORM_TEXTAREA,
   NS_FORM_OBJECT,
   eFormControlsWithoutSubTypesMax,
@@ -88,35 +94,35 @@ enum InputElementTypes {
   eInputElementTypesMax
 };
 
 PR_STATIC_ASSERT((PRUint32)eFormControlsWithoutSubTypesMax < (PRUint32)NS_FORM_BUTTON_ELEMENT);
 PR_STATIC_ASSERT((PRUint32)eButtonElementTypesMax < (PRUint32)NS_FORM_INPUT_ELEMENT);
 PR_STATIC_ASSERT((PRUint32)eInputElementTypesMax  < 1<<8);
 
 #define NS_IFORMCONTROL_IID   \
-{ 0x52dc1f0d, 0x1683, 0x4dd7, \
- { 0xae, 0x0a, 0xc4, 0x76, 0x10, 0x64, 0x2f, 0xa8 } }
+{ 0x0dc5083b, 0xb0a8, 0x48c4, \
+ { 0xb2, 0xeb, 0xc2, 0x4f, 0xfb, 0x7e, 0xc2, 0x8e } }
 
 /**
  * Interface which all form controls (e.g. buttons, checkboxes, text,
  * radio buttons, select, etc) implement in addition to their dom specific
  * interface.
  */
 class nsIFormControl : public nsISupports
 {
 public:
 
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_IFORMCONTROL_IID)
 
   /**
    * Get the form for this form control.
-   * @param aForm the form [OUT]
+   * @return the form
    */
-  NS_IMETHOD GetForm(nsIDOMHTMLFormElement** aForm) = 0;
+  virtual mozilla::dom::Element *GetFormElement() = 0;
 
   /**
    * Set the form for this form control.
    * @param aForm the form.  This must not be null.
    *
    * @note that when setting the form the control is not added to the
    * form.  It adds itself when it gets bound to the tree thereafter,
    * so that it can be properly sorted with the other controls in the
--- a/content/html/content/src/nsGenericHTMLElement.cpp
+++ b/content/html/content/src/nsGenericHTMLElement.cpp
@@ -106,16 +106,19 @@
 #include "nsIEditor.h"
 #include "nsIEditorIMESupport.h"
 #include "nsEventDispatcher.h"
 #include "nsLayoutUtils.h"
 #include "nsContentCreatorFunctions.h"
 #include "mozAutoDocUpdate.h"
 #include "nsHtml5Module.h"
 #include "nsITextControlElement.h"
+#include "mozilla/dom/Element.h"
+
+using namespace mozilla::dom;
 
 #include "nsThreadUtils.h"
 
 class nsINodeInfo;
 class nsIDOMNodeList;
 class nsRuleWalker;
 
 // XXX todo: add in missing out-of-memory checks
@@ -2374,17 +2377,23 @@ nsGenericHTMLFormElement::ClearForm(PRBo
       mForm->RemoveElementFromTable(this, idVal);
     }
   }
 
   UnsetFlags(ADDED_TO_FORM);
   mForm = nsnull;
 }
 
-NS_IMETHODIMP
+Element*
+nsGenericHTMLFormElement::GetFormElement()
+{
+  return mForm;
+}
+
+nsresult
 nsGenericHTMLFormElement::GetForm(nsIDOMHTMLFormElement** aForm)
 {
   NS_ENSURE_ARG_POINTER(aForm);
   NS_IF_ADDREF(*aForm = mForm);
   return NS_OK;
 }
 
 PRUint32
--- a/content/html/content/src/nsGenericHTMLElement.h
+++ b/content/html/content/src/nsGenericHTMLElement.h
@@ -798,20 +798,22 @@ public:
   virtual ~nsGenericHTMLFormElement();
 
   NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr);
 
   virtual PRBool IsNodeOfType(PRUint32 aFlags) const;
   virtual void SaveSubtreeState();
 
   // nsIFormControl
-  NS_IMETHOD GetForm(nsIDOMHTMLFormElement** aForm);
+  virtual mozilla::dom::Element* GetFormElement();
   virtual void SetForm(nsIDOMHTMLFormElement* aForm);
   virtual void ClearForm(PRBool aRemoveFromForm, PRBool aNotify);
 
+  nsresult GetForm(nsIDOMHTMLFormElement** aForm);
+
   NS_IMETHOD SaveState()
   {
     return NS_OK;
   }
   
   virtual PRBool RestoreState(nsPresState* aState)
   {
     return PR_FALSE;
--- a/content/html/content/src/nsHTMLFormElement.h
+++ b/content/html/content/src/nsHTMLFormElement.h
@@ -43,16 +43,17 @@
 #include "nsIDOMNSHTMLFormElement.h"
 #include "nsIWebProgressListener.h"
 #include "nsIRadioGroupContainer.h"
 #include "nsIURI.h"
 #include "nsIWeakReferenceUtils.h"
 #include "nsPIDOMWindow.h"
 #include "nsUnicharUtils.h"
 #include "nsThreadUtils.h"
+#include "nsInterfaceHashtable.h"
 
 class nsFormControlList;
 
 /**
  * hashkey wrapper using nsAString KeyType
  *
  * @see nsTHashtable::EntryType for specification
  */
--- a/content/html/content/src/nsHTMLLegendElement.cpp
+++ b/content/html/content/src/nsHTMLLegendElement.cpp
@@ -30,92 +30,29 @@
  * 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 "nsIDOMHTMLLegendElement.h"
+#include "nsHTMLLegendElement.h"
 #include "nsIDOMHTMLFormElement.h"
 #include "nsIDOMEventTarget.h"
-#include "nsGenericHTMLElement.h"
 #include "nsGkAtoms.h"
 #include "nsStyleConsts.h"
 #include "nsIForm.h"
 #include "nsIFormControl.h"
 #include "nsIEventStateManager.h"
 #include "nsIDocument.h"
 #include "nsPIDOMWindow.h"
 #include "nsFocusManager.h"
 #include "nsIFrame.h"
 
-class nsHTMLLegendElement : public nsGenericHTMLElement,
-                            public nsIDOMHTMLLegendElement
-{
-public:
-  nsHTMLLegendElement(nsINodeInfo *aNodeInfo);
-  virtual ~nsHTMLLegendElement();
-
-  // nsISupports
-  NS_DECL_ISUPPORTS_INHERITED
-
-  // nsIDOMNode
-  NS_FORWARD_NSIDOMNODE(nsGenericHTMLElement::)
-
-  // nsIDOMElement
-  NS_FORWARD_NSIDOMELEMENT(nsGenericHTMLElement::)
-
-  // nsIDOMHTMLElement
-  NS_FORWARD_NSIDOMHTMLELEMENT(nsGenericHTMLElement::)
-
-  // nsIDOMHTMLLegendElement
-  NS_DECL_NSIDOMHTMLLEGENDELEMENT
-
-  // nsGenericHTMLElement
-  NS_IMETHODIMP Focus();
-
-  virtual void PerformAccesskey(PRBool aKeyCausesActivation,
-                                PRBool aIsTrustedEvent);
-
-  // nsIContent
-  virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
-                              nsIContent* aBindingParent,
-                              PRBool aCompileEventHandlers);
-  virtual void UnbindFromTree(PRBool aDeep = PR_TRUE,
-                              PRBool aNullParent = PR_TRUE);
-  virtual PRBool ParseAttribute(PRInt32 aNamespaceID,
-                                nsIAtom* aAttribute,
-                                const nsAString& aValue,
-                                nsAttrValue& aResult);
-  virtual nsChangeHint GetAttributeChangeHint(const nsIAtom* aAttribute,
-                                              PRInt32 aModType) const;
-  nsresult SetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
-                   const nsAString& aValue, PRBool aNotify)
-  {
-    return SetAttr(aNameSpaceID, aName, nsnull, aValue, aNotify);
-  }
-  virtual nsresult SetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
-                           nsIAtom* aPrefix, const nsAString& aValue,
-                           PRBool aNotify);
-  virtual nsresult UnsetAttr(PRInt32 aNameSpaceID, nsIAtom* aAttribute,
-                             PRBool aNotify);
-
-  virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
-
-protected:
-  /**
-   * Get the fieldset content element that contains this legend.
-   * Returns null if there is no fieldset containing this legend.
-   */
-  nsIContent* GetFieldSet();
-};
-
-
 NS_IMPL_NS_NEW_HTML_ELEMENT(Legend)
 
 
 nsHTMLLegendElement::nsHTMLLegendElement(nsINodeInfo *aNodeInfo)
   : nsGenericHTMLElement(aNodeInfo)
 {
 }
 
@@ -142,21 +79,19 @@ NS_HTML_CONTENT_INTERFACE_TABLE_TAIL_CLA
 
 
 NS_IMPL_ELEMENT_CLONE(nsHTMLLegendElement)
 
 
 NS_IMETHODIMP
 nsHTMLLegendElement::GetForm(nsIDOMHTMLFormElement** aForm)
 {
-  *aForm = nsnull;
+  Element *form = GetFormElement();
 
-  nsCOMPtr<nsIFormControl> fieldsetControl = do_QueryInterface(GetFieldSet());
-
-  return fieldsetControl ? fieldsetControl->GetForm(aForm) : NS_OK;
+  return form ? CallQueryInterface(form, aForm) : NS_OK;
 }
 
 
 NS_IMPL_STRING_ATTR(nsHTMLLegendElement, AccessKey, accesskey)
 NS_IMPL_STRING_ATTR(nsHTMLLegendElement, Align, align)
 
 // this contains center, because IE4 does
 static const nsAttrValue::EnumTable kAlignTable[] = {
new file mode 100644
--- /dev/null
+++ b/content/html/content/src/nsHTMLLegendElement.h
@@ -0,0 +1,119 @@
+/* -*- 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 Communicator client 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):
+ *   Mats Palmgren <mats.palmgren@bredband.net>
+ *
+ * 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 nsHTMLLegendElement_h___
+#define nsHTMLLegendElement_h___
+
+#include "nsIDOMHTMLLegendElement.h"
+#include "nsGenericHTMLElement.h"
+
+class nsHTMLLegendElement : public nsGenericHTMLElement,
+                            public nsIDOMHTMLLegendElement
+{
+public:
+  nsHTMLLegendElement(nsINodeInfo *aNodeInfo);
+  virtual ~nsHTMLLegendElement();
+
+  static nsHTMLLegendElement* FromContent(nsIContent *aContent)
+  {
+    if (aContent->IsHTML() && aContent->Tag() == nsGkAtoms::legend)
+      return static_cast<nsHTMLLegendElement*>(aContent);
+    return nsnull;
+  }
+
+  // nsISupports
+  NS_DECL_ISUPPORTS_INHERITED
+
+  // nsIDOMNode
+  NS_FORWARD_NSIDOMNODE(nsGenericHTMLElement::)
+
+  // nsIDOMElement
+  NS_FORWARD_NSIDOMELEMENT(nsGenericHTMLElement::)
+
+  // nsIDOMHTMLElement
+  NS_FORWARD_NSIDOMHTMLELEMENT(nsGenericHTMLElement::)
+
+  // nsIDOMHTMLLegendElement
+  NS_DECL_NSIDOMHTMLLEGENDELEMENT
+
+  // nsGenericHTMLElement
+  NS_IMETHODIMP Focus();
+
+  virtual void PerformAccesskey(PRBool aKeyCausesActivation,
+                                PRBool aIsTrustedEvent);
+
+  // nsIContent
+  virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
+                              nsIContent* aBindingParent,
+                              PRBool aCompileEventHandlers);
+  virtual void UnbindFromTree(PRBool aDeep = PR_TRUE,
+                              PRBool aNullParent = PR_TRUE);
+  virtual PRBool ParseAttribute(PRInt32 aNamespaceID,
+                                nsIAtom* aAttribute,
+                                const nsAString& aValue,
+                                nsAttrValue& aResult);
+  virtual nsChangeHint GetAttributeChangeHint(const nsIAtom* aAttribute,
+                                              PRInt32 aModType) const;
+  nsresult SetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
+                   const nsAString& aValue, PRBool aNotify)
+  {
+    return SetAttr(aNameSpaceID, aName, nsnull, aValue, aNotify);
+  }
+  virtual nsresult SetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
+                           nsIAtom* aPrefix, const nsAString& aValue,
+                           PRBool aNotify);
+  virtual nsresult UnsetAttr(PRInt32 aNameSpaceID, nsIAtom* aAttribute,
+                             PRBool aNotify);
+
+  virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
+
+  mozilla::dom::Element *GetFormElement()
+  {
+    nsCOMPtr<nsIFormControl> fieldsetControl = do_QueryInterface(GetFieldSet());
+
+    return fieldsetControl ? fieldsetControl->GetFormElement() : nsnull;
+  }
+
+protected:
+  /**
+   * Get the fieldset content element that contains this legend.
+   * Returns null if there is no fieldset containing this legend.
+   */
+  nsIContent* GetFieldSet();
+};
+
+#endif /* nsHTMLLegendElement_h___ */
--- a/content/html/content/src/nsHTMLOptionElement.cpp
+++ b/content/html/content/src/nsHTMLOptionElement.cpp
@@ -129,17 +129,18 @@ NS_IMPL_ELEMENT_CLONE(nsHTMLOptionElemen
 
 
 NS_IMETHODIMP
 nsHTMLOptionElement::GetForm(nsIDOMHTMLFormElement** aForm)
 {
   NS_ENSURE_ARG_POINTER(aForm);
   *aForm = nsnull;
 
-  nsCOMPtr<nsIFormControl> selectControl = do_QueryInterface(GetSelect());
+  nsCOMPtr<nsIDOMHTMLSelectElement> selectControl =
+    do_QueryInterface(GetSelect());
 
   if (selectControl) {
     selectControl->GetForm(aForm);
   }
 
   return NS_OK;
 }
 
--- a/content/html/content/src/nsHTMLOptionElement.h
+++ b/content/html/content/src/nsHTMLOptionElement.h
@@ -1,112 +1,120 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=2 sw=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 Communicator client 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):
- *   Pierre Phaneuf <pp@ludusdesign.com>
- *   Mats Palmgren <mats.palmgren@bredband.net>
- *
- * 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 nsHTMLOptionElement_h__
-#define nsHTMLOptionElement_h__
-
-#include "nsGenericHTMLElement.h"
-#include "nsIDOMHTMLOptionElement.h"
-#include "nsIDOMNSHTMLOptionElement.h"
-#include "nsIJSNativeInitializer.h"
-
-class nsHTMLOptionElement : public nsGenericHTMLElement,
-                            public nsIDOMHTMLOptionElement,
-                            public nsIDOMNSHTMLOptionElement,
-                            public nsIJSNativeInitializer
-{
-public:
-  nsHTMLOptionElement(nsINodeInfo *aNodeInfo);
-  virtual ~nsHTMLOptionElement();
-
-  // nsISupports
-  NS_DECL_ISUPPORTS_INHERITED
-
-  // nsIDOMNode
-  NS_FORWARD_NSIDOMNODE(nsGenericHTMLElement::)
-
-  // nsIDOMElement
-  NS_FORWARD_NSIDOMELEMENT(nsGenericHTMLElement::)
-
-  // nsIDOMHTMLElement
-  NS_FORWARD_NSIDOMHTMLELEMENT(nsGenericHTMLElement::)
-
-  // nsIDOMHTMLOptionElement
-  NS_DECL_NSIDOMHTMLOPTIONELEMENT
-
-  // nsIDOMNSHTMLOptionElement
-  NS_IMETHOD SetText(const nsAString & aText); 
-
-  // nsIJSNativeInitializer
-  NS_IMETHOD Initialize(nsISupports* aOwner, JSContext* aContext,
-                        JSObject *aObj, PRUint32 argc, jsval *argv);
-
-  virtual nsChangeHint GetAttributeChangeHint(const nsIAtom* aAttribute,
-                                              PRInt32 aModType) const;
-
-  virtual nsresult BeforeSetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
-                                 const nsAString* aValue, PRBool aNotify);
-  
-  void SetSelectedInternal(PRBool aValue, PRBool aNotify);
-
-  // nsIContent
-  virtual PRInt32 IntrinsicState() const;
-
-  virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
-
-  nsresult CopyInnerTo(nsGenericElement* aDest) const;
-
-protected:
-  /**
-   * Get the select content element that contains this option, this
-   * intentionally does not return nsresult, all we care about is if
-   * there's a select associated with this option or not.
-   * @param aSelectElement the select element (out param)
-   */
-  nsIContent* GetSelect();
-
-  PRPackedBool mSelectedChanged;
-  PRPackedBool mIsSelected;
-
-  // True only while we're under the SetOptionsSelectedByIndex call when our
-  // "selected" attribute is changing and mSelectedChanged is false.
-  PRPackedBool mIsInSetDefaultSelected;
-};
-
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 sw=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 Communicator client 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):
+ *   Pierre Phaneuf <pp@ludusdesign.com>
+ *   Mats Palmgren <mats.palmgren@bredband.net>
+ *
+ * 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 nsHTMLOptionElement_h__
+#define nsHTMLOptionElement_h__
+
+#include "nsGenericHTMLElement.h"
+#include "nsIDOMHTMLOptionElement.h"
+#include "nsIDOMNSHTMLOptionElement.h"
+#include "nsIJSNativeInitializer.h"
+
+class nsHTMLOptionElement : public nsGenericHTMLElement,
+                            public nsIDOMHTMLOptionElement,
+                            public nsIDOMNSHTMLOptionElement,
+                            public nsIJSNativeInitializer
+{
+public:
+  nsHTMLOptionElement(nsINodeInfo *aNodeInfo);
+  virtual ~nsHTMLOptionElement();
+
+  /** Typesafe, non-refcounting cast from nsIContent.  Cheaper than QI. **/
+  static nsHTMLOptionElement* FromContent(nsIContent *aContent)
+  {
+    if (aContent->NodeInfo()->Equals(nsGkAtoms::option, kNameSpaceID_XHTML))
+      return static_cast<nsHTMLOptionElement*>(aContent);
+    return nsnull;
+  }
+
+  // nsISupports
+  NS_DECL_ISUPPORTS_INHERITED
+
+  // nsIDOMNode
+  NS_FORWARD_NSIDOMNODE(nsGenericHTMLElement::)
+
+  // nsIDOMElement
+  NS_FORWARD_NSIDOMELEMENT(nsGenericHTMLElement::)
+
+  // nsIDOMHTMLElement
+  NS_FORWARD_NSIDOMHTMLELEMENT(nsGenericHTMLElement::)
+
+  // nsIDOMHTMLOptionElement
+  NS_DECL_NSIDOMHTMLOPTIONELEMENT
+
+  // nsIDOMNSHTMLOptionElement
+  NS_IMETHOD SetText(const nsAString & aText); 
+
+  // nsIJSNativeInitializer
+  NS_IMETHOD Initialize(nsISupports* aOwner, JSContext* aContext,
+                        JSObject *aObj, PRUint32 argc, jsval *argv);
+
+  virtual nsChangeHint GetAttributeChangeHint(const nsIAtom* aAttribute,
+                                              PRInt32 aModType) const;
+
+  virtual nsresult BeforeSetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
+                                 const nsAString* aValue, PRBool aNotify);
+  
+  void SetSelectedInternal(PRBool aValue, PRBool aNotify);
+
+  // nsIContent
+  virtual PRInt32 IntrinsicState() const;
+
+  virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
+
+  nsresult CopyInnerTo(nsGenericElement* aDest) const;
+
+protected:
+  /**
+   * Get the select content element that contains this option, this
+   * intentionally does not return nsresult, all we care about is if
+   * there's a select associated with this option or not.
+   * @param aSelectElement the select element (out param)
+   */
+  nsIContent* GetSelect();
+
+  PRPackedBool mSelectedChanged;
+  PRPackedBool mIsSelected;
+
+  // True only while we're under the SetOptionsSelectedByIndex call when our
+  // "selected" attribute is changing and mSelectedChanged is false.
+  PRPackedBool mIsInSetDefaultSelected;
+};
+
 #endif
--- a/content/html/content/src/nsHTMLSelectElement.cpp
+++ b/content/html/content/src/nsHTMLSelectElement.cpp
@@ -33,16 +33,17 @@
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsHTMLSelectElement.h"
+#include "nsHTMLOptionElement.h"
 #include "nsIDOMEventTarget.h"
 #include "nsContentCreatorFunctions.h"
 #include "nsGkAtoms.h"
 #include "nsStyleConsts.h"
 #include "nsLayoutUtils.h"
 #include "nsMappedAttributes.h"
 #include "nsIForm.h"
 #include "nsFormSubmission.h"
@@ -64,16 +65,19 @@
 #include "nsIComboboxControlFrame.h"
 #include "nsIListControlFrame.h"
 #include "nsIFrame.h"
 
 #include "nsDOMError.h"
 #include "nsServiceManagerUtils.h"
 #include "nsRuleData.h"
 #include "nsEventDispatcher.h"
+#include "mozilla/dom/Element.h"
+
+using namespace mozilla::dom;
 
 NS_IMPL_ISUPPORTS1(nsSelectState, nsSelectState)
 NS_DEFINE_STATIC_IID_ACCESSOR(nsSelectState, NS_SELECT_STATE_IID)
 
 //----------------------------------------------------------------------
 //
 // nsSafeOptionListMutation
 //
@@ -346,17 +350,17 @@ nsHTMLSelectElement::InsertOptionsIntoLi
                                                   PRInt32* aInsertIndex,
                                                   PRInt32 aDepth)
 {
   // We *assume* here that someone's brain has not gone horribly
   // wrong by putting <option> inside of <option>.  I'm sorry, I'm
   // just not going to look for an option inside of an option.
   // Sue me.
 
-  nsCOMPtr<nsIDOMHTMLOptionElement> optElement(do_QueryInterface(aOptions));
+  nsHTMLOptionElement *optElement = nsHTMLOptionElement::FromContent(aOptions);
   if (optElement) {
     nsresult rv = mOptions->InsertOptionAt(optElement, *aInsertIndex);
     NS_ENSURE_SUCCESS(rv, rv);
     (*aInsertIndex)++;
     return NS_OK;
   }
 
   // If it's at the top level, then we just found out there are non-options
@@ -564,17 +568,17 @@ nsHTMLSelectElement::GetOptionIndexAfter
 
   return retval;
 }
 
 PRInt32
 nsHTMLSelectElement::GetFirstOptionIndex(nsIContent* aOptions)
 {
   PRInt32 listIndex = -1;
-  nsCOMPtr<nsIDOMHTMLOptionElement> optElement(do_QueryInterface(aOptions));
+  nsHTMLOptionElement *optElement = nsHTMLOptionElement::FromContent(aOptions);
   if (optElement) {
     GetOptionIndex(optElement, 0, PR_TRUE, &listIndex);
     // If you nested stuff under the option, you're just plain
     // screwed.  *I'm* not going to aid and abet your evil deed.
     return listIndex;
   }
 
   listIndex = GetFirstChildOptionIndex(aOptions, 0, aOptions->GetChildCount());
@@ -666,18 +670,17 @@ nsHTMLSelectElement::Remove(PRInt32 aInd
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsHTMLSelectElement::GetOptions(nsIDOMHTMLOptionsCollection** aValue)
 {
-  *aValue = mOptions;
-  NS_IF_ADDREF(*aValue);
+  NS_IF_ADDREF(*aValue = GetOptions());
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsHTMLSelectElement::GetType(nsAString& aType)
 {
   PRBool isMultiple;
@@ -790,17 +793,18 @@ nsHTMLSelectElement::SetSelectedIndex(PR
   return rv;
 }
 
 NS_IMETHODIMP
 nsHTMLSelectElement::GetOptionIndex(nsIDOMHTMLOptionElement* aOption,
                                     PRInt32 aStartIndex, PRBool aForward,
                                     PRInt32* aIndex)
 {
-  return mOptions->GetOptionIndex(aOption, aStartIndex, aForward, aIndex);
+  nsCOMPtr<Element> option = do_QueryInterface(aOption);
+  return mOptions->GetOptionIndex(option, aStartIndex, aForward, aIndex);
 }
 
 PRBool
 nsHTMLSelectElement::IsOptionSelectedByIndex(PRInt32 aIndex)
 {
   nsIDOMHTMLOptionElement *option = mOptions->ItemAsOption(aIndex);
   PRBool isSelected = PR_FALSE;
   if (option) {
@@ -1695,17 +1699,17 @@ void nsHTMLSelectElement::DispatchConten
   }
 }
 
 static void
 AddOptionsRecurse(nsIContent* aRoot, nsHTMLOptionCollection* aArray)
 {
   nsIContent* child;
   for(PRUint32 i = 0; (child = aRoot->GetChildAt(i)); ++i) {
-    nsCOMPtr<nsIDOMHTMLOptionElement> opt = do_QueryInterface(child);
+    nsHTMLOptionElement *opt = nsHTMLOptionElement::FromContent(child);
     if (opt) {
       // If we fail here, then at least we've tried our best
       aArray->AppendOption(opt);
     }
     else if (IsOptGroup(child)) {
       AddOptionsRecurse(child, aArray);
     }
   }
@@ -1768,17 +1772,17 @@ nsHTMLOptionCollection::~nsHTMLOptionCol
 void
 nsHTMLOptionCollection::DropReference()
 {
   // Drop our (non ref-counted) reference
   mSelect = nsnull;
 }
 
 nsresult
-nsHTMLOptionCollection::GetOptionIndex(nsIDOMHTMLOptionElement* aOption,
+nsHTMLOptionCollection::GetOptionIndex(mozilla::dom::Element* aOption,
                                        PRInt32 aStartIndex,
                                        PRBool aForward,
                                        PRInt32* aIndex)
 {
   PRInt32 index;
 
   // Make the common case fast
   if (aStartIndex == 0 && aForward) {
@@ -1786,36 +1790,42 @@ nsHTMLOptionCollection::GetOptionIndex(n
     if (index == -1) {
       return NS_ERROR_FAILURE;
     }
     
     *aIndex = index;
     return NS_OK;
   }
 
-  PRInt32 high = mElements.Count();
+  PRInt32 high = mElements.Length();
   PRInt32 step = aForward ? 1 : -1;
 
   for (index = aStartIndex; index < high && index > -1; index += step) {
     if (mElements[index] == aOption) {
       *aIndex = index;
       return NS_OK;
     }
   }
 
   return NS_ERROR_FAILURE;
 }
 
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsHTMLOptionCollection)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsHTMLOptionCollection)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMARRAY(mElements)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSTARRAY(mElements)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsHTMLOptionCollection)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mElements)
+    {
+      PRUint32 i;
+      for (i = 0; i < tmp->mElements.Length(); ++i) {
+        NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mElements[i]");
+        cb.NoteXPCOMChild(static_cast<Element*>(tmp->mElements[i]));
+      }
+    }
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 // nsISupports
 
 DOMCI_DATA(HTMLOptionsCollection, nsHTMLOptionCollection)
 
 // QueryInterface implementation for nsHTMLOptionCollection
 NS_INTERFACE_TABLE_HEAD(nsHTMLOptionCollection)
@@ -1835,17 +1845,17 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE_AMBIGUO
                                            nsIHTMLCollection)
 
 
 // nsIDOMNSHTMLOptionCollection interface
 
 NS_IMETHODIMP
 nsHTMLOptionCollection::GetLength(PRUint32* aLength)
 {
-  *aLength = mElements.Count();
+  *aLength = mElements.Length();
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsHTMLOptionCollection::SetLength(PRUint32 aLength)
 {
   if (!mSelect) {
@@ -1857,45 +1867,47 @@ nsHTMLOptionCollection::SetLength(PRUint
 
 NS_IMETHODIMP
 nsHTMLOptionCollection::SetOption(PRInt32 aIndex,
                                   nsIDOMHTMLOptionElement *aOption)
 {
   if (aIndex < 0 || !mSelect) {
     return NS_OK;
   }
-  
+
   // if the new option is null, just remove this option.  Note that it's safe
   // to pass a too-large aIndex in here.
   if (!aOption) {
     mSelect->Remove(aIndex);
 
     // We're done.
     return NS_OK;
   }
 
   nsresult rv = NS_OK;
 
+  PRUint32 index = PRUint32(aIndex);
+
   // Now we're going to be setting an option in our collection
-  if (aIndex > mElements.Count()) {
+  if (index > mElements.Length()) {
     // Fill our array with blank options up to (but not including, since we're
     // about to change it) aIndex, for compat with other browsers.
-    rv = SetLength(aIndex);
+    rv = SetLength(index);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
-  NS_ASSERTION(aIndex <= mElements.Count(), "SetLength lied");
+  NS_ASSERTION(index <= mElements.Length(), "SetLength lied");
   
   nsCOMPtr<nsIDOMNode> ret;
-  if (aIndex == mElements.Count()) {
+  if (index == mElements.Length()) {
     rv = mSelect->AppendChild(aOption, getter_AddRefs(ret));
   } else {
     // Find the option they're talking about and replace it
     // hold a strong reference to follow COM rules.
-    nsCOMPtr<nsIDOMHTMLOptionElement> refChild = mElements.SafeObjectAt(aIndex);
+    nsCOMPtr<nsIDOMHTMLOptionElement> refChild = ItemAsOption(index);
     NS_ENSURE_TRUE(refChild, NS_ERROR_UNEXPECTED);
 
     nsCOMPtr<nsIDOMNode> parent;
     refChild->GetParentNode(getter_AddRefs(parent));
     if (parent) {
       rv = parent->ReplaceChild(aOption, refChild, getter_AddRefs(ret));
     }
   }
@@ -1929,23 +1941,31 @@ nsHTMLOptionCollection::Item(PRUint32 aI
 
     return rv;
   }
 
   return CallQueryInterface(item, aReturn);
 }
 
 nsISupports*
+nsHTMLOptionCollection::GetNodeAt(PRUint32 aIndex, nsresult* aResult)
+{
+  *aResult = NS_OK;
+
+  return static_cast<Element*>(ItemAsOption(aIndex));
+}
+
+nsISupports*
 nsHTMLOptionCollection::GetNamedItem(const nsAString& aName, nsresult* aResult)
 {
   *aResult = NS_OK;
 
-  PRInt32 count = mElements.Count();
-  for (PRInt32 i = 0; i < count; i++) {
-    nsCOMPtr<nsIContent> content = do_QueryInterface(mElements.ObjectAt(i));
+  PRUint32 count = mElements.Length();
+  for (PRUint32 i = 0; i < count; i++) {
+    nsIContent *content = ItemAsOption(i);
     if (content &&
         (content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name, aName,
                               eCaseMatters) ||
          content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::id, aName,
                               eCaseMatters))) {
       return content;
     }
   }
--- a/content/html/content/src/nsHTMLSelectElement.h
+++ b/content/html/content/src/nsHTMLSelectElement.h
@@ -55,17 +55,17 @@
 #include "nsIHTMLCollection.h"
 
 // PresState
 #include "nsXPCOM.h"
 #include "nsPresState.h"
 #include "nsIComponentManager.h"
 #include "nsCheapSets.h"
 #include "nsLayoutErrors.h"
-
+#include "nsHTMLOptionElement.h"
 
 class nsHTMLSelectElement;
 
 /**
  * The collection of options in the select (what you get back when you do
  * select.options in DOM)
  */
 class nsHTMLOptionCollection: public nsIDOMHTMLOptionsCollection,
@@ -82,88 +82,83 @@ public:
   NS_DECL_NSIDOMHTMLOPTIONSCOLLECTION
 
   // nsIDOMNSHTMLOptionCollection interface
   NS_DECL_NSIDOMNSHTMLOPTIONCOLLECTION
 
   // nsIDOMHTMLCollection interface, all its methods are defined in
   // nsIDOMHTMLOptionsCollection
 
-  virtual nsISupports* GetNodeAt(PRUint32 aIndex, nsresult* aResult)
-  {
-    *aResult = NS_OK;
-
-    return mElements.SafeObjectAt(aIndex);
-  }
+  virtual nsISupports* GetNodeAt(PRUint32 aIndex, nsresult* aResult);
   virtual nsISupports* GetNamedItem(const nsAString& aName, nsresult* aResult);
 
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsHTMLOptionCollection,
                                            nsIHTMLCollection)
 
   // Helpers for nsHTMLSelectElement
   /**
    * Insert an option
    * @param aOption the option to insert
    * @param aIndex the index to insert at
    */
-  PRBool InsertOptionAt(nsIDOMHTMLOptionElement* aOption, PRInt32 aIndex)
+  PRBool InsertOptionAt(nsHTMLOptionElement* aOption, PRUint32 aIndex)
   {
-    return mElements.InsertObjectAt(aOption, aIndex);
+    return !!mElements.InsertElementAt(aIndex, aOption);
   }
 
   /**
    * Remove an option
    * @param aIndex the index of the option to remove
    */
-  void RemoveOptionAt(PRInt32 aIndex)
+  void RemoveOptionAt(PRUint32 aIndex)
   {
-    mElements.RemoveObjectAt(aIndex);
+    mElements.RemoveElementAt(aIndex);
   }
 
   /**
    * Get the option at the index
    * @param aIndex the index
    * @param aReturn the option returned [OUT]
    */
-  nsIDOMHTMLOptionElement *ItemAsOption(PRInt32 aIndex)
+  nsHTMLOptionElement *ItemAsOption(PRUint32 aIndex)
   {
-    return mElements.SafeObjectAt(aIndex);
+    return mElements.SafeElementAt(aIndex, nsRefPtr<nsHTMLOptionElement>());
   }
 
   /**
    * Clears out all options
    */
   void Clear()
   {
     mElements.Clear();
   }
 
   /**
    * Append an option to end of array
    */
-  PRBool AppendOption(nsIDOMHTMLOptionElement* aOption)
+  PRBool AppendOption(nsHTMLOptionElement* aOption)
   {
-    return mElements.AppendObject(aOption);
+    return !!mElements.AppendElement(aOption);
   }
 
   /**
    * Drop the reference to the select.  Called during select destruction.
    */
   void DropReference();
 
   /**
    * See nsISelectElement.idl for documentation on this method
    */
-  nsresult GetOptionIndex(nsIDOMHTMLOptionElement* aOption,
+  nsresult GetOptionIndex(mozilla::dom::Element* aOption,
                           PRInt32 aStartIndex, PRBool aForward,
                           PRInt32* aIndex);
 
 private:
   /** The list of options (holds strong references) */
-  nsCOMArray<nsIDOMHTMLOptionElement> mElements;
+  nsTArray<nsRefPtr<nsHTMLOptionElement> > mElements;
   /** The select element that contains this array */
   nsHTMLSelectElement* mSelect;
 };
 
 #define NS_SELECT_STATE_IID                        \
 { /* 4db54c7c-d159-455f-9d8e-f60ee466dbf3 */       \
   0x4db54c7c,                                      \
   0xd159,                                          \
@@ -304,16 +299,26 @@ public:
                                               PRInt32 aModType) const;
   NS_IMETHOD_(PRBool) IsAttributeMapped(const nsIAtom* aAttribute) const;
 
   virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
 
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED_NO_UNLINK(nsHTMLSelectElement,
                                                      nsGenericHTMLFormElement)
 
+  nsHTMLOptionCollection *GetOptions()
+  {
+    return mOptions;
+  }
+
+  static nsHTMLSelectElement *FromSupports(nsISupports *aSupports)
+  {
+    return static_cast<nsHTMLSelectElement*>(static_cast<nsINode*>(aSupports));
+  }
+
 protected:
   friend class nsSafeOptionListMutation;
 
   // Helper Methods
   /**
    * Check whether the option specified by the index is selected
    * @param aIndex the index
    * @return whether the option at the index is selected
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -209,17 +209,17 @@
 // includes needed for the prototype chain interfaces
 #include "nsIDOMNavigator.h"
 #include "nsIDOMBarProp.h"
 #include "nsIDOMScreen.h"
 #include "nsIDOMDocumentType.h"
 #include "nsIDOMDOMImplementation.h"
 #include "nsIDOMDocumentFragment.h"
 #include "nsIDOMDocumentEvent.h"
-#include "nsIDOMAttr.h"
+#include "nsDOMAttribute.h"
 #include "nsIDOMText.h"
 #include "nsIDOM3Text.h"
 #include "nsIDOMComment.h"
 #include "nsIDOMCDATASection.h"
 #include "nsIDOMProcessingInstruction.h"
 #include "nsIDOMNotation.h"
 #include "nsIDOMNSEvent.h"
 #include "nsIDOMDataContainerEvent.h"
@@ -471,16 +471,18 @@
 // Simple gestures include
 #include "nsIDOMSimpleGestureEvent.h"
 
 #include "nsIDOMNSMouseEvent.h"
 
 #include "nsIEventListenerService.h"
 #include "nsIFrameMessageManager.h"
 #include "mozilla/dom/Element.h"
+#include "nsHTMLSelectElement.h"
+#include "nsHTMLLegendElement.h"
 
 using namespace mozilla::dom;
 
 #include "mozilla/dom/indexedDB/IDBFactory.h"
 #include "mozilla/dom/indexedDB/IDBRequest.h"
 #include "mozilla/dom/indexedDB/IDBDatabase.h"
 #include "mozilla/dom/indexedDB/IDBEvents.h"
 #include "mozilla/dom/indexedDB/IDBObjectStore.h"
@@ -7469,19 +7471,18 @@ nsNodeSH::PreCreate(nsISupports *nativeO
   // Event handling is possible only if (1). If (2) event handling is prevented.
   // If document has never had a script handling object,
   // untrusted scripts (3) shouldn't touch it!
   PRBool hasHadScriptHandlingObject = PR_FALSE;
   NS_ENSURE_STATE(doc->GetScriptHandlingObject(hasHadScriptHandlingObject) ||
                   hasHadScriptHandlingObject ||
                   IsPrivilegedScript());
 
-  nsISupports *native_parent;
-
-  PRBool slimWrappers = PR_TRUE;
+  nsINode *native_parent;
+
   PRBool nodeIsElement = node->IsElement();
   if (nodeIsElement && node->AsElement()->IsXUL()) {
     // For XUL elements, use the parent, if any.
     native_parent = node->GetParent();
 
     if (!native_parent) {
       native_parent = doc;
     }
@@ -7494,81 +7495,77 @@ nsNodeSH::PreCreate(nsISupports *nativeO
     native_parent = doc;
 
     // But for HTML form controls, use the form as scope parent.
     if (nodeIsElement) {
       if (node->IsNodeOfType(nsINode::eHTML_FORM_CONTROL)) {
         nsCOMPtr<nsIFormControl> form_control(do_QueryInterface(node));
 
         if (form_control) {
-          nsCOMPtr<nsIDOMHTMLFormElement> form;
-          form_control->GetForm(getter_AddRefs(form));
+          Element *form = form_control->GetFormElement();
 
           if (form) {
             // Found a form, use it.
             native_parent = form;
           }
         }
-      // Legend isn't an HTML form control but should have its fieldset form
-      // as scope parent at least for backward compatibility.
-      } else if (node->AsElement()->IsHTML() &&
-                 node->AsElement()->Tag() == nsGkAtoms::legend) {
-        nsCOMPtr<nsIDOMHTMLLegendElement> legend(do_QueryInterface(node));
-
+      }
+      else {
+        // Legend isn't an HTML form control but should have its fieldset form
+        // as scope parent at least for backward compatibility.
+        nsHTMLLegendElement *legend =
+          nsHTMLLegendElement::FromContent(node->AsElement());
         if (legend) {
-          nsCOMPtr<nsIDOMHTMLFormElement> form;
-          legend->GetForm(getter_AddRefs(form));
+          Element *form = legend->GetFormElement();
 
           if (form) {
             native_parent = form;
           }
         }
       }
     }
   } else {
     // We're called for a document object; set the parent to be the
     // document's global object, if there is one
 
     // Get the scope object from the document.
-    native_parent = doc->GetScopeObject();
-
-    if (!native_parent) {
+    nsISupports *scope = doc->GetScopeObject();
+
+    if (scope) {
+        jsval v;
+        nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
+        nsresult rv = WrapNative(cx, globalObj, scope, nsnull, PR_FALSE, &v,
+                                 getter_AddRefs(holder));
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        holder->GetJSObject(parentObj);
+    }
+    else {
       // No global object reachable from this document, use the
       // global object that was passed to this method.
 
       *parentObj = globalObj;
-
-      return node->IsInNativeAnonymousSubtree() ?
-        NS_SUCCESS_CHROME_ACCESS_ONLY : NS_OK;
-    }
-
-    slimWrappers = PR_FALSE;
+    }
+
+    // No slim wrappers for a document's scope object.
+    return node->IsInNativeAnonymousSubtree() ?
+      NS_SUCCESS_CHROME_ACCESS_ONLY : NS_OK;
   }
 
   // XXXjst: Maybe we need to find the global to use from the
   // nsIScriptGlobalObject that's reachable from the node we're about
   // to wrap here? But that's not always reachable, let's use
   // globalObj for now...
 
-  if (native_parent == doc && (*parentObj = doc->GetWrapper()))
-    return node->IsInNativeAnonymousSubtree() ?
-      NS_SUCCESS_CHROME_ACCESS_ONLY :
-      (slimWrappers ? NS_SUCCESS_ALLOW_SLIM_WRAPPERS : NS_OK);
-
-  jsval v;
-  nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
-  nsresult rv = WrapNative(cx, globalObj, native_parent, PR_FALSE, &v,
-                           getter_AddRefs(holder));
+  nsresult rv = WrapNativeParent(cx, globalObj, native_parent, native_parent,
+                                 parentObj);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  *parentObj = JSVAL_TO_OBJECT(v);
-
   return node->IsInNativeAnonymousSubtree() ?
-    NS_SUCCESS_CHROME_ACCESS_ONLY :
-    (slimWrappers ? NS_SUCCESS_ALLOW_SLIM_WRAPPERS : NS_OK);
+    NS_SUCCESS_CHROME_ACCESS_ONLY : NS_SUCCESS_ALLOW_SLIM_WRAPPERS;
 }
 
 NS_IMETHODIMP
 nsNodeSH::AddProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
                       JSObject *obj, jsval id, jsval *vp, PRBool *_retval)
 {
   nsNodeSH::PreserveWrapper(GetNative(wrapper, obj));
   return nsEventReceiverSH::AddProperty(wrapper, cx, obj, id, vp, _retval);
@@ -8356,26 +8353,28 @@ nsNamedArraySH::GetProperty(nsIXPConnect
 // NamedNodeMap helper
 
 nsISupports*
 nsNamedNodeMapSH::GetItemAt(nsISupports *aNative, PRUint32 aIndex,
                             nsresult *aResult)
 {
   nsDOMAttributeMap* map = nsDOMAttributeMap::FromSupports(aNative);
 
-  return map->GetItemAt(aIndex, aResult);
+  nsINode *attr = map->GetItemAt(aIndex, aResult);
+  return attr;
 }
 
 nsISupports*
 nsNamedNodeMapSH::GetNamedItem(nsISupports *aNative, const nsAString& aName,
                                nsresult *aResult)
 {
   nsDOMAttributeMap* map = nsDOMAttributeMap::FromSupports(aNative);
 
-  return map->GetNamedItem(aName, aResult);
+  nsINode *attr = map->GetNamedItem(aName, aResult);
+  return attr;
 }
 
 
 // HTMLCollection helper
 
 nsresult
 nsHTMLCollectionSH::GetLength(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
                               JSObject *obj, PRUint32 *length)
@@ -9520,25 +9519,23 @@ NS_IMETHODIMP
 nsHTMLSelectElementSH::GetProperty(nsIXPConnectWrappedNative *wrapper,
                                    JSContext *cx, JSObject *obj, jsval id,
                                    jsval *vp, PRBool *_retval)
 {
   PRInt32 n = GetArrayIndexFromId(cx, id);
 
   nsresult rv = NS_OK;
   if (n >= 0) {
-    nsCOMPtr<nsIDOMHTMLSelectElement> s = do_QueryWrappedNative(wrapper, obj);
-
-    nsCOMPtr<nsIDOMHTMLOptionsCollection> options;
-    s->GetOptions(getter_AddRefs(options));
+    nsHTMLSelectElement *s =
+      nsHTMLSelectElement::FromSupports(GetNative(wrapper, obj));
+
+    nsHTMLOptionCollection *options = s->GetOptions();
 
     if (options) {
-      nsCOMPtr<nsIDOMNode> node;
-
-      options->Item(n, getter_AddRefs(node));
+      nsISupports *node = options->GetNodeAt(n, &rv);
 
       rv = WrapNative(cx, obj, node, &NS_GET_IID(nsIDOMNode), PR_TRUE, vp);
       if (NS_SUCCEEDED(rv)) {
         rv = NS_SUCCESS_I_DID_SOMETHING;
       }
       return rv;
     }
   }
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -2193,26 +2193,30 @@ nsGfxScrollFrameInner::CreateAnonymousCo
   NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
 
   if (canHaveHorizontal) {
     rv = NS_NewElement(getter_AddRefs(mHScrollbarContent),
                        kNameSpaceID_XUL, nodeInfo, PR_FALSE);
     NS_ENSURE_SUCCESS(rv, rv);
     mHScrollbarContent->SetAttr(kNameSpaceID_None, nsGkAtoms::orient,
                                 NS_LITERAL_STRING("horizontal"), PR_FALSE);
+    mHScrollbarContent->SetAttr(kNameSpaceID_None, nsGkAtoms::clickthrough,
+                                NS_LITERAL_STRING("always"), PR_FALSE);
     if (!aElements.AppendElement(mHScrollbarContent))
       return NS_ERROR_OUT_OF_MEMORY;
   }
 
   if (canHaveVertical) {
     rv = NS_NewElement(getter_AddRefs(mVScrollbarContent),
                        kNameSpaceID_XUL, nodeInfo, PR_FALSE);
     NS_ENSURE_SUCCESS(rv, rv);
     mVScrollbarContent->SetAttr(kNameSpaceID_None, nsGkAtoms::orient,
                                 NS_LITERAL_STRING("vertical"), PR_FALSE);
+    mVScrollbarContent->SetAttr(kNameSpaceID_None, nsGkAtoms::clickthrough,
+                                NS_LITERAL_STRING("always"), PR_FALSE);
     if (!aElements.AppendElement(mVScrollbarContent))
       return NS_ERROR_OUT_OF_MEMORY;
   }
 
   if (isResizable) {
     nsCOMPtr<nsINodeInfo> nodeInfo;
     nodeInfo = nodeInfoManager->GetNodeInfo(nsGkAtoms::resizer, nsnull,
                                             kNameSpaceID_XUL);
@@ -2238,16 +2242,18 @@ nsGfxScrollFrameInner::CreateAnonymousCo
         dir.AssignLiteral("bottomend");
         break;
       default:
         NS_WARNING("only resizable types should have resizers");
     }
     mScrollCornerContent->SetAttr(kNameSpaceID_None, nsGkAtoms::dir, dir, PR_FALSE);
     mScrollCornerContent->SetAttr(kNameSpaceID_None, nsGkAtoms::element,
                                   NS_LITERAL_STRING("_parent"), PR_FALSE);
+    mScrollCornerContent->SetAttr(kNameSpaceID_None, nsGkAtoms::clickthrough,
+                                  NS_LITERAL_STRING("always"), PR_FALSE);
 
     if (!aElements.AppendElement(mScrollCornerContent))
       return NS_ERROR_OUT_OF_MEMORY;
   }
   else if (canHaveHorizontal && canHaveVertical) {
     nodeInfo = nodeInfoManager->GetNodeInfo(nsGkAtoms::scrollcorner, nsnull,
                                             kNameSpaceID_XUL);
     rv = NS_NewElement(getter_AddRefs(mScrollCornerContent),
--- a/toolkit/content/widgets/browser.xml
+++ b/toolkit/content/widgets/browser.xml
@@ -46,16 +46,19 @@
   %findBarDTD;
 ]>
 
 <bindings id="browserBindings"
           xmlns="http://www.mozilla.org/xbl"
           xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
   <binding id="browser" extends="xul:browser">
+    <content clickthrough="never">
+      <children/>
+    </content>
     <implementation type="application/javascript" implements="nsIAccessibleProvider, nsIObserver, nsIDOMEventListener">
       <property name="accessibleType" readonly="true">
         <getter>
           <![CDATA[
             return Components.interfaces.nsIAccessibleProvider.OuterDoc;
           ]]>
         </getter>
       </property>
--- a/toolkit/content/widgets/scrollbar.xml
+++ b/toolkit/content/widgets/scrollbar.xml
@@ -12,17 +12,17 @@
       <handler event="contextmenu" preventdefault="true" action="event.stopPropagation();"/>
       <handler event="click" preventdefault="true" action="event.stopPropagation();"/>
       <handler event="dblclick" action="event.stopPropagation();"/>
       <handler event="command" action="event.stopPropagation();"/>
     </handlers>
   </binding>
 
   <binding id="scrollbar" extends="chrome://global/content/bindings/scrollbar.xml#scrollbar-base">
-    <content>
+    <content allowclickthrough="always">
       <xul:scrollbarbutton sbattr="scrollbar-up-top" type="decrement" xbl:inherits="curpos,maxpos,disabled,sborient=orient"/>
       <xul:scrollbarbutton sbattr="scrollbar-down-top" type="increment" xbl:inherits="curpos,maxpos,disabled,sborient=orient"/>
       <xul:slider flex="1" xbl:inherits="disabled,curpos,maxpos,pageincrement,increment,orient,sborient=orient">
         <xul:thumb sbattr="scrollbar-thumb" xbl:inherits="orient,sborient=orient,collapsed=disabled" 
                    align="center" pack="center"/>
       </xul:slider>
       <xul:scrollbarbutton sbattr="scrollbar-up-bottom" type="decrement" xbl:inherits="curpos,maxpos,disabled,sborient=orient"/>
       <xul:scrollbarbutton sbattr="scrollbar-down-bottom" type="increment" xbl:inherits="curpos,maxpos,disabled,sborient=orient"/>
--- a/toolkit/content/widgets/tree.xml
+++ b/toolkit/content/widgets/tree.xml
@@ -25,17 +25,17 @@
           return aEvent.ctrlKey;
 #endif
         ]]></body>
       </method>
     </implementation>
   </binding>
 
   <binding id="tree" extends="chrome://global/content/bindings/tree.xml#tree-base">
-    <content hidevscroll="true" hidehscroll="true">
+    <content hidevscroll="true" hidehscroll="true" clickthrough="never">
       <children includes="treecols"/>
       <xul:stack class="tree-stack" flex="1">
         <xul:treerows class="tree-rows" flex="1" xbl:inherits="hidevscroll">
           <children/>
         </xul:treerows>
         <xul:textbox anonid="input" class="tree-input" left="0" top="0" hidden="true"/>
       </xul:stack>
       <xul:hbox xbl:inherits="collapsed=hidehscroll">
--- a/widget/src/cocoa/nsChildView.h
+++ b/widget/src/cocoa/nsChildView.h
@@ -138,17 +138,23 @@ extern "C" long TSMProcessRawKeyEvent(Ev
   // Valid when mKeyPressSent is true.
   PRBool mKeyPressHandled;
 
   // needed for NSTextInput implementation
   NSRange mMarkedRange;
   
   // when mouseDown: is called, we store its event here (strong)
   NSEvent* mLastMouseDownEvent;
-  
+
+  // Whether the last mouse down event was blocked from Gecko.
+  BOOL mBlockedLastMouseDown;
+
+  // when acceptsFirstMouse: is called, we store the event here (strong)
+  NSEvent* mClickThroughMouseDownEvent;
+
   // rects that were invalidated during a draw, so have pending drawing
   NSMutableArray* mPendingDirtyRects;
   BOOL mPendingFullDisplay;
   BOOL mPendingDisplay;
 
   // Holds our drag service across multiple drag calls. The reference to the
   // service is obtained when the mouse enters the view and is released when
   // the mouse exits or there is a drop. This prevents us from having to
@@ -237,17 +243,18 @@ extern "C" long TSMProcessRawKeyEvent(Ev
 @end
 
 class ChildViewMouseTracker {
 
 public:
 
   static void MouseMoved(NSEvent* aEvent);
   static void OnDestroyView(ChildView* aView);
-  static BOOL WindowAcceptsEvent(NSWindow* aWindow, NSEvent* aEvent);
+  static BOOL WindowAcceptsEvent(NSWindow* aWindow, NSEvent* aEvent,
+                                 ChildView* aView, BOOL isClickThrough = NO);
   static void ReEvaluateMouseEnterState(NSEvent* aEvent = nil);
   static ChildView* ViewForEvent(NSEvent* aEvent);
 
   static ChildView* sLastMouseEventView;
 
 private:
 
   static NSWindow* WindowForEvent(NSEvent* aEvent);
--- a/widget/src/cocoa/nsChildView.mm
+++ b/widget/src/cocoa/nsChildView.mm
@@ -199,16 +199,18 @@ PRUint32 nsChildView::sLastInputEventCou
 #endif
 
 - (BOOL)isFirstResponder;
 
 - (BOOL)isDragInProgress;
 
 - (void)fireKeyEventForFlagsChanged:(NSEvent*)theEvent keyDown:(BOOL)isKeyDown;
 
+- (BOOL)inactiveWindowAcceptsMouseEvent:(NSEvent*)aEvent;
+
 @end
 
 #pragma mark -
 
 // Key code constants
 enum
 {
   kEscapeKeyCode      = 0x35,
@@ -2201,22 +2203,24 @@ NSEvent* gLastDragMouseDownEvent = nil;
 #else
     mPluginEventModel = NPEventModelCocoa;
 #endif
     mCurKeyEvent = nil;
     mKeyDownHandled = PR_FALSE;
     mKeyPressHandled = NO;
     mKeyPressSent = NO;
     mPendingDisplay = NO;
+    mBlockedLastMouseDown = NO;
 
     // initialization for NSTextInput
     mMarkedRange.location = NSNotFound;
     mMarkedRange.length = 0;
 
     mLastMouseDownEvent = nil;
+    mClickThroughMouseDownEvent = nil;
     mDragService = nsnull;
 
 #ifndef NP_NO_CARBON
     mPluginTSMDoc = nil;
 #endif
     mPluginComplexTextInputRequested = NO;
 
     mGestureState = eGestureState_None;
@@ -2271,16 +2275,17 @@ NSEvent* gLastDragMouseDownEvent = nil;
 
 - (void)dealloc
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
   [mGLContext release];
   [mPendingDirtyRects release];
   [mLastMouseDownEvent release];
+  [mClickThroughMouseDownEvent release];
   ChildViewMouseTracker::OnDestroyView(self);
 #ifndef NP_NO_CARBON
   if (mPluginTSMDoc)
     ::DeleteTSMDocument(mPluginTSMDoc);
 #endif
 
   [[NSNotificationCenter defaultCenter] removeObserver:self];
   [[NSDistributedNotificationCenter defaultCenter] removeObserver:self];
@@ -2480,16 +2485,30 @@ NSEvent* gLastDragMouseDownEvent = nil;
 
 // We accept key and mouse events, so don't keep passing them up the chain. Allow
 // this to be a 'focused' widget for event dispatch.
 - (BOOL)acceptsFirstResponder
 {
   return YES;
 }
 
+// Accept mouse down events on background windows
+- (BOOL)acceptsFirstMouse:(NSEvent*)aEvent
+{
+  if (![[self window] isKindOfClass:[PopupWindow class]]) {
+    // We rely on this function to tell us that the mousedown was on a
+    // background window. Inside mouseDown we can't tell whether we were
+    // inactive because at that point we've already been made active.
+    // Unfortunately, acceptsFirstMouse is called for PopupWindows even when
+    // their parent window is active, so ignore this on them for now.
+    mClickThroughMouseDownEvent = [aEvent retain];
+  }
+  return YES;
+}
+
 - (void)viewWillMoveToWindow:(NSWindow *)newWindow
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
   if (!newWindow)
     HideChildPluginViews(self);
 
   [super viewWillMoveToWindow:newWindow];
@@ -3089,19 +3108,19 @@ static BOOL DrawingAtWindowTop(CGContext
 // to prevent it for mouseup too, we need to call [NSApp preventWindowOrdering]
 // when handling the mousedown event.
 - (BOOL)shouldDelayWindowOrderingForEvent:(NSEvent*)aEvent
 {
   // Always using system-provided window ordering for normal windows.
   if (![[self window] isKindOfClass:[PopupWindow class]])
     return NO;
 
-  // Don't reorder when we're already accepting mouse events, for example
-  // because we're a context menu.
-  return ChildViewMouseTracker::WindowAcceptsEvent([self window], aEvent);
+  // Don't reorder when we don't have a parent window, like when we're a
+  // context menu or a tooltip.
+  return ![[self window] parentWindow];
 }
 
 - (void)mouseDown:(NSEvent*)theEvent
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
   if ([self shouldDelayWindowOrderingForEvent:theEvent]) {
     [NSApp preventWindowOrdering];
@@ -3117,36 +3136,54 @@ static BOOL DrawingAtWindowTop(CGContext
   else {
     [mLastMouseDownEvent release];
     mLastMouseDownEvent = [theEvent retain];
   }
 
   [gLastDragMouseDownEvent release];
   gLastDragMouseDownEvent = [theEvent retain];
 
+  // We need isClickThrough because at this point the window we're in might
+  // already have become main, so the check for isMainWindow in
+  // WindowAcceptsEvent isn't enough. It also has to check isClickThrough.
+  BOOL isClickThrough = (theEvent == mClickThroughMouseDownEvent);
+  [mClickThroughMouseDownEvent release];
+  mClickThroughMouseDownEvent = nil;
+
   nsAutoRetainCocoaObject kungFuDeathGrip(self);
 
   if ([self maybeRollup:theEvent] ||
-      !ChildViewMouseTracker::WindowAcceptsEvent([self window], theEvent))
+      !ChildViewMouseTracker::WindowAcceptsEvent([self window], theEvent, self, isClickThrough)) {
+    // Remember blocking because that means we want to block mouseup as well.
+    mBlockedLastMouseDown = YES;
     return;
+  }
 
 #if USE_CLICK_HOLD_CONTEXTMENU
   // fire off timer to check for click-hold after two seconds. retains |theEvent|
   [self performSelector:@selector(clickHoldCallback:) withObject:theEvent afterDelay:2.0];
 #endif
 
   // in order to send gecko events we'll need a gecko widget
   if (!mGeckoChild)
     return;
 
   NSUInteger modifierFlags = [theEvent modifierFlags];
 
   nsMouseEvent geckoEvent(PR_TRUE, NS_MOUSE_BUTTON_DOWN, nsnull, nsMouseEvent::eReal);
   [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
-  geckoEvent.clickCount = [theEvent clickCount];
+
+  NSInteger clickCount = [theEvent clickCount];
+  if (mBlockedLastMouseDown && clickCount > 1) {
+    // Don't send a double click if the first click of the double click was
+    // blocked.
+    clickCount--;
+  }
+  geckoEvent.clickCount = clickCount;
+
   if (modifierFlags & NSControlKeyMask)
     geckoEvent.button = nsMouseEvent::eRightButton;
   else
     geckoEvent.button = nsMouseEvent::eLeftButton;
 
   // Create event for use by plugins.
   // This is going to our child view so we don't need to look up the destination
   // event type.
@@ -3165,35 +3202,36 @@ static BOOL DrawingAtWindowTop(CGContext
   if (mPluginEventModel == NPEventModelCocoa) {
     InitNPCocoaEvent(&cocoaEvent);
     NSPoint point = [self convertPoint:[theEvent locationInWindow] fromView:nil];
     cocoaEvent.type = NPCocoaEventMouseDown;
     cocoaEvent.data.mouse.modifierFlags = modifierFlags;
     cocoaEvent.data.mouse.pluginX = point.x;
     cocoaEvent.data.mouse.pluginY = point.y;
     cocoaEvent.data.mouse.buttonNumber = [theEvent buttonNumber];
-    cocoaEvent.data.mouse.clickCount = [theEvent clickCount];
+    cocoaEvent.data.mouse.clickCount = clickCount;
     cocoaEvent.data.mouse.deltaX = [theEvent deltaX];
     cocoaEvent.data.mouse.deltaY = [theEvent deltaY];
     cocoaEvent.data.mouse.deltaZ = [theEvent deltaZ];
     geckoEvent.pluginEvent = &cocoaEvent;
   }
 
   mGeckoChild->DispatchWindowEvent(geckoEvent);
+  mBlockedLastMouseDown = NO;
 
   // XXX maybe call markedTextSelectionChanged:client: here?
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
 - (void)mouseUp:(NSEvent *)theEvent
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
-  if (!mGeckoChild)
+  if (!mGeckoChild || mBlockedLastMouseDown)
     return;
 
   nsAutoRetainCocoaObject kungFuDeathGrip(self);
 
   nsMouseEvent geckoEvent(PR_TRUE, NS_MOUSE_BUTTON_UP, nsnull, nsMouseEvent::eReal);
   [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
   if ([theEvent modifierFlags] & NSControlKeyMask)
     geckoEvent.button = nsMouseEvent::eRightButton;
@@ -3545,17 +3583,17 @@ static BOOL DrawingAtWindowTop(CGContext
 
 - (void)otherMouseDown:(NSEvent *)theEvent
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
   nsAutoRetainCocoaObject kungFuDeathGrip(self);
 
   if ([self maybeRollup:theEvent] ||
-      !ChildViewMouseTracker::WindowAcceptsEvent([self window], theEvent))
+      !ChildViewMouseTracker::WindowAcceptsEvent([self window], theEvent, self))
     return;
 
   if (!mGeckoChild)
     return;
 
   nsMouseEvent geckoEvent(PR_TRUE, NS_MOUSE_BUTTON_DOWN, nsnull, nsMouseEvent::eReal);
   [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
   geckoEvent.button = nsMouseEvent::eMiddleButton;
@@ -4175,18 +4213,18 @@ static PRBool IsNormalCharInputtingEvent
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
 - (void) convertCocoaMouseEvent:(NSEvent*)aMouseEvent toGeckoEvent:(nsInputEvent*)outGeckoEvent
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
-  NS_ASSERTION(aMouseEvent && outGeckoEvent, "convertCocoaMouseEvent:toGeckoEvent: requires non-null arguments");
-  if (!aMouseEvent || !outGeckoEvent)
+  NS_ASSERTION(outGeckoEvent, "convertCocoaMouseEvent:toGeckoEvent: requires non-null aoutGeckoEvent");
+  if (!outGeckoEvent)
     return;
 
   [self convertGenericCocoaEvent:aMouseEvent toGeckoEvent:outGeckoEvent];
 
   // convert point to view coordinate system
   NSPoint locationInWindow = nsCocoaUtils::EventLocationForWindow(aMouseEvent, [self window]);
   NSPoint localPoint = [self convertPoint:locationInWindow fromView:nil];
   outGeckoEvent->refPoint.x = static_cast<nscoord>(localPoint.x);
@@ -5654,16 +5692,23 @@ static const char* ToEscapedString(NSStr
     geckoEvent.pluginEvent = &cocoaEvent;
   }
 
   mGeckoChild->DispatchWindowEvent(geckoEvent);
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
+- (BOOL)inactiveWindowAcceptsMouseEvent:(NSEvent*)aEvent
+{
+  nsMouseEvent geckoEvent(PR_TRUE, NS_MOUSE_ACTIVATE, nsnull, nsMouseEvent::eReal);
+  [self convertCocoaMouseEvent:aEvent toGeckoEvent:&geckoEvent];
+  return !mGeckoChild->DispatchWindowEvent(geckoEvent);
+}
+
 - (void)updateCocoaPluginFocusStatus:(BOOL)hasFocus
 {
   if (!mGeckoChild)
     return;
 
   nsGUIEvent pluginEvent(PR_TRUE, NS_NON_RETARGETED_PLUGIN_EVENT, mGeckoChild);
   NPCocoaEvent cocoaEvent;
   InitNPCocoaEvent(&cocoaEvent);
@@ -6356,23 +6401,27 @@ ChildViewMouseTracker::MouseMoved(NSEven
   ReEvaluateMouseEnterState(aEvent);
   [sLastMouseEventView handleMouseMoved:aEvent];
 }
 
 ChildView*
 ChildViewMouseTracker::ViewForEvent(NSEvent* aEvent)
 {
   NSWindow* window = WindowForEvent(aEvent);
-  if (!window || !WindowAcceptsEvent(window, aEvent))
+  if (!window)
     return nil;
 
   NSPoint windowEventLocation = nsCocoaUtils::EventLocationForWindow(aEvent, window);
   NSView* view = [[[window contentView] superview] hitTest:windowEventLocation];
   NS_ASSERTION(view, "How can the mouse be over a window but not over a view in that window?");
-  return [view isKindOfClass:[ChildView class]] ? (ChildView*)view : nil;
+  if (![view isKindOfClass:[ChildView class]])
+    return nil;
+
+  ChildView* childView = (ChildView*)view;
+  return WindowAcceptsEvent(window, aEvent, childView) ? childView : nil;
 }
 
 static CGWindowLevel kDockWindowLevel = 0;
 static CGWindowLevel kPopupWindowLevel = 0;
 static CGWindowLevel kFloatingWindowLevel = 0;
 
 static BOOL WindowNumberIsUnderPoint(NSInteger aWindowNumber, NSPoint aPoint) {
   NSWindow* window = [NSApp windowWithWindowNumber:aWindowNumber];
@@ -6464,17 +6513,18 @@ ChildViewMouseTracker::WindowForEvent(NS
 
   // This will return nil if windowNumber belongs to a window that we don't own.
   return [NSApp windowWithWindowNumber:windowNumber];
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
 }
 
 BOOL
-ChildViewMouseTracker::WindowAcceptsEvent(NSWindow* aWindow, NSEvent* aEvent)
+ChildViewMouseTracker::WindowAcceptsEvent(NSWindow* aWindow, NSEvent* aEvent,
+                                          ChildView* aView, BOOL aIsClickThrough)
 {
   // Right mouse down events may get through to all windows, even to a top level
   // window with an open sheet.
   if (!aWindow || [aEvent type] == NSRightMouseDown)
     return YES;
 
   id delegate = [aWindow delegate];
   if (!delegate || ![delegate isKindOfClass:[WindowDelegate class]])
@@ -6490,17 +6540,17 @@ ChildViewMouseTracker::WindowAcceptsEven
   NSWindow* topLevelWindow = nil;
 
   switch (windowType) {
     case eWindowType_popup:
       // If this is a context menu, it won't have a parent. So we'll always
       // accept mouse move events on context menus even when none of our windows
       // is active, which is the right thing to do.
       // For panels, the parent window is the XUL window that owns the panel.
-      return WindowAcceptsEvent([aWindow parentWindow], aEvent);
+      return WindowAcceptsEvent([aWindow parentWindow], aEvent, aView, aIsClickThrough);
 
     case eWindowType_toplevel:
     case eWindowType_dialog:
       if ([aWindow attachedSheet])
         return NO;
 
       topLevelWindow = aWindow;
       break;
@@ -6513,25 +6563,25 @@ ChildViewMouseTracker::WindowAcceptsEven
       break;
     }
 
     default:
       return YES;
   }
 
   if (!topLevelWindow ||
-      [topLevelWindow isMainWindow] ||
+      ([topLevelWindow isMainWindow] && !aIsClickThrough) ||
       [aEvent type] == NSOtherMouseDown ||
       (([aEvent modifierFlags] & NSCommandKeyMask) != 0 &&
        [aEvent type] != NSMouseMoved))
     return YES;
 
   // If we're here then we're dealing with a left click or mouse move on an
-  // inactive window or something similar. Return NO for now.
-  return NO;
+  // inactive window or something similar. Ask Gecko what to do.
+  return [aView inactiveWindowAcceptsMouseEvent:aEvent];
 }
 
 #pragma mark -
 
 #ifndef NP_NO_CARBON
 
 // Target for text services events sent as the result of calls made to
 // TSMProcessRawKeyEvent() in [ChildView keyDown:] (above) when a plugin has
--- a/widget/src/cocoa/nsCocoaWindow.mm
+++ b/widget/src/cocoa/nsCocoaWindow.mm
@@ -2387,19 +2387,19 @@ ContentPatternDrawCallback(void* aInfo, 
     case NSLeftMouseDragged:
     case NSRightMouseDragged:
     case NSOtherMouseDragged:
       if ((contentView = [self contentView])) {
         // Since [anEvent window] might not be us, we can't use [anEvent locationInWindow].
         windowLocation = nsCocoaUtils::EventLocationForWindow(anEvent, self);
         target = [contentView hitTest:[contentView convertPoint:windowLocation fromView:nil]];
         // If the hit test failed, the event is targeted here but is not over the window.
-        // Target it at the first responder.
+        // Send it to our content view.
         if (!target)
-          target = (NSView*)[self firstResponder];
+          target = contentView;
       }
       break;
     default:
       break;
   }
   if (target) {
     switch (type) {
       case NSScrollWheel:
--- a/widget/tests/native_mouse_mac_window.xul
+++ b/widget/tests/native_mouse_mac_window.xul
@@ -164,40 +164,55 @@
       return JSON.stringify({
         type: e.type, target: e.target.nodeName, screenX: e.screenX, screenY: e.screenY
       });
     }
 
     function clearExpectedEvents() {
       while (gExpectedEvents.length > 0) {
         var expectedEvent = gExpectedEvents.shift();
-        var errFun = expectedEvent.todoShouldHaveFired ? todo : ok;
-        errFun(false, "didn't receive expected event: " + eventToString(expectedEvent));
+        if (expectedEvent.sometimesFiresButShouldnt) {
+          // We didn't really expect it anyway, so it's good that it didn't fire.
+          ok(true, "Didn't receive unexpected event: " + eventToString(expectedEvent));
+        } else {
+          var errFun = expectedEvent.shouldFireButDoesnt || expectedEvent.shouldFireButSometimesDoesnt ? todo : ok;
+          errFun(false, "Didn't receive expected event: " + eventToString(expectedEvent));
+        }
       }
     }
 
     var gEventNum = 0;
 
     function eventMonitor(e) {
       printDebug("got event: " + eventToString(e) + "\n");
+      processEvent(e);
+    }
+
+    function processEvent(e) {
       var expectedEvent = gExpectedEvents.shift();
-      while (expectedEvent && expectedEvent.todoShouldHaveFired) {
-        todo(false, "Should have got event: " + eventToString(expectedEvent));
-        expectedEvent = gExpectedEvents.shift();
-      }
       if (!expectedEvent) {
         ok(false, "received event I didn't expect: " + eventToString(e));
         return true;
       }
+      if (e.type != expectedEvent.type) {
+        // Didn't get expectedEvent.
+        if (expectedEvent.sometimesFiresButShouldnt) {
+          // We didn't really expect it anyway, so it's good that it didn't fire.
+          ok(true, "Didn't receive unexpected event: " + eventToString(expectedEvent));
+        } else {
+          var errFun = expectedEvent.shouldFireButDoesnt || expectedEvent.shouldFireButSometimesDoesnt ? todo : ok;
+          errFun(false, "Didn't receive expected event: " + eventToString(expectedEvent));
+        }
+        return processEvent(e);
+      }
       gEventNum++;
       is(e.screenX, expectedEvent.screenX, gEventNum + " | wrong X coord for event " + eventToString(e));
       is(e.screenY, expectedEvent.screenY, gEventNum + " | wrong Y coord for event " + eventToString(e));
-      is(e.type, expectedEvent.type, gEventNum + " | wrong event type for event " + eventToString(e));
       is(e.target, expectedEvent.target, gEventNum + " | wrong target for event " + eventToString(e));
-      if (expectedEvent.todoShouldNotHaveFired) {
+      if (expectedEvent.firesButShouldnt || expectedEvent.sometimesFiresButShouldnt) {
         todo(false, gEventNum + " | Got an event that should not have fired: " + eventToString(e));
       }
     }
 
     function observe(elem, fun, add) {
       var addOrRemove = add ? "addEventListener" : "removeEventListener";
       elem[addOrRemove]("mousemove", fun, false);
       elem[addOrRemove]("mouseover", fun, false);
@@ -229,16 +244,24 @@
         var _tooltip = right.document.createElementNS(XUL_NS, "tooltip");
         _tooltip.setAttribute("id", "tip");
         _tooltip.setAttribute("width", "80");
         _tooltip.setAttribute("height", "20");
         right.document.documentElement.appendChild(_tooltip);
         return _tooltip;
       })();
       var tests = [
+
+        // Part 1: Disallow click-through
+
+        function blockClickThrough(callback) {
+          document.documentElement.setAttribute("clickthrough", "never");
+          gRightWindow.document.documentElement.setAttribute("clickthrough", "never");
+          callback();
+        },
         // Enter the left window, which is focused.
         [150, 150, NSMouseMoved, null, left, [
           { type: "mouseover", target: leftElem },
           { type: "mousemove", target: leftElem }
         ]],
         // Test that moving inside the window fires mousemove events.
         [170, 150, NSMouseMoved, null, left, [
           { type: "mousemove", target: leftElem },
@@ -248,17 +271,17 @@
           { type: "mouseout", target: leftElem },
         ]],
         // ... and entering a mouseover event.
         [170, 120, NSMouseMoved, null, left, [
           { type: "mouseover", target: leftElem },
           { type: "mousemove", target: leftElem },
         ]],
         // Move over the right window, which is inactive.
-        // Inactive windows shouldn't respond to mousemove events,
+        // Inactive windows shouldn't respond to mousemove events when clickthrough="never",
         // so we should only get a mouseout event, no mouseover event.
         [400, 150, NSMouseMoved, null, right, [
           { type: "mouseout", target: leftElem },
         ]],
         // Left-clicking while holding Cmd and middle clicking should work even
         // on inactive windows, but without making them active.
         [400, 150, NSLeftMouseDown, null, right, [
           { type: "mousedown", target: rightElem },
@@ -280,104 +303,101 @@
           { type: "mouseover", target: rightElem },
         ]],
         [400, 150, NSLeftMouseUp, null, right, [
         ]],
         // Now it's focused, so we should get a mousedown event when clicking.
         [400, 150, NSLeftMouseDown, null, right, [
           { type: "mousedown", target: rightElem },
         ]],
-        // Let's drag to the right without letting the button go. It would be better
-        // if the mouseover event had fired as soon as the mouse entered the window,
-        // and not only when dragging, but that's ok.
+        // Let's drag to the right without letting the button go.
         [410, 150, NSLeftMouseDragged, null, right, [
           { type: "mousemove", target: rightElem },
         ]],
         // Let go of the mouse.
         [410, 150, NSLeftMouseUp, null, right, [
           { type: "mouseup", target: rightElem },
           { type: "click", target: rightElem },
         ]],
         // Now we're being sneaky. The left window is inactive, but *right*-clicks to it
         // should still get through. Test that.
         // Ideally we'd be bracketing that event with over and out events, too, but it
         // probably doesn't matter too much.
         [150, 170, NSRightMouseDown, null, left, [
-          { type: "mouseover", target: leftElem, todoShouldHaveFired: true },
+          { type: "mouseover", target: leftElem, shouldFireButDoesnt: true },
           { type: "mousedown", target: leftElem },
-          { type: "mouseout", target: leftElem, todoShouldHaveFired: true },
+          { type: "mouseout", target: leftElem, shouldFireButDoesnt: true },
         ]],
         // Let go of the mouse.
         [150, 170, NSRightMouseUp, null, left, [
-          { type: "mouseover", target: leftElem, todoShouldHaveFired: true },
+          { type: "mouseover", target: leftElem, shouldFireButDoesnt: true },
           { type: "mouseup", target: leftElem },
           { type: "click", target: leftElem },
-          { type: "mouseout", target: leftElem, todoShouldHaveFired: true },
+          { type: "mouseout", target: leftElem, shouldFireButDoesnt: true },
         ]],
         // Right clicking hasn't focused it, so the window is still inactive.
         // Let's focus it; this time without the mouse, for variaton's sake.
         // Still, mouseout and mouseover events should fire.
         function raiseLeftWindow(callback) {
           clearExpectedEvents();
           gExpectedEvents.push({ screenX: 150, screenY: 170, type: "mouseout", target: rightElem });
           gExpectedEvents.push({ screenX: 150, screenY: 170, type: "mouseover", target: leftElem });
           focusAndThen(left, function () { SimpleTest.executeSoon(callback); });
         },
         // It's active, so it should respond to mousemove events now.
         [150, 170, NSMouseMoved, null, left, [
           { type: "mousemove", target: leftElem },
         ]],
-
         // This was boring... let's introduce a popup. It will overlap both the left
         // and the right window.
         function openPopupInLeftWindow(callback) {
           eventListenOnce(gPopup, "popupshown", callback);
           gPopup.openPopupAtScreen(150, 50, true);
         },
         // Move the mouse over the popup.
         // We'll get duplicate events on the popup; ignore them.
         [200, 80, NSMouseMoved, gPopup, left, [
           { type: "mouseout", target: leftElem },
           { type: "mouseover", target: gPopup },
-          { type: "mouseover", target: gPopup, todoShouldNotHaveFired: true },
+          { type: "mouseover", target: gPopup, firesButShouldnt: true },
           { type: "mousemove", target: gPopup },
-          { type: "mousemove", target: gPopup, todoShouldNotHaveFired: true },
+          { type: "mousemove", target: gPopup, firesButShouldnt: true },
         ]],
         // Move the mouse back over the left window outside the popup.
         [160, 170, NSMouseMoved, null, left, [
           { type: "mouseout", target: gPopup },
-          { type: "mouseout", target: gPopup, todoShouldNotHaveFired: true },
+          { type: "mouseout", target: gPopup, firesButShouldnt: true },
           { type: "mouseover", target: leftElem },
           { type: "mousemove", target: leftElem },
         ]],
         // Back over the popup... (double events again)
         [190, 80, NSMouseMoved, gPopup, left, [
           { type: "mouseout", target: leftElem },
           { type: "mouseover", target: gPopup },
-          { type: "mouseover", target: gPopup, todoShouldNotHaveFired: true },
+          { type: "mouseover", target: gPopup, firesButShouldnt: true },
           { type: "mousemove", target: gPopup },
-          { type: "mousemove", target: gPopup, todoShouldNotHaveFired: true },
+          { type: "mousemove", target: gPopup, firesButShouldnt: true },
         ]],
         // ...and over into the right window. (... again)
         // It's inactive, so it shouldn't get mouseover events yet.
         [400, 170, NSMouseMoved, null, right, [
           { type: "mouseout", target: gPopup },
-          { type: "mouseout", target: gPopup, todoShouldNotHaveFired: true },
+          { type: "mouseout", target: gPopup, firesButShouldnt: true },
         ]],
         // Again, no mouse events please, even though a popup is open. (bug 425556)
         [400, 180, NSMouseMoved, null, right, [
         ]],
         // Activate the right window with a click.
         // This will close the popup and make the mouse enter the right window.
         [400, 180, NSLeftMouseDown, null, right, [
           { type: "mouseover", target: rightElem },
         ]],
         [400, 180, NSLeftMouseUp, null, right, [
         ]],
-        function verifyPopupClosed(callback) {
+        function verifyPopupClosed2(callback) {
           is(gPopup.popupBoxObject.popupState, "closed", "popup should have closed when clicking");
           callback();
         },
         // Now the right window is active; click it again, just for fun.
         [400, 180, NSLeftMouseDown, null, right, [
           { type: "mousedown", target: rightElem },
         ]],
         [400, 180, NSLeftMouseUp, null, right, [
@@ -393,28 +413,24 @@
         },
         // Move the mouse to trigger the appearance of the tooltip.
         // ... and what's that, a mousemove event without preceding mouseover? Bad.
         [410, 180, NSMouseMoved, null, right, [
           { type: "mousemove", target: rightElem },
         ]],
         // Wait for the tooltip to appear.
         function (callback) {
-          var timer = setTimeout(callback, 2000); // just in case the tooltip is shy
-          eventListenOnce(rightElem, "popupshown", function () {
-            clearTimeout(timer);
-            callback();
-          });
+          eventListenOnce(rightElem, "popupshown", callback);
         },
         // Now the tooltip is visible.
         // Move the mouse a little to the right, but send the event to the tooltip's
         // widget, even though the mouse is not over the tooltip, because that's what
         // Mac OS X does.
         [411, 180, NSMouseMoved, tooltip, right, [
-          { type: "mousemove", target: rightElem, todoShouldHaveFired: !!navigator.oscpu.match(/Mac OS X 10\.6$/) },
+          { type: "mousemove", target: rightElem },
         ]],
         // Move another pixel. This time send the event to the right widget.
         // However, that must not make a difference.
         [412, 180, NSMouseMoved, null, right, [
           { type: "mousemove", target: rightElem },
         ]],
         // Move up and click to make the tooltip go away.
         [412, 80, NSMouseMoved, null, right, [
@@ -455,26 +471,305 @@
         [260, 171, NSMouseMoved, null, left, [
         ]],
         [261, 171, NSMouseMoved, panel, left, [
         ]],
         // Let's be evil and click it.
         [261, 171, NSLeftMouseDown, panel, left, [
         ]],
         [261, 171, NSLeftMouseUp, panel, left, [
-          { type: "mouseup", target: panel },
         ]],
         // This didn't focus the window, unfortunately, so let's do it ourselves.
         function raiseLeftWindowTakeTwo(callback) {
           focusAndThen(left, callback);
         },
         // Now mouse events should get through to the panel (which is now over the
         // right window).
         [387, 170, NSMouseMoved, null, right, [
-          { type: "mouseover", target: panel },
+          { type: "mouseover", target: panel, shouldFireButSometimesDoesnt: true },
+          { type: "mousemove", target: panel, shouldFireButSometimesDoesnt: true },
+        ]],
+        [387, 171, NSMouseMoved, null, left, [
+          { type: "mouseover", target: panel, sometimesFiresButShouldnt: true },
+          { type: "mousemove", target: panel },
+        ]],
+        [388, 171, NSMouseMoved, panel, left, [
+          { type: "mousemove", target: panel },
+        ]],
+        // Click the panel.
+        [388, 171, NSLeftMouseDown, panel, left, [
+          { type: "mousedown", target: panel }
+        ]],
+        [388, 171, NSLeftMouseUp, panel, left, [
+          { type: "mouseup", target: panel },
+          { type: "click", target: panel },
+        ]],
+
+        // Last test for this part: Hit testing in the Canyon of Nowhere -
+        // the pixel row directly south of the panel, over the left window.
+        // Before bug 515003 we wrongly thought the mouse wasn't over any window.
+        [173, 200, NSMouseMoved, panel, left, [
+          { type: "mouseout", target: panel },
+          { type: "mouseover", target: leftElem },
+          { type: "mousemove", target: leftElem },
+        ]],
+        [173, 201, NSMouseMoved, panel, left, [
+          { type: "mousemove", target: leftElem },
+        ]],
+
+        // Part 2: Allow click-through
+
+        function hideThatPanel(callback) {
+          eventListenOnce(panel, "popuphidden", callback);
+          panel.hidePopup();
+        },
+        function unblockClickThrough(callback) {
+          document.documentElement.removeAttribute("clickthrough");
+          gRightWindow.document.documentElement.removeAttribute("clickthrough");
+          callback();
+        },
+        // Enter the left window, which is focused.
+        [150, 150, NSMouseMoved, null, left, [
+          { type: "mousemove", target: leftElem }
+        ]],
+        // Test that moving inside the window fires mousemove events.
+        [170, 150, NSMouseMoved, null, left, [
+          { type: "mousemove", target: leftElem },
+        ]],
+        // Leaving the window should fire a mouseout event...
+        [170, 20, NSMouseMoved, null, left, [
+          { type: "mouseout", target: leftElem },
+        ]],
+        // ... and entering a mouseover event.
+        [170, 120, NSMouseMoved, null, left, [
+          { type: "mouseover", target: leftElem },
+          { type: "mousemove", target: leftElem },
+        ]],
+        // Move over the right window, which is inactive but still accepts
+        // mouse events.
+        [400, 150, NSMouseMoved, null, right, [
+          { type: "mouseout", target: leftElem },
+          { type: "mouseover", target: rightElem },
+          { type: "mousemove", target: rightElem },
+        ]],
+        // Left-clicking while holding Cmd and middle clicking should work
+        // on inactive windows, but without making them active.
+        [400, 150, NSLeftMouseDown, null, right, [
+          { type: "mousedown", target: rightElem },
+        ], NSCommandKeyMask],
+        [400, 150, NSLeftMouseUp, null, right, [
+          { type: "mouseup", target: rightElem },
+          { type: "click", target: rightElem },
+        ], NSCommandKeyMask],
+        [400, 150, NSOtherMouseDown, null, right, [
+          { type: "mousedown", target: rightElem },
+        ]],
+        [400, 150, NSOtherMouseUp, null, right, [
+          { type: "mouseup", target: rightElem },
+          { type: "click", target: rightElem },
+        ]],
+        // Clicking an inactive window should make it active
+        [400, 150, NSLeftMouseDown, null, right, [
+          { type: "mousedown", target: rightElem },
+        ]],
+        [400, 150, NSLeftMouseUp, null, right, [
+          { type: "mouseup", target: rightElem },
+          { type: "click", target: rightElem },
+        ]],
+        // Now it's focused.
+        [401, 150, NSLeftMouseDown, null, right, [
+          { type: "mousedown", target: rightElem },
+        ]],
+        // Let's drag to the right without letting the button go.
+        [410, 150, NSLeftMouseDragged, null, right, [
+          { type: "mousemove", target: rightElem },
+        ]],
+        // Let go of the mouse.
+        [410, 150, NSLeftMouseUp, null, right, [
+          { type: "mouseup", target: rightElem },
+          { type: "click", target: rightElem },
+        ]],
+        // Now we're being sneaky. The left window is inactive, but *right*-clicks to it
+        // should still get through. Test that.
+        // Ideally we'd be bracketing that event with over and out events, too, but it
+        // probably doesn't matter too much.
+        [150, 170, NSRightMouseDown, null, left, [
+          { type: "mouseover", target: leftElem, shouldFireButDoesnt: true },
+          { type: "mousedown", target: leftElem },
+          { type: "mouseout", target: leftElem, shouldFireButDoesnt: true },
+        ]],
+        // Let go of the mouse.
+        [150, 170, NSRightMouseUp, null, left, [
+          { type: "mouseover", target: leftElem, shouldFireButDoesnt: true },
+          { type: "mouseup", target: leftElem },
+          { type: "click", target: leftElem },
+          { type: "mouseout", target: leftElem, shouldFireButDoesnt: true },
+        ]],
+        // Right clicking hasn't focused it, so the window is still inactive.
+        // Let's focus it; this time without the mouse, for variaton's sake.
+        // Still, mouseout and mouseover events should fire.
+        function raiseLeftWindow(callback) {
+          clearExpectedEvents();
+          gExpectedEvents.push({ screenX: 150, screenY: 170, type: "mouseout", target: rightElem });
+          gExpectedEvents.push({ screenX: 150, screenY: 170, type: "mouseover", target: leftElem });
+          focusAndThen(left, function () { SimpleTest.executeSoon(callback); });
+        },
+        // It's active, so it should respond to mousemove events now.
+        [150, 170, NSMouseMoved, null, left, [
+          { type: "mousemove", target: leftElem },
+        ]],
+
+        // This was boring... let's introduce a popup. It will overlap both the left
+        // and the right window.
+        function openPopupInLeftWindow(callback) {
+          eventListenOnce(gPopup, "popupshown", callback);
+          gPopup.openPopupAtScreen(150, 50, true);
+        },
+        // Move the mouse over the popup.
+        // We'll get duplicate events on the popup; ignore them.
+        [200, 80, NSMouseMoved, gPopup, left, [
+          { type: "mouseout", target: leftElem },
+          { type: "mouseover", target: gPopup },
+          { type: "mouseover", target: gPopup, firesButShouldnt: true },
+          { type: "mousemove", target: gPopup },
+          { type: "mousemove", target: gPopup, firesButShouldnt: true },
+        ]],
+        // Move the mouse back over the left window outside the popup.
+        [160, 170, NSMouseMoved, null, left, [
+          { type: "mouseout", target: gPopup },
+          { type: "mouseout", target: gPopup, firesButShouldnt: true },
+          { type: "mouseover", target: leftElem },
+          { type: "mousemove", target: leftElem },
+        ]],
+        // Back over the popup... (double events again)
+        [190, 80, NSMouseMoved, gPopup, left, [
+          { type: "mouseout", target: leftElem },
+          { type: "mouseover", target: gPopup },
+          { type: "mouseover", target: gPopup, firesButShouldnt: true },
+          { type: "mousemove", target: gPopup },
+          { type: "mousemove", target: gPopup, firesButShouldnt: true },
+        ]],
+        // ...and over into the right window. (... again)
+        [400, 170, NSMouseMoved, null, right, [
+          { type: "mouseout", target: gPopup },
+          { type: "mouseout", target: gPopup, firesButShouldnt: true },
+          { type: "mouseover", target: rightElem },
+          { type: "mousemove", target: rightElem },
+        ]],
+        [400, 180, NSMouseMoved, null, right, [
+          { type: "mousemove", target: rightElem },
+        ]],
+        // Activate the right window with a click.
+        [400, 180, NSLeftMouseDown, null, right, [
+          { type: "mousedown", target: rightElem },
+        ]],
+        [400, 180, NSLeftMouseUp, null, right, [
+          { type: "mouseup", target: rightElem },
+          { type: "click", target: rightElem },
+        ]],
+        function verifyPopupClosed2(callback) {
+          is(gPopup.popupBoxObject.popupState, "closed", "popup should have closed when clicking");
+          callback();
+        },
+        // Now the right window is active; click it again, just for fun.
+        [400, 180, NSLeftMouseDown, null, right, [
+          { type: "mousedown", target: rightElem },
+        ]],
+        [400, 180, NSLeftMouseUp, null, right, [
+          { type: "mouseup", target: rightElem },
+          { type: "click", target: rightElem },
+        ]],
+
+        // Time for our next trick: a tooltip!
+        // Install the tooltip, but don't show it yet.
+        function setTooltip(callback) {
+          rightElem.setAttribute("tooltip", "tip");
+          callback();
+        },
+        // Move the mouse to trigger the appearance of the tooltip.
+        // ... and what's that, a mousemove event without preceding mouseover? Bad.
+        [410, 180, NSMouseMoved, null, right, [
+          { type: "mousemove", target: rightElem },
+        ]],
+        // Wait for the tooltip to appear.
+        function (callback) {
+          eventListenOnce(rightElem, "popupshown", callback);
+        },
+        // Now the tooltip is visible.
+        // Move the mouse a little to the right, but send the event to the tooltip's
+        // widget, even though the mouse is not over the tooltip, because that's what
+        // Mac OS X does.
+        [411, 180, NSMouseMoved, tooltip, right, [
+          { type: "mousemove", target: rightElem },
+        ]],
+        // Move another pixel. This time send the event to the right widget.
+        // However, that must not make a difference.
+        [412, 180, NSMouseMoved, null, right, [
+          { type: "mousemove", target: rightElem },
+        ]],
+        // Move up and click to make the tooltip go away.
+        [412, 80, NSMouseMoved, null, right, [
+          { type: "mousemove", target: rightElem },
+        ]],
+        [412, 80, NSLeftMouseDown, null, right, [
+          { type: "mousedown", target: rightElem },
+        ]],
+        [412, 80, NSLeftMouseUp, null, right, [
+          { type: "mouseup", target: rightElem },
+          { type: "click", target: rightElem },
+        ]],
+        // OK, next round. Open a panel in the left window, which is inactive.
+        function openPanel2(callback) {
+          eventListenOnce(panel, "popupshown", callback);
+          panel.openPopupAtScreen(150, 150, false);
+        },
+        // The panel is parented, so it will be z-ordered over its parent but
+        // under the active window.
+        // Now we move the mouse over the part where the panel rect intersects the
+        // right window's rect. Since the panel is under the window, all the events
+        // should target the right window.
+        // Try with sending to three different targets.
+        [390, 170, NSMouseMoved, null, right, [
+          { type: "mousemove", target: rightElem },
+        ]],
+        [390, 171, NSMouseMoved, null, left, [
+          { type: "mousemove", target: rightElem },
+        ]],
+        [391, 171, NSMouseMoved, panel, left, [
+          { type: "mousemove", target: rightElem },
+        ]],
+        // Now move off the right window, so that the mouse is directly over the
+        // panel.
+        [260, 170, NSMouseMoved, null, left, [
+          { type: "mouseout", target: rightElem },
+          { type: "mouseover", target: panel, shouldFireButSometimesDoesnt: true },
+          { type: "mousemove", target: panel, shouldFireButSometimesDoesnt: true },
+        ]],
+        [260, 171, NSMouseMoved, null, left, [
+          { type: "mouseover", target: panel, sometimesFiresButShouldnt: true },
+          { type: "mousemove", target: panel, shouldFireButSometimesDoesnt: true },
+        ]],
+        [261, 171, NSMouseMoved, panel, left, [
+          { type: "mouseover", target: panel, sometimesFiresButShouldnt: true },
+          { type: "mousemove", target: panel, shouldFireButSometimesDoesnt: true },
+        ]],
+        // Let's be evil and click it.
+        [261, 171, NSLeftMouseDown, panel, left, [
+          { type: "mousedown", target: panel },
+        ]],
+        [261, 171, NSLeftMouseUp, panel, left, [
+          { type: "mouseup", target: panel },
+          { type: "click", target: panel },
+        ]],
+        // This didn't focus the window, unfortunately, so let's do it ourselves.
+        function raiseLeftWindowTakeTwo(callback) {
+          focusAndThen(left, callback);
+        },
+        [387, 170, NSMouseMoved, null, right, [
+          { type: "mouseover", target: panel, sometimesFiresButShouldnt: true },
           { type: "mousemove", target: panel },
         ]],
         [387, 171, NSMouseMoved, null, left, [
           { type: "mousemove", target: panel },
         ]],
         [388, 171, NSMouseMoved, panel, left, [
           { type: "mousemove", target: panel },
         ]],