author | Morris Tseng <mtseng@mozilla.com> |
Tue, 19 May 2015 20:59:00 -0400 | |
changeset 246261 | c816a43492118415d01f7dc44148c720eb7dbb21 |
parent 246260 | 3fa23f765209c4dff4f662832ff3ec54498cba4d |
child 246262 | 970957a8ad2a3e2e1a6b32a86999e0f38ead860f |
push id | 60390 |
push user | ryanvm@gmail.com |
push date | Fri, 29 May 2015 13:59:14 +0000 |
treeherder | mozilla-inbound@6c009ae07819 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | roc, smaug |
bugs | 1155493 |
milestone | 41.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
new file mode 100644 --- /dev/null +++ b/dom/webidl/CaretStateChangedEvent.webidl @@ -0,0 +1,32 @@ +/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +enum CaretChangedReason { + "visibilitychange", + "updateposition", + "longpressonemptycontent", + "taponcaret", + "presscaret", + "releasecaret" +}; + +dictionary CaretStateChangedEventInit : EventInit { + boolean collapsed = true; + DOMRectReadOnly? boundingClientRect = null; + CaretChangedReason reason = "visibilitychange"; + boolean caretVisible = false; + boolean selectionVisible = false; +}; + +[Constructor(DOMString type, optional CaretStateChangedEventInit eventInit), + ChromeOnly] +interface CaretStateChangedEvent : Event { + readonly attribute boolean collapsed; + readonly attribute DOMRectReadOnly? boundingClientRect; + readonly attribute CaretChangedReason reason; + readonly attribute boolean caretVisible; + readonly attribute boolean selectionVisible; +};
--- a/dom/webidl/moz.build +++ b/dom/webidl/moz.build @@ -722,16 +722,17 @@ GENERATED_EVENTS_WEBIDL_FILES = [ 'AutocompleteErrorEvent.webidl', 'BlobEvent.webidl', 'CallEvent.webidl', 'CallGroupErrorEvent.webidl', 'CameraClosedEvent.webidl', 'CameraConfigurationEvent.webidl', 'CameraFacesDetectedEvent.webidl', 'CameraStateChangeEvent.webidl', + 'CaretStateChangedEvent.webidl', 'CFStateChangeEvent.webidl', 'CloseEvent.webidl', 'CSSFontFaceLoadEvent.webidl', 'DataErrorEvent.webidl', 'DataStoreChangeEvent.webidl', 'DeviceLightEvent.webidl', 'DeviceOrientationEvent.webidl', 'DeviceProximityEvent.webidl',
--- a/layout/base/AccessibleCaretManager.cpp +++ b/layout/base/AccessibleCaretManager.cpp @@ -861,9 +861,80 @@ AccessibleCaretManager::LaunchCaretTimeo void AccessibleCaretManager::CancelCaretTimeoutTimer() { if (mCaretTimeoutTimer) { mCaretTimeoutTimer->Cancel(); } } +void +AccessibleCaretManager::DispatchCaretStateChangedEvent(CaretChangedReason aReason) const +{ + MOZ_ASSERT(nsContentUtils::IsSafeToRunScript()); + // 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(); + init.mBoundingClientRect = domRect; + } 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; + bool ret; + doc->DispatchEvent(event, &ret); +} + } // namespace mozilla
--- a/layout/base/AccessibleCaretManager.h +++ b/layout/base/AccessibleCaretManager.h @@ -8,16 +8,17 @@ #define AccessibleCaretManager_h #include "nsCOMPtr.h" #include "nsCoord.h" #include "nsIFrame.h" #include "nsISelectionListener.h" #include "nsRefPtr.h" #include "nsWeakReference.h" +#include "mozilla/dom/CaretStateChangedEvent.h" #include "mozilla/EventForwards.h" #include "mozilla/UniquePtr.h" #include "mozilla/WeakPtr.h" class nsFrameSelection; class nsIContent; class nsIPresShell; struct nsPoint; @@ -127,16 +128,20 @@ protected: nsresult DragCaretInternal(const nsPoint& aPoint); nsPoint AdjustDragBoundary(const nsPoint& aPoint) const; void ClearMaintainedSelection() const; dom::Selection* GetSelection() const; already_AddRefed<nsFrameSelection> GetFrameSelection() const; nsIContent* GetFocusedContent() const; + // This function will call FlushPendingNotifications. So caller must ensure + // everything exists after calling this method. + void DispatchCaretStateChangedEvent(dom::CaretChangedReason aReason) const; + // If we're dragging the first caret, we do not want to drag it over the // previous character of the second caret. Same as the second caret. So we // check if content offset exceeds the previous/next character of second/first // caret base the active caret. bool CompareRangeWithContentOffset(nsIFrame::ContentOffsets& aOffsets); // Timeout in milliseconds to hide the AccessibleCaret under cursor mode while // no one touches it.