This commit is contained in:
Rob Campbell 2010-07-16 11:15:49 -03:00
commit 36eee9eb95
30 changed files with 891 additions and 415 deletions

View File

@ -2188,9 +2188,7 @@ nsAccessible::GetRelationByType(PRUint32 aRelationType,
// 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());

View File

@ -3101,6 +3101,7 @@
role="presentation"/>
<xul:toolbarbutton anonid="close-button"
tabindex="-1"
clickthrough="never"
class="tab-close-button"/>
</content>

View File

@ -222,6 +222,11 @@ public:
*/
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

View File

@ -170,15 +170,11 @@ nsFormContentList::nsFormContentList(nsIDOMHTMLFormElement *aForm,
// 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);
}

View File

@ -1521,6 +1521,21 @@ 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,
@ -1549,16 +1564,7 @@ nsContentUtils::ContentIsCrossDocDescendantOf(nsINode* aPossibleDescendant,
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;
@ -1915,11 +1921,10 @@ static inline void KeyAppendAtom(nsIAtom* aAtom, nsACString& aKey)
KeyAppendString(nsAtomCString(aAtom), aKey);
}
static inline PRBool IsAutocompleteOff(nsIDOMElement* aElement)
static inline PRBool IsAutocompleteOff(nsIContent* aElement)
{
nsAutoString autocomplete;
aElement->GetAttribute(NS_LITERAL_STRING("autocomplete"), autocomplete);
return autocomplete.LowerCaseEqualsLiteral("off");
return aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::autocomplete,
NS_LITERAL_STRING("off"), eIgnoreCase);
}
/*static*/ nsresult
@ -1948,8 +1953,7 @@ nsContentUtils::GenerateStateKey(nsIContent* aContent,
return NS_OK;
}
nsCOMPtr<nsIDOMElement> element(do_QueryInterface(aContent));
if (element && IsAutocompleteOff(element)) {
if (IsAutocompleteOff(aContent)) {
return NS_OK;
}
@ -1995,10 +1999,8 @@ nsContentUtils::GenerateStateKey(nsIContent* aContent,
// 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;
@ -2007,8 +2009,7 @@ nsContentUtils::GenerateStateKey(nsIContent* aContent,
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
@ -2034,7 +2035,7 @@ nsContentUtils::GenerateStateKey(nsIContent* aContent,
// Append the form name
nsAutoString formName;
formElement->GetName(formName);
formElement->GetAttr(kNameSpaceID_None, nsGkAtoms::name, formName);
KeyAppendString(formName, aKey);
} else {

View File

@ -72,11 +72,10 @@ nsDOMAttributeMap::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;
}
@ -101,12 +100,13 @@ 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;
}
@ -131,12 +131,11 @@ 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;
}
@ -154,13 +153,10 @@ 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);
@ -177,7 +173,8 @@ nsDOMAttributeMap::RemoveAttribute(nsINodeInfo* aNodeInfo, nsIDOMNode** aReturn)
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.
@ -189,29 +186,28 @@ nsDOMAttributeMap::RemoveAttribute(nsINodeInfo* aNodeInfo, nsIDOMNode** aReturn)
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;
@ -221,7 +217,7 @@ nsDOMAttributeMap::GetAttribute(nsINodeInfo* aNodeInfo)
return node;
}
nsIDOMNode*
nsDOMAttribute*
nsDOMAttributeMap::GetNamedItem(const nsAString& aAttrName, nsresult *aResult)
{
*aResult = NS_OK;
@ -230,12 +226,7 @@ nsDOMAttributeMap::GetNamedItem(const nsAString& aAttrName, nsresult *aResult)
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);
}
}
@ -282,12 +273,13 @@ nsDOMAttributeMap::SetNamedItemInternal(nsIDOMNode *aNode,
// 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) {
@ -389,8 +381,7 @@ nsDOMAttributeMap::RemoveNamedItem(const nsAString& aName,
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);
@ -400,12 +391,12 @@ nsDOMAttributeMap::RemoveNamedItem(const nsAString& aName,
}
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))) {
@ -417,7 +408,7 @@ nsDOMAttributeMap::GetItemAt(PRUint32 aIndex, nsresult *aResult)
if (ni) {
node = GetAttribute(ni);
}
if (!node) {
else {
*aResult = NS_ERROR_OUT_OF_MEMORY;
}
}
@ -493,7 +484,13 @@ nsDOMAttributeMap::GetNamedItemNSInternal(const nsAString& aNamespaceURI,
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;
}
}

