void SelectionManager::ProcessTextSelChangeEvent(AccEvent* aEvent) { AccTextSelChangeEvent* event = downcast_accEvent(aEvent); Selection* sel = static_cast<Selection*>(event->mSel.get()); // Fire selection change event if it's not pure caret-move selection change. if (sel->GetRangeCount() != 1 || !sel->IsCollapsed()) nsEventShell::FireEvent(aEvent); // Fire caret move event if there's a caret in the selection. nsINode* caretCntrNode = nsCoreUtils::GetDOMNodeFromDOMPoint(sel->GetFocusNode(), sel->GetFocusOffset()); if (!caretCntrNode) return; HyperTextAccessible* caretCntr = nsAccUtils::GetTextContainer(caretCntrNode); NS_ASSERTION(caretCntr, "No text container for focus while there's one for common ancestor?!"); if (!caretCntr) return; int32_t caretOffset = -1; if (NS_SUCCEEDED(caretCntr->GetCaretOffset(&caretOffset)) && caretOffset != -1) { nsRefPtr<AccCaretMoveEvent> caretMoveEvent = new AccCaretMoveEvent(caretCntr, caretOffset, aEvent->FromUserInput()); nsEventShell::FireEvent(caretMoveEvent); } }
AccessibleCaretManager::CaretMode AccessibleCaretManager::GetCaretMode() const { Selection* selection = GetSelection(); if (!selection) { return CaretMode::None; } uint32_t rangeCount = selection->RangeCount(); if (rangeCount <= 0) { return CaretMode::None; } if (selection->IsCollapsed()) { return CaretMode::Cursor; } return CaretMode::Selection; }
void AccessibleCaretManager::DispatchCaretStateChangedEvent(CaretChangedReason aReason) const { // Holding PresShell to prevent AccessibleCaretManager to be destroyed. nsCOMPtr<nsIPresShell> presShell = mPresShell; FlushLayout(); if (presShell->IsDestroying()) { return; } Selection* sel = GetSelection(); if (!sel) { return; } nsIDocument* doc = mPresShell->GetDocument(); MOZ_ASSERT(doc); CaretStateChangedEventInit init; init.mBubbles = true; const nsRange* range = sel->GetAnchorFocusRange(); nsINode* commonAncestorNode = nullptr; if (range) { commonAncestorNode = range->GetCommonAncestor(); } if (!commonAncestorNode) { commonAncestorNode = sel->GetFrameSelection()->GetAncestorLimiter(); } RefPtr<DOMRect> domRect = new DOMRect(ToSupports(doc)); nsRect rect = nsLayoutUtils::GetSelectionBoundingRect(sel); nsIFrame* commonAncestorFrame = nullptr; nsIFrame* rootFrame = mPresShell->GetRootFrame(); if (commonAncestorNode && commonAncestorNode->IsContent()) { commonAncestorFrame = commonAncestorNode->AsContent()->GetPrimaryFrame(); } if (commonAncestorFrame && rootFrame) { nsLayoutUtils::TransformRect(rootFrame, commonAncestorFrame, rect); nsRect clampedRect = nsLayoutUtils::ClampRectToScrollFrames(commonAncestorFrame, rect); nsLayoutUtils::TransformRect(commonAncestorFrame, rootFrame, clampedRect); domRect->SetLayoutRect(clampedRect); init.mSelectionVisible = !clampedRect.IsEmpty(); } else { domRect->SetLayoutRect(rect); init.mSelectionVisible = true; } // Send isEditable info w/ event detail. This info can help determine // whether to show cut command on selection dialog or not. init.mSelectionEditable = commonAncestorFrame && GetEditingHostForFrame(commonAncestorFrame); init.mBoundingClientRect = domRect; init.mReason = aReason; init.mCollapsed = sel->IsCollapsed(); init.mCaretVisible = mFirstCaret->IsLogicallyVisible() || mSecondCaret->IsLogicallyVisible(); init.mCaretVisuallyVisible = mFirstCaret->IsVisuallyVisible() || mSecondCaret->IsVisuallyVisible(); sel->Stringify(init.mSelectedTextContent); RefPtr<CaretStateChangedEvent> event = CaretStateChangedEvent::Constructor(doc, NS_LITERAL_STRING("mozcaretstatechanged"), init); event->SetTrusted(true); event->GetInternalNSEvent()->mFlags.mOnlyChromeDispatch = true; AC_LOG("%s: reason %d, collapsed %d, caretVisible %d", __FUNCTION__, init.mReason, init.mCollapsed, init.mCaretVisible); (new AsyncEventDispatcher(doc, event))->RunDOMEventWhenSafe(); }
void AccessibleCaretManager::DispatchCaretStateChangedEvent(CaretChangedReason aReason) const { // Holding PresShell to prevent AccessibleCaretManager to be destroyed. nsCOMPtr<nsIPresShell> presShell = mPresShell; // XXX: Do we need to flush layout? presShell->FlushPendingNotifications(Flush_Layout); if (presShell->IsDestroying()) { return; } Selection* sel = GetSelection(); if (!sel) { return; } nsIDocument* doc = mPresShell->GetDocument(); MOZ_ASSERT(doc); CaretStateChangedEventInit init; init.mBubbles = true; const nsRange* range = sel->GetAnchorFocusRange(); nsINode* commonAncestorNode = nullptr; if (range) { commonAncestorNode = range->GetCommonAncestor(); } if (!commonAncestorNode) { commonAncestorNode = sel->GetFrameSelection()->GetAncestorLimiter(); } nsRefPtr<DOMRect> domRect = new DOMRect(ToSupports(doc)); nsRect rect = nsContentUtils::GetSelectionBoundingRect(sel); nsIFrame* commonAncestorFrame = nullptr; nsIFrame* rootFrame = mPresShell->GetRootFrame(); if (commonAncestorNode && commonAncestorNode->IsContent()) { commonAncestorFrame = commonAncestorNode->AsContent()->GetPrimaryFrame(); } if (commonAncestorFrame && rootFrame) { nsLayoutUtils::TransformRect(rootFrame, commonAncestorFrame, rect); nsRect clampedRect = nsLayoutUtils::ClampRectToScrollFrames(commonAncestorFrame, rect); nsLayoutUtils::TransformRect(commonAncestorFrame, rootFrame, clampedRect); domRect->SetLayoutRect(clampedRect); init.mSelectionVisible = !clampedRect.IsEmpty(); } else { domRect->SetLayoutRect(rect); init.mSelectionVisible = true; } init.mBoundingClientRect = domRect; init.mReason = aReason; init.mCollapsed = sel->IsCollapsed(); init.mCaretVisible = mFirstCaret->IsLogicallyVisible() || mSecondCaret->IsLogicallyVisible(); nsRefPtr<CaretStateChangedEvent> event = CaretStateChangedEvent::Constructor(doc, NS_LITERAL_STRING("mozcaretstatechanged"), init); event->SetTrusted(true); event->GetInternalNSEvent()->mFlags.mOnlyChromeDispatch = true; (new AsyncEventDispatcher(doc, event))->RunDOMEventWhenSafe(); }