author | Rob 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 id | 14426 |
push user | rcampbell@mozilla.com |
push date | Fri, 16 Jul 2010 14:16:39 +0000 |
treeherder | mozilla-central@cdeaa6dff0a7 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
milestone | 2.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
|
--- 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 }, ]],