Implement CSS3 :target pseudo-class, which matches the target of the fragment identifier of the document's URL. Change the semantics of nsIPresShell::GoToAnchor by requiring that it be called whenever the current target changes, with an additional boolean parameter specifying whether to scroll. b=188734 r=glazman sr=bzbarsky

This commit is contained in:
dbaron%dbaron.org 2003-01-13 23:10:53 +00:00
Родитель 3f8b500ca5
Коммит 1e96329338
14 изменённых файлов: 136 добавлений и 63 удалений

Просмотреть файл

@ -123,8 +123,9 @@ public:
#define NS_EVENT_STATE_FOCUS 0x0002 // content has focus
#define NS_EVENT_STATE_HOVER 0x0004 // mouse is hovering over content
#define NS_EVENT_STATE_DRAGOVER 0x0008 // drag is hovering over content
#define NS_EVENT_STATE_URLTARGET 0x0010 // content is URL's target (ref)
// The following states are used only for ContentStatesChanged
#define NS_EVENT_STATE_CHECKED 0x0010
#define NS_EVENT_STATE_CHECKED 0x0020
enum EFocusedWithType {
eEventFocusedByUnknown, // focus gained via unknown method

Просмотреть файл

@ -3803,13 +3803,16 @@ nsEventStateManager::GetContentState(nsIContent *aContent, PRInt32& aState)
if (aContent == mDragOverContent) {
aState |= NS_EVENT_STATE_DRAGOVER;
}
if (aContent == mURLTargetContent) {
aState |= NS_EVENT_STATE_URLTARGET;
}
return NS_OK;
}
NS_IMETHODIMP
nsEventStateManager::SetContentState(nsIContent *aContent, PRInt32 aState)
{
const PRInt32 maxNotify = 5;
const PRInt32 maxNotify = 6;
// We must initialize this array with memset for the sake of the boneheaded
// OS X compiler. See bug 134934.
nsIContent *notifyContent[maxNotify];
@ -3831,6 +3834,12 @@ nsEventStateManager::SetContentState(nsIContent *aContent, PRInt32 aState)
mDragOverContent = aContent;
}
if ((aState & NS_EVENT_STATE_URLTARGET) && (aContent != mURLTargetContent)) {
notifyContent[5] = mURLTargetContent;
NS_IF_ADDREF(notifyContent[5]);
mURLTargetContent = aContent;
}
if ((aState & NS_EVENT_STATE_ACTIVE) && (aContent != mActiveContent)) {
//transferring ref to notifyContent from mActiveContent
notifyContent[2] = mActiveContent;

Просмотреть файл

@ -257,6 +257,7 @@ protected:
nsCOMPtr<nsIContent> mActiveContent;
nsCOMPtr<nsIContent> mHoverContent;
nsCOMPtr<nsIContent> mDragOverContent;
nsCOMPtr<nsIContent> mURLTargetContent;
nsCOMPtr<nsIContent> mCurrentFocus;
PRInt32 mLastFocusedWith;
PRInt32 mCurrentTabIndex;

Просмотреть файл

@ -4346,7 +4346,7 @@ HTMLContentSink::ScrollToRef()
// Check an empty string which might be caused by the UTF-8 conversion
if (!ref.IsEmpty()) {
rv = shell->GoToAnchor(ref);
rv = shell->GoToAnchor(ref, PR_TRUE);
} else {
rv = NS_ERROR_FAILURE;
}
@ -4362,7 +4362,7 @@ HTMLContentSink::ScrollToRef()
rv = CharsetConvRef(docCharset, unescapedRef, ref);
if (NS_SUCCEEDED(rv) && !ref.IsEmpty())
rv = shell->GoToAnchor(ref);
rv = shell->GoToAnchor(ref, PR_TRUE);
}
}

Просмотреть файл

