Bug 1195672 - Make focus changing by long tap behaves like by single tap. f=mtseng, r=roc
authorTing-Yu Lin <tlin@mozilla.com>
Wed, 19 Aug 2015 15:54:10 +0800
changeset 258798 233a48d2d3785f919d753ae12c67e35c41456ad2
parent 258797 b356dbdf1043978e202a97bb9729d722d6c776ca
child 258799 6a5121bdb27779c2a3e42b5dfbf1462c3cfa5263
push id29263
push userryanvm@gmail.com
push dateSun, 23 Aug 2015 21:18:49 +0000
treeherdermozilla-central@4ccdd06e51d7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs1195672
milestone43.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
Bug 1195672 - Make focus changing by long tap behaves like by single tap. f=mtseng, r=roc We want the focus changing behavior by long tap as close as to the one by single tap. The only functional change is that we always clear old focus and re-focus the window if a focusable frame cannot be found. This behavior is the same as the single tap implemented in EventStateManager::PostHandleEvent(). Besides, ChangeFocus now returns the new focusable frame instead of bool which provides more information.
layout/base/AccessibleCaretManager.cpp
layout/base/AccessibleCaretManager.h
--- a/layout/base/AccessibleCaretManager.cpp
+++ b/layout/base/AccessibleCaretManager.cpp
@@ -497,56 +497,48 @@ AccessibleCaretManager::GetCaretMode() c
 
   if (selection->IsCollapsed()) {
     return CaretMode::Cursor;
   }
 
   return CaretMode::Selection;
 }
 
-bool
+nsIFrame*
 AccessibleCaretManager::ChangeFocus(nsIFrame* aFrame) const
 {
-  nsIFrame* currFrame = aFrame;
-  nsIContent* newFocusContent = nullptr;
-  while (currFrame) {
-    int32_t tabIndexUnused = 0;
-    if (currFrame->IsFocusable(&tabIndexUnused, true)) {
-      newFocusContent = currFrame->GetContent();
-      nsCOMPtr<nsIDOMElement> domElement(do_QueryInterface(newFocusContent));
-      if (domElement)
-        break;
+  // This implementation is similar to EventStateManager::PostHandleEvent().
+  // Look for the nearest enclosing focusable frame.
+  nsIFrame* focusableFrame = aFrame;
+  while (focusableFrame) {
+    if (focusableFrame->IsFocusable(nullptr, true)) {
+      break;
     }
-    currFrame = currFrame->GetParent();
+    focusableFrame = focusableFrame->GetParent();
   }
 
-  // If target frame is focusable, we should move focus to it. If target frame
-  // isn't focusable, and our previous focused content is editable, we should
-  // clear focus.
+  // If a focusable frame is found, move focus to it. Otherwise, clear the old
+  // focus then re-focus the window.
   nsFocusManager* fm = nsFocusManager::GetFocusManager();
-  if (newFocusContent && currFrame) {
-    nsCOMPtr<nsIDOMElement> domElement(do_QueryInterface(newFocusContent));
-    fm->SetFocus(domElement, 0);
+  MOZ_ASSERT(fm);
+
+  if (focusableFrame) {
+    nsIContent* focusableContent = focusableFrame->GetContent();
+    MOZ_ASSERT(focusableContent, "Focusable frame must have content!");
+    nsCOMPtr<nsIDOMElement> focusableElement = do_QueryInterface(focusableContent);
+    fm->SetFocus(focusableElement, nsIFocusManager::FLAG_BYMOUSE);
   } else {
-    nsIContent* focusedContent = GetFocusedContent();
-    if (focusedContent) {
-      // Clear focus if content was editable element, or contentEditable.
-      nsGenericHTMLElement* focusedGeneric =
-        nsGenericHTMLElement::FromContent(focusedContent);
-      if (focusedContent->GetTextEditorRootContent() ||
-          (focusedGeneric && focusedGeneric->IsContentEditable())) {
-        nsIDOMWindow* win = mPresShell->GetDocument()->GetWindow();
-        if (win) {
-          fm->ClearFocus(win);
-        }
-      }
+    nsIDOMWindow* win = mPresShell->GetDocument()->GetWindow();
+    if (win) {
+      fm->ClearFocus(win);
+      fm->SetFocusedWindow(win);
     }
   }
 
-  return (newFocusContent && currFrame);
+  return focusableFrame;
 }
 
 nsresult
 AccessibleCaretManager::SelectWord(nsIFrame* aFrame, const nsPoint& aPoint) const
 {
   SetSelectionDragState(true);
   nsFrame* frame = static_cast<nsFrame*>(aFrame);
   nsresult rs = frame->SelectByTypeAtPoint(mPresShell->GetPresContext(), aPoint,
--- a/layout/base/AccessibleCaretManager.h
+++ b/layout/base/AccessibleCaretManager.h
@@ -110,17 +110,20 @@ protected:
 
   void UpdateCarets();
   void HideCarets();
 
   void UpdateCaretsForCursorMode();
   void UpdateCaretsForSelectionMode();
   void UpdateCaretsForTilt();
 
-  bool ChangeFocus(nsIFrame* aFrame) const;
+  // Change focus to the nearest enclosing focusable frame of aFrame.
+  // @return focusable frame if there is any; nullptr otherwise.
+  nsIFrame* ChangeFocus(nsIFrame* aFrame) const;
+
   nsresult SelectWord(nsIFrame* aFrame, const nsPoint& aPoint) const;
   void SetSelectionDragState(bool aState) const;
   void SetSelectionDirection(nsDirection aDir) const;
 
   // If aBackward is false, find the first node from the first range in current
   // selection, and return the frame and the offset into that frame. If aBackward
   // is true, find the last node from the last range instead.
   nsIFrame* FindFirstNodeWithFrame(bool aBackward, int32_t* aOutOffset) const;