TextComposition.cpp 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854
  1. /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. #include "ContentEventHandler.h"
  6. #include "IMEContentObserver.h"
  7. #include "IMEStateManager.h"
  8. #include "nsContentUtils.h"
  9. #include "nsIContent.h"
  10. #include "nsIEditor.h"
  11. #include "nsIPresShell.h"
  12. #include "nsPresContext.h"
  13. #include "mozilla/AutoRestore.h"
  14. #include "mozilla/EventDispatcher.h"
  15. #include "mozilla/IMEStateManager.h"
  16. #include "mozilla/MiscEvents.h"
  17. #include "mozilla/Preferences.h"
  18. #include "mozilla/TextComposition.h"
  19. #include "mozilla/TextEvents.h"
  20. #include "mozilla/Unused.h"
  21. #include "mozilla/dom/TabParent.h"
  22. #include "nsPluginInstanceOwner.h"
  23. using namespace mozilla::widget;
  24. namespace mozilla {
  25. #define IDEOGRAPHIC_SPACE (NS_LITERAL_STRING(u"\x3000"))
  26. /******************************************************************************
  27. * TextComposition
  28. ******************************************************************************/
  29. bool TextComposition::sHandlingSelectionEvent = false;
  30. TextComposition::TextComposition(nsPresContext* aPresContext,
  31. nsINode* aNode,
  32. TabParent* aTabParent,
  33. WidgetCompositionEvent* aCompositionEvent)
  34. : mPresContext(aPresContext)
  35. , mNode(aNode)
  36. , mTabParent(aTabParent)
  37. , mNativeContext(aCompositionEvent->mNativeIMEContext)
  38. , mCompositionStartOffset(0)
  39. , mTargetClauseOffsetInComposition(0)
  40. , mIsSynthesizedForTests(aCompositionEvent->mFlags.mIsSynthesizedForTests)
  41. , mIsComposing(false)
  42. , mIsEditorHandlingEvent(false)
  43. , mIsRequestingCommit(false)
  44. , mIsRequestingCancel(false)
  45. , mRequestedToCommitOrCancel(false)
  46. , mWasNativeCompositionEndEventDiscarded(false)
  47. , mAllowControlCharacters(
  48. Preferences::GetBool("dom.compositionevent.allow_control_characters",
  49. false))
  50. , mWasCompositionStringEmpty(true)
  51. {
  52. MOZ_ASSERT(aCompositionEvent->mNativeIMEContext.IsValid());
  53. }
  54. void
  55. TextComposition::Destroy()
  56. {
  57. mPresContext = nullptr;
  58. mNode = nullptr;
  59. mTabParent = nullptr;
  60. // TODO: If the editor is still alive and this is held by it, we should tell
  61. // this being destroyed for cleaning up the stuff.
  62. }
  63. bool
  64. TextComposition::IsValidStateForComposition(nsIWidget* aWidget) const
  65. {
  66. return !Destroyed() && aWidget && !aWidget->Destroyed() &&
  67. mPresContext->GetPresShell() &&
  68. !mPresContext->GetPresShell()->IsDestroying();
  69. }
  70. bool
  71. TextComposition::MaybeDispatchCompositionUpdate(
  72. const WidgetCompositionEvent* aCompositionEvent)
  73. {
  74. MOZ_RELEASE_ASSERT(!mTabParent);
  75. if (!IsValidStateForComposition(aCompositionEvent->mWidget)) {
  76. return false;
  77. }
  78. if (mLastData == aCompositionEvent->mData) {
  79. return true;
  80. }
  81. CloneAndDispatchAs(aCompositionEvent, eCompositionUpdate);
  82. return IsValidStateForComposition(aCompositionEvent->mWidget);
  83. }
  84. BaseEventFlags
  85. TextComposition::CloneAndDispatchAs(
  86. const WidgetCompositionEvent* aCompositionEvent,
  87. EventMessage aMessage,
  88. nsEventStatus* aStatus,
  89. EventDispatchingCallback* aCallBack)
  90. {
  91. MOZ_RELEASE_ASSERT(!mTabParent);
  92. MOZ_ASSERT(IsValidStateForComposition(aCompositionEvent->mWidget),
  93. "Should be called only when it's safe to dispatch an event");
  94. WidgetCompositionEvent compositionEvent(aCompositionEvent->IsTrusted(),
  95. aMessage, aCompositionEvent->mWidget);
  96. compositionEvent.mTime = aCompositionEvent->mTime;
  97. compositionEvent.mTimeStamp = aCompositionEvent->mTimeStamp;
  98. compositionEvent.mData = aCompositionEvent->mData;
  99. compositionEvent.mNativeIMEContext = aCompositionEvent->mNativeIMEContext;
  100. compositionEvent.mOriginalMessage = aCompositionEvent->mMessage;
  101. compositionEvent.mFlags.mIsSynthesizedForTests =
  102. aCompositionEvent->mFlags.mIsSynthesizedForTests;
  103. nsEventStatus dummyStatus = nsEventStatus_eConsumeNoDefault;
  104. nsEventStatus* status = aStatus ? aStatus : &dummyStatus;
  105. if (aMessage == eCompositionUpdate) {
  106. mLastData = compositionEvent.mData;
  107. mLastRanges = aCompositionEvent->mRanges;
  108. }
  109. DispatchEvent(&compositionEvent, status, aCallBack, aCompositionEvent);
  110. return compositionEvent.mFlags;
  111. }
  112. void
  113. TextComposition::DispatchEvent(WidgetCompositionEvent* aDispatchEvent,
  114. nsEventStatus* aStatus,
  115. EventDispatchingCallback* aCallBack,
  116. const WidgetCompositionEvent *aOriginalEvent)
  117. {
  118. nsPluginInstanceOwner::GeneratePluginEvent(aOriginalEvent,
  119. aDispatchEvent);
  120. EventDispatcher::Dispatch(mNode, mPresContext,
  121. aDispatchEvent, nullptr, aStatus, aCallBack);
  122. OnCompositionEventDispatched(aDispatchEvent);
  123. }
  124. void
  125. TextComposition::OnCompositionEventDiscarded(
  126. WidgetCompositionEvent* aCompositionEvent)
  127. {
  128. // Note that this method is never called for synthesized events for emulating
  129. // commit or cancel composition.
  130. MOZ_ASSERT(aCompositionEvent->IsTrusted(),
  131. "Shouldn't be called with untrusted event");
  132. if (mTabParent) {
  133. // The composition event should be discarded in the child process too.
  134. Unused << mTabParent->SendCompositionEvent(*aCompositionEvent);
  135. }
  136. // XXX If composition events are discarded, should we dispatch them with
  137. // runnable event? However, even if we do so, it might make native IME
  138. // confused due to async modification. Especially when native IME is
  139. // TSF.
  140. if (!aCompositionEvent->CausesDOMCompositionEndEvent()) {
  141. return;
  142. }
  143. mWasNativeCompositionEndEventDiscarded = true;
  144. }
  145. static inline bool
  146. IsControlChar(uint32_t aCharCode)
  147. {
  148. return aCharCode < ' ' || aCharCode == 0x7F;
  149. }
  150. static size_t
  151. FindFirstControlCharacter(const nsAString& aStr)
  152. {
  153. const char16_t* sourceBegin = aStr.BeginReading();
  154. const char16_t* sourceEnd = aStr.EndReading();
  155. for (const char16_t* source = sourceBegin; source < sourceEnd; ++source) {
  156. if (*source != '\t' && IsControlChar(*source)) {
  157. return source - sourceBegin;
  158. }
  159. }
  160. return -1;
  161. }
  162. static void
  163. RemoveControlCharactersFrom(nsAString& aStr, TextRangeArray* aRanges)
  164. {
  165. size_t firstControlCharOffset = FindFirstControlCharacter(aStr);
  166. if (firstControlCharOffset == (size_t)-1) {
  167. return;
  168. }
  169. nsAutoString copy(aStr);
  170. const char16_t* sourceBegin = copy.BeginReading();
  171. const char16_t* sourceEnd = copy.EndReading();
  172. char16_t* dest = aStr.BeginWriting();
  173. if (NS_WARN_IF(!dest)) {
  174. return;
  175. }
  176. char16_t* curDest = dest + firstControlCharOffset;
  177. size_t i = firstControlCharOffset;
  178. for (const char16_t* source = sourceBegin + firstControlCharOffset;
  179. source < sourceEnd; ++source) {
  180. if (*source == '\t' || !IsControlChar(*source)) {
  181. *curDest = *source;
  182. ++curDest;
  183. ++i;
  184. } else if (aRanges) {
  185. aRanges->RemoveCharacter(i);
  186. }
  187. }
  188. aStr.SetLength(curDest - dest);
  189. }
  190. void
  191. TextComposition::DispatchCompositionEvent(
  192. WidgetCompositionEvent* aCompositionEvent,
  193. nsEventStatus* aStatus,
  194. EventDispatchingCallback* aCallBack,
  195. bool aIsSynthesized)
  196. {
  197. mWasCompositionStringEmpty = mString.IsEmpty();
  198. // If the content is a container of TabParent, composition should be in the
  199. // remote process.
  200. if (mTabParent) {
  201. Unused << mTabParent->SendCompositionEvent(*aCompositionEvent);
  202. aCompositionEvent->StopPropagation();
  203. if (aCompositionEvent->CausesDOMTextEvent()) {
  204. mLastData = aCompositionEvent->mData;
  205. mLastRanges = aCompositionEvent->mRanges;
  206. // Although, the composition event hasn't been actually handled yet,
  207. // emulate an editor to be handling the composition event.
  208. EditorWillHandleCompositionChangeEvent(aCompositionEvent);
  209. EditorDidHandleCompositionChangeEvent();
  210. }
  211. return;
  212. }
  213. if (!mAllowControlCharacters) {
  214. RemoveControlCharactersFrom(aCompositionEvent->mData,
  215. aCompositionEvent->mRanges);
  216. }
  217. if (aCompositionEvent->mMessage == eCompositionCommitAsIs) {
  218. NS_ASSERTION(!aCompositionEvent->mRanges,
  219. "mRanges of eCompositionCommitAsIs should be null");
  220. aCompositionEvent->mRanges = nullptr;
  221. NS_ASSERTION(aCompositionEvent->mData.IsEmpty(),
  222. "mData of eCompositionCommitAsIs should be empty string");
  223. bool removePlaceholderCharacter =
  224. Preferences::GetBool("intl.ime.remove_placeholder_character_at_commit",
  225. false);
  226. if (removePlaceholderCharacter && mLastData == IDEOGRAPHIC_SPACE) {
  227. // If the last data is an ideographic space (FullWidth space), it might be
  228. // a placeholder character of some Chinese IME. So, committing with
  229. // this data might not be expected by users. Let's use empty string.
  230. aCompositionEvent->mData.Truncate();
  231. } else {
  232. aCompositionEvent->mData = mLastData;
  233. }
  234. } else if (aCompositionEvent->mMessage == eCompositionCommit) {
  235. NS_ASSERTION(!aCompositionEvent->mRanges,
  236. "mRanges of eCompositionCommit should be null");
  237. aCompositionEvent->mRanges = nullptr;
  238. }
  239. if (!IsValidStateForComposition(aCompositionEvent->mWidget)) {
  240. *aStatus = nsEventStatus_eConsumeNoDefault;
  241. return;
  242. }
  243. // If this instance has requested to commit or cancel composition but
  244. // is not synthesizing commit event, that means that the IME commits or
  245. // cancels the composition asynchronously. Typically, iBus behaves so.
  246. // Then, synthesized events which were dispatched immediately after
  247. // the request has already committed our editor's composition string and
  248. // told it to web apps. Therefore, we should ignore the delayed events.
  249. if (mRequestedToCommitOrCancel && !aIsSynthesized) {
  250. *aStatus = nsEventStatus_eConsumeNoDefault;
  251. return;
  252. }
  253. // IME may commit composition with empty string for a commit request or
  254. // with non-empty string for a cancel request. We should prevent such
  255. // unexpected result. E.g., web apps may be confused if they implement
  256. // autocomplete which attempts to commit composition forcibly when the user
  257. // selects one of suggestions but composition string is cleared by IME.
  258. // Note that most Chinese IMEs don't expose actual composition string to us.
  259. // They typically tell us an IDEOGRAPHIC SPACE or empty string as composition
  260. // string. Therefore, we should hack it only when:
  261. // 1. committing string is empty string at requesting commit but the last
  262. // data isn't IDEOGRAPHIC SPACE.
  263. // 2. non-empty string is committed at requesting cancel.
  264. if (!aIsSynthesized && (mIsRequestingCommit || mIsRequestingCancel)) {
  265. nsString* committingData = nullptr;
  266. switch (aCompositionEvent->mMessage) {
  267. case eCompositionEnd:
  268. case eCompositionChange:
  269. case eCompositionCommitAsIs:
  270. case eCompositionCommit:
  271. committingData = &aCompositionEvent->mData;
  272. break;
  273. default:
  274. NS_WARNING("Unexpected event comes during committing or "
  275. "canceling composition");
  276. break;
  277. }
  278. if (committingData) {
  279. if (mIsRequestingCommit && committingData->IsEmpty() &&
  280. mLastData != IDEOGRAPHIC_SPACE) {
  281. committingData->Assign(mLastData);
  282. } else if (mIsRequestingCancel && !committingData->IsEmpty()) {
  283. committingData->Truncate();
  284. }
  285. }
  286. }
  287. bool dispatchEvent = true;
  288. bool dispatchDOMTextEvent = aCompositionEvent->CausesDOMTextEvent();
  289. // When mIsComposing is false but the committing string is different from
  290. // the last data (E.g., previous eCompositionChange event made the
  291. // composition string empty or didn't have clause information), we don't
  292. // need to dispatch redundant DOM text event.
  293. if (dispatchDOMTextEvent &&
  294. aCompositionEvent->mMessage != eCompositionChange &&
  295. !mIsComposing && mLastData == aCompositionEvent->mData) {
  296. dispatchEvent = dispatchDOMTextEvent = false;
  297. }
  298. // widget may dispatch redundant eCompositionChange event
  299. // which modifies neither composition string, clauses nor caret
  300. // position. In such case, we shouldn't dispatch DOM events.
  301. if (dispatchDOMTextEvent &&
  302. aCompositionEvent->mMessage == eCompositionChange &&
  303. mLastData == aCompositionEvent->mData &&
  304. mRanges && aCompositionEvent->mRanges &&
  305. mRanges->Equals(*aCompositionEvent->mRanges)) {
  306. dispatchEvent = dispatchDOMTextEvent = false;
  307. }
  308. if (dispatchDOMTextEvent) {
  309. if (!MaybeDispatchCompositionUpdate(aCompositionEvent)) {
  310. return;
  311. }
  312. }
  313. if (dispatchEvent) {
  314. // If the composition event should cause a DOM text event, we should
  315. // overwrite the event message as eCompositionChange because due to
  316. // the limitation of mapping between event messages and DOM event types,
  317. // we cannot map multiple event messages to a DOM event type.
  318. if (dispatchDOMTextEvent &&
  319. aCompositionEvent->mMessage != eCompositionChange) {
  320. aCompositionEvent->mFlags =
  321. CloneAndDispatchAs(aCompositionEvent, eCompositionChange,
  322. aStatus, aCallBack);
  323. } else {
  324. DispatchEvent(aCompositionEvent, aStatus, aCallBack);
  325. }
  326. } else {
  327. *aStatus = nsEventStatus_eConsumeNoDefault;
  328. }
  329. if (!IsValidStateForComposition(aCompositionEvent->mWidget)) {
  330. return;
  331. }
  332. // Emulate editor behavior of compositionchange event (DOM text event) handler
  333. // if no editor handles composition events.
  334. if (dispatchDOMTextEvent && !HasEditor()) {
  335. EditorWillHandleCompositionChangeEvent(aCompositionEvent);
  336. EditorDidHandleCompositionChangeEvent();
  337. }
  338. if (aCompositionEvent->CausesDOMCompositionEndEvent()) {
  339. // Dispatch a compositionend event if it's necessary.
  340. if (aCompositionEvent->mMessage != eCompositionEnd) {
  341. CloneAndDispatchAs(aCompositionEvent, eCompositionEnd);
  342. }
  343. MOZ_ASSERT(!mIsComposing, "Why is the editor still composing?");
  344. MOZ_ASSERT(!HasEditor(), "Why does the editor still keep to hold this?");
  345. }
  346. MaybeNotifyIMEOfCompositionEventHandled(aCompositionEvent);
  347. }
  348. // static
  349. void
  350. TextComposition::HandleSelectionEvent(nsPresContext* aPresContext,
  351. TabParent* aTabParent,
  352. WidgetSelectionEvent* aSelectionEvent)
  353. {
  354. // If the content is a container of TabParent, composition should be in the
  355. // remote process.
  356. if (aTabParent) {
  357. Unused << aTabParent->SendSelectionEvent(*aSelectionEvent);
  358. aSelectionEvent->StopPropagation();
  359. return;
  360. }
  361. ContentEventHandler handler(aPresContext);
  362. AutoRestore<bool> saveHandlingSelectionEvent(sHandlingSelectionEvent);
  363. sHandlingSelectionEvent = true;
  364. // XXX During setting selection, a selection listener may change selection
  365. // again. In such case, sHandlingSelectionEvent doesn't indicate if
  366. // the selection change is caused by a selection event. However, it
  367. // must be non-realistic scenario.
  368. handler.OnSelectionEvent(aSelectionEvent);
  369. }
  370. uint32_t
  371. TextComposition::GetSelectionStartOffset()
  372. {
  373. nsCOMPtr<nsIWidget> widget = mPresContext->GetRootWidget();
  374. WidgetQueryContentEvent selectedTextEvent(true, eQuerySelectedText, widget);
  375. // Due to a bug of widget, mRanges may not be nullptr even though composition
  376. // string is empty. So, we need to check it here for avoiding to return
  377. // odd start offset.
  378. if (!mLastData.IsEmpty() && mRanges && mRanges->HasClauses()) {
  379. selectedTextEvent.InitForQuerySelectedText(
  380. ToSelectionType(mRanges->GetFirstClause()->mRangeType));
  381. } else {
  382. NS_WARNING_ASSERTION(
  383. !mLastData.IsEmpty() || !mRanges || !mRanges->HasClauses(),
  384. "Shouldn't have empty clause info when composition string is empty");
  385. selectedTextEvent.InitForQuerySelectedText(SelectionType::eNormal);
  386. }
  387. // The editor which has this composition is observed by active
  388. // IMEContentObserver, we can use the cache of it.
  389. RefPtr<IMEContentObserver> contentObserver =
  390. IMEStateManager::GetActiveContentObserver();
  391. bool doQuerySelection = true;
  392. if (contentObserver) {
  393. if (contentObserver->IsManaging(this)) {
  394. doQuerySelection = false;
  395. contentObserver->HandleQueryContentEvent(&selectedTextEvent);
  396. }
  397. // If another editor already has focus, we cannot retrieve selection
  398. // in the editor which has this composition...
  399. else if (NS_WARN_IF(contentObserver->GetPresContext() == mPresContext)) {
  400. return 0; // XXX Is this okay?
  401. }
  402. }
  403. // Otherwise, using slow path (i.e., compute every time with
  404. // ContentEventHandler)
  405. if (doQuerySelection) {
  406. ContentEventHandler handler(mPresContext);
  407. handler.HandleQueryContentEvent(&selectedTextEvent);
  408. }
  409. if (NS_WARN_IF(!selectedTextEvent.mSucceeded)) {
  410. return 0; // XXX Is this okay?
  411. }
  412. return selectedTextEvent.mReply.mOffset;
  413. }
  414. void
  415. TextComposition::OnCompositionEventDispatched(
  416. const WidgetCompositionEvent* aCompositionEvent)
  417. {
  418. MOZ_RELEASE_ASSERT(!mTabParent);
  419. if (!IsValidStateForComposition(aCompositionEvent->mWidget)) {
  420. return;
  421. }
  422. // Every composition event may cause changing composition start offset,
  423. // especially when there is no composition string. Therefore, we need to
  424. // update mCompositionStartOffset with the latest offset.
  425. MOZ_ASSERT(aCompositionEvent->mMessage != eCompositionStart ||
  426. mWasCompositionStringEmpty,
  427. "mWasCompositionStringEmpty should be true if the dispatched "
  428. "event is eCompositionStart");
  429. if (mWasCompositionStringEmpty &&
  430. !aCompositionEvent->CausesDOMCompositionEndEvent()) {
  431. // If there was no composition string, current selection start may be the
  432. // offset for inserting composition string.
  433. // Update composition start offset with current selection start.
  434. mCompositionStartOffset = GetSelectionStartOffset();
  435. mTargetClauseOffsetInComposition = 0;
  436. }
  437. if (aCompositionEvent->CausesDOMTextEvent()) {
  438. mTargetClauseOffsetInComposition = aCompositionEvent->TargetClauseOffset();
  439. }
  440. }
  441. void
  442. TextComposition::OnStartOffsetUpdatedInChild(uint32_t aStartOffset)
  443. {
  444. mCompositionStartOffset = aStartOffset;
  445. }
  446. void
  447. TextComposition::MaybeNotifyIMEOfCompositionEventHandled(
  448. const WidgetCompositionEvent* aCompositionEvent)
  449. {
  450. if (aCompositionEvent->mMessage != eCompositionStart &&
  451. !aCompositionEvent->CausesDOMTextEvent()) {
  452. return;
  453. }
  454. RefPtr<IMEContentObserver> contentObserver =
  455. IMEStateManager::GetActiveContentObserver();
  456. // When IMEContentObserver is managing the editor which has this composition,
  457. // composition event handled notification should be sent after the observer
  458. // notifies all pending notifications. Therefore, we should use it.
  459. // XXX If IMEContentObserver suddenly loses focus after here and notifying
  460. // widget of pending notifications, we won't notify widget of composition
  461. // event handled. Although, this is a bug but it should be okay since
  462. // destroying IMEContentObserver notifies IME of blur. So, native IME
  463. // handler can treat it as this notification too.
  464. if (contentObserver && contentObserver->IsManaging(this)) {
  465. contentObserver->MaybeNotifyCompositionEventHandled();
  466. return;
  467. }
  468. // Otherwise, e.g., this composition is in non-active window, we should
  469. // notify widget directly.
  470. NotifyIME(NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED);
  471. }
  472. void
  473. TextComposition::DispatchCompositionEventRunnable(EventMessage aEventMessage,
  474. const nsAString& aData,
  475. bool aIsSynthesizingCommit)
  476. {
  477. nsContentUtils::AddScriptRunner(
  478. new CompositionEventDispatcher(this, mNode, aEventMessage, aData,
  479. aIsSynthesizingCommit));
  480. }
  481. nsresult
  482. TextComposition::RequestToCommit(nsIWidget* aWidget, bool aDiscard)
  483. {
  484. // If this composition is already requested to be committed or canceled,
  485. // we don't need to request it again because even if the first request
  486. // failed, new request won't success, probably. And we shouldn't synthesize
  487. // events for committing or canceling composition twice or more times.
  488. if (mRequestedToCommitOrCancel) {
  489. return NS_OK;
  490. }
  491. RefPtr<TextComposition> kungFuDeathGrip(this);
  492. const nsAutoString lastData(mLastData);
  493. {
  494. AutoRestore<bool> saveRequestingCancel(mIsRequestingCancel);
  495. AutoRestore<bool> saveRequestingCommit(mIsRequestingCommit);
  496. if (aDiscard) {
  497. mIsRequestingCancel = true;
  498. mIsRequestingCommit = false;
  499. } else {
  500. mIsRequestingCancel = false;
  501. mIsRequestingCommit = true;
  502. }
  503. // FYI: CompositionEvents caused by a call of NotifyIME() may be
  504. // discarded by PresShell if it's not safe to dispatch the event.
  505. nsresult rv =
  506. aWidget->NotifyIME(IMENotification(aDiscard ?
  507. REQUEST_TO_CANCEL_COMPOSITION :
  508. REQUEST_TO_COMMIT_COMPOSITION));
  509. if (NS_WARN_IF(NS_FAILED(rv))) {
  510. return rv;
  511. }
  512. }
  513. mRequestedToCommitOrCancel = true;
  514. // If the request is performed synchronously, this must be already destroyed.
  515. if (Destroyed()) {
  516. return NS_OK;
  517. }
  518. // Otherwise, synthesize the commit in content.
  519. nsAutoString data(aDiscard ? EmptyString() : lastData);
  520. if (data == mLastData) {
  521. DispatchCompositionEventRunnable(eCompositionCommitAsIs, EmptyString(),
  522. true);
  523. } else {
  524. DispatchCompositionEventRunnable(eCompositionCommit, data, true);
  525. }
  526. return NS_OK;
  527. }
  528. nsresult
  529. TextComposition::NotifyIME(IMEMessage aMessage)
  530. {
  531. NS_ENSURE_TRUE(mPresContext, NS_ERROR_NOT_AVAILABLE);
  532. return IMEStateManager::NotifyIME(aMessage, mPresContext);
  533. }
  534. void
  535. TextComposition::EditorWillHandleCompositionChangeEvent(
  536. const WidgetCompositionEvent* aCompositionChangeEvent)
  537. {
  538. mIsComposing = aCompositionChangeEvent->IsComposing();
  539. mRanges = aCompositionChangeEvent->mRanges;
  540. mIsEditorHandlingEvent = true;
  541. MOZ_ASSERT(mLastData == aCompositionChangeEvent->mData,
  542. "The text of a compositionchange event must be same as previous data "
  543. "attribute value of the latest compositionupdate event");
  544. }
  545. void
  546. TextComposition::OnEditorDestroyed()
  547. {
  548. MOZ_RELEASE_ASSERT(!mTabParent);
  549. MOZ_ASSERT(!mIsEditorHandlingEvent,
  550. "The editor should have stopped listening events");
  551. nsCOMPtr<nsIWidget> widget = GetWidget();
  552. if (NS_WARN_IF(!widget)) {
  553. // XXX If this could happen, how do we notify IME of destroying the editor?
  554. return;
  555. }
  556. // Try to cancel the composition.
  557. RequestToCommit(widget, true);
  558. }
  559. void
  560. TextComposition::EditorDidHandleCompositionChangeEvent()
  561. {
  562. mString = mLastData;
  563. mIsEditorHandlingEvent = false;
  564. }
  565. void
  566. TextComposition::StartHandlingComposition(nsIEditor* aEditor)
  567. {
  568. MOZ_RELEASE_ASSERT(!mTabParent);
  569. MOZ_ASSERT(!HasEditor(), "There is a handling editor already");
  570. mEditorWeak = do_GetWeakReference(aEditor);
  571. }
  572. void
  573. TextComposition::EndHandlingComposition(nsIEditor* aEditor)
  574. {
  575. MOZ_RELEASE_ASSERT(!mTabParent);
  576. #ifdef DEBUG
  577. nsCOMPtr<nsIEditor> editor = GetEditor();
  578. MOZ_ASSERT(editor == aEditor, "Another editor handled the composition?");
  579. #endif // #ifdef DEBUG
  580. mEditorWeak = nullptr;
  581. }
  582. already_AddRefed<nsIEditor>
  583. TextComposition::GetEditor() const
  584. {
  585. nsCOMPtr<nsIEditor> editor = do_QueryReferent(mEditorWeak);
  586. return editor.forget();
  587. }
  588. bool
  589. TextComposition::HasEditor() const
  590. {
  591. nsCOMPtr<nsIEditor> editor = GetEditor();
  592. return !!editor;
  593. }
  594. /******************************************************************************
  595. * TextComposition::CompositionEventDispatcher
  596. ******************************************************************************/
  597. TextComposition::CompositionEventDispatcher::CompositionEventDispatcher(
  598. TextComposition* aComposition,
  599. nsINode* aEventTarget,
  600. EventMessage aEventMessage,
  601. const nsAString& aData,
  602. bool aIsSynthesizedEvent)
  603. : mTextComposition(aComposition)
  604. , mEventTarget(aEventTarget)
  605. , mData(aData)
  606. , mEventMessage(aEventMessage)
  607. , mIsSynthesizedEvent(aIsSynthesizedEvent)
  608. {
  609. }
  610. NS_IMETHODIMP
  611. TextComposition::CompositionEventDispatcher::Run()
  612. {
  613. // The widget can be different from the widget which has dispatched
  614. // composition events because GetWidget() returns a widget which is proper
  615. // for calling NotifyIME(). However, this must no be problem since both
  616. // widget should share native IME context. Therefore, even if an event
  617. // handler uses the widget for requesting IME to commit or cancel, it works.
  618. nsCOMPtr<nsIWidget> widget(mTextComposition->GetWidget());
  619. if (!mTextComposition->IsValidStateForComposition(widget)) {
  620. return NS_OK; // cannot dispatch any events anymore
  621. }
  622. RefPtr<nsPresContext> presContext = mTextComposition->mPresContext;
  623. nsEventStatus status = nsEventStatus_eIgnore;
  624. switch (mEventMessage) {
  625. case eCompositionStart: {
  626. WidgetCompositionEvent compStart(true, eCompositionStart, widget);
  627. compStart.mNativeIMEContext = mTextComposition->mNativeContext;
  628. WidgetQueryContentEvent selectedText(true, eQuerySelectedText, widget);
  629. ContentEventHandler handler(presContext);
  630. handler.OnQuerySelectedText(&selectedText);
  631. NS_ASSERTION(selectedText.mSucceeded, "Failed to get selected text");
  632. compStart.mData = selectedText.mReply.mString;
  633. compStart.mFlags.mIsSynthesizedForTests =
  634. mTextComposition->IsSynthesizedForTests();
  635. IMEStateManager::DispatchCompositionEvent(mEventTarget, presContext,
  636. &compStart, &status, nullptr,
  637. mIsSynthesizedEvent);
  638. break;
  639. }
  640. case eCompositionChange:
  641. case eCompositionCommitAsIs:
  642. case eCompositionCommit: {
  643. WidgetCompositionEvent compEvent(true, mEventMessage, widget);
  644. compEvent.mNativeIMEContext = mTextComposition->mNativeContext;
  645. if (mEventMessage != eCompositionCommitAsIs) {
  646. compEvent.mData = mData;
  647. }
  648. compEvent.mFlags.mIsSynthesizedForTests =
  649. mTextComposition->IsSynthesizedForTests();
  650. IMEStateManager::DispatchCompositionEvent(mEventTarget, presContext,
  651. &compEvent, &status, nullptr,
  652. mIsSynthesizedEvent);
  653. break;
  654. }
  655. default:
  656. MOZ_CRASH("Unsupported event");
  657. }
  658. return NS_OK;
  659. }
  660. /******************************************************************************
  661. * TextCompositionArray
  662. ******************************************************************************/
  663. TextCompositionArray::index_type
  664. TextCompositionArray::IndexOf(const NativeIMEContext& aNativeIMEContext)
  665. {
  666. if (!aNativeIMEContext.IsValid()) {
  667. return NoIndex;
  668. }
  669. for (index_type i = Length(); i > 0; --i) {
  670. if (ElementAt(i - 1)->GetNativeIMEContext() == aNativeIMEContext) {
  671. return i - 1;
  672. }
  673. }
  674. return NoIndex;
  675. }
  676. TextCompositionArray::index_type
  677. TextCompositionArray::IndexOf(nsIWidget* aWidget)
  678. {
  679. return IndexOf(aWidget->GetNativeIMEContext());
  680. }
  681. TextCompositionArray::index_type
  682. TextCompositionArray::IndexOf(nsPresContext* aPresContext)
  683. {
  684. for (index_type i = Length(); i > 0; --i) {
  685. if (ElementAt(i - 1)->GetPresContext() == aPresContext) {
  686. return i - 1;
  687. }
  688. }
  689. return NoIndex;
  690. }
  691. TextCompositionArray::index_type
  692. TextCompositionArray::IndexOf(nsPresContext* aPresContext,
  693. nsINode* aNode)
  694. {
  695. index_type index = IndexOf(aPresContext);
  696. if (index == NoIndex) {
  697. return NoIndex;
  698. }
  699. nsINode* node = ElementAt(index)->GetEventTargetNode();
  700. return node == aNode ? index : NoIndex;
  701. }
  702. TextComposition*
  703. TextCompositionArray::GetCompositionFor(nsIWidget* aWidget)
  704. {
  705. index_type i = IndexOf(aWidget);
  706. if (i == NoIndex) {
  707. return nullptr;
  708. }
  709. return ElementAt(i);
  710. }
  711. TextComposition*
  712. TextCompositionArray::GetCompositionFor(
  713. const WidgetCompositionEvent* aCompositionEvent)
  714. {
  715. index_type i = IndexOf(aCompositionEvent->mNativeIMEContext);
  716. if (i == NoIndex) {
  717. return nullptr;
  718. }
  719. return ElementAt(i);
  720. }
  721. TextComposition*
  722. TextCompositionArray::GetCompositionFor(nsPresContext* aPresContext)
  723. {
  724. index_type i = IndexOf(aPresContext);
  725. if (i == NoIndex) {
  726. return nullptr;
  727. }
  728. return ElementAt(i);
  729. }
  730. TextComposition*
  731. TextCompositionArray::GetCompositionFor(nsPresContext* aPresContext,
  732. nsINode* aNode)
  733. {
  734. index_type i = IndexOf(aPresContext, aNode);
  735. if (i == NoIndex) {
  736. return nullptr;
  737. }
  738. return ElementAt(i);
  739. }
  740. TextComposition*
  741. TextCompositionArray::GetCompositionInContent(nsPresContext* aPresContext,
  742. nsIContent* aContent)
  743. {
  744. // There should be only one composition per content object.
  745. for (index_type i = Length(); i > 0; --i) {
  746. nsINode* node = ElementAt(i - 1)->GetEventTargetNode();
  747. if (node && nsContentUtils::ContentIsDescendantOf(node, aContent)) {
  748. return ElementAt(i - 1);
  749. }
  750. }
  751. return nullptr;
  752. }
  753. } // namespace mozilla