@ -3554,11 +3554,12 @@ static PRBool ValueIncludes(const nsString& aValueList, const nsString& aValue,
inline PRBool IsEventPseudo(nsIAtom* aAtom)
{
return PRBool ((nsCSSPseudoClasses::active == aAtom) ||
(nsCSSPseudoClasses::mozDragOver == aAtom) ||
(nsCSSPseudoClasses::focus == aAtom) ||
(nsCSSPseudoClasses::hover == aAtom));
// XXX selected, enabled, disabled, selection?
return nsCSSPseudoClasses::active == aAtom ||
nsCSSPseudoClasses::mozDragOver == aAtom ||
nsCSSPseudoClasses::focus == aAtom ||
nsCSSPseudoClasses::hover == aAtom ||
nsCSSPseudoClasses::target == aAtom;
// XXX selected, enabled, disabled, selection?
}
inline PRBool IsLinkPseudo(nsIAtom* aAtom)
@ -3844,6 +3845,10 @@ static PRBool SelectorMatches(RuleProcessorData &data,
result = (aStateMask & NS_EVENT_STATE_DRAGOVER) ||
(localTrue == (0 != (data.mEventState & NS_EVENT_STATE_DRAGOVER)));
}
else if (nsCSSPseudoClasses::target == pseudoClass->mAtom) {
result = (aStateMask & NS_EVENT_STATE_URLTARGET) ||
(localTrue == (0 != (data.mEventState & NS_EVENT_STATE_URLTARGET)));
}
}
}
else if (IsLinkPseudo(pseudoClass->mAtom)) {
@ -4443,7 +4448,8 @@ PRBool IsStateSelector(nsCSSSelector& aSelector)
(pseudoClass->mAtom == nsCSSPseudoClasses::checked) ||
(pseudoClass->mAtom == nsCSSPseudoClasses::mozDragOver) ||
(pseudoClass->mAtom == nsCSSPseudoClasses::focus) ||
(pseudoClass->mAtom == nsCSSPseudoClasses::hover)) {
(pseudoClass->mAtom == nsCSSPseudoClasses::hover) ||
(pseudoClass->mAtom == nsCSSPseudoClasses::target)) {
return PR_TRUE;
}
pseudoClass = pseudoClass->mNext;

Просмотреть файл

@ -64,6 +64,7 @@ CSS_PSEUDO_CLASS(enabled, ":enabled")
CSS_PSEUDO_CLASS(focus, ":focus")
CSS_PSEUDO_CLASS(hover, ":hover")
CSS_PSEUDO_CLASS(mozDragOver, ":-moz-drag-over")
CSS_PSEUDO_CLASS(target, ":target")
CSS_PSEUDO_CLASS(firstChild, ":first-child")
CSS_PSEUDO_CLASS(firstNode, ":first-node")

Просмотреть файл

@ -297,7 +297,7 @@ nsXMLContentSink::ScrollToRef()
// Check an empty string which might be caused by the UTF-8 conversion
if (!ref.IsEmpty())
rv = shell->GoToAnchor(ref);
rv = shell->GoToAnchor(ref, PR_TRUE);
else
rv = NS_ERROR_FAILURE;
@ -312,7 +312,7 @@ nsXMLContentSink::ScrollToRef()
rv = CharsetConvRef(docCharset, unescapedRef, ref);
if (NS_SUCCEEDED(rv) && !ref.IsEmpty())
rv = shell->GoToAnchor(ref);
rv = shell->GoToAnchor(ref, PR_TRUE);
}
}
}

Просмотреть файл

