Bug 426082, part 3: Reflect a label's :hover/:active state on its associated natively themed control.

This patch adds an aFollowLabels argument to nsIEventStateManager::GetContentState which defaults to false. Setting it to true will return NS_EVENT_STATE_HOVER / _ACTIVE for labeled controls even if only their label is subject to this state. At the moment this flag is only set by native theming, so there won't be an observable change for the :hover / :active pseudoclasses.
In order to make dynamic state changes on the label work, SetContentState will now always notify the labeled control of state changes when its label changes state.
r=smaug
This commit is contained in:
Markus Stange 2010-05-31 19:49:55 +02:00
parent 98b0d9be1f
commit 23751c9400
4 changed files with 49 additions and 9 deletions

View File

@ -85,7 +85,18 @@ public:
NS_IMETHOD GetEventTarget(nsIFrame **aFrame) = 0;
NS_IMETHOD GetEventTargetContent(nsEvent* aEvent, nsIContent** aContent) = 0;
virtual PRInt32 GetContentState(nsIContent *aContent) = 0;
/**
* Returns the content state of aContent.
* @param aContent The control whose state is requested.
* @param aFollowLabels Whether to reflect a label's content state on its
* associated control. If aFollowLabels is true and
* aContent is a control which has a label that has the
* hover or active content state set, GetContentState
* will pretend that those states are also set on aContent.
* @return The content state.
*/
virtual PRInt32 GetContentState(nsIContent *aContent,
PRBool aFollowLabels = PR_FALSE) = 0;
/**
* Notify that the given NS_EVENT_STATE_* bit has changed for this content.

View File

@ -66,6 +66,7 @@
#include "nsIDOMHTMLAnchorElement.h"
#include "nsIDOMHTMLInputElement.h"
#include "nsIDOMNSHTMLInputElement.h"
#include "nsIDOMNSHTMLLabelElement.h"
#include "nsIDOMHTMLSelectElement.h"
#include "nsIDOMHTMLTextAreaElement.h"
#include "nsIDOMHTMLAreaElement.h"
@ -3977,25 +3978,48 @@ nsEventStateManager::GetEventTargetContent(nsEvent* aEvent,
return NS_OK;
}
static already_AddRefed<nsIContent>
GetLabelTarget(nsIContent* aLabel)
{
nsCOMPtr<nsIDOMNSHTMLLabelElement> label = do_QueryInterface(aLabel);
if (!label)
return nsnull;
nsCOMPtr<nsIDOMHTMLElement> target;
label->GetControl(getter_AddRefs(target));
nsIContent* targetContent = nsnull;
if (target) {
CallQueryInterface(target, &targetContent);
}
return targetContent;
}
static bool
IsAncestorOf(nsIContent* aPossibleAncestor, nsIContent* aPossibleDescendant)
IsAncestorOf(nsIContent* aPossibleAncestor, nsIContent* aPossibleDescendant,
PRBool aFollowLabels)
{
for (; aPossibleDescendant; aPossibleDescendant = aPossibleDescendant->GetParent()) {
if (aPossibleAncestor == aPossibleDescendant)
return true;
if (aFollowLabels) {
nsCOMPtr<nsIContent> labelTarget = GetLabelTarget(aPossibleDescendant);
if (labelTarget == aPossibleAncestor)
return true;
}
}
return false;
}
PRInt32
nsEventStateManager::GetContentState(nsIContent *aContent)
nsEventStateManager::GetContentState(nsIContent *aContent, PRBool aFollowLabels)
{
PRInt32 state = aContent->IntrinsicState();
if (IsAncestorOf(aContent, mActiveContent)) {
if (IsAncestorOf(aContent, mActiveContent, aFollowLabels)) {
state |= NS_EVENT_STATE_ACTIVE;
}
if (IsAncestorOf(aContent, mHoverContent)) {
if (IsAncestorOf(aContent, mHoverContent, aFollowLabels)) {
state |= NS_EVENT_STATE_HOVER;
}
@ -4071,6 +4095,10 @@ NotifyAncestors(nsIDocument* aDocument, nsIContent* aStartNode,
{
while (aStartNode && aStartNode != aStopBefore) {
aDocument->ContentStatesChanged(aStartNode, nsnull, aState);
nsCOMPtr<nsIContent> labelTarget = GetLabelTarget(aStartNode);
if (labelTarget) {
aDocument->ContentStatesChanged(labelTarget, nsnull, aState);
}
aStartNode = aStartNode->GetParent();
}
}
@ -4281,14 +4309,14 @@ nsEventStateManager::ContentRemoved(nsIDocument* aDocument, nsIContent* aContent
nsContentUtils::ContentIsDescendantOf(mHoverContent, aContent)) {
// Since hover is hierarchical, set the current hover to the
// content's parent node.
mHoverContent = aContent->GetParent();
SetContentState(aContent->GetParent(), NS_EVENT_STATE_HOVER);
}
if (mActiveContent &&
nsContentUtils::ContentIsDescendantOf(mActiveContent, aContent)) {
// Active is hierarchical, so set the current active to the
// content's parent node.
mActiveContent = aContent->GetParent();
SetContentState(aContent->GetParent(), NS_EVENT_STATE_ACTIVE);
}
if (mDragOverContent &&

View File

@ -120,7 +120,8 @@ public:
NS_IMETHOD GetEventTarget(nsIFrame **aFrame);
NS_IMETHOD GetEventTargetContent(nsEvent* aEvent, nsIContent** aContent);
virtual PRInt32 GetContentState(nsIContent *aContent);
virtual PRInt32 GetContentState(nsIContent *aContent,
PRBool aFollowLabels = PR_FALSE);
virtual PRBool SetContentState(nsIContent *aContent, PRInt32 aState);
NS_IMETHOD ContentRemoved(nsIDocument* aDocument, nsIContent* aContent);
NS_IMETHOD EventStatusOK(nsGUIEvent* aEvent, PRBool *aOK);

View File

@ -90,7 +90,7 @@ nsNativeTheme::GetContentState(nsIFrame* aFrame, PRUint8 aWidgetType)
return 0;
nsIEventStateManager* esm = shell->GetPresContext()->EventStateManager();
PRInt32 flags = esm->GetContentState(aFrame->GetContent());
PRInt32 flags = esm->GetContentState(aFrame->GetContent(), PR_TRUE);
if (isXULCheckboxRadio && aWidgetType == NS_THEME_RADIO) {
if (IsFocused(aFrame))