PRBool nsCaret::DrawAtPositionWithHint(nsIDOMNode* aNode, PRInt32 aOffset, nsFrameSelection::HINT aFrameHint, PRUint8 aBidiLevel, PRBool aInvalidate) { nsCOMPtr<nsIContent> contentNode = do_QueryInterface(aNode); if (!contentNode) return PR_FALSE; nsIFrame* theFrame = nsnull; PRInt32 theFrameOffset = 0; nsresult rv = GetCaretFrameForNodeOffset(contentNode, aOffset, aFrameHint, aBidiLevel, &theFrame, &theFrameOffset); if (NS_FAILED(rv) || !theFrame) return PR_FALSE; // now we have a frame, check whether it's appropriate to show the caret here const nsStyleUserInterface* userinterface = theFrame->GetStyleUserInterface(); if ((!mIgnoreUserModify && userinterface->mUserModify == NS_STYLE_USER_MODIFY_READ_ONLY) || (userinterface->mUserInput == NS_STYLE_USER_INPUT_NONE) || (userinterface->mUserInput == NS_STYLE_USER_INPUT_DISABLED)) { return PR_FALSE; } if (!mDrawn) { // save stuff so we can figure out what frame we're in later. mLastContent = contentNode; mLastContentOffset = aOffset; mLastHint = aFrameHint; mLastBidiLevel = aBidiLevel; // If there has been a reflow, set the caret Bidi level to the level of the current frame if (aBidiLevel & BIDI_LEVEL_UNDEFINED) { nsCOMPtr<nsFrameSelection> frameSelection = GetFrameSelection(); if (!frameSelection) return PR_FALSE; frameSelection->SetCaretBidiLevel(NS_GET_EMBEDDING_LEVEL(theFrame)); } // Only update the caret's rect when we're not currently drawn. rv = UpdateCaretRects(theFrame, theFrameOffset); if (NS_FAILED(rv)) return PR_FALSE; } if (aInvalidate) InvalidateRects(mCaretRect, mHookRect, theFrame); return PR_TRUE; }
nsresult nsWebBrowserFind::ClearFrameSelection(nsIDOMWindow *aWindow) { NS_ENSURE_ARG(aWindow); nsCOMPtr<nsISelection> selection; GetFrameSelection(aWindow, getter_AddRefs(selection)); if (selection) selection->RemoveAllRanges(); return NS_OK; }
nsresult nsWebBrowserFind::ClearFrameSelection(nsPIDOMWindowOuter* aWindow) { NS_ENSURE_ARG(aWindow); nsCOMPtr<nsISelection> selection = GetFrameSelection(aWindow); if (selection) { selection->RemoveAllRanges(); } return NS_OK; }
nsIFrame* nsCaret::GetGeometry(nsISelection* aSelection, nsRect* aRect) { nsCOMPtr<nsIDOMNode> focusNode; nsresult rv = aSelection->GetFocusNode(getter_AddRefs(focusNode)); if (NS_FAILED(rv) || !focusNode) return nsnull; PRInt32 focusOffset; rv = aSelection->GetFocusOffset(&focusOffset); if (NS_FAILED(rv)) return nsnull; nsCOMPtr<nsIContent> contentNode = do_QueryInterface(focusNode); if (!contentNode) return nsnull; // find the frame that contains the content node that has focus nsIFrame* theFrame = nsnull; PRInt32 theFrameOffset = 0; nsCOMPtr<nsFrameSelection> frameSelection = GetFrameSelection(); if (!frameSelection) return nsnull; PRUint8 bidiLevel = frameSelection->GetCaretBidiLevel(); rv = GetCaretFrameForNodeOffset(contentNode, focusOffset, frameSelection->GetHint(), bidiLevel, &theFrame, &theFrameOffset); if (NS_FAILED(rv) || !theFrame) return nsnull; nsPoint framePos(0, 0); rv = theFrame->GetPointFromOffset(theFrameOffset, &framePos); if (NS_FAILED(rv)) return nsnull; // now add the frame offset to the view offset, and we're done nscoord height = theFrame->GetSize().height; nscoord width = ComputeMetrics(theFrame, theFrameOffset, height).mCaretWidth; *aRect = nsRect(framePos.x, 0, width, height); return theFrame; }
nsresult nsCaret::DrawAtPosition(nsIDOMNode* aNode, PRInt32 aOffset) { NS_ENSURE_ARG(aNode); PRUint8 bidiLevel; nsCOMPtr<nsFrameSelection> frameSelection = GetFrameSelection(); if (!frameSelection) return NS_ERROR_FAILURE; bidiLevel = frameSelection->GetCaretBidiLevel(); // DrawAtPosition is used by consumers who want us to stay drawn where they // tell us. Setting mBlinkRate to 0 tells us to not set a timer to erase // ourselves, our consumer will take care of that. mBlinkRate = 0; // XXX we need to do more work here to get the correct hint. nsresult rv = DrawAtPositionWithHint(aNode, aOffset, nsFrameSelection::HINTLEFT, bidiLevel, PR_TRUE) ? NS_OK : NS_ERROR_FAILURE; ToggleDrawnStatus(); return rv; }
/* This method handles finding in a single window (aka frame). */ nsresult nsWebBrowserFind::SearchInFrame(nsIDOMWindow* aWindow, bool aWrapping, bool* aDidFind) { NS_ENSURE_ARG(aWindow); NS_ENSURE_ARG_POINTER(aDidFind); *aDidFind = false; nsCOMPtr<nsIDOMDocument> domDoc; nsresult rv = aWindow->GetDocument(getter_AddRefs(domDoc)); NS_ENSURE_SUCCESS(rv, rv); if (!domDoc) return NS_ERROR_FAILURE; // Do security check, to ensure that the frame we're searching is // acccessible from the frame where the Find is being run. // get a uri for the window nsCOMPtr<nsIDocument> theDoc = do_QueryInterface(domDoc); if (!theDoc) return NS_ERROR_FAILURE; nsCOMPtr<nsIScriptSecurityManager> secMan = do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr<nsIPrincipal> subject; rv = secMan->GetSubjectPrincipal(getter_AddRefs(subject)); NS_ENSURE_SUCCESS(rv, rv); if (subject) { bool subsumes; rv = subject->Subsumes(theDoc->NodePrincipal(), &subsumes); NS_ENSURE_SUCCESS(rv, rv); if (!subsumes) { return NS_ERROR_DOM_PROP_ACCESS_DENIED; } } nsCOMPtr<nsIFind> find = do_CreateInstance(NS_FIND_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); (void) find->SetCaseSensitive(mMatchCase); (void) find->SetFindBackwards(mFindBackwards); // XXX Make and set a line breaker here, once that's implemented. (void) find->SetWordBreaker(0); // Now make sure the content (for actual finding) and frame (for // selection) models are up to date. theDoc->FlushPendingNotifications(Flush_Frames); nsCOMPtr<nsISelection> sel; GetFrameSelection(aWindow, getter_AddRefs(sel)); NS_ENSURE_ARG_POINTER(sel); nsCOMPtr<nsIDOMRange> searchRange = nsFind::CreateRange(theDoc); NS_ENSURE_ARG_POINTER(searchRange); nsCOMPtr<nsIDOMRange> startPt = nsFind::CreateRange(theDoc); NS_ENSURE_ARG_POINTER(startPt); nsCOMPtr<nsIDOMRange> endPt = nsFind::CreateRange(theDoc); NS_ENSURE_ARG_POINTER(endPt); nsCOMPtr<nsIDOMRange> foundRange; // If !aWrapping, search from selection to end if (!aWrapping) rv = GetSearchLimits(searchRange, startPt, endPt, domDoc, sel, false); // If aWrapping, search the part of the starting frame // up to the point where we left off. else rv = GetSearchLimits(searchRange, startPt, endPt, domDoc, sel, true); NS_ENSURE_SUCCESS(rv, rv); rv = find->Find(mSearchString.get(), searchRange, startPt, endPt, getter_AddRefs(foundRange)); if (NS_SUCCEEDED(rv) && foundRange) { *aDidFind = true; sel->RemoveAllRanges(); // Beware! This may flush notifications via synchronous // ScrollSelectionIntoView. SetSelectionAndScroll(aWindow, foundRange); } return rv; }
nsresult nsCaret::GetCaretFrameForNodeOffset(nsIContent* aContentNode, PRInt32 aOffset, nsFrameSelection::HINT aFrameHint, PRUint8 aBidiLevel, nsIFrame** aReturnFrame, PRInt32* aReturnOffset) { //get frame selection and find out what frame to use... nsCOMPtr<nsIPresShell> presShell = do_QueryReferent(mPresShell); if (!presShell) return NS_ERROR_FAILURE; nsCOMPtr<nsFrameSelection> frameSelection = GetFrameSelection(); if (!frameSelection) return NS_ERROR_FAILURE; nsIFrame* theFrame = nsnull; PRInt32 theFrameOffset = 0; theFrame = frameSelection->GetFrameForNodeOffset(aContentNode, aOffset, aFrameHint, &theFrameOffset); if (!theFrame) return NS_ERROR_FAILURE; // if theFrame is after a text frame that's logically at the end of the line // (e.g. if theFrame is a <br> frame), then put the caret at the end of // that text frame instead. This way, the caret will be positioned as if // trailing whitespace was not trimmed. AdjustCaretFrameForLineEnd(&theFrame, &theFrameOffset); // Mamdouh : modification of the caret to work at rtl and ltr with Bidi // // Direction Style from this->GetStyleData() // now in (visibility->mDirection) // ------------------ // NS_STYLE_DIRECTION_LTR : LTR or Default // NS_STYLE_DIRECTION_RTL // NS_STYLE_DIRECTION_INHERIT if (mBidiUI) { // If there has been a reflow, take the caret Bidi level to be the level of the current frame if (aBidiLevel & BIDI_LEVEL_UNDEFINED) aBidiLevel = NS_GET_EMBEDDING_LEVEL(theFrame); PRInt32 start; PRInt32 end; nsIFrame* frameBefore; nsIFrame* frameAfter; PRUint8 levelBefore; // Bidi level of the character before the caret PRUint8 levelAfter; // Bidi level of the character after the caret theFrame->GetOffsets(start, end); if (start == 0 || end == 0 || start == theFrameOffset || end == theFrameOffset) { nsPrevNextBidiLevels levels = frameSelection-> GetPrevNextBidiLevels(aContentNode, aOffset, PR_FALSE); /* Boundary condition, we need to know the Bidi levels of the characters before and after the caret */ if (levels.mFrameBefore || levels.mFrameAfter) { frameBefore = levels.mFrameBefore; frameAfter = levels.mFrameAfter; levelBefore = levels.mLevelBefore; levelAfter = levels.mLevelAfter; if ((levelBefore != levelAfter) || (aBidiLevel != levelBefore)) { aBidiLevel = NS_MAX(aBidiLevel, NS_MIN(levelBefore, levelAfter)); // rule c3 aBidiLevel = NS_MIN(aBidiLevel, NS_MAX(levelBefore, levelAfter)); // rule c4 if (aBidiLevel == levelBefore // rule c1 || (aBidiLevel > levelBefore && aBidiLevel < levelAfter && !((aBidiLevel ^ levelBefore) & 1)) // rule c5 || (aBidiLevel < levelBefore && aBidiLevel > levelAfter && !((aBidiLevel ^ levelBefore) & 1))) // rule c9 { if (theFrame != frameBefore) { if (frameBefore) // if there is a frameBefore, move into it { theFrame = frameBefore; theFrame->GetOffsets(start, end); theFrameOffset = end; } else { // if there is no frameBefore, we must be at the beginning of the line // so we stay with the current frame. // Exception: when the first frame on the line has a different Bidi level from the paragraph level, there is no // real frame for the caret to be in. We have to find the visually first frame on the line. PRUint8 baseLevel = NS_GET_BASE_LEVEL(frameAfter); if (baseLevel != levelAfter) { nsPeekOffsetStruct pos; pos.SetData(eSelectBeginLine, eDirPrevious, 0, 0, PR_FALSE, PR_TRUE, PR_FALSE, PR_TRUE); if (NS_SUCCEEDED(frameAfter->PeekOffset(&pos))) { theFrame = pos.mResultFrame; theFrameOffset = pos.mContentOffset; } } } } } else if (aBidiLevel == levelAfter // rule c2 || (aBidiLevel > levelBefore && aBidiLevel < levelAfter && !((aBidiLevel ^ levelAfter) & 1)) // rule c6 || (aBidiLevel < levelBefore && aBidiLevel > levelAfter && !((aBidiLevel ^ levelAfter) & 1))) // rule c10 { if (theFrame != frameAfter) { if (frameAfter) { // if there is a frameAfter, move into it theFrame = frameAfter; theFrame->GetOffsets(start, end); theFrameOffset = start; } else { // if there is no frameAfter, we must be at the end of the line // so we stay with the current frame. // Exception: when the last frame on the line has a different Bidi level from the paragraph level, there is no // real frame for the caret to be in. We have to find the visually last frame on the line. PRUint8 baseLevel = NS_GET_BASE_LEVEL(frameBefore); if (baseLevel != levelBefore) { nsPeekOffsetStruct pos; pos.SetData(eSelectEndLine, eDirNext, 0, 0, PR_FALSE, PR_TRUE, PR_FALSE, PR_TRUE); if (NS_SUCCEEDED(frameBefore->PeekOffset(&pos))) { theFrame = pos.mResultFrame; theFrameOffset = pos.mContentOffset; } } } } } else if (aBidiLevel > levelBefore && aBidiLevel < levelAfter // rule c7/8 && !((levelBefore ^ levelAfter) & 1) // before and after have the same parity && ((aBidiLevel ^ levelAfter) & 1)) // caret has different parity { if (NS_SUCCEEDED(frameSelection->GetFrameFromLevel(frameAfter, eDirNext, aBidiLevel, &theFrame))) { theFrame->GetOffsets(start, end); levelAfter = NS_GET_EMBEDDING_LEVEL(theFrame); if (aBidiLevel & 1) // c8: caret to the right of the rightmost character theFrameOffset = (levelAfter & 1) ? start : end; else // c7: caret to the left of the leftmost character theFrameOffset = (levelAfter & 1) ? end : start; } } else if (aBidiLevel < levelBefore && aBidiLevel > levelAfter // rule c11/12 && !((levelBefore ^ levelAfter) & 1) // before and after have the same parity && ((aBidiLevel ^ levelAfter) & 1)) // caret has different parity { if (NS_SUCCEEDED(frameSelection->GetFrameFromLevel(frameBefore, eDirPrevious, aBidiLevel, &theFrame))) { theFrame->GetOffsets(start, end); levelBefore = NS_GET_EMBEDDING_LEVEL(theFrame); if (aBidiLevel & 1) // c12: caret to the left of the leftmost character theFrameOffset = (levelBefore & 1) ? end : start; else // c11: caret to the right of the rightmost character theFrameOffset = (levelBefore & 1) ? start : end; } } } } } } *aReturnFrame = theFrame; *aReturnOffset = theFrameOffset; return NS_OK; }
//----------------------------------------------------------------------------- nsresult nsCaret::GetCaretCoordinates(EViewCoordinates aRelativeToType, nsISelection *aDOMSel, nsRect *outCoordinates, PRBool *outIsCollapsed, nsIView **outView) { if (!mPresShell) return NS_ERROR_NOT_INITIALIZED; if (!outCoordinates || !outIsCollapsed) return NS_ERROR_NULL_POINTER; nsCOMPtr<nsISelection> domSelection = aDOMSel; if (outView) *outView = nsnull; // fill in defaults for failure outCoordinates->x = -1; outCoordinates->y = -1; outCoordinates->width = -1; outCoordinates->height = -1; *outIsCollapsed = PR_FALSE; nsresult err = domSelection->GetIsCollapsed(outIsCollapsed); if (NS_FAILED(err)) return err; nsCOMPtr<nsIDOMNode> focusNode; err = domSelection->GetFocusNode(getter_AddRefs(focusNode)); if (NS_FAILED(err)) return err; if (!focusNode) return NS_ERROR_FAILURE; PRInt32 focusOffset; err = domSelection->GetFocusOffset(&focusOffset); if (NS_FAILED(err)) return err; nsCOMPtr<nsIContent> contentNode = do_QueryInterface(focusNode); if (!contentNode) return NS_ERROR_FAILURE; // find the frame that contains the content node that has focus nsIFrame* theFrame = nsnull; PRInt32 theFrameOffset = 0; nsCOMPtr<nsFrameSelection> frameSelection = GetFrameSelection(); if (!frameSelection) return NS_ERROR_FAILURE; PRUint8 bidiLevel = frameSelection->GetCaretBidiLevel(); err = GetCaretFrameForNodeOffset(contentNode, focusOffset, frameSelection->GetHint(), bidiLevel, &theFrame, &theFrameOffset); if (NS_FAILED(err) || !theFrame) return err; nsPoint viewOffset(0, 0); nsIView *drawingView; // views are not refcounted GetViewForRendering(theFrame, aRelativeToType, viewOffset, &drawingView, outView); if (!drawingView) return NS_ERROR_UNEXPECTED; nsPoint framePos(0, 0); err = theFrame->GetPointFromOffset(theFrameOffset, &framePos); if (NS_FAILED(err)) return err; // we don't need drawingView anymore so reuse that; reset viewOffset values for our purposes if (aRelativeToType == eClosestViewCoordinates) { theFrame->GetOffsetFromView(viewOffset, &drawingView); if (outView) *outView = drawingView; } // now add the frame offset to the view offset, and we're done viewOffset += framePos; outCoordinates->x = viewOffset.x; outCoordinates->y = viewOffset.y; outCoordinates->height = theFrame->GetSize().height; outCoordinates->width = ComputeMetrics(theFrame, theFrameOffset, outCoordinates->height).mCaretWidth; return NS_OK; }
void nsCaret::DrawCaret(PRBool aInvalidate) { // Do we need to draw the caret at all? if (!MustDrawCaret(PR_FALSE)) return; // Can we draw the caret now? nsCOMPtr<nsIPresShell> presShell = do_QueryReferent(mPresShell); NS_ENSURE_TRUE(presShell, /**/); { PRBool isPaintingSuppressed; presShell->IsPaintingSuppressed(&isPaintingSuppressed); if (isPaintingSuppressed) { if (!mDrawn) mPendingDraw = PR_TRUE; // PresShell::UnsuppressAndInvalidate() will call CheckCaretDrawingState() // to get us drawn. return; } } nsCOMPtr<nsIDOMNode> node; PRInt32 offset; nsFrameSelection::HINT hint; PRUint8 bidiLevel; if (!mDrawn) { nsCOMPtr<nsISelection> domSelection = do_QueryReferent(mDomSelectionWeak); nsCOMPtr<nsISelectionPrivate> privateSelection(do_QueryInterface(domSelection)); if (!privateSelection) return; PRBool isCollapsed = PR_FALSE; domSelection->GetIsCollapsed(&isCollapsed); if (!mShowDuringSelection && !isCollapsed) return; PRBool hintRight; privateSelection->GetInterlinePosition(&hintRight);//translate hint. hint = hintRight ? nsFrameSelection::HINTRIGHT : nsFrameSelection::HINTLEFT; // get the node and offset, which is where we want the caret to draw domSelection->GetFocusNode(getter_AddRefs(node)); if (!node) return; if (NS_FAILED(domSelection->GetFocusOffset(&offset))) return; nsCOMPtr<nsFrameSelection> frameSelection = GetFrameSelection(); if (!frameSelection) return; bidiLevel = frameSelection->GetCaretBidiLevel(); mPendingDraw = PR_FALSE; } else { if (!mLastContent) { mDrawn = PR_FALSE; return; } if (!mLastContent->IsInDoc()) { mLastContent = nsnull; mDrawn = PR_FALSE; return; } node = do_QueryInterface(mLastContent); offset = mLastContentOffset; hint = mLastHint; bidiLevel = mLastBidiLevel; } DrawAtPositionWithHint(node, offset, hint, bidiLevel, aInvalidate); ToggleDrawnStatus(); }