@ -5649,16 +5649,19 @@ nsDocShell::ScrollIfAnchor(nsIURI * aURI, PRBool * aWasAnchor, PRUint32 aLoadTyp
GetCurScrollPos(ScrollOrientation_X, cx);
GetCurScrollPos(ScrollOrientation_Y, cy);
nsCOMPtr<nsIPresShell> shell;
rv = GetPresShell(getter_AddRefs(shell));
if (NS_FAILED(rv))
return rv;
if (!sNewRef.IsEmpty()) {
nsCOMPtr<nsIPresShell> shell = nsnull;
rv = GetPresShell(getter_AddRefs(shell));
if (NS_SUCCEEDED(rv) && shell) {
if (shell) {
*aWasAnchor = PR_TRUE;
// anchor is there, but if it's a load from history,
// we don't have any anchor jumping to do
if (aLoadType == LOAD_HISTORY || aLoadType == LOAD_RELOAD_NORMAL)
return rv;
PRBool scroll = aLoadType != LOAD_HISTORY &&
aLoadType != LOAD_RELOAD_NORMAL;
char *str = ToNewCString(sNewRef);
@ -5670,7 +5673,7 @@ nsDocShell::ScrollIfAnchor(nsIURI * aURI, PRBool * aWasAnchor, PRUint32 aLoadTyp
// We try the UTF-8 string first, and then try the
// document's charset (see below).
rv = shell->GoToAnchor(NS_ConvertUTF8toUCS2(str));
rv = shell->GoToAnchor(NS_ConvertUTF8toUCS2(str), scroll);
nsMemory::Free(str);
// Above will fail if the anchor name is not UTF-8.
@ -5703,12 +5706,15 @@ nsDocShell::ScrollIfAnchor(nsIURI * aURI, PRBool * aWasAnchor, PRUint32 aLoadTyp
getter_Copies(uStr));
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
rv = shell->GoToAnchor(uStr);
rv = shell->GoToAnchor(uStr, scroll);
}
}
}
else {
*aWasAnchor = PR_TRUE;
// Tell the shell it's at an anchor, without scrolling.
shell->GoToAnchor(NS_LITERAL_STRING(""), PR_FALSE);
// An empty anchor was found, but if it's a load from history,
// we don't have to jump to the top of the page. Scrollbar

Просмотреть файл

@ -352,10 +352,14 @@ public:
/**
* Scrolls the view of the document so that the anchor with the specified
* name is displayed at the top of the window
* Informs the pres shell that the document is now at the anchor with
* the given name. If |aScroll| is true, scrolls the view of the
* document so that the anchor with the specified name is displayed at
* the top of the window. If |aAnchorName| is empty, then this informs
* the pres shell that there is no current target, and |aScroll| must
* be false.
*/
NS_IMETHOD GoToAnchor(const nsAString& aAnchorName) = 0;
NS_IMETHOD GoToAnchor(const nsAString& aAnchorName, PRBool aScroll) = 0;
/**
* Scrolls the view of the document so that the frame is displayed at the

Просмотреть файл

@ -1105,7 +1105,7 @@ public:
nsIRenderingContext** aContext);
NS_IMETHOD CantRenderReplacedElement(nsIPresContext* aPresContext,
nsIFrame* aFrame);
NS_IMETHOD GoToAnchor(const nsAString& aAnchorName);
NS_IMETHOD GoToAnchor(const nsAString& aAnchorName, PRBool aScroll);
NS_IMETHOD ScrollFrameIntoView(nsIFrame *aFrame,
PRIntn aVPercent,
@ -3930,8 +3930,18 @@ PresShell::CantRenderReplacedElement(nsIPresContext* aPresContext,
}
NS_IMETHODIMP
PresShell::GoToAnchor(const nsAString& aAnchorName)
PresShell::GoToAnchor(const nsAString& aAnchorName, PRBool aScroll)
{
nsCOMPtr<nsIEventStateManager> esm;
mPresContext->GetEventStateManager(getter_AddRefs(esm));
if (aAnchorName.IsEmpty()) {
NS_ASSERTION(!aScroll, "can't scroll to empty anchor name");
if (esm) {
esm->SetContentState(nsnull, NS_EVENT_STATE_URLTARGET);
}
return NS_OK;
}
nsCOMPtr<nsIDOMDocument> doc = do_QueryInterface(mDocument);
nsCOMPtr<nsIDOMHTMLDocument> htmlDoc = do_QueryInterface(mDocument);
nsresult rv = NS_OK;
@ -4025,11 +4035,16 @@ PresShell::GoToAnchor(const nsAString& aAnchorName)
}
}
if (content) {
nsIFrame* frame = nsnull;
if (esm) {
esm->SetContentState(content, NS_EVENT_STATE_URLTARGET);
}
if (content) {
// Get the primary frame
if (NS_SUCCEEDED(GetPrimaryFrameFor(content, &frame)) && frame) {
nsIFrame* frame = nsnull;
if (aScroll &&
NS_SUCCEEDED(GetPrimaryFrameFor(content, &frame)) &&
frame) {
rv = ScrollFrameIntoView(frame, NS_PRESSHELL_SCROLL_TOP,
NS_PRESSHELL_SCROLL_ANYWHERE);
@ -4055,8 +4070,7 @@ PresShell::GoToAnchor(const nsAString& aAnchorName)
SelectRange(jumpToRange);
}
nsCOMPtr<nsIEventStateManager> esm;
if (NS_SUCCEEDED(mPresContext->GetEventStateManager(getter_AddRefs(esm))) && esm) {
if (esm) {
PRBool isSelectionWithFocus;
esm->MoveFocusToCaret(PR_TRUE, &isSelectionWithFocus);
}
@ -4071,15 +4085,18 @@ PresShell::GoToAnchor(const nsAString& aAnchorName)
mPresContext->GetCompatibilityMode(&compatMode);
if ((NS_LossyConvertUCS2toASCII(aAnchorName).EqualsIgnoreCase("top")) &&
(compatMode == eCompatibility_NavQuirks) &&
(mViewManager)) {
// Get the viewport scroller
nsIScrollableView* scrollingView;
mViewManager->GetRootScrollableView(&scrollingView);
if (scrollingView) {
// Scroll to the top of the page
scrollingView->ScrollTo(0, 0, NS_VMREFRESH_IMMEDIATE);
rv = NS_OK;
(compatMode == eCompatibility_NavQuirks)) {
rv = NS_OK;
// Check |aScroll| after setting |rv| so we set |rv| to the same
// thing whether or not |aScroll| is true.
if (aScroll && mViewManager) {
// Get the viewport scroller
nsIScrollableView* scrollingView;
mViewManager->GetRootScrollableView(&scrollingView);
if (scrollingView) {
// Scroll to the top of the page
scrollingView->ScrollTo(0, 0, NS_VMREFRESH_IMMEDIATE);
}
}
}
}

Просмотреть файл

@ -352,10 +352,14 @@ public:
/**
* Scrolls the view of the document so that the anchor with the specified
* name is displayed at the top of the window
* Informs the pres shell that the document is now at the anchor with
* the given name. If |aScroll| is true, scrolls the view of the
* document so that the anchor with the specified name is displayed at
* the top of the window. If |aAnchorName| is empty, then this informs
* the pres shell that there is no current target, and |aScroll| must
* be false.
*/
NS_IMETHOD GoToAnchor(const nsAString& aAnchorName) = 0;
NS_IMETHOD GoToAnchor(const nsAString& aAnchorName, PRBool aScroll) = 0;
/**
* Scrolls the view of the document so that the frame is displayed at the

Просмотреть файл

@ -1105,7 +1105,7 @@ public:
nsIRenderingContext** aContext);
NS_IMETHOD CantRenderReplacedElement(nsIPresContext* aPresContext,
nsIFrame* aFrame);
NS_IMETHOD GoToAnchor(const nsAString& aAnchorName);
NS_IMETHOD GoToAnchor(const nsAString& aAnchorName, PRBool aScroll);
NS_IMETHOD ScrollFrameIntoView(nsIFrame *aFrame,
PRIntn aVPercent,
@ -3930,8 +3930,18 @@ PresShell::CantRenderReplacedElement(nsIPresContext* aPresContext,
}
NS_IMETHODIMP
PresShell::GoToAnchor(const nsAString& aAnchorName)
PresShell::GoToAnchor(const nsAString& aAnchorName, PRBool aScroll)
{
nsCOMPtr<nsIEventStateManager> esm;
mPresContext->GetEventStateManager(getter_AddRefs(esm));
if (aAnchorName.IsEmpty()) {
NS_ASSERTION(!aScroll, "can't scroll to empty anchor name");
if (esm) {
esm->SetContentState(nsnull, NS_EVENT_STATE_URLTARGET);
}
return NS_OK;
}
nsCOMPtr<nsIDOMDocument> doc = do_QueryInterface(mDocument);
nsCOMPtr<nsIDOMHTMLDocument> htmlDoc = do_QueryInterface(mDocument);
nsresult rv = NS_OK;
@ -4025,11 +4035,16 @@ PresShell::GoToAnchor(const nsAString& aAnchorName)
}
}
if (content) {
nsIFrame* frame = nsnull;
if (esm) {
esm->SetContentState(content, NS_EVENT_STATE_URLTARGET);
}
if (content) {
// Get the primary frame
if (NS_SUCCEEDED(GetPrimaryFrameFor(content, &frame)) && frame) {
nsIFrame* frame = nsnull;
if (aScroll &&
NS_SUCCEEDED(GetPrimaryFrameFor(content, &frame)) &&
frame) {
rv = ScrollFrameIntoView(frame, NS_PRESSHELL_SCROLL_TOP,
NS_PRESSHELL_SCROLL_ANYWHERE);
@ -4055,8 +4070,7 @@ PresShell::GoToAnchor(const nsAString& aAnchorName)
SelectRange(jumpToRange);
}
nsCOMPtr<nsIEventStateManager> esm;
if (NS_SUCCEEDED(mPresContext->GetEventStateManager(getter_AddRefs(esm))) && esm) {
if (esm) {
PRBool isSelectionWithFocus;
esm->MoveFocusToCaret(PR_TRUE, &isSelectionWithFocus);
}
@ -4071,15 +4085,18 @@ PresShell::GoToAnchor(const nsAString& aAnchorName)
mPresContext->GetCompatibilityMode(&compatMode);
if ((NS_LossyConvertUCS2toASCII(aAnchorName).EqualsIgnoreCase("top")) &&
(compatMode == eCompatibility_NavQuirks) &&
(mViewManager)) {
// Get the viewport scroller
nsIScrollableView* scrollingView;
mViewManager->GetRootScrollableView(&scrollingView);
if (scrollingView) {
// Scroll to the top of the page
scrollingView->ScrollTo(0, 0, NS_VMREFRESH_IMMEDIATE);
rv = NS_OK;
(compatMode == eCompatibility_NavQuirks)) {
rv = NS_OK;
// Check |aScroll| after setting |rv| so we set |rv| to the same
// thing whether or not |aScroll| is true.
if (aScroll && mViewManager) {
// Get the viewport scroller
nsIScrollableView* scrollingView;
mViewManager->GetRootScrollableView(&scrollingView);
if (scrollingView) {
// Scroll to the top of the page
scrollingView->ScrollTo(0, 0, NS_VMREFRESH_IMMEDIATE);
}
}
}
}

Просмотреть файл

@ -64,6 +64,7 @@ CSS_PSEUDO_CLASS(enabled, ":enabled")
CSS_PSEUDO_CLASS(focus, ":focus")
CSS_PSEUDO_CLASS(hover, ":hover")
CSS_PSEUDO_CLASS(mozDragOver, ":-moz-drag-over")
CSS_PSEUDO_CLASS(target, ":target")
CSS_PSEUDO_CLASS(firstChild, ":first-child")
CSS_PSEUDO_CLASS(firstNode, ":first-node")

Просмотреть файл

@ -3554,11 +3554,12 @@ static PRBool ValueIncludes(const nsString& aValueList, const nsString& aValue,
inline PRBool IsEventPseudo(nsIAtom* aAtom)
{
return PRBool ((nsCSSPseudoClasses::active == aAtom) ||
(nsCSSPseudoClasses::mozDragOver == aAtom) ||
(nsCSSPseudoClasses::focus == aAtom) ||
(nsCSSPseudoClasses::hover == aAtom));
// XXX selected, enabled, disabled, selection?
return nsCSSPseudoClasses::active == aAtom ||
nsCSSPseudoClasses::mozDragOver == aAtom ||
nsCSSPseudoClasses::focus == aAtom ||
nsCSSPseudoClasses::hover == aAtom ||
nsCSSPseudoClasses::target == aAtom;
// XXX selected, enabled, disabled, selection?
}
inline PRBool IsLinkPseudo(nsIAtom* aAtom)
@ -3844,6 +3845,10 @@ static PRBool SelectorMatches(RuleProcessorData &data,
result = (aStateMask & NS_EVENT_STATE_DRAGOVER) ||
(localTrue == (0 != (data.mEventState & NS_EVENT_STATE_DRAGOVER)));
}
else if (nsCSSPseudoClasses::target == pseudoClass->mAtom) {
result = (aStateMask & NS_EVENT_STATE_URLTARGET) ||
(localTrue == (0 != (data.mEventState & NS_EVENT_STATE_URLTARGET)));
}
}
}
else if (IsLinkPseudo(pseudoClass->mAtom)) {
@ -4443,7 +4448,8 @@ PRBool IsStateSelector(nsCSSSelector& aSelector)
(pseudoClass->mAtom == nsCSSPseudoClasses::checked) ||
(pseudoClass->mAtom == nsCSSPseudoClasses::mozDragOver) ||
(pseudoClass->mAtom == nsCSSPseudoClasses::focus) ||
(pseudoClass->mAtom == nsCSSPseudoClasses::hover)) {
(pseudoClass->mAtom == nsCSSPseudoClasses::hover) ||
(pseudoClass->mAtom == nsCSSPseudoClasses::target)) {
return PR_TRUE;
}
pseudoClass = pseudoClass->mNext;