LCOV - code coverage report
Current view: top level - layout/base - AccessibleCaret.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 3 198 1.5 %
Date: 2017-07-14 16:53:18 Functions: 0 25 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "AccessibleCaret.h"
       8             : 
       9             : #include "AccessibleCaretLogger.h"
      10             : #include "mozilla/FloatingPoint.h"
      11             : #include "mozilla/Preferences.h"
      12             : #include "mozilla/ToString.h"
      13             : #include "nsCanvasFrame.h"
      14             : #include "nsCaret.h"
      15             : #include "nsDOMTokenList.h"
      16             : #include "nsIFrame.h"
      17             : 
      18             : namespace mozilla {
      19             : using namespace dom;
      20             : 
      21             : #undef AC_LOG
      22             : #define AC_LOG(message, ...)                                                   \
      23             :   AC_LOG_BASE("AccessibleCaret (%p): " message, this, ##__VA_ARGS__);
      24             : 
      25             : #undef AC_LOGV
      26             : #define AC_LOGV(message, ...)                                                  \
      27             :   AC_LOGV_BASE("AccessibleCaret (%p): " message, this, ##__VA_ARGS__);
      28             : 
      29           0 : NS_IMPL_ISUPPORTS(AccessibleCaret::DummyTouchListener, nsIDOMEventListener)
      30             : 
      31             : float AccessibleCaret::sWidth = 0.0f;
      32             : float AccessibleCaret::sHeight = 0.0f;
      33             : float AccessibleCaret::sMarginLeft = 0.0f;
      34             : float AccessibleCaret::sBarWidth = 0.0f;
      35             : 
      36           3 : NS_NAMED_LITERAL_STRING(AccessibleCaret::sTextOverlayElementId, "text-overlay");
      37           3 : NS_NAMED_LITERAL_STRING(AccessibleCaret::sCaretImageElementId, "image");
      38           3 : NS_NAMED_LITERAL_STRING(AccessibleCaret::sSelectionBarElementId, "bar");
      39             : 
      40             : #define AC_PROCESS_ENUM_TO_STREAM(e) case(e): aStream << #e; break;
      41             : std::ostream&
      42           0 : operator<<(std::ostream& aStream, const AccessibleCaret::Appearance& aAppearance)
      43             : {
      44             :   using Appearance = AccessibleCaret::Appearance;
      45           0 :   switch (aAppearance) {
      46           0 :     AC_PROCESS_ENUM_TO_STREAM(Appearance::None);
      47           0 :     AC_PROCESS_ENUM_TO_STREAM(Appearance::Normal);
      48           0 :     AC_PROCESS_ENUM_TO_STREAM(Appearance::NormalNotShown);
      49           0 :     AC_PROCESS_ENUM_TO_STREAM(Appearance::Left);
      50           0 :     AC_PROCESS_ENUM_TO_STREAM(Appearance::Right);
      51             :   }
      52           0 :   return aStream;
      53             : }
      54             : 
      55             : std::ostream&
      56           0 : operator<<(std::ostream& aStream,
      57             :            const AccessibleCaret::PositionChangedResult& aResult)
      58             : {
      59             :   using PositionChangedResult = AccessibleCaret::PositionChangedResult;
      60           0 :   switch (aResult) {
      61           0 :     AC_PROCESS_ENUM_TO_STREAM(PositionChangedResult::NotChanged);
      62           0 :     AC_PROCESS_ENUM_TO_STREAM(PositionChangedResult::Changed);
      63           0 :     AC_PROCESS_ENUM_TO_STREAM(PositionChangedResult::Invisible);
      64             :   }
      65           0 :   return aStream;
      66             : }
      67             : #undef AC_PROCESS_ENUM_TO_STREAM
      68             : 
      69             : // -----------------------------------------------------------------------------
      70             : // Implementation of AccessibleCaret methods
      71             : 
      72           0 : AccessibleCaret::AccessibleCaret(nsIPresShell* aPresShell)
      73           0 :   : mPresShell(aPresShell)
      74             : {
      75             :   // Check all resources required.
      76           0 :   if (mPresShell) {
      77           0 :     MOZ_ASSERT(RootFrame());
      78           0 :     MOZ_ASSERT(mPresShell->GetDocument());
      79           0 :     MOZ_ASSERT(mPresShell->GetCanvasFrame());
      80           0 :     MOZ_ASSERT(mPresShell->GetCanvasFrame()->GetCustomContentContainer());
      81             : 
      82           0 :     InjectCaretElement(mPresShell->GetDocument());
      83             :   }
      84             : 
      85             :   static bool prefsAdded = false;
      86           0 :   if (!prefsAdded) {
      87           0 :     Preferences::AddFloatVarCache(&sWidth, "layout.accessiblecaret.width");
      88           0 :     Preferences::AddFloatVarCache(&sHeight, "layout.accessiblecaret.height");
      89           0 :     Preferences::AddFloatVarCache(&sMarginLeft, "layout.accessiblecaret.margin-left");
      90           0 :     Preferences::AddFloatVarCache(&sBarWidth, "layout.accessiblecaret.bar.width");
      91           0 :     prefsAdded = true;
      92             :   }
      93           0 : }
      94             : 
      95           0 : AccessibleCaret::~AccessibleCaret()
      96             : {
      97           0 :   if (mPresShell) {
      98           0 :     RemoveCaretElement(mPresShell->GetDocument());
      99             :   }
     100           0 : }
     101             : 
     102             : void
     103           0 : AccessibleCaret::SetAppearance(Appearance aAppearance)
     104             : {
     105           0 :   if (mAppearance == aAppearance) {
     106           0 :     return;
     107             :   }
     108             : 
     109           0 :   ErrorResult rv;
     110           0 :   CaretElement()->ClassList()->Remove(AppearanceString(mAppearance), rv);
     111           0 :   MOZ_ASSERT(!rv.Failed(), "Remove old appearance failed!");
     112             : 
     113           0 :   CaretElement()->ClassList()->Add(AppearanceString(aAppearance), rv);
     114           0 :   MOZ_ASSERT(!rv.Failed(), "Add new appearance failed!");
     115             : 
     116           0 :   AC_LOG("%s: %s -> %s", __FUNCTION__, ToString(mAppearance).c_str(),
     117             :          ToString(aAppearance).c_str());
     118             : 
     119           0 :   mAppearance = aAppearance;
     120             : 
     121             :   // Need to reset rect since the cached rect will be compared in SetPosition.
     122           0 :   if (mAppearance == Appearance::None) {
     123           0 :     mImaginaryCaretRect = nsRect();
     124           0 :     mZoomLevel = 0.0f;
     125             :   }
     126             : }
     127             : 
     128             : void
     129           0 : AccessibleCaret::SetSelectionBarEnabled(bool aEnabled)
     130             : {
     131           0 :   if (mSelectionBarEnabled == aEnabled) {
     132           0 :     return;
     133             :   }
     134             : 
     135           0 :   AC_LOG("Set selection bar %s", aEnabled ? "Enabled" : "Disabled");
     136             : 
     137           0 :   ErrorResult rv;
     138           0 :   CaretElement()->ClassList()->Toggle(NS_LITERAL_STRING("no-bar"),
     139           0 :                                       Optional<bool>(!aEnabled), rv);
     140           0 :   MOZ_ASSERT(!rv.Failed());
     141             : 
     142           0 :   mSelectionBarEnabled = aEnabled;
     143             : }
     144             : 
     145             : /* static */ nsAutoString
     146           0 : AccessibleCaret::AppearanceString(Appearance aAppearance)
     147             : {
     148           0 :   nsAutoString string;
     149           0 :   switch (aAppearance) {
     150             :   case Appearance::None:
     151             :   case Appearance::NormalNotShown:
     152           0 :     string = NS_LITERAL_STRING("none");
     153           0 :     break;
     154             :   case Appearance::Normal:
     155           0 :     string = NS_LITERAL_STRING("normal");
     156           0 :     break;
     157             :   case Appearance::Right:
     158           0 :     string = NS_LITERAL_STRING("right");
     159           0 :     break;
     160             :   case Appearance::Left:
     161           0 :     string = NS_LITERAL_STRING("left");
     162           0 :     break;
     163             :   }
     164           0 :   return string;
     165             : }
     166             : 
     167             : bool
     168           0 : AccessibleCaret::Intersects(const AccessibleCaret& aCaret) const
     169             : {
     170           0 :   MOZ_ASSERT(mPresShell == aCaret.mPresShell);
     171             : 
     172           0 :   if (!IsVisuallyVisible() || !aCaret.IsVisuallyVisible()) {
     173           0 :     return false;
     174             :   }
     175             : 
     176           0 :   nsRect rect = nsLayoutUtils::GetRectRelativeToFrame(CaretElement(), RootFrame());
     177           0 :   nsRect rhsRect = nsLayoutUtils::GetRectRelativeToFrame(aCaret.CaretElement(), RootFrame());
     178           0 :   return rect.Intersects(rhsRect);
     179             : }
     180             : 
     181             : bool
     182           0 : AccessibleCaret::Contains(const nsPoint& aPoint, TouchArea aTouchArea) const
     183             : {
     184           0 :   if (!IsVisuallyVisible()) {
     185           0 :     return false;
     186             :   }
     187             : 
     188             :   nsRect textOverlayRect =
     189           0 :     nsLayoutUtils::GetRectRelativeToFrame(TextOverlayElement(), RootFrame());
     190             :   nsRect caretImageRect =
     191           0 :     nsLayoutUtils::GetRectRelativeToFrame(CaretImageElement(), RootFrame());
     192             : 
     193           0 :   if (aTouchArea == TouchArea::CaretImage) {
     194           0 :     return caretImageRect.Contains(aPoint);
     195             :   }
     196             : 
     197           0 :   MOZ_ASSERT(aTouchArea == TouchArea::Full, "Unexpected TouchArea type!");
     198           0 :   return textOverlayRect.Contains(aPoint) || caretImageRect.Contains(aPoint);
     199             : }
     200             : 
     201             : void
     202           0 : AccessibleCaret::EnsureApzAware()
     203             : {
     204             :   // If the caret element was cloned, the listener might have been lost. So
     205             :   // if that's the case we register a dummy listener if there isn't one on
     206             :   // the element already.
     207           0 :   if (!CaretElement()->IsApzAware()) {
     208           0 :     CaretElement()->AddEventListener(NS_LITERAL_STRING("touchstart"),
     209           0 :                                      mDummyTouchListener, false);
     210             :   }
     211           0 : }
     212             : 
     213             : void
     214           0 : AccessibleCaret::InjectCaretElement(nsIDocument* aDocument)
     215             : {
     216           0 :   ErrorResult rv;
     217           0 :   nsCOMPtr<Element> element = CreateCaretElement(aDocument);
     218           0 :   mCaretElementHolder = aDocument->InsertAnonymousContent(*element, rv);
     219             : 
     220           0 :   MOZ_ASSERT(!rv.Failed(), "Insert anonymous content should not fail!");
     221           0 :   MOZ_ASSERT(mCaretElementHolder.get(), "We must have anonymous content!");
     222             : 
     223             :   // InsertAnonymousContent will clone the element to make an AnonymousContent.
     224             :   // Since event listeners are not being cloned when cloning a node, we need to
     225             :   // add the listener here.
     226           0 :   EnsureApzAware();
     227           0 : }
     228             : 
     229             : already_AddRefed<Element>
     230           0 : AccessibleCaret::CreateCaretElement(nsIDocument* aDocument) const
     231             : {
     232             :   // Content structure of AccessibleCaret
     233             :   // <div class="moz-accessiblecaret">  <- CaretElement()
     234             :   //   <div id="text-overlay"           <- TextOverlayElement()
     235             :   //   <div id="image">                 <- CaretImageElement()
     236             :   //   <div id="bar">                   <- SelectionBarElement()
     237             : 
     238           0 :   ErrorResult rv;
     239           0 :   nsCOMPtr<Element> parent = aDocument->CreateHTMLElement(nsGkAtoms::div);
     240           0 :   parent->ClassList()->Add(NS_LITERAL_STRING("moz-accessiblecaret"), rv);
     241           0 :   parent->ClassList()->Add(NS_LITERAL_STRING("none"), rv);
     242           0 :   parent->ClassList()->Add(NS_LITERAL_STRING("no-bar"), rv);
     243             : 
     244             :   auto CreateAndAppendChildElement = [aDocument, &parent](
     245           0 :     const nsLiteralString& aElementId)
     246           0 :   {
     247           0 :     nsCOMPtr<Element> child = aDocument->CreateHTMLElement(nsGkAtoms::div);
     248           0 :     child->SetAttr(kNameSpaceID_None, nsGkAtoms::id, aElementId, true);
     249           0 :     parent->AppendChildTo(child, false);
     250           0 :   };
     251             : 
     252           0 :   CreateAndAppendChildElement(sTextOverlayElementId);
     253           0 :   CreateAndAppendChildElement(sCaretImageElementId);
     254           0 :   CreateAndAppendChildElement(sSelectionBarElementId);
     255             : 
     256           0 :   return parent.forget();
     257             : }
     258             : 
     259             : void
     260           0 : AccessibleCaret::RemoveCaretElement(nsIDocument* aDocument)
     261             : {
     262           0 :   CaretElement()->RemoveEventListener(NS_LITERAL_STRING("touchstart"),
     263           0 :                                       mDummyTouchListener, false);
     264             : 
     265           0 :   ErrorResult rv;
     266           0 :   aDocument->RemoveAnonymousContent(*mCaretElementHolder, rv);
     267             :   // It's OK rv is failed since nsCanvasFrame might not exists now.
     268           0 :   rv.SuppressException();
     269           0 : }
     270             : 
     271             : AccessibleCaret::PositionChangedResult
     272           0 : AccessibleCaret::SetPosition(nsIFrame* aFrame, int32_t aOffset)
     273             : {
     274           0 :   if (!CustomContentContainerFrame()) {
     275           0 :     return PositionChangedResult::NotChanged;
     276             :   }
     277             : 
     278             :   nsRect imaginaryCaretRectInFrame =
     279           0 :     nsCaret::GetGeometryForFrame(aFrame, aOffset, nullptr);
     280             : 
     281           0 :   imaginaryCaretRectInFrame =
     282           0 :     nsLayoutUtils::ClampRectToScrollFrames(aFrame, imaginaryCaretRectInFrame);
     283             : 
     284           0 :   if (imaginaryCaretRectInFrame.IsEmpty()) {
     285             :     // Don't bother to set the caret position since it's invisible.
     286           0 :     mImaginaryCaretRect = nsRect();
     287           0 :     mZoomLevel = 0.0f;
     288           0 :     return PositionChangedResult::Invisible;
     289             :   }
     290             : 
     291           0 :   nsRect imaginaryCaretRect = imaginaryCaretRectInFrame;
     292           0 :   nsLayoutUtils::TransformRect(aFrame, RootFrame(), imaginaryCaretRect);
     293           0 :   float zoomLevel = GetZoomLevel();
     294             : 
     295           0 :   if (imaginaryCaretRect.IsEqualEdges(mImaginaryCaretRect) &&
     296           0 :       FuzzyEqualsMultiplicative(zoomLevel, mZoomLevel)) {
     297           0 :     return PositionChangedResult::NotChanged;
     298             :   }
     299             : 
     300           0 :   mImaginaryCaretRect = imaginaryCaretRect;
     301           0 :   mZoomLevel = zoomLevel;
     302             : 
     303             :   // SetCaretElementStyle() requires the input rect relative to container frame.
     304           0 :   nsRect imaginaryCaretRectInContainerFrame = imaginaryCaretRectInFrame;
     305           0 :   nsLayoutUtils::TransformRect(aFrame, CustomContentContainerFrame(),
     306           0 :                                imaginaryCaretRectInContainerFrame);
     307           0 :   SetCaretElementStyle(imaginaryCaretRectInContainerFrame, mZoomLevel);
     308             : 
     309           0 :   return PositionChangedResult::Changed;
     310             : }
     311             : 
     312             : nsIFrame*
     313           0 : AccessibleCaret::CustomContentContainerFrame() const
     314             : {
     315           0 :   nsCanvasFrame* canvasFrame = mPresShell->GetCanvasFrame();
     316           0 :   Element* container = canvasFrame->GetCustomContentContainer();
     317           0 :   nsIFrame* containerFrame = container->GetPrimaryFrame();
     318           0 :   return containerFrame;
     319             : }
     320             : 
     321             : void
     322           0 : AccessibleCaret::SetCaretElementStyle(const nsRect& aRect, float aZoomLevel)
     323             : {
     324           0 :   nsPoint position = CaretElementPosition(aRect);
     325           0 :   nsAutoString styleStr;
     326           0 :   styleStr.AppendPrintf("left: %dpx; top: %dpx; "
     327             :                         "width: ",
     328             :                         nsPresContext::AppUnitsToIntCSSPixels(position.x),
     329           0 :                         nsPresContext::AppUnitsToIntCSSPixels(position.y));
     330             :   // We can't use AppendPrintf here, because it does locale-specific
     331             :   // formatting of floating-point values.
     332           0 :   styleStr.AppendFloat(sWidth/aZoomLevel);
     333           0 :   styleStr.AppendLiteral("px; height: ");
     334           0 :   styleStr.AppendFloat(sHeight/aZoomLevel);
     335           0 :   styleStr.AppendLiteral("px; margin-left: ");
     336           0 :   styleStr.AppendFloat(sMarginLeft/aZoomLevel);
     337           0 :   styleStr.AppendLiteral("px");
     338             : 
     339           0 :   CaretElement()->SetAttr(kNameSpaceID_None, nsGkAtoms::style, styleStr, true);
     340           0 :   AC_LOG("%s: %s", __FUNCTION__, NS_ConvertUTF16toUTF8(styleStr).get());
     341             : 
     342             :   // Set style string for children.
     343           0 :   SetTextOverlayElementStyle(aRect, aZoomLevel);
     344           0 :   SetCaretImageElementStyle(aRect, aZoomLevel);
     345           0 :   SetSelectionBarElementStyle(aRect, aZoomLevel);
     346           0 : }
     347             : 
     348             : void
     349           0 : AccessibleCaret::SetTextOverlayElementStyle(const nsRect& aRect,
     350             :                                             float aZoomLevel)
     351             : {
     352           0 :   nsAutoString styleStr;
     353           0 :   styleStr.AppendPrintf("height: %dpx;",
     354           0 :                         nsPresContext::AppUnitsToIntCSSPixels(aRect.height));
     355           0 :   TextOverlayElement()->SetAttr(kNameSpaceID_None, nsGkAtoms::style, styleStr,
     356           0 :                                 true);
     357           0 :   AC_LOG("%s: %s", __FUNCTION__, NS_ConvertUTF16toUTF8(styleStr).get());
     358           0 : }
     359             : 
     360             : void
     361           0 : AccessibleCaret::SetCaretImageElementStyle(const nsRect& aRect,
     362             :                                            float aZoomLevel)
     363             : {
     364           0 :   nsAutoString styleStr;
     365           0 :   styleStr.AppendPrintf("margin-top: %dpx;",
     366           0 :                         nsPresContext::AppUnitsToIntCSSPixels(aRect.height));
     367           0 :   CaretImageElement()->SetAttr(kNameSpaceID_None, nsGkAtoms::style, styleStr,
     368           0 :                                true);
     369           0 :   AC_LOG("%s: %s", __FUNCTION__, NS_ConvertUTF16toUTF8(styleStr).get());
     370           0 : }
     371             : 
     372             : void
     373           0 : AccessibleCaret::SetSelectionBarElementStyle(const nsRect& aRect,
     374             :                                              float aZoomLevel)
     375             : {
     376           0 :   nsAutoString styleStr;
     377           0 :   styleStr.AppendPrintf("height: %dpx; width: ",
     378           0 :                         nsPresContext::AppUnitsToIntCSSPixels(aRect.height));
     379             :   // We can't use AppendPrintf here, because it does locale-specific
     380             :   // formatting of floating-point values.
     381           0 :   styleStr.AppendFloat(sBarWidth / aZoomLevel);
     382           0 :   styleStr.AppendLiteral("px");
     383             : 
     384           0 :   SelectionBarElement()->SetAttr(kNameSpaceID_None, nsGkAtoms::style, styleStr,
     385           0 :                                  true);
     386           0 :   AC_LOG("%s: %s", __FUNCTION__, NS_ConvertUTF16toUTF8(styleStr).get());
     387           0 : }
     388             : 
     389             : float
     390           0 : AccessibleCaret::GetZoomLevel()
     391             : {
     392             :   // Full zoom on desktop.
     393           0 :   float fullZoom = mPresShell->GetPresContext()->GetFullZoom();
     394             : 
     395             :   // Pinch-zoom on fennec.
     396           0 :   float resolution = mPresShell->GetCumulativeResolution();
     397             : 
     398           0 :   return fullZoom * resolution;
     399             : }
     400             : 
     401             : } // namespace mozilla

Generated by: LCOV version 1.13