void nsHTMLSelectListAccessible::CacheOptSiblings(nsIContent *aParentContent) { nsCOMPtr<nsIPresShell> presShell(do_QueryReferent(mWeakShell)); PRUint32 numChildren = aParentContent->GetChildCount(); for (PRUint32 count = 0; count < numChildren; count ++) { nsIContent *childContent = aParentContent->GetChildAt(count); if (!childContent->IsHTML()) { continue; } nsCOMPtr<nsIAtom> tag = childContent->Tag(); if (tag == nsAccessibilityAtoms::option || tag == nsAccessibilityAtoms::optgroup) { // Get an accessible for option or optgroup and cache it. nsRefPtr<nsAccessible> accessible = GetAccService()->GetOrCreateAccessible(childContent, presShell, mWeakShell); if (accessible) AppendChild(accessible); // Deep down into optgroup element. if (tag == nsAccessibilityAtoms::optgroup) CacheOptSiblings(childContent); } } }
void nsXFormsAccessible::CacheSelectChildren(nsIDOMNode *aContainerNode) { nsCOMPtr<nsIDOMNode> container(aContainerNode); if (!container) container = do_QueryInterface(mContent); nsCOMPtr<nsIDOMNodeList> children; sXFormsService->GetSelectChildrenFor(container, getter_AddRefs(children)); if (!children) return; nsCOMPtr<nsIPresShell> presShell(do_QueryReferent(mWeakShell)); PRUint32 length = 0; children->GetLength(&length); for (PRUint32 index = 0; index < length; index++) { nsCOMPtr<nsIDOMNode> DOMChild; children->Item(index, getter_AddRefs(DOMChild)); if (!DOMChild) continue; nsCOMPtr<nsIContent> child(do_QueryInterface(DOMChild)); nsAccessible* accessible = GetAccService()->GetOrCreateAccessible(child, presShell, mWeakShell); if (!accessible) continue; AppendChild(accessible); } }
nsresult nsHTMLTableAccessible::RemoveRowsOrColumnsFromSelection(PRInt32 aIndex, PRUint32 aTarget, PRBool aIsOuter) { nsITableLayout *tableLayout = GetTableLayout(); NS_ENSURE_STATE(tableLayout); nsCOMPtr<nsIPresShell> presShell(GetPresShell()); nsRefPtr<nsFrameSelection> tableSelection = const_cast<nsFrameSelection*>(presShell->ConstFrameSelection()); PRBool doUnselectRow = (aTarget == nsISelectionPrivate::TABLESELECTION_ROW); nsresult rv = NS_OK; PRInt32 count = 0; if (doUnselectRow) rv = GetColumnCount(&count); else rv = GetRowCount(&count); PRInt32 startRowIdx = doUnselectRow ? aIndex : 0; PRInt32 endRowIdx = doUnselectRow ? aIndex : count - 1; PRInt32 startColIdx = doUnselectRow ? 0 : aIndex; PRInt32 endColIdx = doUnselectRow ? count - 1 : aIndex; if (aIsOuter) return tableSelection->RestrictCellsToSelection(mContent, startRowIdx, startColIdx, endRowIdx, endColIdx); return tableSelection->RemoveCellsFromSelection(mContent, startRowIdx, startColIdx, endRowIdx, endColIdx); }
NS_IMETHODIMP nsTypeAheadFind::IsRangeVisible(nsIDOMRange *aRange, bool aMustBeInViewPort, bool *aResult) { // Jump through hoops to extract the docShell from the range. nsCOMPtr<nsIDOMNode> node; aRange->GetStartContainer(getter_AddRefs(node)); nsCOMPtr<nsIDOMDocument> document; node->GetOwnerDocument(getter_AddRefs(document)); nsCOMPtr<nsIDOMWindow> window; document->GetDefaultView(getter_AddRefs(window)); nsCOMPtr<nsIWebNavigation> navNav (do_GetInterface(window)); nsCOMPtr<nsIDocShell> docShell (do_GetInterface(navNav)); // Set up the arguments needed to check if a range is visible. nsCOMPtr<nsIPresShell> presShell (docShell->GetPresShell()); nsRefPtr<nsPresContext> presContext = presShell->GetPresContext(); nsCOMPtr<nsIDOMRange> startPointRange = new nsRange(presShell->GetDocument()); *aResult = IsRangeVisible(presShell, presContext, aRange, aMustBeInViewPort, false, getter_AddRefs(startPointRange), nullptr); return NS_OK; }
/** * Possible states: focused, focusable, unavailable(disabled), offscreen */ nsresult nsXULTabAccessible::GetStateInternal(PRUint32 *aState, PRUint32 *aExtraState) { // get focus and disable status from base class nsresult rv = nsLeafAccessible::GetStateInternal(aState, aExtraState); NS_ENSURE_A11Y_SUCCESS(rv, rv); // In the past, tabs have been focusable in classic theme // They may be again in the future // Check style for -moz-user-focus: normal to see if it's focusable *aState &= ~nsIAccessibleStates::STATE_FOCUSABLE; nsCOMPtr<nsIContent> content(do_QueryInterface(mDOMNode)); nsCOMPtr<nsIPresShell> presShell(do_QueryReferent(mWeakShell)); if (presShell && content) { nsIFrame *frame = presShell->GetPrimaryFrameFor(content); if (frame) { const nsStyleUserInterface* ui = frame->GetStyleUserInterface(); if (ui->mUserFocus == NS_STYLE_USER_FOCUS_NORMAL) *aState |= nsIAccessibleStates::STATE_FOCUSABLE; } } // Check whether the tab is selected *aState |= nsIAccessibleStates::STATE_SELECTABLE; *aState &= ~nsIAccessibleStates::STATE_SELECTED; nsCOMPtr<nsIDOMXULSelectControlItemElement> tab(do_QueryInterface(mDOMNode)); if (tab) { PRBool selected = PR_FALSE; if (NS_SUCCEEDED(tab->GetSelected(&selected)) && selected) *aState |= nsIAccessibleStates::STATE_SELECTED; } return NS_OK; }
// nsAccessNode protected nsPresContext* nsAccessNode::GetPresContext() { nsCOMPtr<nsIPresShell> presShell(GetPresShell()); if (!presShell) { return nsnull; } return presShell->GetPresContext(); }
NS_IMETHODIMP nsHTMLSelectOptionAccessible::DoAction(PRUint8 index) { if (index == eAction_Select) { // default action nsCOMPtr<nsIDOMHTMLOptionElement> newHTMLOption(do_QueryInterface(mDOMNode)); if (!newHTMLOption) return NS_ERROR_FAILURE; // Clear old selection nsCOMPtr<nsIDOMNode> oldHTMLOptionNode, selectNode; nsCOMPtr<nsIAccessible> parent(GetParent()); nsCOMPtr<nsIAccessNode> accessNode(do_QueryInterface(parent)); NS_ASSERTION(accessNode, "Unable to QI to nsIAccessNode"); accessNode->GetDOMNode(getter_AddRefs(selectNode)); GetFocusedOptionNode(selectNode, getter_AddRefs(oldHTMLOptionNode)); nsCOMPtr<nsIDOMHTMLOptionElement> oldHTMLOption(do_QueryInterface(oldHTMLOptionNode)); if (oldHTMLOption) oldHTMLOption->SetSelected(PR_FALSE); // Set new selection newHTMLOption->SetSelected(PR_TRUE); // If combo box, and open, close it // First, get the <select> widgets list control frame nsCOMPtr<nsIDOMNode> testSelectNode; nsCOMPtr<nsIDOMNode> thisNode(do_QueryInterface(mDOMNode)); do { thisNode->GetParentNode(getter_AddRefs(testSelectNode)); nsCOMPtr<nsIDOMHTMLSelectElement> selectControl(do_QueryInterface(testSelectNode)); if (selectControl) break; thisNode = testSelectNode; } while (testSelectNode); nsCOMPtr<nsIPresShell> presShell(do_QueryReferent(mWeakShell)); nsCOMPtr<nsIContent> selectContent(do_QueryInterface(testSelectNode)); nsCOMPtr<nsIDOMHTMLOptionElement> option(do_QueryInterface(mDOMNode)); if (!testSelectNode || !selectContent || !presShell || !option) return NS_ERROR_FAILURE; nsIFrame *selectFrame = presShell->GetPrimaryFrameFor(selectContent); nsIComboboxControlFrame *comboBoxFrame = do_QueryFrame(selectFrame); if (comboBoxFrame) { nsIFrame *listFrame = comboBoxFrame->GetDropDown(); if (comboBoxFrame->IsDroppedDown() && listFrame) { // use this list control frame to roll up the list nsIListControlFrame *listControlFrame = do_QueryFrame(listFrame); if (listControlFrame) { PRInt32 newIndex = 0; option->GetIndex(&newIndex); listControlFrame->ComboboxFinish(newIndex); } } } return NS_OK; } return NS_ERROR_INVALID_ARG; }
// nsAccessNode protected nsPresContext* nsAccessNode::GetPresContext() { if (IsDefunct()) return nsnull; nsIPresShell* presShell(mDoc->PresShell()); return presShell ? presShell->GetPresContext() : nsnull; }
NS_IMETHODIMP nsHTMLSelectOptionAccessible::DoAction(PRUint8 index) { if (index == eAction_Select) { // default action nsCOMPtr<nsIDOMHTMLOptionElement> newHTMLOption(do_QueryInterface(mContent)); if (!newHTMLOption) return NS_ERROR_FAILURE; // Clear old selection nsAccessible* parent = GetParent(); NS_ASSERTION(parent, "No parent!"); nsCOMPtr<nsIContent> oldHTMLOptionContent = GetFocusedOption(parent->GetContent()); nsCOMPtr<nsIDOMHTMLOptionElement> oldHTMLOption = do_QueryInterface(oldHTMLOptionContent); if (oldHTMLOption) oldHTMLOption->SetSelected(PR_FALSE); // Set new selection newHTMLOption->SetSelected(PR_TRUE); // If combo box, and open, close it // First, get the <select> widgets list control frame nsIContent *selectContent = mContent; do { selectContent = selectContent->GetParent(); nsCOMPtr<nsIDOMHTMLSelectElement> selectControl = do_QueryInterface(selectContent); if (selectControl) break; } while (selectContent); nsCOMPtr<nsIPresShell> presShell(do_QueryReferent(mWeakShell)); nsCOMPtr<nsIDOMHTMLOptionElement> option(do_QueryInterface(mContent)); if (!selectContent || !presShell || !option) return NS_ERROR_FAILURE; nsIFrame *selectFrame = selectContent->GetPrimaryFrame(); nsIComboboxControlFrame *comboBoxFrame = do_QueryFrame(selectFrame); if (comboBoxFrame) { nsIFrame *listFrame = comboBoxFrame->GetDropDown(); if (comboBoxFrame->IsDroppedDown() && listFrame) { // use this list control frame to roll up the list nsIListControlFrame *listControlFrame = do_QueryFrame(listFrame); if (listControlFrame) { PRInt32 newIndex = 0; option->GetIndex(&newIndex); listControlFrame->ComboboxFinish(newIndex); } } } return NS_OK; } return NS_ERROR_INVALID_ARG; }
nsIFrame* nsHTMLSelectOptionAccessible::GetBoundsFrame() { PRUint32 state; nsCOMPtr<nsIContent> content = GetSelectState(&state); if (state & nsIAccessibleStates::STATE_COLLAPSED) { nsCOMPtr<nsIPresShell> presShell(GetPresShell()); if (!presShell) { return nsnull; } return presShell->GetPrimaryFrameFor(content); } return nsAccessible::GetBoundsFrame(); }
NS_IMETHODIMP nsHTMLLinkAccessible::GetValue(nsAString& aValue) { aValue.Truncate(); nsresult rv = nsHyperTextAccessible::GetValue(aValue); NS_ENSURE_SUCCESS(rv, rv); if (!aValue.IsEmpty()) return NS_OK; nsCOMPtr<nsIPresShell> presShell(do_QueryReferent(mWeakShell)); nsCOMPtr<nsIDOMNode> DOMNode(do_QueryInterface(mContent)); return presShell->GetLinkLocation(DOMNode, aValue); }
/** * If the DOM node's frame has an accessible or the DOMNode * itself implements nsIAccessible return it. */ PRBool nsAccessibleTreeWalker::GetAccessible() { if (!mAccService) { return false; } mState.accessible = nsnull; nsCOMPtr<nsIPresShell> presShell(do_QueryReferent(mWeakShell)); if (NS_SUCCEEDED(mAccService->GetAccessible(mState.domNode, presShell, mWeakShell, &mState.frame, &mState.isHidden, getter_AddRefs(mState.accessible)))) { NS_ASSERTION(mState.accessible, "No accessible but no failure return code"); return true; } return false; }
/** * If the DOM node's frame has an accessible or the DOMNode * itself implements nsIAccessible return it. */ PRBool nsAccessibleTreeWalker::GetAccessible() { if (!mAccService) { return PR_FALSE; } mState.accessible = nsnull; nsCOMPtr<nsIPresShell> presShell(do_QueryReferent(mWeakShell)); nsIFrame *frame = mState.frame.GetFrame(); mAccService->GetAccessible(mState.domNode, presShell, mWeakShell, &frame, &mState.isHidden, getter_AddRefs(mState.accessible)); mState.frame = frame; return mState.accessible ? PR_TRUE : PR_FALSE; }
nsresult nsHTMLTableAccessible::AddRowOrColumnToSelection(PRInt32 aIndex, PRUint32 aTarget) { PRBool doSelectRow = (aTarget == nsISelectionPrivate::TABLESELECTION_ROW); nsITableLayout *tableLayout = GetTableLayout(); NS_ENSURE_STATE(tableLayout); nsCOMPtr<nsIDOMElement> cellElm; PRInt32 startRowIdx, startColIdx, rowSpan, colSpan, actualRowSpan, actualColSpan; PRBool isSelected = PR_FALSE; nsresult rv = NS_OK; PRInt32 count = 0; if (doSelectRow) rv = GetColumnCount(&count); else rv = GetRowCount(&count); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr<nsIPresShell> presShell(GetPresShell()); nsRefPtr<nsFrameSelection> tableSelection = const_cast<nsFrameSelection*>(presShell->ConstFrameSelection()); for (PRInt32 idx = 0; idx < count; idx++) { PRInt32 rowIdx = doSelectRow ? aIndex : idx; PRInt32 colIdx = doSelectRow ? idx : aIndex; rv = tableLayout->GetCellDataAt(rowIdx, colIdx, *getter_AddRefs(cellElm), startRowIdx, startColIdx, rowSpan, colSpan, actualRowSpan, actualColSpan, isSelected); if (NS_SUCCEEDED(rv) && !isSelected) { nsCOMPtr<nsIContent> cellContent(do_QueryInterface(cellElm)); rv = tableSelection->SelectCellElement(cellContent); NS_ENSURE_SUCCESS(rv, rv); } } return NS_OK; }
nsAccessibleTreeWalker::nsAccessibleTreeWalker(nsIWeakReference* aPresShell, nsIDOMNode* aNode, PRBool aWalkAnonContent): mWeakShell(aPresShell), mAccService(do_GetService("@mozilla.org/accessibilityService;1")) { mState.domNode = aNode; mState.prevState = nsnull; mState.siblingIndex = eSiblingsUninitialized; mState.siblingList = nsnull; mState.isHidden = false; mState.frame = nsnull; if (aWalkAnonContent) { nsCOMPtr<nsIPresShell> presShell(do_QueryReferent(mWeakShell)); if (presShell) mBindingManager = presShell->GetDocument()->BindingManager(); } MOZ_COUNT_CTOR(nsAccessibleTreeWalker); }
already_AddRefed<nsAccessible> nsAccTreeWalker::GetNextChildInternal(PRBool aNoWalkUp) { if (!mState || !mState->content) return nsnull; if (!mState->childList) mState->childList = mState->content->GetChildren(mChildType); nsCOMPtr<nsIPresShell> presShell(do_QueryReferent(mWeakShell)); PRUint32 length = 0; if (mState->childList) mState->childList->GetLength(&length); while (mState->childIdx < length) { nsIContent* childNode = mState->childList->GetNodeAt(mState->childIdx); mState->childIdx++; PRBool isHidden = PR_FALSE; nsRefPtr<nsAccessible> accessible = GetAccService()->GetOrCreateAccessible(childNode, presShell, mWeakShell, &isHidden); if (accessible) return accessible.forget(); // Walk down into subtree to find accessibles. if (!isHidden) { if (!PushState(childNode)) break; accessible = GetNextChildInternal(PR_TRUE); if (accessible) return accessible.forget(); } } // No more children, get back to the parent. PopState(); return aNoWalkUp ? nsnull : GetNextChildInternal(PR_FALSE); }
nsAccessible* nsAccTreeWalker::NextChildInternal(bool aNoWalkUp) { if (!mState || !mState->content) return nsnull; if (!mState->childList) mState->childList = mState->content->GetChildren(mChildFilter); nsCOMPtr<nsIPresShell> presShell(do_QueryReferent(mWeakShell)); PRUint32 length = 0; if (mState->childList) mState->childList->GetLength(&length); while (mState->childIdx < length) { nsIContent* childNode = mState->childList->GetNodeAt(mState->childIdx); mState->childIdx++; bool isSubtreeHidden = false; nsAccessible* accessible = GetAccService()->GetOrCreateAccessible(childNode, presShell, mWeakShell, &isSubtreeHidden); if (accessible) return accessible; // Walk down into subtree to find accessibles. if (!isSubtreeHidden) { if (!PushState(childNode)) break; accessible = NextChildInternal(true); if (accessible) return accessible; } } // No more children, get back to the parent. PopState(); return aNoWalkUp ? nsnull : NextChildInternal(false); }
NS_IMETHODIMP nsTypeAheadFind::Find(const nsAString& aSearchString, bool aLinksOnly, uint16_t* aResult) { *aResult = FIND_NOTFOUND; nsCOMPtr<nsIPresShell> presShell (GetPresShell()); if (!presShell) { nsCOMPtr<nsIDocShell> ds (do_QueryReferent(mDocShell)); NS_ENSURE_TRUE(ds, NS_ERROR_FAILURE); presShell = ds->GetPresShell(); NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE); mPresShell = do_GetWeakReference(presShell); } nsCOMPtr<nsISelection> selection; nsCOMPtr<nsISelectionController> selectionController = do_QueryReferent(mSelectionController); if (!selectionController) { GetSelection(presShell, getter_AddRefs(selectionController), getter_AddRefs(selection)); // cache for reuse mSelectionController = do_GetWeakReference(selectionController); } else { selectionController->GetSelection( nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection)); } if (selection) selection->CollapseToStart(); if (aSearchString.IsEmpty()) { mTypeAheadBuffer.Truncate(); // These will be initialized to their true values after the first character // is typed mStartFindRange = nullptr; mSelectionController = nullptr; *aResult = FIND_FOUND; return NS_OK; } bool atEnd = false; if (mTypeAheadBuffer.Length()) { const nsAString& oldStr = Substring(mTypeAheadBuffer, 0, mTypeAheadBuffer.Length()); const nsAString& newStr = Substring(aSearchString, 0, mTypeAheadBuffer.Length()); if (oldStr.Equals(newStr)) atEnd = true; const nsAString& newStr2 = Substring(aSearchString, 0, aSearchString.Length()); const nsAString& oldStr2 = Substring(mTypeAheadBuffer, 0, aSearchString.Length()); if (oldStr2.Equals(newStr2)) atEnd = true; if (!atEnd) mStartFindRange = nullptr; } if (!mIsSoundInitialized && !mNotFoundSoundURL.IsEmpty()) { // This makes sure system sound library is loaded so that // there's no lag before the first sound is played // by waiting for the first keystroke, we still get the startup time benefits. mIsSoundInitialized = true; mSoundInterface = do_CreateInstance("@mozilla.org/sound;1"); if (mSoundInterface && !mNotFoundSoundURL.EqualsLiteral("beep")) { mSoundInterface->Init(); } } #ifdef XP_WIN // After each keystroke, ensure sound object is destroyed, to free up memory // allocated for error sound, otherwise Windows' nsISound impl // holds onto the last played sound, using up memory. mSoundInterface = nullptr; #endif int32_t bufferLength = mTypeAheadBuffer.Length(); mTypeAheadBuffer = aSearchString; bool isFirstVisiblePreferred = false; // --------- Initialize find if 1st char ---------- if (bufferLength == 0) { // If you can see the selection (not collapsed or thru caret browsing), // or if already focused on a page element, start there. // Otherwise we're going to start at the first visible element bool isSelectionCollapsed = true; if (selection) selection->GetIsCollapsed(&isSelectionCollapsed); // If true, we will scan from top left of visible area // If false, we will scan from start of selection isFirstVisiblePreferred = !atEnd && !mCaretBrowsingOn && isSelectionCollapsed; if (isFirstVisiblePreferred) { // Get the focused content. If there is a focused node, ensure the // selection is at that point. Otherwise, we will just want to start // from the caret position or the beginning of the document. nsPresContext* presContext = presShell->GetPresContext(); NS_ENSURE_TRUE(presContext, NS_OK); nsCOMPtr<nsIDocument> document = do_QueryInterface(presShell->GetDocument()); if (!document) return NS_ERROR_UNEXPECTED; nsCOMPtr<nsIDOMWindow> window = do_QueryInterface(document->GetWindow()); nsCOMPtr<nsIFocusManager> fm = do_GetService(FOCUSMANAGER_CONTRACTID); if (fm) { nsCOMPtr<nsIDOMElement> focusedElement; nsCOMPtr<nsIDOMWindow> focusedWindow; fm->GetFocusedElementForWindow(window, false, getter_AddRefs(focusedWindow), getter_AddRefs(focusedElement)); // If the root element is focused, then it's actually the document // that has the focus, so ignore this. if (focusedElement && !SameCOMIdentity(focusedElement, document->GetRootElement())) { fm->MoveCaretToFocus(window); isFirstVisiblePreferred = false; } } } } // ----------- Find the text! --------------------- // Beware! This may flush notifications via synchronous // ScrollSelectionIntoView. nsresult rv = FindItNow(nullptr, aLinksOnly, isFirstVisiblePreferred, false, aResult); // ---------Handle success or failure --------------- if (NS_SUCCEEDED(rv)) { if (mTypeAheadBuffer.Length() == 1) { // If first letter, store where the first find succeeded // (mStartFindRange) mStartFindRange = nullptr; if (selection) { nsCOMPtr<nsIDOMRange> startFindRange; selection->GetRangeAt(0, getter_AddRefs(startFindRange)); if (startFindRange) startFindRange->CloneRange(getter_AddRefs(mStartFindRange)); } } } else { // Error sound if (mTypeAheadBuffer.Length() > mLastFindLength) PlayNotFoundSound(); } SaveFind(); return NS_OK; }
nsresult nsTypeAheadFind::FindItNow(nsIPresShell *aPresShell, bool aIsLinksOnly, bool aIsFirstVisiblePreferred, bool aFindPrev, uint16_t* aResult) { *aResult = FIND_NOTFOUND; mFoundLink = nullptr; mFoundEditable = nullptr; mFoundRange = nullptr; mCurrentWindow = nullptr; nsCOMPtr<nsIPresShell> startingPresShell (GetPresShell()); if (!startingPresShell) { nsCOMPtr<nsIDocShell> ds = do_QueryReferent(mDocShell); NS_ENSURE_TRUE(ds, NS_ERROR_FAILURE); startingPresShell = ds->GetPresShell(); mPresShell = do_GetWeakReference(startingPresShell); } nsCOMPtr<nsIPresShell> presShell(aPresShell); if (!presShell) { presShell = startingPresShell; // this is the current document if (!presShell) return NS_ERROR_FAILURE; } nsRefPtr<nsPresContext> presContext = presShell->GetPresContext(); if (!presContext) return NS_ERROR_FAILURE; nsCOMPtr<nsISelection> selection; nsCOMPtr<nsISelectionController> selectionController = do_QueryReferent(mSelectionController); if (!selectionController) { GetSelection(presShell, getter_AddRefs(selectionController), getter_AddRefs(selection)); // cache for reuse mSelectionController = do_GetWeakReference(selectionController); } else { selectionController->GetSelection( nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection)); } nsCOMPtr<nsIDocShell> startingDocShell(presContext->GetDocShell()); NS_ASSERTION(startingDocShell, "Bug 175321 Crashes with Type Ahead Find [@ nsTypeAheadFind::FindItNow]"); if (!startingDocShell) return NS_ERROR_FAILURE; nsCOMPtr<nsIDocShellTreeItem> rootContentTreeItem; nsCOMPtr<nsIDocShell> currentDocShell; startingDocShell->GetSameTypeRootTreeItem(getter_AddRefs(rootContentTreeItem)); nsCOMPtr<nsIDocShell> rootContentDocShell = do_QueryInterface(rootContentTreeItem); if (!rootContentDocShell) return NS_ERROR_FAILURE; nsCOMPtr<nsISimpleEnumerator> docShellEnumerator; rootContentDocShell->GetDocShellEnumerator(nsIDocShellTreeItem::typeContent, nsIDocShell::ENUMERATE_FORWARDS, getter_AddRefs(docShellEnumerator)); // Default: can start at the current document nsCOMPtr<nsISupports> currentContainer = do_QueryInterface(rootContentDocShell); // Iterate up to current shell, if there's more than 1 that we're // dealing with bool hasMoreDocShells; while (NS_SUCCEEDED(docShellEnumerator->HasMoreElements(&hasMoreDocShells)) && hasMoreDocShells) { docShellEnumerator->GetNext(getter_AddRefs(currentContainer)); currentDocShell = do_QueryInterface(currentContainer); if (!currentDocShell || currentDocShell == startingDocShell || aIsFirstVisiblePreferred) break; } // ------------ Get ranges ready ---------------- nsCOMPtr<nsIDOMRange> returnRange; nsCOMPtr<nsIPresShell> focusedPS; if (NS_FAILED(GetSearchContainers(currentContainer, (!aIsFirstVisiblePreferred || mStartFindRange) ? selectionController.get() : nullptr, aIsFirstVisiblePreferred, aFindPrev, getter_AddRefs(presShell), getter_AddRefs(presContext)))) { return NS_ERROR_FAILURE; } int16_t rangeCompareResult = 0; if (!mStartPointRange) { mStartPointRange = new nsRange(presShell->GetDocument()); } mStartPointRange->CompareBoundaryPoints(nsIDOMRange::START_TO_START, mSearchRange, &rangeCompareResult); // No need to wrap find in doc if starting at beginning bool hasWrapped = (rangeCompareResult < 0); if (mTypeAheadBuffer.IsEmpty() || !EnsureFind()) return NS_ERROR_FAILURE; mFind->SetFindBackwards(aFindPrev); while (true) { // ----- Outer while loop: go through all docs ----- while (true) { // === Inner while loop: go through a single doc === mFind->Find(mTypeAheadBuffer.get(), mSearchRange, mStartPointRange, mEndPointRange, getter_AddRefs(returnRange)); if (!returnRange) break; // Nothing found in this doc, go to outer loop (try next doc) // ------- Test resulting found range for success conditions ------ bool isInsideLink = false, isStartingLink = false; if (aIsLinksOnly) { // Don't check if inside link when searching all text RangeStartsInsideLink(returnRange, presShell, &isInsideLink, &isStartingLink); } bool usesIndependentSelection; if (!IsRangeVisible(presShell, presContext, returnRange, aIsFirstVisiblePreferred, false, getter_AddRefs(mStartPointRange), &usesIndependentSelection) || (aIsLinksOnly && !isInsideLink) || (mStartLinksOnlyPref && aIsLinksOnly && !isStartingLink)) { // ------ Failure ------ // At this point mStartPointRange got updated to the first // visible range in the viewport. We _may_ be able to just // start there, if it's not taking us in the wrong direction. if (aFindPrev) { // We can continue at the end of mStartPointRange if its end is before // the start of returnRange or coincides with it. Otherwise, we need // to continue at the start of returnRange. int16_t compareResult; nsresult rv = mStartPointRange->CompareBoundaryPoints(nsIDOMRange::START_TO_END, returnRange, &compareResult); if (NS_SUCCEEDED(rv) && compareResult <= 0) { // OK to start at the end of mStartPointRange mStartPointRange->Collapse(false); } else { // Start at the beginning of returnRange returnRange->CloneRange(getter_AddRefs(mStartPointRange)); mStartPointRange->Collapse(true); } } else { // We can continue at the start of mStartPointRange if its start is // after the end of returnRange or coincides with it. Otherwise, we // need to continue at the end of returnRange. int16_t compareResult; nsresult rv = mStartPointRange->CompareBoundaryPoints(nsIDOMRange::END_TO_START, returnRange, &compareResult); if (NS_SUCCEEDED(rv) && compareResult >= 0) { // OK to start at the start of mStartPointRange mStartPointRange->Collapse(true); } else { // Start at the end of returnRange returnRange->CloneRange(getter_AddRefs(mStartPointRange)); mStartPointRange->Collapse(false); } } continue; } mFoundRange = returnRange; // ------ Success! ------- // Hide old selection (new one may be on a different controller) if (selection) { selection->CollapseToStart(); SetSelectionModeAndRepaint(nsISelectionController::SELECTION_ON); } // Make sure new document is selected if (presShell != startingPresShell) { // We are in a new document (because of frames/iframes) mPresShell = do_GetWeakReference(presShell); } nsCOMPtr<nsIDocument> document = do_QueryInterface(presShell->GetDocument()); NS_ASSERTION(document, "Wow, presShell doesn't have document!"); if (!document) return NS_ERROR_UNEXPECTED; nsCOMPtr<nsPIDOMWindow> window = document->GetInnerWindow(); NS_ASSERTION(window, "document has no window"); if (!window) return NS_ERROR_UNEXPECTED; nsCOMPtr<nsIFocusManager> fm = do_GetService(FOCUSMANAGER_CONTRACTID); if (usesIndependentSelection) { /* If a search result is found inside an editable element, we'll focus * the element only if focus is in our content window, i.e. * |if (focusedWindow.top == ourWindow.top)| */ bool shouldFocusEditableElement = false; if (fm) { nsCOMPtr<nsIDOMWindow> focusedWindow; nsresult rv = fm->GetFocusedWindow(getter_AddRefs(focusedWindow)); if (NS_SUCCEEDED(rv)) { nsCOMPtr<nsPIDOMWindow> fwPI(do_QueryInterface(focusedWindow, &rv)); if (NS_SUCCEEDED(rv)) { nsCOMPtr<nsIDocShellTreeItem> fwTreeItem (do_QueryInterface(fwPI->GetDocShell(), &rv)); if (NS_SUCCEEDED(rv)) { nsCOMPtr<nsIDocShellTreeItem> fwRootTreeItem; rv = fwTreeItem->GetSameTypeRootTreeItem(getter_AddRefs(fwRootTreeItem)); if (NS_SUCCEEDED(rv) && fwRootTreeItem == rootContentTreeItem) shouldFocusEditableElement = true; } } } } // We may be inside an editable element, and therefore the selection // may be controlled by a different selection controller. Walk up the // chain of parent nodes to see if we find one. nsCOMPtr<nsIDOMNode> node; returnRange->GetStartContainer(getter_AddRefs(node)); while (node) { nsCOMPtr<nsIDOMNSEditableElement> editable = do_QueryInterface(node); if (editable) { // Inside an editable element. Get the correct selection // controller and selection. nsCOMPtr<nsIEditor> editor; editable->GetEditor(getter_AddRefs(editor)); NS_ASSERTION(editor, "Editable element has no editor!"); if (!editor) { break; } editor->GetSelectionController( getter_AddRefs(selectionController)); if (selectionController) { selectionController->GetSelection( nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection)); } mFoundEditable = do_QueryInterface(node); if (!shouldFocusEditableElement) break; // Otherwise move focus/caret to editable element if (fm) fm->SetFocus(mFoundEditable, 0); break; } nsIDOMNode* tmp = node; tmp->GetParentNode(getter_AddRefs(node)); } // If we reach here without setting mFoundEditable, then something // besides editable elements can cause us to have an independent // selection controller. I don't know whether this is possible. // Currently, we simply fall back to grabbing the document's selection // controller in this case. Perhaps we should reject this find match // and search again. NS_ASSERTION(mFoundEditable, "Independent selection controller on " "non-editable element!"); } if (!mFoundEditable) { // Not using a separate selection controller, so just get the // document's controller and selection. GetSelection(presShell, getter_AddRefs(selectionController), getter_AddRefs(selection)); } mSelectionController = do_GetWeakReference(selectionController); // Select the found text if (selection) { selection->RemoveAllRanges(); selection->AddRange(returnRange); } if (!mFoundEditable && fm) { nsCOMPtr<nsIDOMWindow> win = do_QueryInterface(window); fm->MoveFocus(win, nullptr, nsIFocusManager::MOVEFOCUS_CARET, nsIFocusManager::FLAG_NOSCROLL | nsIFocusManager::FLAG_NOSWITCHFRAME, getter_AddRefs(mFoundLink)); } // Change selection color to ATTENTION and scroll to it. Careful: we // must wait until after we goof with focus above before changing to // ATTENTION, or when we MoveFocus() and the selection is not on a // link, we'll blur, which will lose the ATTENTION. if (selectionController) { // Beware! This may flush notifications via synchronous // ScrollSelectionIntoView. SetSelectionModeAndRepaint(nsISelectionController::SELECTION_ATTENTION); selectionController->ScrollSelectionIntoView( nsISelectionController::SELECTION_NORMAL, nsISelectionController::SELECTION_WHOLE_SELECTION, nsISelectionController::SCROLL_CENTER_VERTICALLY | nsISelectionController::SCROLL_SYNCHRONOUS); } mCurrentWindow = window; *aResult = hasWrapped ? FIND_WRAPPED : FIND_FOUND; return NS_OK; } // ======= end-inner-while (go through a single document) ========== // ---------- Nothing found yet, try next document ------------- bool hasTriedFirstDoc = false; do { // ==== Second inner loop - get another while ==== if (NS_SUCCEEDED(docShellEnumerator->HasMoreElements(&hasMoreDocShells)) && hasMoreDocShells) { docShellEnumerator->GetNext(getter_AddRefs(currentContainer)); NS_ASSERTION(currentContainer, "HasMoreElements lied to us!"); currentDocShell = do_QueryInterface(currentContainer); if (currentDocShell) break; } else if (hasTriedFirstDoc) // Avoid potential infinite loop return NS_ERROR_FAILURE; // No content doc shells // Reached last doc shell, loop around back to first doc shell rootContentDocShell->GetDocShellEnumerator(nsIDocShellTreeItem::typeContent, nsIDocShell::ENUMERATE_FORWARDS, getter_AddRefs(docShellEnumerator)); hasTriedFirstDoc = true; } while (docShellEnumerator); // ==== end second inner while === bool continueLoop = false; if (currentDocShell != startingDocShell) continueLoop = true; // Try next document else if (!hasWrapped || aIsFirstVisiblePreferred) { // Finished searching through docshells: // If aFirstVisiblePreferred == true, we may need to go through all // docshells twice -once to look for visible matches, the second time // for any match aIsFirstVisiblePreferred = false; hasWrapped = true; continueLoop = true; // Go through all docs again } if (continueLoop) { if (NS_FAILED(GetSearchContainers(currentContainer, nullptr, aIsFirstVisiblePreferred, aFindPrev, getter_AddRefs(presShell), getter_AddRefs(presContext)))) { continue; } if (aFindPrev) { // Reverse mode: swap start and end points, so that we start // at end of document and go to beginning nsCOMPtr<nsIDOMRange> tempRange; mStartPointRange->CloneRange(getter_AddRefs(tempRange)); if (!mEndPointRange) { mEndPointRange = new nsRange(presShell->GetDocument()); } mStartPointRange = mEndPointRange; mEndPointRange = tempRange; } continue; } // ------------- Failed -------------- break; } // end-outer-while: go through all docs return NS_ERROR_FAILURE; }