ContentCache.cpp 51 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382
  1. /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  2. * vim: sw=2 ts=8 et :
  3. */
  4. /* This Source Code Form is subject to the terms of the Mozilla Public
  5. * License, v. 2.0. If a copy of the MPL was not distributed with this
  6. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  7. #include "mozilla/ContentCache.h"
  8. #include "mozilla/IMEStateManager.h"
  9. #include "mozilla/Logging.h"
  10. #include "mozilla/TextComposition.h"
  11. #include "mozilla/TextEvents.h"
  12. #include "nsIWidget.h"
  13. #include "mozilla/RefPtr.h"
  14. #include "mozilla/Move.h"
  15. namespace mozilla {
  16. using namespace widget;
  17. static const char*
  18. GetBoolName(bool aBool)
  19. {
  20. return aBool ? "true" : "false";
  21. }
  22. static const char*
  23. GetNotificationName(const IMENotification* aNotification)
  24. {
  25. if (!aNotification) {
  26. return "Not notification";
  27. }
  28. return ToChar(aNotification->mMessage);
  29. }
  30. class GetRectText : public nsAutoCString
  31. {
  32. public:
  33. explicit GetRectText(const LayoutDeviceIntRect& aRect)
  34. {
  35. Assign("{ x=");
  36. AppendInt(aRect.x);
  37. Append(", y=");
  38. AppendInt(aRect.y);
  39. Append(", width=");
  40. AppendInt(aRect.width);
  41. Append(", height=");
  42. AppendInt(aRect.height);
  43. Append(" }");
  44. }
  45. virtual ~GetRectText() {}
  46. };
  47. class GetWritingModeName : public nsAutoCString
  48. {
  49. public:
  50. explicit GetWritingModeName(const WritingMode& aWritingMode)
  51. {
  52. if (!aWritingMode.IsVertical()) {
  53. Assign("Horizontal");
  54. return;
  55. }
  56. if (aWritingMode.IsVerticalLR()) {
  57. Assign("Vertical (LTR)");
  58. return;
  59. }
  60. Assign("Vertical (RTL)");
  61. }
  62. virtual ~GetWritingModeName() {}
  63. };
  64. class GetEscapedUTF8String final : public NS_ConvertUTF16toUTF8
  65. {
  66. public:
  67. explicit GetEscapedUTF8String(const nsAString& aString)
  68. : NS_ConvertUTF16toUTF8(aString)
  69. {
  70. Escape();
  71. }
  72. explicit GetEscapedUTF8String(const char16ptr_t aString)
  73. : NS_ConvertUTF16toUTF8(aString)
  74. {
  75. Escape();
  76. }
  77. GetEscapedUTF8String(const char16ptr_t aString, uint32_t aLength)
  78. : NS_ConvertUTF16toUTF8(aString, aLength)
  79. {
  80. Escape();
  81. }
  82. private:
  83. void Escape()
  84. {
  85. ReplaceSubstring("\r", "\\r");
  86. ReplaceSubstring("\n", "\\n");
  87. ReplaceSubstring("\t", "\\t");
  88. }
  89. };
  90. /*****************************************************************************
  91. * mozilla::ContentCache
  92. *****************************************************************************/
  93. LazyLogModule sContentCacheLog("ContentCacheWidgets");
  94. ContentCache::ContentCache()
  95. : mCompositionStart(UINT32_MAX)
  96. {
  97. }
  98. /*****************************************************************************
  99. * mozilla::ContentCacheInChild
  100. *****************************************************************************/
  101. ContentCacheInChild::ContentCacheInChild()
  102. : ContentCache()
  103. {
  104. }
  105. void
  106. ContentCacheInChild::Clear()
  107. {
  108. MOZ_LOG(sContentCacheLog, LogLevel::Info,
  109. ("0x%p Clear()", this));
  110. mCompositionStart = UINT32_MAX;
  111. mText.Truncate();
  112. mSelection.Clear();
  113. mFirstCharRect.SetEmpty();
  114. mCaret.Clear();
  115. mTextRectArray.Clear();
  116. mEditorRect.SetEmpty();
  117. }
  118. bool
  119. ContentCacheInChild::CacheAll(nsIWidget* aWidget,
  120. const IMENotification* aNotification)
  121. {
  122. MOZ_LOG(sContentCacheLog, LogLevel::Info,
  123. ("0x%p CacheAll(aWidget=0x%p, aNotification=%s)",
  124. this, aWidget, GetNotificationName(aNotification)));
  125. if (NS_WARN_IF(!CacheText(aWidget, aNotification)) ||
  126. NS_WARN_IF(!CacheEditorRect(aWidget, aNotification))) {
  127. return false;
  128. }
  129. return true;
  130. }
  131. bool
  132. ContentCacheInChild::CacheSelection(nsIWidget* aWidget,
  133. const IMENotification* aNotification)
  134. {
  135. MOZ_LOG(sContentCacheLog, LogLevel::Info,
  136. ("0x%p CacheSelection(aWidget=0x%p, aNotification=%s)",
  137. this, aWidget, GetNotificationName(aNotification)));
  138. mCaret.Clear();
  139. mSelection.Clear();
  140. nsEventStatus status = nsEventStatus_eIgnore;
  141. WidgetQueryContentEvent selection(true, eQuerySelectedText, aWidget);
  142. aWidget->DispatchEvent(&selection, status);
  143. if (NS_WARN_IF(!selection.mSucceeded)) {
  144. MOZ_LOG(sContentCacheLog, LogLevel::Error,
  145. ("0x%p CacheSelection(), FAILED, "
  146. "couldn't retrieve the selected text", this));
  147. return false;
  148. }
  149. if (selection.mReply.mReversed) {
  150. mSelection.mAnchor =
  151. selection.mReply.mOffset + selection.mReply.mString.Length();
  152. mSelection.mFocus = selection.mReply.mOffset;
  153. } else {
  154. mSelection.mAnchor = selection.mReply.mOffset;
  155. mSelection.mFocus =
  156. selection.mReply.mOffset + selection.mReply.mString.Length();
  157. }
  158. mSelection.mWritingMode = selection.GetWritingMode();
  159. return CacheCaret(aWidget, aNotification) &&
  160. CacheTextRects(aWidget, aNotification);
  161. }
  162. bool
  163. ContentCacheInChild::CacheCaret(nsIWidget* aWidget,
  164. const IMENotification* aNotification)
  165. {
  166. MOZ_LOG(sContentCacheLog, LogLevel::Info,
  167. ("0x%p CacheCaret(aWidget=0x%p, aNotification=%s)",
  168. this, aWidget, GetNotificationName(aNotification)));
  169. mCaret.Clear();
  170. if (NS_WARN_IF(!mSelection.IsValid())) {
  171. return false;
  172. }
  173. // XXX Should be mSelection.mFocus?
  174. mCaret.mOffset = mSelection.StartOffset();
  175. nsEventStatus status = nsEventStatus_eIgnore;
  176. WidgetQueryContentEvent caretRect(true, eQueryCaretRect, aWidget);
  177. caretRect.InitForQueryCaretRect(mCaret.mOffset);
  178. aWidget->DispatchEvent(&caretRect, status);
  179. if (NS_WARN_IF(!caretRect.mSucceeded)) {
  180. MOZ_LOG(sContentCacheLog, LogLevel::Error,
  181. ("0x%p CacheCaret(), FAILED, "
  182. "couldn't retrieve the caret rect at offset=%u",
  183. this, mCaret.mOffset));
  184. mCaret.Clear();
  185. return false;
  186. }
  187. mCaret.mRect = caretRect.mReply.mRect;
  188. MOZ_LOG(sContentCacheLog, LogLevel::Info,
  189. ("0x%p CacheCaret(), Succeeded, "
  190. "mSelection={ mAnchor=%u, mFocus=%u, mWritingMode=%s }, "
  191. "mCaret={ mOffset=%u, mRect=%s }",
  192. this, mSelection.mAnchor, mSelection.mFocus,
  193. GetWritingModeName(mSelection.mWritingMode).get(), mCaret.mOffset,
  194. GetRectText(mCaret.mRect).get()));
  195. return true;
  196. }
  197. bool
  198. ContentCacheInChild::CacheEditorRect(nsIWidget* aWidget,
  199. const IMENotification* aNotification)
  200. {
  201. MOZ_LOG(sContentCacheLog, LogLevel::Info,
  202. ("0x%p CacheEditorRect(aWidget=0x%p, aNotification=%s)",
  203. this, aWidget, GetNotificationName(aNotification)));
  204. nsEventStatus status = nsEventStatus_eIgnore;
  205. WidgetQueryContentEvent editorRectEvent(true, eQueryEditorRect, aWidget);
  206. aWidget->DispatchEvent(&editorRectEvent, status);
  207. if (NS_WARN_IF(!editorRectEvent.mSucceeded)) {
  208. MOZ_LOG(sContentCacheLog, LogLevel::Error,
  209. ("0x%p CacheEditorRect(), FAILED, "
  210. "couldn't retrieve the editor rect", this));
  211. return false;
  212. }
  213. mEditorRect = editorRectEvent.mReply.mRect;
  214. MOZ_LOG(sContentCacheLog, LogLevel::Info,
  215. ("0x%p CacheEditorRect(), Succeeded, "
  216. "mEditorRect=%s", this, GetRectText(mEditorRect).get()));
  217. return true;
  218. }
  219. bool
  220. ContentCacheInChild::CacheText(nsIWidget* aWidget,
  221. const IMENotification* aNotification)
  222. {
  223. MOZ_LOG(sContentCacheLog, LogLevel::Info,
  224. ("0x%p CacheText(aWidget=0x%p, aNotification=%s)",
  225. this, aWidget, GetNotificationName(aNotification)));
  226. nsEventStatus status = nsEventStatus_eIgnore;
  227. WidgetQueryContentEvent queryText(true, eQueryTextContent, aWidget);
  228. queryText.InitForQueryTextContent(0, UINT32_MAX);
  229. aWidget->DispatchEvent(&queryText, status);
  230. if (NS_WARN_IF(!queryText.mSucceeded)) {
  231. MOZ_LOG(sContentCacheLog, LogLevel::Error,
  232. ("0x%p CacheText(), FAILED, couldn't retrieve whole text", this));
  233. mText.Truncate();
  234. return false;
  235. }
  236. mText = queryText.mReply.mString;
  237. MOZ_LOG(sContentCacheLog, LogLevel::Info,
  238. ("0x%p CacheText(), Succeeded, mText.Length()=%u", this, mText.Length()));
  239. return CacheSelection(aWidget, aNotification);
  240. }
  241. bool
  242. ContentCacheInChild::QueryCharRect(nsIWidget* aWidget,
  243. uint32_t aOffset,
  244. LayoutDeviceIntRect& aCharRect) const
  245. {
  246. aCharRect.SetEmpty();
  247. nsEventStatus status = nsEventStatus_eIgnore;
  248. WidgetQueryContentEvent textRect(true, eQueryTextRect, aWidget);
  249. textRect.InitForQueryTextRect(aOffset, 1);
  250. aWidget->DispatchEvent(&textRect, status);
  251. if (NS_WARN_IF(!textRect.mSucceeded)) {
  252. return false;
  253. }
  254. aCharRect = textRect.mReply.mRect;
  255. // Guarantee the rect is not empty.
  256. if (NS_WARN_IF(!aCharRect.height)) {
  257. aCharRect.height = 1;
  258. }
  259. if (NS_WARN_IF(!aCharRect.width)) {
  260. aCharRect.width = 1;
  261. }
  262. return true;
  263. }
  264. bool
  265. ContentCacheInChild::QueryCharRectArray(nsIWidget* aWidget,
  266. uint32_t aOffset,
  267. uint32_t aLength,
  268. RectArray& aCharRectArray) const
  269. {
  270. nsEventStatus status = nsEventStatus_eIgnore;
  271. WidgetQueryContentEvent textRects(true, eQueryTextRectArray, aWidget);
  272. textRects.InitForQueryTextRectArray(aOffset, aLength);
  273. aWidget->DispatchEvent(&textRects, status);
  274. if (NS_WARN_IF(!textRects.mSucceeded)) {
  275. aCharRectArray.Clear();
  276. return false;
  277. }
  278. aCharRectArray = Move(textRects.mReply.mRectArray);
  279. return true;
  280. }
  281. bool
  282. ContentCacheInChild::CacheTextRects(nsIWidget* aWidget,
  283. const IMENotification* aNotification)
  284. {
  285. MOZ_LOG(sContentCacheLog, LogLevel::Info,
  286. ("0x%p CacheTextRects(aWidget=0x%p, aNotification=%s), "
  287. "mCaret={ mOffset=%u, IsValid()=%s }",
  288. this, aWidget, GetNotificationName(aNotification), mCaret.mOffset,
  289. GetBoolName(mCaret.IsValid())));
  290. mCompositionStart = UINT32_MAX;
  291. mTextRectArray.Clear();
  292. mSelection.ClearAnchorCharRects();
  293. mSelection.ClearFocusCharRects();
  294. mSelection.mRect.SetEmpty();
  295. mFirstCharRect.SetEmpty();
  296. if (NS_WARN_IF(!mSelection.IsValid())) {
  297. return false;
  298. }
  299. // Retrieve text rects in composition string if there is.
  300. RefPtr<TextComposition> textComposition =
  301. IMEStateManager::GetTextCompositionFor(aWidget);
  302. if (textComposition) {
  303. // mCompositionStart may be updated by some composition event handlers.
  304. // So, let's update it with the latest information.
  305. mCompositionStart = textComposition->NativeOffsetOfStartComposition();
  306. // Note that TextComposition::String() may not be modified here because
  307. // it's modified after all edit action listeners are performed but this
  308. // is called while some of them are performed.
  309. uint32_t length = textComposition->LastData().Length();
  310. mTextRectArray.mStart = mCompositionStart;
  311. if (NS_WARN_IF(!QueryCharRectArray(aWidget, mTextRectArray.mStart, length,
  312. mTextRectArray.mRects))) {
  313. MOZ_LOG(sContentCacheLog, LogLevel::Error,
  314. ("0x%p CacheTextRects(), FAILED, "
  315. "couldn't retrieve text rect array of the composition string", this));
  316. }
  317. }
  318. if (mTextRectArray.InRange(mSelection.mAnchor) &&
  319. (!mSelection.mAnchor || mTextRectArray.InRange(mSelection.mAnchor - 1))) {
  320. mSelection.mAnchorCharRects[eNextCharRect] =
  321. mTextRectArray.GetRect(mSelection.mAnchor);
  322. if (mSelection.mAnchor) {
  323. mSelection.mAnchorCharRects[ePrevCharRect] =
  324. mTextRectArray.GetRect(mSelection.mAnchor - 1);
  325. }
  326. } else {
  327. RectArray rects;
  328. uint32_t startOffset = mSelection.mAnchor ? mSelection.mAnchor - 1 : 0;
  329. uint32_t length = mSelection.mAnchor ? 2 : 1;
  330. if (NS_WARN_IF(!QueryCharRectArray(aWidget, startOffset, length, rects))) {
  331. MOZ_LOG(sContentCacheLog, LogLevel::Error,
  332. ("0x%p CacheTextRects(), FAILED, "
  333. "couldn't retrieve text rect array around the selection anchor (%u)",
  334. this, mSelection.mAnchor));
  335. MOZ_ASSERT(mSelection.mAnchorCharRects[ePrevCharRect].IsEmpty());
  336. MOZ_ASSERT(mSelection.mAnchorCharRects[eNextCharRect].IsEmpty());
  337. } else {
  338. if (rects.Length() > 1) {
  339. mSelection.mAnchorCharRects[ePrevCharRect] = rects[0];
  340. mSelection.mAnchorCharRects[eNextCharRect] = rects[1];
  341. } else if (rects.Length()) {
  342. mSelection.mAnchorCharRects[eNextCharRect] = rects[0];
  343. MOZ_ASSERT(mSelection.mAnchorCharRects[ePrevCharRect].IsEmpty());
  344. }
  345. }
  346. }
  347. if (mSelection.Collapsed()) {
  348. mSelection.mFocusCharRects[0] = mSelection.mAnchorCharRects[0];
  349. mSelection.mFocusCharRects[1] = mSelection.mAnchorCharRects[1];
  350. } else if (mTextRectArray.InRange(mSelection.mFocus) &&
  351. (!mSelection.mFocus ||
  352. mTextRectArray.InRange(mSelection.mFocus - 1))) {
  353. mSelection.mFocusCharRects[eNextCharRect] =
  354. mTextRectArray.GetRect(mSelection.mFocus);
  355. if (mSelection.mFocus) {
  356. mSelection.mFocusCharRects[ePrevCharRect] =
  357. mTextRectArray.GetRect(mSelection.mFocus - 1);
  358. }
  359. } else {
  360. RectArray rects;
  361. uint32_t startOffset = mSelection.mFocus ? mSelection.mFocus - 1 : 0;
  362. uint32_t length = mSelection.mFocus ? 2 : 1;
  363. if (NS_WARN_IF(!QueryCharRectArray(aWidget, startOffset, length, rects))) {
  364. MOZ_LOG(sContentCacheLog, LogLevel::Error,
  365. ("0x%p CacheTextRects(), FAILED, "
  366. "couldn't retrieve text rect array around the selection focus (%u)",
  367. this, mSelection.mFocus));
  368. MOZ_ASSERT(mSelection.mFocusCharRects[ePrevCharRect].IsEmpty());
  369. MOZ_ASSERT(mSelection.mFocusCharRects[eNextCharRect].IsEmpty());
  370. } else {
  371. if (rects.Length() > 1) {
  372. mSelection.mFocusCharRects[ePrevCharRect] = rects[0];
  373. mSelection.mFocusCharRects[eNextCharRect] = rects[1];
  374. } else if (rects.Length()) {
  375. mSelection.mFocusCharRects[eNextCharRect] = rects[0];
  376. MOZ_ASSERT(mSelection.mFocusCharRects[ePrevCharRect].IsEmpty());
  377. }
  378. }
  379. }
  380. if (!mSelection.Collapsed()) {
  381. nsEventStatus status = nsEventStatus_eIgnore;
  382. WidgetQueryContentEvent textRect(true, eQueryTextRect, aWidget);
  383. textRect.InitForQueryTextRect(mSelection.StartOffset(),
  384. mSelection.Length());
  385. aWidget->DispatchEvent(&textRect, status);
  386. if (NS_WARN_IF(!textRect.mSucceeded)) {
  387. MOZ_LOG(sContentCacheLog, LogLevel::Error,
  388. ("0x%p CacheTextRects(), FAILED, "
  389. "couldn't retrieve text rect of whole selected text", this));
  390. } else {
  391. mSelection.mRect = textRect.mReply.mRect;
  392. }
  393. }
  394. if (!mSelection.mFocus) {
  395. mFirstCharRect = mSelection.mFocusCharRects[eNextCharRect];
  396. } else if (mSelection.mFocus == 1) {
  397. mFirstCharRect = mSelection.mFocusCharRects[ePrevCharRect];
  398. } else if (!mSelection.mAnchor) {
  399. mFirstCharRect = mSelection.mAnchorCharRects[eNextCharRect];
  400. } else if (mSelection.mAnchor == 1) {
  401. mFirstCharRect = mSelection.mFocusCharRects[ePrevCharRect];
  402. } else if (mTextRectArray.InRange(0)) {
  403. mFirstCharRect = mTextRectArray.GetRect(0);
  404. } else {
  405. LayoutDeviceIntRect charRect;
  406. if (NS_WARN_IF(!QueryCharRect(aWidget, 0, charRect))) {
  407. MOZ_LOG(sContentCacheLog, LogLevel::Error,
  408. ("0x%p CacheTextRects(), FAILED, "
  409. "couldn't retrieve first char rect", this));
  410. } else {
  411. mFirstCharRect = charRect;
  412. }
  413. }
  414. MOZ_LOG(sContentCacheLog, LogLevel::Info,
  415. ("0x%p CacheTextRects(), Succeeded, "
  416. "mText.Length()=%u, mTextRectArray={ mStart=%u, mRects.Length()=%u }, "
  417. "mSelection={ mAnchor=%u, mAnchorCharRects[eNextCharRect]=%s, "
  418. "mAnchorCharRects[ePrevCharRect]=%s, mFocus=%u, "
  419. "mFocusCharRects[eNextCharRect]=%s, mFocusCharRects[ePrevCharRect]=%s, "
  420. "mRect=%s }, mFirstCharRect=%s",
  421. this, mText.Length(), mTextRectArray.mStart,
  422. mTextRectArray.mRects.Length(), mSelection.mAnchor,
  423. GetRectText(mSelection.mAnchorCharRects[eNextCharRect]).get(),
  424. GetRectText(mSelection.mAnchorCharRects[ePrevCharRect]).get(),
  425. mSelection.mFocus,
  426. GetRectText(mSelection.mFocusCharRects[eNextCharRect]).get(),
  427. GetRectText(mSelection.mFocusCharRects[ePrevCharRect]).get(),
  428. GetRectText(mSelection.mRect).get(), GetRectText(mFirstCharRect).get()));
  429. return true;
  430. }
  431. void
  432. ContentCacheInChild::SetSelection(nsIWidget* aWidget,
  433. uint32_t aStartOffset,
  434. uint32_t aLength,
  435. bool aReversed,
  436. const WritingMode& aWritingMode)
  437. {
  438. MOZ_LOG(sContentCacheLog, LogLevel::Info,
  439. ("0x%p SetSelection(aStartOffset=%u, "
  440. "aLength=%u, aReversed=%s, aWritingMode=%s), mText.Length()=%u",
  441. this, aStartOffset, aLength, GetBoolName(aReversed),
  442. GetWritingModeName(aWritingMode).get(), mText.Length()));
  443. if (!aReversed) {
  444. mSelection.mAnchor = aStartOffset;
  445. mSelection.mFocus = aStartOffset + aLength;
  446. } else {
  447. mSelection.mAnchor = aStartOffset + aLength;
  448. mSelection.mFocus = aStartOffset;
  449. }
  450. mSelection.mWritingMode = aWritingMode;
  451. if (NS_WARN_IF(!CacheCaret(aWidget))) {
  452. return;
  453. }
  454. Unused << NS_WARN_IF(!CacheTextRects(aWidget));
  455. }
  456. /*****************************************************************************
  457. * mozilla::ContentCacheInParent
  458. *****************************************************************************/
  459. ContentCacheInParent::ContentCacheInParent()
  460. : ContentCache()
  461. , mCommitStringByRequest(nullptr)
  462. , mPendingEventsNeedingAck(0)
  463. , mCompositionStartInChild(UINT32_MAX)
  464. , mPendingCompositionCount(0)
  465. , mWidgetHasComposition(false)
  466. {
  467. }
  468. void
  469. ContentCacheInParent::AssignContent(const ContentCache& aOther,
  470. nsIWidget* aWidget,
  471. const IMENotification* aNotification)
  472. {
  473. mText = aOther.mText;
  474. mSelection = aOther.mSelection;
  475. mFirstCharRect = aOther.mFirstCharRect;
  476. mCaret = aOther.mCaret;
  477. mTextRectArray = aOther.mTextRectArray;
  478. mEditorRect = aOther.mEditorRect;
  479. // Only when there is one composition, the TextComposition instance in this
  480. // process is managing the composition in the remote process. Therefore,
  481. // we shouldn't update composition start offset of TextComposition with
  482. // old composition which is still being handled by the child process.
  483. if (mWidgetHasComposition && mPendingCompositionCount == 1) {
  484. IMEStateManager::MaybeStartOffsetUpdatedInChild(aWidget, mCompositionStart);
  485. }
  486. // When the widget has composition, we should set mCompositionStart to
  487. // *current* composition start offset. Note that, in strictly speaking,
  488. // widget should not use WidgetQueryContentEvent if there are some pending
  489. // compositions (i.e., when mPendingCompositionCount is 2 or more).
  490. mCompositionStartInChild = aOther.mCompositionStart;
  491. if (mWidgetHasComposition) {
  492. if (aOther.mCompositionStart != UINT32_MAX) {
  493. mCompositionStart = aOther.mCompositionStart;
  494. } else {
  495. mCompositionStart = mSelection.StartOffset();
  496. NS_WARNING_ASSERTION(mCompositionStart != UINT32_MAX,
  497. "mCompositionStart shouldn't be invalid offset when "
  498. "the widget has composition");
  499. }
  500. } else {
  501. mCompositionStart = UINT32_MAX;
  502. }
  503. MOZ_LOG(sContentCacheLog, LogLevel::Info,
  504. ("0x%p AssignContent(aNotification=%s), "
  505. "Succeeded, mText.Length()=%u, mSelection={ mAnchor=%u, mFocus=%u, "
  506. "mWritingMode=%s, mAnchorCharRects[eNextCharRect]=%s, "
  507. "mAnchorCharRects[ePrevCharRect]=%s, mFocusCharRects[eNextCharRect]=%s, "
  508. "mFocusCharRects[ePrevCharRect]=%s, mRect=%s }, "
  509. "mFirstCharRect=%s, mCaret={ mOffset=%u, mRect=%s }, mTextRectArray={ "
  510. "mStart=%u, mRects.Length()=%u }, mWidgetHasComposition=%s, "
  511. "mPendingCompositionCount=%u, mCompositionStart=%u, mEditorRect=%s",
  512. this, GetNotificationName(aNotification),
  513. mText.Length(), mSelection.mAnchor, mSelection.mFocus,
  514. GetWritingModeName(mSelection.mWritingMode).get(),
  515. GetRectText(mSelection.mAnchorCharRects[eNextCharRect]).get(),
  516. GetRectText(mSelection.mAnchorCharRects[ePrevCharRect]).get(),
  517. GetRectText(mSelection.mFocusCharRects[eNextCharRect]).get(),
  518. GetRectText(mSelection.mFocusCharRects[ePrevCharRect]).get(),
  519. GetRectText(mSelection.mRect).get(), GetRectText(mFirstCharRect).get(),
  520. mCaret.mOffset, GetRectText(mCaret.mRect).get(), mTextRectArray.mStart,
  521. mTextRectArray.mRects.Length(), GetBoolName(mWidgetHasComposition),
  522. mPendingCompositionCount, mCompositionStart,
  523. GetRectText(mEditorRect).get()));
  524. }
  525. bool
  526. ContentCacheInParent::HandleQueryContentEvent(WidgetQueryContentEvent& aEvent,
  527. nsIWidget* aWidget) const
  528. {
  529. MOZ_ASSERT(aWidget);
  530. aEvent.mSucceeded = false;
  531. aEvent.mReply.mFocusedWidget = aWidget;
  532. // ContentCache doesn't store offset of its start with XP linebreaks.
  533. // So, we don't support to query contents relative to composition start
  534. // offset with XP linebreaks.
  535. if (NS_WARN_IF(!aEvent.mUseNativeLineBreak)) {
  536. MOZ_LOG(sContentCacheLog, LogLevel::Error,
  537. ("0x%p HandleQueryContentEvent(), FAILED due to query with XP linebreaks",
  538. this));
  539. return false;
  540. }
  541. if (NS_WARN_IF(!aEvent.mInput.IsValidOffset())) {
  542. MOZ_LOG(sContentCacheLog, LogLevel::Error,
  543. ("0x%p HandleQueryContentEvent(), FAILED due to invalid offset",
  544. this));
  545. return false;
  546. }
  547. if (NS_WARN_IF(!aEvent.mInput.IsValidEventMessage(aEvent.mMessage))) {
  548. MOZ_LOG(sContentCacheLog, LogLevel::Error,
  549. ("0x%p HandleQueryContentEvent(), FAILED due to invalid event message",
  550. this));
  551. return false;
  552. }
  553. bool isRelativeToInsertionPoint = aEvent.mInput.mRelativeToInsertionPoint;
  554. if (isRelativeToInsertionPoint) {
  555. if (aWidget->PluginHasFocus()) {
  556. if (NS_WARN_IF(!aEvent.mInput.MakeOffsetAbsolute(0))) {
  557. MOZ_LOG(sContentCacheLog, LogLevel::Error,
  558. ("0x%p HandleQueryContentEvent(), FAILED due to "
  559. "aEvent.mInput.MakeOffsetAbsolute(0) failure, aEvent={ mMessage=%s, "
  560. "mInput={ mOffset=%d, mLength=%d } }",
  561. this, ToChar(aEvent.mMessage), aEvent.mInput.mOffset,
  562. aEvent.mInput.mLength));
  563. return false;
  564. }
  565. } else if (mWidgetHasComposition) {
  566. if (NS_WARN_IF(!aEvent.mInput.MakeOffsetAbsolute(mCompositionStart))) {
  567. MOZ_LOG(sContentCacheLog, LogLevel::Error,
  568. ("0x%p HandleQueryContentEvent(), FAILED due to "
  569. "aEvent.mInput.MakeOffsetAbsolute(mCompositionStart) failure, "
  570. "mCompositionStart=%d, aEvent={ mMessage=%s, "
  571. "mInput={ mOffset=%d, mLength=%d } }",
  572. this, mCompositionStart, ToChar(aEvent.mMessage),
  573. aEvent.mInput.mOffset, aEvent.mInput.mLength));
  574. return false;
  575. }
  576. } else if (NS_WARN_IF(!mSelection.IsValid())) {
  577. MOZ_LOG(sContentCacheLog, LogLevel::Error,
  578. ("0x%p HandleQueryContentEvent(), FAILED due to mSelection is invalid",
  579. this));
  580. return false;
  581. } else if (NS_WARN_IF(!aEvent.mInput.MakeOffsetAbsolute(
  582. mSelection.StartOffset()))) {
  583. MOZ_LOG(sContentCacheLog, LogLevel::Error,
  584. ("0x%p HandleQueryContentEvent(), FAILED due to "
  585. "aEvent.mInput.MakeOffsetAbsolute(mSelection.StartOffset()) "
  586. "failure, mSelection={ StartOffset()=%d, Length()=%d }, "
  587. "aEvent={ mMessage=%s, mInput={ mOffset=%d, mLength=%d } }",
  588. this, mSelection.StartOffset(), mSelection.Length(),
  589. ToChar(aEvent.mMessage), aEvent.mInput.mOffset,
  590. aEvent.mInput.mLength));
  591. return false;
  592. }
  593. }
  594. switch (aEvent.mMessage) {
  595. case eQuerySelectedText:
  596. MOZ_LOG(sContentCacheLog, LogLevel::Info,
  597. ("0x%p HandleQueryContentEvent("
  598. "aEvent={ mMessage=eQuerySelectedText }, aWidget=0x%p)",
  599. this, aWidget));
  600. if (aWidget->PluginHasFocus()) {
  601. MOZ_LOG(sContentCacheLog, LogLevel::Info,
  602. ("0x%p HandleQueryContentEvent(), "
  603. "return emtpy selection becasue plugin has focus",
  604. this));
  605. aEvent.mSucceeded = true;
  606. aEvent.mReply.mOffset = 0;
  607. aEvent.mReply.mReversed = false;
  608. aEvent.mReply.mHasSelection = false;
  609. return true;
  610. }
  611. if (NS_WARN_IF(!IsSelectionValid())) {
  612. // If content cache hasn't been initialized properly, make the query
  613. // failed.
  614. MOZ_LOG(sContentCacheLog, LogLevel::Error,
  615. ("0x%p HandleQueryContentEvent(), "
  616. "FAILED because mSelection is not valid", this));
  617. return true;
  618. }
  619. aEvent.mReply.mOffset = mSelection.StartOffset();
  620. if (mSelection.Collapsed()) {
  621. aEvent.mReply.mString.Truncate(0);
  622. } else {
  623. if (NS_WARN_IF(mSelection.EndOffset() > mText.Length())) {
  624. MOZ_LOG(sContentCacheLog, LogLevel::Error,
  625. ("0x%p HandleQueryContentEvent(), "
  626. "FAILED because mSelection.EndOffset()=%u is larger than "
  627. "mText.Length()=%u",
  628. this, mSelection.EndOffset(), mText.Length()));
  629. return false;
  630. }
  631. aEvent.mReply.mString =
  632. Substring(mText, aEvent.mReply.mOffset, mSelection.Length());
  633. }
  634. aEvent.mReply.mReversed = mSelection.Reversed();
  635. aEvent.mReply.mHasSelection = true;
  636. aEvent.mReply.mWritingMode = mSelection.mWritingMode;
  637. MOZ_LOG(sContentCacheLog, LogLevel::Info,
  638. ("0x%p HandleQueryContentEvent(), "
  639. "Succeeded, aEvent={ mReply={ mOffset=%u, mString=\"%s\", "
  640. "mReversed=%s, mHasSelection=%s, mWritingMode=%s } }",
  641. this, aEvent.mReply.mOffset,
  642. GetEscapedUTF8String(aEvent.mReply.mString).get(),
  643. GetBoolName(aEvent.mReply.mReversed),
  644. GetBoolName(aEvent.mReply.mHasSelection),
  645. GetWritingModeName(aEvent.mReply.mWritingMode).get()));
  646. break;
  647. case eQueryTextContent: {
  648. MOZ_LOG(sContentCacheLog, LogLevel::Info,
  649. ("0x%p HandleQueryContentEvent("
  650. "aEvent={ mMessage=eQueryTextContent, mInput={ mOffset=%u, "
  651. "mLength=%u } }, aWidget=0x%p), mText.Length()=%u",
  652. this, aEvent.mInput.mOffset,
  653. aEvent.mInput.mLength, aWidget, mText.Length()));
  654. uint32_t inputOffset = aEvent.mInput.mOffset;
  655. uint32_t inputEndOffset =
  656. std::min(aEvent.mInput.EndOffset(), mText.Length());
  657. if (NS_WARN_IF(inputEndOffset < inputOffset)) {
  658. MOZ_LOG(sContentCacheLog, LogLevel::Error,
  659. ("0x%p HandleQueryContentEvent(), "
  660. "FAILED because inputOffset=%u is larger than inputEndOffset=%u",
  661. this, inputOffset, inputEndOffset));
  662. return false;
  663. }
  664. aEvent.mReply.mOffset = inputOffset;
  665. aEvent.mReply.mString =
  666. Substring(mText, inputOffset, inputEndOffset - inputOffset);
  667. MOZ_LOG(sContentCacheLog, LogLevel::Info,
  668. ("0x%p HandleQueryContentEvent(), "
  669. "Succeeded, aEvent={ mReply={ mOffset=%u, mString.Length()=%u } }",
  670. this, aEvent.mReply.mOffset, aEvent.mReply.mString.Length()));
  671. break;
  672. }
  673. case eQueryTextRect:
  674. MOZ_LOG(sContentCacheLog, LogLevel::Info,
  675. ("0x%p HandleQueryContentEvent("
  676. "aEvent={ mMessage=eQueryTextRect, mInput={ mOffset=%u, "
  677. "mLength=%u } }, aWidget=0x%p), mText.Length()=%u",
  678. this, aEvent.mInput.mOffset, aEvent.mInput.mLength, aWidget,
  679. mText.Length()));
  680. if (NS_WARN_IF(!IsSelectionValid())) {
  681. // If content cache hasn't been initialized properly, make the query
  682. // failed.
  683. MOZ_LOG(sContentCacheLog, LogLevel::Error,
  684. ("0x%p HandleQueryContentEvent(), "
  685. "FAILED because mSelection is not valid", this));
  686. return true;
  687. }
  688. // Note that if the query is relative to insertion point, the query was
  689. // probably requested by native IME. In such case, we should return
  690. // non-empty rect since returning failure causes IME showing its window
  691. // at odd position.
  692. if (aEvent.mInput.mLength) {
  693. if (NS_WARN_IF(!GetUnionTextRects(aEvent.mInput.mOffset,
  694. aEvent.mInput.mLength,
  695. isRelativeToInsertionPoint,
  696. aEvent.mReply.mRect))) {
  697. // XXX We don't have cache for this request.
  698. MOZ_LOG(sContentCacheLog, LogLevel::Error,
  699. ("0x%p HandleQueryContentEvent(), "
  700. "FAILED to get union rect", this));
  701. return false;
  702. }
  703. } else {
  704. // If the length is 0, we should return caret rect instead.
  705. if (NS_WARN_IF(!GetCaretRect(aEvent.mInput.mOffset,
  706. isRelativeToInsertionPoint,
  707. aEvent.mReply.mRect))) {
  708. MOZ_LOG(sContentCacheLog, LogLevel::Error,
  709. ("0x%p HandleQueryContentEvent(), "
  710. "FAILED to get caret rect", this));
  711. return false;
  712. }
  713. }
  714. if (aEvent.mInput.mOffset < mText.Length()) {
  715. aEvent.mReply.mString =
  716. Substring(mText, aEvent.mInput.mOffset,
  717. mText.Length() >= aEvent.mInput.EndOffset() ?
  718. aEvent.mInput.mLength : UINT32_MAX);
  719. } else {
  720. aEvent.mReply.mString.Truncate(0);
  721. }
  722. aEvent.mReply.mOffset = aEvent.mInput.mOffset;
  723. // XXX This may be wrong if storing range isn't in the selection range.
  724. aEvent.mReply.mWritingMode = mSelection.mWritingMode;
  725. MOZ_LOG(sContentCacheLog, LogLevel::Info,
  726. ("0x%p HandleQueryContentEvent(), "
  727. "Succeeded, aEvent={ mReply={ mOffset=%u, mString=\"%s\", "
  728. "mWritingMode=%s, mRect=%s } }",
  729. this, aEvent.mReply.mOffset,
  730. GetEscapedUTF8String(aEvent.mReply.mString).get(),
  731. GetWritingModeName(aEvent.mReply.mWritingMode).get(),
  732. GetRectText(aEvent.mReply.mRect).get()));
  733. break;
  734. case eQueryCaretRect:
  735. MOZ_LOG(sContentCacheLog, LogLevel::Info,
  736. ("0x%p HandleQueryContentEvent("
  737. "aEvent={ mMessage=eQueryCaretRect, mInput={ mOffset=%u } }, "
  738. "aWidget=0x%p), mText.Length()=%u",
  739. this, aEvent.mInput.mOffset, aWidget, mText.Length()));
  740. if (NS_WARN_IF(!IsSelectionValid())) {
  741. // If content cache hasn't been initialized properly, make the query
  742. // failed.
  743. MOZ_LOG(sContentCacheLog, LogLevel::Error,
  744. ("0x%p HandleQueryContentEvent(), "
  745. "FAILED because mSelection is not valid", this));
  746. return true;
  747. }
  748. // Note that if the query is relative to insertion point, the query was
  749. // probably requested by native IME. In such case, we should return
  750. // non-empty rect since returning failure causes IME showing its window
  751. // at odd position.
  752. if (NS_WARN_IF(!GetCaretRect(aEvent.mInput.mOffset,
  753. isRelativeToInsertionPoint,
  754. aEvent.mReply.mRect))) {
  755. MOZ_LOG(sContentCacheLog, LogLevel::Error,
  756. ("0x%p HandleQueryContentEvent(), "
  757. "FAILED to get caret rect", this));
  758. return false;
  759. }
  760. aEvent.mReply.mOffset = aEvent.mInput.mOffset;
  761. MOZ_LOG(sContentCacheLog, LogLevel::Info,
  762. ("0x%p HandleQueryContentEvent(), "
  763. "Succeeded, aEvent={ mReply={ mOffset=%u, mRect=%s } }",
  764. this, aEvent.mReply.mOffset, GetRectText(aEvent.mReply.mRect).get()));
  765. break;
  766. case eQueryEditorRect:
  767. MOZ_LOG(sContentCacheLog, LogLevel::Info,
  768. ("0x%p HandleQueryContentEvent("
  769. "aEvent={ mMessage=eQueryEditorRect }, aWidget=0x%p)",
  770. this, aWidget));
  771. aEvent.mReply.mRect = mEditorRect;
  772. MOZ_LOG(sContentCacheLog, LogLevel::Info,
  773. ("0x%p HandleQueryContentEvent(), "
  774. "Succeeded, aEvent={ mReply={ mRect=%s } }",
  775. this, GetRectText(aEvent.mReply.mRect).get()));
  776. break;
  777. default:
  778. break;
  779. }
  780. aEvent.mSucceeded = true;
  781. return true;
  782. }
  783. bool
  784. ContentCacheInParent::GetTextRect(uint32_t aOffset,
  785. bool aRoundToExistingOffset,
  786. LayoutDeviceIntRect& aTextRect) const
  787. {
  788. MOZ_LOG(sContentCacheLog, LogLevel::Info,
  789. ("0x%p GetTextRect(aOffset=%u, "
  790. "aRoundToExistingOffset=%s), "
  791. "mTextRectArray={ mStart=%u, mRects.Length()=%u }, "
  792. "mSelection={ mAnchor=%u, mFocus=%u }",
  793. this, aOffset, GetBoolName(aRoundToExistingOffset),
  794. mTextRectArray.mStart, mTextRectArray.mRects.Length(),
  795. mSelection.mAnchor, mSelection.mFocus));
  796. if (!aOffset) {
  797. NS_WARNING_ASSERTION(!mFirstCharRect.IsEmpty(), "empty rect");
  798. aTextRect = mFirstCharRect;
  799. return !aTextRect.IsEmpty();
  800. }
  801. if (aOffset == mSelection.mAnchor) {
  802. NS_WARNING_ASSERTION(!mSelection.mAnchorCharRects[eNextCharRect].IsEmpty(),
  803. "empty rect");
  804. aTextRect = mSelection.mAnchorCharRects[eNextCharRect];
  805. return !aTextRect.IsEmpty();
  806. }
  807. if (mSelection.mAnchor && aOffset == mSelection.mAnchor - 1) {
  808. NS_WARNING_ASSERTION(!mSelection.mAnchorCharRects[ePrevCharRect].IsEmpty(),
  809. "empty rect");
  810. aTextRect = mSelection.mAnchorCharRects[ePrevCharRect];
  811. return !aTextRect.IsEmpty();
  812. }
  813. if (aOffset == mSelection.mFocus) {
  814. NS_WARNING_ASSERTION(!mSelection.mFocusCharRects[eNextCharRect].IsEmpty(),
  815. "empty rect");
  816. aTextRect = mSelection.mFocusCharRects[eNextCharRect];
  817. return !aTextRect.IsEmpty();
  818. }
  819. if (mSelection.mFocus && aOffset == mSelection.mFocus - 1) {
  820. NS_WARNING_ASSERTION(!mSelection.mFocusCharRects[ePrevCharRect].IsEmpty(),
  821. "empty rect");
  822. aTextRect = mSelection.mFocusCharRects[ePrevCharRect];
  823. return !aTextRect.IsEmpty();
  824. }
  825. uint32_t offset = aOffset;
  826. if (!mTextRectArray.InRange(aOffset)) {
  827. if (!aRoundToExistingOffset) {
  828. aTextRect.SetEmpty();
  829. return false;
  830. }
  831. if (!mTextRectArray.IsValid()) {
  832. // If there are no rects in mTextRectArray, we should refer the start of
  833. // the selection because IME must query a char rect around it if there is
  834. // no composition.
  835. aTextRect = mSelection.StartCharRect();
  836. return !aTextRect.IsEmpty();
  837. }
  838. if (offset < mTextRectArray.StartOffset()) {
  839. offset = mTextRectArray.StartOffset();
  840. } else {
  841. offset = mTextRectArray.EndOffset() - 1;
  842. }
  843. }
  844. aTextRect = mTextRectArray.GetRect(offset);
  845. return !aTextRect.IsEmpty();
  846. }
  847. bool
  848. ContentCacheInParent::GetUnionTextRects(
  849. uint32_t aOffset,
  850. uint32_t aLength,
  851. bool aRoundToExistingOffset,
  852. LayoutDeviceIntRect& aUnionTextRect) const
  853. {
  854. MOZ_LOG(sContentCacheLog, LogLevel::Info,
  855. ("0x%p GetUnionTextRects(aOffset=%u, "
  856. "aLength=%u, aRoundToExistingOffset=%s), mTextRectArray={ "
  857. "mStart=%u, mRects.Length()=%u }, "
  858. "mSelection={ mAnchor=%u, mFocus=%u }",
  859. this, aOffset, aLength, GetBoolName(aRoundToExistingOffset),
  860. mTextRectArray.mStart, mTextRectArray.mRects.Length(),
  861. mSelection.mAnchor, mSelection.mFocus));
  862. CheckedInt<uint32_t> endOffset =
  863. CheckedInt<uint32_t>(aOffset) + aLength;
  864. if (!endOffset.isValid()) {
  865. return false;
  866. }
  867. if (!mSelection.Collapsed() &&
  868. aOffset == mSelection.StartOffset() && aLength == mSelection.Length()) {
  869. NS_WARNING_ASSERTION(!mSelection.mRect.IsEmpty(), "empty rect");
  870. aUnionTextRect = mSelection.mRect;
  871. return !aUnionTextRect.IsEmpty();
  872. }
  873. if (aLength == 1) {
  874. if (!aOffset) {
  875. NS_WARNING_ASSERTION(!mFirstCharRect.IsEmpty(), "empty rect");
  876. aUnionTextRect = mFirstCharRect;
  877. return !aUnionTextRect.IsEmpty();
  878. }
  879. if (aOffset == mSelection.mAnchor) {
  880. NS_WARNING_ASSERTION(
  881. !mSelection.mAnchorCharRects[eNextCharRect].IsEmpty(), "empty rect");
  882. aUnionTextRect = mSelection.mAnchorCharRects[eNextCharRect];
  883. return !aUnionTextRect.IsEmpty();
  884. }
  885. if (mSelection.mAnchor && aOffset == mSelection.mAnchor - 1) {
  886. NS_WARNING_ASSERTION(
  887. !mSelection.mAnchorCharRects[ePrevCharRect].IsEmpty(), "empty rect");
  888. aUnionTextRect = mSelection.mAnchorCharRects[ePrevCharRect];
  889. return !aUnionTextRect.IsEmpty();
  890. }
  891. if (aOffset == mSelection.mFocus) {
  892. NS_WARNING_ASSERTION(
  893. !mSelection.mFocusCharRects[eNextCharRect].IsEmpty(), "empty rect");
  894. aUnionTextRect = mSelection.mFocusCharRects[eNextCharRect];
  895. return !aUnionTextRect.IsEmpty();
  896. }
  897. if (mSelection.mFocus && aOffset == mSelection.mFocus - 1) {
  898. NS_WARNING_ASSERTION(
  899. !mSelection.mFocusCharRects[ePrevCharRect].IsEmpty(), "empty rect");
  900. aUnionTextRect = mSelection.mFocusCharRects[ePrevCharRect];
  901. return !aUnionTextRect.IsEmpty();
  902. }
  903. }
  904. // Even if some text rects are not cached of the queried range,
  905. // we should return union rect when the first character's rect is cached
  906. // since the first character rect is important and the others are not so
  907. // in most cases.
  908. if (!aOffset && aOffset != mSelection.mAnchor &&
  909. aOffset != mSelection.mFocus && !mTextRectArray.InRange(aOffset)) {
  910. // The first character rect isn't cached.
  911. return false;
  912. }
  913. if ((aRoundToExistingOffset && mTextRectArray.HasRects()) ||
  914. mTextRectArray.IsOverlappingWith(aOffset, aLength)) {
  915. aUnionTextRect =
  916. mTextRectArray.GetUnionRectAsFarAsPossible(aOffset, aLength,
  917. aRoundToExistingOffset);
  918. } else {
  919. aUnionTextRect.SetEmpty();
  920. }
  921. if (!aOffset) {
  922. aUnionTextRect = aUnionTextRect.Union(mFirstCharRect);
  923. }
  924. if (aOffset <= mSelection.mAnchor && mSelection.mAnchor < endOffset.value()) {
  925. aUnionTextRect =
  926. aUnionTextRect.Union(mSelection.mAnchorCharRects[eNextCharRect]);
  927. }
  928. if (mSelection.mAnchor && aOffset <= mSelection.mAnchor - 1 &&
  929. mSelection.mAnchor - 1 < endOffset.value()) {
  930. aUnionTextRect =
  931. aUnionTextRect.Union(mSelection.mAnchorCharRects[ePrevCharRect]);
  932. }
  933. if (aOffset <= mSelection.mFocus && mSelection.mFocus < endOffset.value()) {
  934. aUnionTextRect =
  935. aUnionTextRect.Union(mSelection.mFocusCharRects[eNextCharRect]);
  936. }
  937. if (mSelection.mFocus && aOffset <= mSelection.mFocus - 1 &&
  938. mSelection.mFocus - 1 < endOffset.value()) {
  939. aUnionTextRect =
  940. aUnionTextRect.Union(mSelection.mFocusCharRects[ePrevCharRect]);
  941. }
  942. return !aUnionTextRect.IsEmpty();
  943. }
  944. bool
  945. ContentCacheInParent::GetCaretRect(uint32_t aOffset,
  946. bool aRoundToExistingOffset,
  947. LayoutDeviceIntRect& aCaretRect) const
  948. {
  949. MOZ_LOG(sContentCacheLog, LogLevel::Info,
  950. ("0x%p GetCaretRect(aOffset=%u, "
  951. "aRoundToExistingOffset=%s), "
  952. "mCaret={ mOffset=%u, mRect=%s, IsValid()=%s }, mTextRectArray={ "
  953. "mStart=%u, mRects.Length()=%u }, mSelection={ mAnchor=%u, mFocus=%u, "
  954. "mWritingMode=%s, mAnchorCharRects[eNextCharRect]=%s, "
  955. "mAnchorCharRects[ePrevCharRect]=%s, mFocusCharRects[eNextCharRect]=%s, "
  956. "mFocusCharRects[ePrevCharRect]=%s }, mFirstCharRect=%s",
  957. this, aOffset, GetBoolName(aRoundToExistingOffset),
  958. mCaret.mOffset, GetRectText(mCaret.mRect).get(),
  959. GetBoolName(mCaret.IsValid()), mTextRectArray.mStart,
  960. mTextRectArray.mRects.Length(), mSelection.mAnchor, mSelection.mFocus,
  961. GetWritingModeName(mSelection.mWritingMode).get(),
  962. GetRectText(mSelection.mAnchorCharRects[eNextCharRect]).get(),
  963. GetRectText(mSelection.mAnchorCharRects[ePrevCharRect]).get(),
  964. GetRectText(mSelection.mFocusCharRects[eNextCharRect]).get(),
  965. GetRectText(mSelection.mFocusCharRects[ePrevCharRect]).get(),
  966. GetRectText(mFirstCharRect).get()));
  967. if (mCaret.IsValid() && mCaret.mOffset == aOffset) {
  968. aCaretRect = mCaret.mRect;
  969. return true;
  970. }
  971. // Guess caret rect from the text rect if it's stored.
  972. if (!GetTextRect(aOffset, aRoundToExistingOffset, aCaretRect)) {
  973. // There might be previous character rect in the cache. If so, we can
  974. // guess the caret rect with it.
  975. if (!aOffset ||
  976. !GetTextRect(aOffset - 1, aRoundToExistingOffset, aCaretRect)) {
  977. aCaretRect.SetEmpty();
  978. return false;
  979. }
  980. if (mSelection.mWritingMode.IsVertical()) {
  981. aCaretRect.y = aCaretRect.YMost();
  982. } else {
  983. // XXX bidi-unaware.
  984. aCaretRect.x = aCaretRect.XMost();
  985. }
  986. }
  987. // XXX This is not bidi aware because we don't cache each character's
  988. // direction. However, this is usually used by IME, so, assuming the
  989. // character is in LRT context must not cause any problem.
  990. if (mSelection.mWritingMode.IsVertical()) {
  991. aCaretRect.height = mCaret.IsValid() ? mCaret.mRect.height : 1;
  992. } else {
  993. aCaretRect.width = mCaret.IsValid() ? mCaret.mRect.width : 1;
  994. }
  995. return true;
  996. }
  997. bool
  998. ContentCacheInParent::OnCompositionEvent(const WidgetCompositionEvent& aEvent)
  999. {
  1000. MOZ_LOG(sContentCacheLog, LogLevel::Info,
  1001. ("0x%p OnCompositionEvent(aEvent={ "
  1002. "mMessage=%s, mData=\"%s\" (Length()=%u), mRanges->Length()=%u }), "
  1003. "mPendingEventsNeedingAck=%u, mWidgetHasComposition=%s, "
  1004. "mPendingCompositionCount=%u, mCommitStringByRequest=0x%p",
  1005. this, ToChar(aEvent.mMessage),
  1006. GetEscapedUTF8String(aEvent.mData).get(), aEvent.mData.Length(),
  1007. aEvent.mRanges ? aEvent.mRanges->Length() : 0, mPendingEventsNeedingAck,
  1008. GetBoolName(mWidgetHasComposition), mPendingCompositionCount,
  1009. mCommitStringByRequest));
  1010. // We must be able to simulate the selection because
  1011. // we might not receive selection updates in time
  1012. if (!mWidgetHasComposition) {
  1013. if (aEvent.mWidget && aEvent.mWidget->PluginHasFocus()) {
  1014. // If focus is on plugin, we cannot get selection range
  1015. mCompositionStart = 0;
  1016. } else if (mCompositionStartInChild != UINT32_MAX) {
  1017. // If there is pending composition in the remote process, let's use
  1018. // its start offset temporarily because this stores a lot of information
  1019. // around it and the user must look around there, so, showing some UI
  1020. // around it must make sense.
  1021. mCompositionStart = mCompositionStartInChild;
  1022. } else {
  1023. mCompositionStart = mSelection.StartOffset();
  1024. }
  1025. MOZ_ASSERT(aEvent.mMessage == eCompositionStart);
  1026. MOZ_RELEASE_ASSERT(mPendingCompositionCount < UINT8_MAX);
  1027. mPendingCompositionCount++;
  1028. }
  1029. mWidgetHasComposition = !aEvent.CausesDOMCompositionEndEvent();
  1030. if (!mWidgetHasComposition) {
  1031. mCompositionStart = UINT32_MAX;
  1032. }
  1033. // During REQUEST_TO_COMMIT_COMPOSITION or REQUEST_TO_CANCEL_COMPOSITION,
  1034. // widget usually sends a eCompositionChange and/or eCompositionCommit event
  1035. // to finalize or clear the composition, respectively. In this time,
  1036. // we need to intercept all composition events here and pass the commit
  1037. // string for returning to the remote process as a result of
  1038. // RequestIMEToCommitComposition(). Then, eCommitComposition event will
  1039. // be dispatched with the committed string in the remote process internally.
  1040. if (mCommitStringByRequest) {
  1041. MOZ_ASSERT(aEvent.mMessage == eCompositionChange ||
  1042. aEvent.mMessage == eCompositionCommit);
  1043. *mCommitStringByRequest = aEvent.mData;
  1044. return false;
  1045. }
  1046. mPendingEventsNeedingAck++;
  1047. return true;
  1048. }
  1049. void
  1050. ContentCacheInParent::OnSelectionEvent(
  1051. const WidgetSelectionEvent& aSelectionEvent)
  1052. {
  1053. MOZ_LOG(sContentCacheLog, LogLevel::Info,
  1054. ("0x%p OnSelectionEvent(aEvent={ "
  1055. "mMessage=%s, mOffset=%u, mLength=%u, mReversed=%s, "
  1056. "mExpandToClusterBoundary=%s, mUseNativeLineBreak=%s }), "
  1057. "mPendingEventsNeedingAck=%u, mWidgetHasComposition=%s, "
  1058. "mPendingCompositionCount=%u",
  1059. this, ToChar(aSelectionEvent.mMessage),
  1060. aSelectionEvent.mOffset, aSelectionEvent.mLength,
  1061. GetBoolName(aSelectionEvent.mReversed),
  1062. GetBoolName(aSelectionEvent.mExpandToClusterBoundary),
  1063. GetBoolName(aSelectionEvent.mUseNativeLineBreak), mPendingEventsNeedingAck,
  1064. GetBoolName(mWidgetHasComposition), mPendingCompositionCount));
  1065. mPendingEventsNeedingAck++;
  1066. }
  1067. void
  1068. ContentCacheInParent::OnEventNeedingAckHandled(nsIWidget* aWidget,
  1069. EventMessage aMessage)
  1070. {
  1071. // This is called when the child process receives WidgetCompositionEvent or
  1072. // WidgetSelectionEvent.
  1073. MOZ_LOG(sContentCacheLog, LogLevel::Info,
  1074. ("0x%p OnEventNeedingAckHandled(aWidget=0x%p, "
  1075. "aMessage=%s), mPendingEventsNeedingAck=%u, mPendingCompositionCount=%u",
  1076. this, aWidget, ToChar(aMessage), mPendingEventsNeedingAck));
  1077. if (WidgetCompositionEvent::IsFollowedByCompositionEnd(aMessage)) {
  1078. MOZ_RELEASE_ASSERT(mPendingCompositionCount > 0);
  1079. mPendingCompositionCount--;
  1080. }
  1081. MOZ_RELEASE_ASSERT(mPendingEventsNeedingAck > 0);
  1082. if (--mPendingEventsNeedingAck) {
  1083. return;
  1084. }
  1085. FlushPendingNotifications(aWidget);
  1086. }
  1087. bool
  1088. ContentCacheInParent::RequestIMEToCommitComposition(nsIWidget* aWidget,
  1089. bool aCancel,
  1090. nsAString& aCommittedString)
  1091. {
  1092. MOZ_LOG(sContentCacheLog, LogLevel::Info,
  1093. ("0x%p RequestToCommitComposition(aWidget=%p, "
  1094. "aCancel=%s), mWidgetHasComposition=%s, mCommitStringByRequest=%p",
  1095. this, aWidget, GetBoolName(aCancel), GetBoolName(mWidgetHasComposition),
  1096. mCommitStringByRequest));
  1097. MOZ_ASSERT(!mCommitStringByRequest);
  1098. RefPtr<TextComposition> composition =
  1099. IMEStateManager::GetTextCompositionFor(aWidget);
  1100. if (NS_WARN_IF(!composition)) {
  1101. MOZ_LOG(sContentCacheLog, LogLevel::Warning,
  1102. (" 0x%p RequestToCommitComposition(), "
  1103. "does nothing due to no composition", this));
  1104. return false;
  1105. }
  1106. mCommitStringByRequest = &aCommittedString;
  1107. aWidget->NotifyIME(IMENotification(aCancel ? REQUEST_TO_CANCEL_COMPOSITION :
  1108. REQUEST_TO_COMMIT_COMPOSITION));
  1109. mCommitStringByRequest = nullptr;
  1110. MOZ_LOG(sContentCacheLog, LogLevel::Info,
  1111. (" 0x%p RequestToCommitComposition(), "
  1112. "mWidgetHasComposition=%s, the composition %s committed synchronously",
  1113. this, GetBoolName(mWidgetHasComposition),
  1114. composition->Destroyed() ? "WAS" : "has NOT been"));
  1115. if (!composition->Destroyed()) {
  1116. // When the composition isn't committed synchronously, the remote process's
  1117. // TextComposition instance will synthesize commit events and wait to
  1118. // receive delayed composition events. When TextComposition instances both
  1119. // in this process and the remote process will be destroyed when delayed
  1120. // composition events received. TextComposition instance in the parent
  1121. // process will dispatch following composition events and be destroyed
  1122. // normally. On the other hand, TextComposition instance in the remote
  1123. // process won't dispatch following composition events and will be
  1124. // destroyed by IMEStateManager::DispatchCompositionEvent().
  1125. return false;
  1126. }
  1127. // When the composition is committed synchronously, the commit string will be
  1128. // returned to the remote process. Then, PuppetWidget will dispatch
  1129. // eCompositionCommit event with the returned commit string (i.e., the value
  1130. // is aCommittedString of this method). Finally, TextComposition instance in
  1131. // the remote process will be destroyed by
  1132. // IMEStateManager::DispatchCompositionEvent() at receiving the
  1133. // eCompositionCommit event (Note that TextComposition instance in this
  1134. // process was already destroyed).
  1135. return true;
  1136. }
  1137. void
  1138. ContentCacheInParent::MaybeNotifyIME(nsIWidget* aWidget,
  1139. const IMENotification& aNotification)
  1140. {
  1141. if (!mPendingEventsNeedingAck) {
  1142. IMEStateManager::NotifyIME(aNotification, aWidget, true);
  1143. return;
  1144. }
  1145. switch (aNotification.mMessage) {
  1146. case NOTIFY_IME_OF_SELECTION_CHANGE:
  1147. mPendingSelectionChange.MergeWith(aNotification);
  1148. break;
  1149. case NOTIFY_IME_OF_TEXT_CHANGE:
  1150. mPendingTextChange.MergeWith(aNotification);
  1151. break;
  1152. case NOTIFY_IME_OF_POSITION_CHANGE:
  1153. mPendingLayoutChange.MergeWith(aNotification);
  1154. break;
  1155. case NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED:
  1156. mPendingCompositionUpdate.MergeWith(aNotification);
  1157. break;
  1158. default:
  1159. MOZ_CRASH("Unsupported notification");
  1160. break;
  1161. }
  1162. }
  1163. void
  1164. ContentCacheInParent::FlushPendingNotifications(nsIWidget* aWidget)
  1165. {
  1166. MOZ_ASSERT(!mPendingEventsNeedingAck);
  1167. // New notifications which are notified during flushing pending notifications
  1168. // should be merged again.
  1169. mPendingEventsNeedingAck++;
  1170. nsCOMPtr<nsIWidget> kungFuDeathGrip(aWidget);
  1171. // First, text change notification should be sent because selection change
  1172. // notification notifies IME of current selection range in the latest content.
  1173. // So, IME may need the latest content before that.
  1174. if (mPendingTextChange.HasNotification()) {
  1175. IMENotification notification(mPendingTextChange);
  1176. if (!aWidget->Destroyed()) {
  1177. mPendingTextChange.Clear();
  1178. IMEStateManager::NotifyIME(notification, aWidget, true);
  1179. }
  1180. }
  1181. if (mPendingSelectionChange.HasNotification()) {
  1182. IMENotification notification(mPendingSelectionChange);
  1183. if (!aWidget->Destroyed()) {
  1184. mPendingSelectionChange.Clear();
  1185. IMEStateManager::NotifyIME(notification, aWidget, true);
  1186. }
  1187. }
  1188. // Layout change notification should be notified after selection change
  1189. // notification because IME may want to query position of new caret position.
  1190. if (mPendingLayoutChange.HasNotification()) {
  1191. IMENotification notification(mPendingLayoutChange);
  1192. if (!aWidget->Destroyed()) {
  1193. mPendingLayoutChange.Clear();
  1194. IMEStateManager::NotifyIME(notification, aWidget, true);
  1195. }
  1196. }
  1197. // Finally, send composition update notification because it notifies IME of
  1198. // finishing handling whole sending events.
  1199. if (mPendingCompositionUpdate.HasNotification()) {
  1200. IMENotification notification(mPendingCompositionUpdate);
  1201. if (!aWidget->Destroyed()) {
  1202. mPendingCompositionUpdate.Clear();
  1203. IMEStateManager::NotifyIME(notification, aWidget, true);
  1204. }
  1205. }
  1206. if (!--mPendingEventsNeedingAck && !aWidget->Destroyed() &&
  1207. (mPendingTextChange.HasNotification() ||
  1208. mPendingSelectionChange.HasNotification() ||
  1209. mPendingLayoutChange.HasNotification() ||
  1210. mPendingCompositionUpdate.HasNotification())) {
  1211. FlushPendingNotifications(aWidget);
  1212. }
  1213. }
  1214. /*****************************************************************************
  1215. * mozilla::ContentCache::TextRectArray
  1216. *****************************************************************************/
  1217. LayoutDeviceIntRect
  1218. ContentCache::TextRectArray::GetRect(uint32_t aOffset) const
  1219. {
  1220. LayoutDeviceIntRect rect;
  1221. if (InRange(aOffset)) {
  1222. rect = mRects[aOffset - mStart];
  1223. }
  1224. return rect;
  1225. }
  1226. LayoutDeviceIntRect
  1227. ContentCache::TextRectArray::GetUnionRect(uint32_t aOffset,
  1228. uint32_t aLength) const
  1229. {
  1230. LayoutDeviceIntRect rect;
  1231. if (!InRange(aOffset, aLength)) {
  1232. return rect;
  1233. }
  1234. for (uint32_t i = 0; i < aLength; i++) {
  1235. rect = rect.Union(mRects[aOffset - mStart + i]);
  1236. }
  1237. return rect;
  1238. }
  1239. LayoutDeviceIntRect
  1240. ContentCache::TextRectArray::GetUnionRectAsFarAsPossible(
  1241. uint32_t aOffset,
  1242. uint32_t aLength,
  1243. bool aRoundToExistingOffset) const
  1244. {
  1245. LayoutDeviceIntRect rect;
  1246. if (!HasRects() ||
  1247. (!aRoundToExistingOffset && !IsOverlappingWith(aOffset, aLength))) {
  1248. return rect;
  1249. }
  1250. uint32_t startOffset = std::max(aOffset, mStart);
  1251. if (aRoundToExistingOffset && startOffset >= EndOffset()) {
  1252. startOffset = EndOffset() - 1;
  1253. }
  1254. uint32_t endOffset = std::min(aOffset + aLength, EndOffset());
  1255. if (aRoundToExistingOffset && endOffset < mStart + 1) {
  1256. endOffset = mStart + 1;
  1257. }
  1258. if (NS_WARN_IF(endOffset < startOffset)) {
  1259. return rect;
  1260. }
  1261. for (uint32_t i = 0; i < endOffset - startOffset; i++) {
  1262. rect = rect.Union(mRects[startOffset - mStart + i]);
  1263. }
  1264. return rect;
  1265. }
  1266. } // namespace mozilla