View File

@ -45,7 +45,7 @@
#include "nsIDOMNamedNodeMap.h"
#include "nsString.h"
#include "nsInterfaceHashtable.h"
#include "nsRefPtrHashtable.h"
#include "nsCycleCollectionParticipant.h"
#include "prbit.h"
#include "nsIDOMNode.h"
@ -160,7 +160,7 @@ public:
*/
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
@ -170,8 +170,8 @@ public:
*/
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)
{
@ -217,24 +217,7 @@ private:
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.

View File

@ -5619,12 +5619,12 @@ nsDocument::SetDocumentURI(const nsAString& aDocumentURI)
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?!");

View File

@ -203,6 +203,7 @@ 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")

View File

@ -38,14 +38,13 @@
#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;
@ -250,9 +249,6 @@ public:
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

View File

@ -2703,6 +2703,27 @@ nsEventStateManager::DecideGestureEvent(nsGestureNotifyEvent* aEvent,
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,
@ -3166,6 +3187,19 @@ nsEventStateManager::PostHandleEvent(nsPresContext* aPresContext,
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

View File

@ -45,6 +45,12 @@ 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,
@ -93,8 +99,8 @@ PR_STATIC_ASSERT((PRUint32)eButtonElementTypesMax < (PRUint32)NS_FORM_INPUT_ELEM
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,
@ -109,9 +115,9 @@ public:
/**
* 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.

View File

@ -111,6 +111,9 @@
#include "mozAutoDocUpdate.h"
#include "nsHtml5Module.h"
#include "nsITextControlElement.h"
#include "mozilla/dom/Element.h"
using namespace mozilla::dom;
#include "nsThreadUtils.h"
@ -2379,7 +2382,13 @@ nsGenericHTMLFormElement::ClearForm(PRBool aRemoveFromForm,
mForm = nsnull;
}
NS_IMETHODIMP
Element*
nsGenericHTMLFormElement::GetFormElement()
{
return mForm;
}
nsresult
nsGenericHTMLFormElement::GetForm(nsIDOMHTMLFormElement** aForm)
{
NS_ENSURE_ARG_POINTER(aForm);

View File

@ -803,10 +803,12 @@ public:
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;

View File

@ -48,6 +48,7 @@
#include "nsPIDOMWindow.h"
#include "nsUnicharUtils.h"
#include "nsThreadUtils.h"
#include "nsInterfaceHashtable.h"
class nsFormControlList;

View File

@ -35,10 +35,9 @@
* 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"
@ -49,68 +48,6 @@
#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)
@ -147,11 +84,9 @@ 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;
}

View File

@ -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___ */

View File

@ -134,7 +134,8 @@ 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);

View File

@ -55,6 +55,14 @@ 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

View File

@ -38,6 +38,7 @@
* ***** END LICENSE BLOCK ***** */
#include "nsHTMLSelectElement.h"
#include "nsHTMLOptionElement.h"
#include "nsIDOMEventTarget.h"
#include "nsContentCreatorFunctions.h"
#include "nsGkAtoms.h"
@ -69,6 +70,9 @@
#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)
@ -351,7 +355,7 @@ nsHTMLSelectElement::InsertOptionsIntoListRecurse(nsIContent* aOptions,
// 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);
@ -569,7 +573,7 @@ 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
@ -671,8 +675,7 @@ nsHTMLSelectElement::Remove(PRInt32 aIndex)
NS_IMETHODIMP
nsHTMLSelectElement::GetOptions(nsIDOMHTMLOptionsCollection** aValue)
{
*aValue = mOptions;
NS_IF_ADDREF(*aValue);
NS_IF_ADDREF(*aValue = GetOptions());
return NS_OK;
}
@ -795,7 +798,8 @@ 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
@ -1700,7 +1704,7 @@ 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);
@ -1773,7 +1777,7 @@ nsHTMLOptionCollection::DropReference()
}
nsresult
nsHTMLOptionCollection::GetOptionIndex(nsIDOMHTMLOptionElement* aOption,
nsHTMLOptionCollection::GetOptionIndex(mozilla::dom::Element* aOption,
PRInt32 aStartIndex,
PRBool aForward,
PRInt32* aIndex)
@ -1791,7 +1795,7 @@ nsHTMLOptionCollection::GetOptionIndex(nsIDOMHTMLOptionElement* aOption,
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) {
@ -1807,10 +1811,16 @@ nsHTMLOptionCollection::GetOptionIndex(nsIDOMHTMLOptionElement* aOption,
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
@ -1840,7 +1850,7 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE_AMBIGUOUS(nsHTMLOptionCollection,
NS_IMETHODIMP
nsHTMLOptionCollection::GetLength(PRUint32* aLength)
{
*aLength = mElements.Count();
*aLength = mElements.Length();
return NS_OK;
}
@ -1874,23 +1884,25 @@ nsHTMLOptionCollection::SetOption(PRInt32 aIndex,
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;
@ -1933,14 +1945,22 @@ nsHTMLOptionCollection::Item(PRUint32 aIndex, nsIDOMNode** aReturn)
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) ||

View File

@ -60,7 +60,7 @@
#include "nsIComponentManager.h"
#include "nsCheapSets.h"
#include "nsLayoutErrors.h"
#include "nsHTMLOptionElement.h"
class nsHTMLSelectElement;
@ -87,12 +87,7 @@ public:
// 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,
@ -104,18 +99,18 @@ public:
* @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);
}
/**
@ -123,9 +118,9 @@ public:
* @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>());
}
/**
@ -139,9 +134,9 @@ public:
/**
* Append an option to end of array
*/
PRBool AppendOption(nsIDOMHTMLOptionElement* aOption)
PRBool AppendOption(nsHTMLOptionElement* aOption)
{
return mElements.AppendObject(aOption);
return !!mElements.AppendElement(aOption);
}
/**
@ -152,13 +147,13 @@ public:
/**
* 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;
};
@ -309,6 +304,16 @@ public:
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;

View File

@ -214,7 +214,7 @@
#include "nsIDOMDOMImplementation.h"
#include "nsIDOMDocumentFragment.h"
#include "nsIDOMDocumentEvent.h"
#include "nsIDOMAttr.h"
#include "nsDOMAttribute.h"
#include "nsIDOMText.h"
#include "nsIDOM3Text.h"
#include "nsIDOMComment.h"
@ -476,6 +476,8 @@
#include "nsIEventListenerService.h"
#include "nsIFrameMessageManager.h"
#include "mozilla/dom/Element.h"
#include "nsHTMLSelectElement.h"
#include "nsHTMLLegendElement.h"
using namespace mozilla::dom;
@ -7474,9 +7476,8 @@ nsNodeSH::PreCreate(nsISupports *nativeObj, JSContext *cx, JSObject *globalObj,
hasHadScriptHandlingObject ||
IsPrivilegedScript());
nsISupports *native_parent;
nsINode *native_parent;
PRBool slimWrappers = PR_TRUE;
PRBool nodeIsElement = node->IsElement();
if (nodeIsElement && node->AsElement()->IsXUL()) {
// For XUL elements, use the parent, if any.
@ -7499,23 +7500,21 @@ nsNodeSH::PreCreate(nsISupports *nativeObj, JSContext *cx, JSObject *globalObj,
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;
}
}
}
else {
// 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));
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;
@ -7528,19 +7527,27 @@ nsNodeSH::PreCreate(nsISupports *nativeObj, JSContext *cx, JSObject *globalObj,
// document's global object, if there is one
// Get the scope object from the document.
native_parent = doc->GetScopeObject();
nsISupports *scope = doc->GetScopeObject();
if (!native_parent) {
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
@ -7548,22 +7555,12 @@ nsNodeSH::PreCreate(nsISupports *nativeObj, JSContext *cx, JSObject *globalObj,
// 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
@ -8361,7 +8358,8 @@ nsNamedNodeMapSH::GetItemAt(nsISupports *aNative, PRUint32 aIndex,
{
nsDOMAttributeMap* map = nsDOMAttributeMap::FromSupports(aNative);
return map->GetItemAt(aIndex, aResult);
nsINode *attr = map->GetItemAt(aIndex, aResult);
return attr;
}
nsISupports*
@ -8370,7 +8368,8 @@ nsNamedNodeMapSH::GetNamedItem(nsISupports *aNative, const nsAString& aName,
{
nsDOMAttributeMap* map = nsDOMAttributeMap::FromSupports(aNative);
return map->GetNamedItem(aName, aResult);
nsINode *attr = map->GetNamedItem(aName, aResult);
return attr;
}
@ -9525,15 +9524,13 @@ nsHTMLSelectElementSH::GetProperty(nsIXPConnectWrappedNative *wrapper,
nsresult rv = NS_OK;
if (n >= 0) {
nsCOMPtr<nsIDOMHTMLSelectElement> s = do_QueryWrappedNative(wrapper, obj);
nsHTMLSelectElement *s =
nsHTMLSelectElement::FromSupports(GetNative(wrapper, obj));
nsCOMPtr<nsIDOMHTMLOptionsCollection> options;
s->GetOptions(getter_AddRefs(options));
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)) {

View File

@ -2198,6 +2198,8 @@ nsGfxScrollFrameInner::CreateAnonymousContent(nsTArray<nsIContent*>& aElements)
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;
}
@ -2208,6 +2210,8 @@ nsGfxScrollFrameInner::CreateAnonymousContent(nsTArray<nsIContent*>& aElements)
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;
}
@ -2243,6 +2247,8 @@ nsGfxScrollFrameInner::CreateAnonymousContent(nsTArray<nsIContent*>& aElements)
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;

View File

@ -51,6 +51,9 @@
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>

View File

@ -17,7 +17,7 @@
</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">

View File

@ -30,7 +30,7 @@
</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">

View File

@ -144,6 +144,12 @@ extern "C" long TSMProcessRawKeyEvent(EventRef carbonEvent);
// 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;
@ -242,7 +248,8 @@ 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);

View File

@ -204,6 +204,8 @@ PRUint32 nsChildView::sLastInputEventCount = 0;
- (void)fireKeyEventForFlagsChanged:(NSEvent*)theEvent keyDown:(BOOL)isKeyDown;
- (BOOL)inactiveWindowAcceptsMouseEvent:(NSEvent*)aEvent;
@end
#pragma mark -
@ -2206,12 +2208,14 @@ NSEvent* gLastDragMouseDownEvent = nil;
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
@ -2276,6 +2280,7 @@ NSEvent* gLastDragMouseDownEvent = nil;
[mGLContext release];
[mPendingDirtyRects release];
[mLastMouseDownEvent release];
[mClickThroughMouseDownEvent release];
ChildViewMouseTracker::OnDestroyView(self);
#ifndef NP_NO_CARBON
if (mPluginTSMDoc)
@ -2485,6 +2490,20 @@ NSEvent* gLastDragMouseDownEvent = nil;
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;
@ -3094,9 +3113,9 @@ static BOOL DrawingAtWindowTop(CGContextRef aContext)
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
@ -3122,11 +3141,21 @@ static BOOL DrawingAtWindowTop(CGContextRef aContext)
[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|
@ -3141,7 +3170,15 @@ static BOOL DrawingAtWindowTop(CGContextRef aContext)
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
@ -3170,7 +3207,7 @@ static BOOL DrawingAtWindowTop(CGContextRef aContext)
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];
@ -3178,6 +3215,7 @@ static BOOL DrawingAtWindowTop(CGContextRef aContext)
}
mGeckoChild->DispatchWindowEvent(geckoEvent);
mBlockedLastMouseDown = NO;
// XXX maybe call markedTextSelectionChanged:client: here?
@ -3188,7 +3226,7 @@ static BOOL DrawingAtWindowTop(CGContextRef aContext)
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
if (!mGeckoChild)
if (!mGeckoChild || mBlockedLastMouseDown)
return;
nsAutoRetainCocoaObject kungFuDeathGrip(self);
@ -3550,7 +3588,7 @@ static BOOL DrawingAtWindowTop(CGContextRef aContext)
nsAutoRetainCocoaObject kungFuDeathGrip(self);
if ([self maybeRollup:theEvent] ||
!ChildViewMouseTracker::WindowAcceptsEvent([self window], theEvent))
!ChildViewMouseTracker::WindowAcceptsEvent([self window], theEvent, self))
return;
if (!mGeckoChild)
@ -4180,8 +4218,8 @@ static PRBool IsNormalCharInputtingEvent(const nsKeyEvent& aEvent)
{
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];
@ -5659,6 +5697,13 @@ static const char* ToEscapedString(NSString* aString, nsCAutoString& aBuf)
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)
@ -6361,13 +6406,17 @@ 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;
@ -6469,7 +6518,8 @@ ChildViewMouseTracker::WindowForEvent(NSEvent* anEvent)
}
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.
@ -6495,7 +6545,7 @@ ChildViewMouseTracker::WindowAcceptsEvent(NSWindow* aWindow, NSEvent* aEvent)
// 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:
@ -6518,15 +6568,15 @@ ChildViewMouseTracker::WindowAcceptsEvent(NSWindow* aWindow, NSEvent* aEvent)
}
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 -

View File

@ -2392,9 +2392,9 @@ ContentPatternDrawCallback(void* aInfo, CGContextRef aContext)
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:

View File

@ -169,8 +169,13 @@
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));
}
}
}
@ -178,21 +183,31 @@
function eventMonitor(e) {
printDebug("got event: " + eventToString(e) + "\n");
var expectedEvent = gExpectedEvents.shift();
while (expectedEvent && expectedEvent.todoShouldHaveFired) {
todo(false, "Should have got event: " + eventToString(expectedEvent));
expectedEvent = gExpectedEvents.shift();
processEvent(e);
}
function processEvent(e) {
var 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));
}
}
@ -234,6 +249,14 @@
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 },
@ -253,7 +276,7 @@
{ 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 },
@ -285,9 +308,7 @@
[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 },
]],
@ -301,16 +322,16 @@
// 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.
@ -325,7 +346,6 @@
[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) {
@ -337,14 +357,14 @@
[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 },
]],
@ -352,15 +372,15 @@
[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, [
@ -372,7 +392,7 @@
]],
[400, 180, NSLeftMouseUp, null, right, [
]],
function verifyPopupClosed(callback) {
function verifyPopupClosed2(callback) {
is(gPopup.popupBoxObject.popupState, "closed", "popup should have closed when clicking");
callback();
},
@ -398,18 +418,14 @@
]],
// 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.
@ -460,7 +476,6 @@
[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) {
@ -469,7 +484,287 @@
// 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, [