Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "mozilla/Logging.h"
8 :
9 : #include "ContentEventHandler.h"
10 : #include "IMEContentObserver.h"
11 : #include "mozilla/AsyncEventDispatcher.h"
12 : #include "mozilla/AutoRestore.h"
13 : #include "mozilla/EventStateManager.h"
14 : #include "mozilla/IMEStateManager.h"
15 : #include "mozilla/MouseEvents.h"
16 : #include "mozilla/TextComposition.h"
17 : #include "mozilla/TextEvents.h"
18 : #include "mozilla/dom/Element.h"
19 : #include "nsContentUtils.h"
20 : #include "nsGkAtoms.h"
21 : #include "nsIAtom.h"
22 : #include "nsIContent.h"
23 : #include "nsIDocument.h"
24 : #include "nsIDOMDocument.h"
25 : #include "nsIDOMRange.h"
26 : #include "nsIFrame.h"
27 : #include "nsINode.h"
28 : #include "nsIPresShell.h"
29 : #include "nsISelectionController.h"
30 : #include "nsISelectionPrivate.h"
31 : #include "nsISupports.h"
32 : #include "nsIWidget.h"
33 : #include "nsPresContext.h"
34 : #include "nsRefreshDriver.h"
35 : #include "nsWeakReference.h"
36 : #include "WritingModes.h"
37 :
38 : namespace mozilla {
39 :
40 : using namespace widget;
41 :
42 : LazyLogModule sIMECOLog("IMEContentObserver");
43 :
44 : static const char*
45 0 : ToChar(bool aBool)
46 : {
47 0 : return aBool ? "true" : "false";
48 : }
49 :
50 : class WritingModeToString final : public nsAutoCString
51 : {
52 : public:
53 0 : explicit WritingModeToString(const WritingMode& aWritingMode)
54 0 : {
55 0 : if (!aWritingMode.IsVertical()) {
56 0 : AssignLiteral("Horizontal");
57 0 : return;
58 : }
59 0 : if (aWritingMode.IsVerticalLR()) {
60 0 : AssignLiteral("Vertical (LR)");
61 0 : return;
62 : }
63 0 : AssignLiteral("Vertical (RL)");
64 : }
65 0 : virtual ~WritingModeToString() {}
66 : };
67 :
68 : class SelectionChangeDataToString final : public nsAutoCString
69 : {
70 : public:
71 0 : explicit SelectionChangeDataToString(
72 : const IMENotification::SelectionChangeDataBase& aData)
73 0 : {
74 0 : if (!aData.IsValid()) {
75 0 : AppendLiteral("{ IsValid()=false }");
76 0 : return;
77 : }
78 0 : AppendPrintf("{ mOffset=%u, ", aData.mOffset);
79 0 : if (aData.mString->Length() > 20) {
80 0 : AppendPrintf("mString.Length()=%u, ", aData.mString->Length());
81 : } else {
82 0 : AppendPrintf("mString=\"%s\" (Length()=%u), ",
83 0 : NS_ConvertUTF16toUTF8(*aData.mString).get(),
84 0 : aData.mString->Length());
85 : }
86 0 : AppendPrintf("GetWritingMode()=%s, mReversed=%s, mCausedByComposition=%s, "
87 : "mCausedBySelectionEvent=%s }",
88 0 : WritingModeToString(aData.GetWritingMode()).get(),
89 0 : ToChar(aData.mReversed),
90 0 : ToChar(aData.mCausedByComposition),
91 0 : ToChar(aData.mCausedBySelectionEvent));
92 : }
93 0 : virtual ~SelectionChangeDataToString() {}
94 : };
95 :
96 : class TextChangeDataToString final : public nsAutoCString
97 : {
98 : public:
99 0 : explicit TextChangeDataToString(
100 : const IMENotification::TextChangeDataBase& aData)
101 0 : {
102 0 : if (!aData.IsValid()) {
103 0 : AppendLiteral("{ IsValid()=false }");
104 0 : return;
105 : }
106 0 : AppendPrintf("{ mStartOffset=%u, mRemovedEndOffset=%u, mAddedEndOffset=%u, "
107 : "mCausedOnlyByComposition=%s, "
108 : "mIncludingChangesDuringComposition=%s, "
109 : "mIncludingChangesWithoutComposition=%s }",
110 0 : aData.mStartOffset, aData.mRemovedEndOffset,
111 0 : aData.mAddedEndOffset,
112 0 : ToChar(aData.mCausedOnlyByComposition),
113 0 : ToChar(aData.mIncludingChangesDuringComposition),
114 0 : ToChar(aData.mIncludingChangesWithoutComposition));
115 : }
116 0 : virtual ~TextChangeDataToString() {}
117 : };
118 :
119 : /******************************************************************************
120 : * mozilla::IMEContentObserver
121 : ******************************************************************************/
122 :
123 : NS_IMPL_CYCLE_COLLECTION_CLASS(IMEContentObserver)
124 :
125 : // Note that we don't need to add mFirstAddedNodeContainer nor
126 : // mLastAddedNodeContainer to cycle collection because they are non-null only
127 : // during short time and shouldn't be touched while they are non-null.
128 :
129 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IMEContentObserver)
130 0 : nsAutoScriptBlocker scriptBlocker;
131 :
132 0 : tmp->NotifyIMEOfBlur();
133 0 : tmp->UnregisterObservers();
134 :
135 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mSelection)
136 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mRootContent)
137 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mEditableNode)
138 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocShell)
139 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mEditorBase)
140 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentObserver)
141 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mEndOfAddedTextCache.mContainerNode)
142 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mStartOfRemovingTextRangeCache.mContainerNode)
143 :
144 0 : tmp->mIMENotificationRequests = nullptr;
145 0 : tmp->mESM = nullptr;
146 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
147 :
148 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IMEContentObserver)
149 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWidget)
150 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFocusedWidget)
151 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSelection)
152 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRootContent)
153 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEditableNode)
154 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocShell)
155 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEditorBase)
156 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentObserver)
157 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEndOfAddedTextCache.mContainerNode)
158 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(
159 : mStartOfRemovingTextRangeCache.mContainerNode)
160 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
161 :
162 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IMEContentObserver)
163 0 : NS_INTERFACE_MAP_ENTRY(nsISelectionListener)
164 0 : NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
165 0 : NS_INTERFACE_MAP_ENTRY(nsIReflowObserver)
166 0 : NS_INTERFACE_MAP_ENTRY(nsIScrollObserver)
167 0 : NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
168 0 : NS_INTERFACE_MAP_ENTRY(nsIEditorObserver)
169 0 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsISelectionListener)
170 0 : NS_INTERFACE_MAP_END
171 :
172 0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(IMEContentObserver)
173 0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(IMEContentObserver)
174 :
175 0 : IMEContentObserver::IMEContentObserver()
176 : : mFirstAddedNodeOffset(0)
177 : , mLastAddedNodeOffset(0)
178 : , mESM(nullptr)
179 : , mIMENotificationRequests(nullptr)
180 : , mSuppressNotifications(0)
181 : , mPreCharacterDataChangeLength(-1)
182 : , mSendingNotification(NOTIFY_IME_OF_NOTHING)
183 : , mIsObserving(false)
184 : , mIMEHasFocus(false)
185 : , mNeedsToNotifyIMEOfFocusSet(false)
186 : , mNeedsToNotifyIMEOfTextChange(false)
187 : , mNeedsToNotifyIMEOfSelectionChange(false)
188 : , mNeedsToNotifyIMEOfPositionChange(false)
189 : , mNeedsToNotifyIMEOfCompositionEventHandled(false)
190 0 : , mIsHandlingQueryContentEvent(false)
191 : {
192 : #ifdef DEBUG
193 0 : mTextChangeData.Test();
194 : #endif
195 0 : }
196 :
197 : void
198 0 : IMEContentObserver::Init(nsIWidget* aWidget,
199 : nsPresContext* aPresContext,
200 : nsIContent* aContent,
201 : EditorBase* aEditorBase)
202 : {
203 0 : State state = GetState();
204 0 : if (NS_WARN_IF(state == eState_Observing)) {
205 0 : return; // Nothing to do.
206 : }
207 :
208 0 : bool firstInitialization = state != eState_StoppedObserving;
209 0 : if (!firstInitialization) {
210 : // If this is now trying to initialize with new contents, all observers
211 : // should be registered again for simpler implementation.
212 0 : UnregisterObservers();
213 0 : Clear();
214 : }
215 :
216 0 : mESM = aPresContext->EventStateManager();
217 0 : mESM->OnStartToObserveContent(this);
218 :
219 0 : mWidget = aWidget;
220 0 : mIMENotificationRequests = &mWidget->IMENotificationRequestsRef();
221 :
222 0 : if (aWidget->GetInputContext().mIMEState.mEnabled == IMEState::PLUGIN) {
223 0 : if (!InitWithPlugin(aPresContext, aContent)) {
224 0 : Clear();
225 0 : return;
226 : }
227 : } else {
228 0 : if (!InitWithEditor(aPresContext, aContent, aEditorBase)) {
229 0 : Clear();
230 0 : return;
231 : }
232 : }
233 :
234 0 : if (firstInitialization) {
235 : // Now, try to send NOTIFY_IME_OF_FOCUS to IME via the widget.
236 0 : MaybeNotifyIMEOfFocusSet();
237 : // When this is called first time, IME has not received NOTIFY_IME_OF_FOCUS
238 : // yet since NOTIFY_IME_OF_FOCUS will be sent to widget asynchronously.
239 : // So, we need to do nothing here. After NOTIFY_IME_OF_FOCUS has been
240 : // sent, OnIMEReceivedFocus() will be called and content, selection and/or
241 : // position changes will be observed
242 0 : return;
243 : }
244 :
245 : // When this is called after editor reframing (i.e., the root editable node
246 : // is also recreated), IME has usually received NOTIFY_IME_OF_FOCUS. In this
247 : // case, we need to restart to observe content, selection and/or position
248 : // changes in new root editable node.
249 0 : ObserveEditableNode();
250 :
251 0 : if (!NeedsToNotifyIMEOfSomething()) {
252 0 : return;
253 : }
254 :
255 : // Some change events may wait to notify IME because this was being
256 : // initialized. It is the time to flush them.
257 0 : FlushMergeableNotifications();
258 : }
259 :
260 : void
261 0 : IMEContentObserver::OnIMEReceivedFocus()
262 : {
263 : // While Init() notifies IME of focus, pending layout may be flushed
264 : // because the notification may cause querying content. Then, recursive
265 : // call of Init() with the latest content may occur. In such case, we
266 : // shouldn't keep first initialization which notified IME of focus.
267 0 : if (GetState() != eState_Initializing) {
268 0 : return;
269 : }
270 :
271 : // NOTIFY_IME_OF_FOCUS might cause recreating IMEContentObserver
272 : // instance via IMEStateManager::UpdateIMEState(). So, this
273 : // instance might already have been destroyed, check it.
274 0 : if (!mRootContent) {
275 0 : return;
276 : }
277 :
278 : // Start to observe which is needed by IME when IME actually has focus.
279 0 : ObserveEditableNode();
280 :
281 0 : if (!NeedsToNotifyIMEOfSomething()) {
282 0 : return;
283 : }
284 :
285 : // Some change events may wait to notify IME because this was being
286 : // initialized. It is the time to flush them.
287 0 : FlushMergeableNotifications();
288 : }
289 :
290 : bool
291 0 : IMEContentObserver::InitWithEditor(nsPresContext* aPresContext,
292 : nsIContent* aContent,
293 : EditorBase* aEditorBase)
294 : {
295 0 : MOZ_ASSERT(aEditorBase);
296 :
297 : mEditableNode =
298 0 : IMEStateManager::GetRootEditableNode(aPresContext, aContent);
299 0 : if (NS_WARN_IF(!mEditableNode)) {
300 0 : return false;
301 : }
302 :
303 0 : mEditorBase = aEditorBase;
304 0 : if (NS_WARN_IF(!mEditorBase)) {
305 0 : return false;
306 : }
307 :
308 0 : nsIPresShell* presShell = aPresContext->PresShell();
309 :
310 : // get selection and root content
311 0 : nsCOMPtr<nsISelectionController> selCon;
312 0 : if (mEditableNode->IsNodeOfType(nsINode::eCONTENT)) {
313 : nsIFrame* frame =
314 0 : static_cast<nsIContent*>(mEditableNode.get())->GetPrimaryFrame();
315 0 : if (NS_WARN_IF(!frame)) {
316 0 : return false;
317 : }
318 :
319 0 : frame->GetSelectionController(aPresContext,
320 0 : getter_AddRefs(selCon));
321 : } else {
322 : // mEditableNode is a document
323 0 : selCon = do_QueryInterface(presShell);
324 : }
325 :
326 0 : if (NS_WARN_IF(!selCon)) {
327 0 : return false;
328 : }
329 :
330 0 : selCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
331 0 : getter_AddRefs(mSelection));
332 0 : if (NS_WARN_IF(!mSelection)) {
333 0 : return false;
334 : }
335 :
336 0 : nsCOMPtr<nsIDOMRange> selDomRange;
337 0 : if (NS_SUCCEEDED(mSelection->GetRangeAt(0, getter_AddRefs(selDomRange)))) {
338 0 : nsRange* selRange = static_cast<nsRange*>(selDomRange.get());
339 0 : if (NS_WARN_IF(!selRange) || NS_WARN_IF(!selRange->GetStartContainer())) {
340 0 : return false;
341 : }
342 :
343 : mRootContent = selRange->GetStartContainer()->
344 0 : GetSelectionRootContent(presShell);
345 : } else {
346 0 : mRootContent = mEditableNode->GetSelectionRootContent(presShell);
347 : }
348 0 : if (!mRootContent && mEditableNode->IsNodeOfType(nsINode::eDOCUMENT)) {
349 : // The document node is editable, but there are no contents, this document
350 : // is not editable.
351 0 : return false;
352 : }
353 :
354 0 : if (NS_WARN_IF(!mRootContent)) {
355 0 : return false;
356 : }
357 :
358 0 : mDocShell = aPresContext->GetDocShell();
359 0 : if (NS_WARN_IF(!mDocShell)) {
360 0 : return false;
361 : }
362 :
363 0 : mDocumentObserver = new DocumentObserver(*this);
364 :
365 0 : MOZ_ASSERT(!WasInitializedWithPlugin());
366 :
367 0 : return true;
368 : }
369 :
370 : bool
371 0 : IMEContentObserver::InitWithPlugin(nsPresContext* aPresContext,
372 : nsIContent* aContent)
373 : {
374 0 : if (NS_WARN_IF(!aContent) ||
375 0 : NS_WARN_IF(aContent->GetDesiredIMEState().mEnabled != IMEState::PLUGIN)) {
376 0 : return false;
377 : }
378 0 : nsIFrame* frame = aContent->GetPrimaryFrame();
379 0 : if (NS_WARN_IF(!frame)) {
380 0 : return false;
381 : }
382 0 : nsCOMPtr<nsISelectionController> selCon;
383 0 : frame->GetSelectionController(aPresContext, getter_AddRefs(selCon));
384 0 : if (NS_WARN_IF(!selCon)) {
385 0 : return false;
386 : }
387 0 : selCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
388 0 : getter_AddRefs(mSelection));
389 0 : if (NS_WARN_IF(!mSelection)) {
390 0 : return false;
391 : }
392 :
393 0 : mEditorBase = nullptr;
394 0 : mEditableNode = aContent;
395 0 : mRootContent = aContent;
396 : // Should be safe to clear mDocumentObserver here even though it *might*
397 : // grab this instance because this is called by Init() and the callers of
398 : // it and MaybeReinitialize() grabs this instance with local RefPtr.
399 : // So, this won't cause refcount of this instance become 0.
400 0 : mDocumentObserver = nullptr;
401 :
402 0 : mDocShell = aPresContext->GetDocShell();
403 0 : if (NS_WARN_IF(!mDocShell)) {
404 0 : return false;
405 : }
406 :
407 0 : MOZ_ASSERT(WasInitializedWithPlugin());
408 :
409 0 : return true;
410 : }
411 :
412 : bool
413 0 : IMEContentObserver::WasInitializedWithPlugin() const
414 : {
415 0 : return mDocShell && !mEditorBase;
416 : }
417 :
418 : void
419 0 : IMEContentObserver::Clear()
420 : {
421 0 : mEditorBase = nullptr;
422 0 : mSelection = nullptr;
423 0 : mEditableNode = nullptr;
424 0 : mRootContent = nullptr;
425 0 : mDocShell = nullptr;
426 : // Should be safe to clear mDocumentObserver here even though it grabs
427 : // this instance in most cases because this is called by Init() or Destroy().
428 : // The callers of Init() grab this instance with local RefPtr.
429 : // The caller of Destroy() also grabs this instance with local RefPtr.
430 : // So, this won't cause refcount of this instance become 0.
431 0 : mDocumentObserver = nullptr;
432 0 : }
433 :
434 : void
435 0 : IMEContentObserver::ObserveEditableNode()
436 : {
437 0 : MOZ_RELEASE_ASSERT(mSelection);
438 0 : MOZ_RELEASE_ASSERT(mRootContent);
439 0 : MOZ_RELEASE_ASSERT(GetState() != eState_Observing);
440 :
441 : // If this is called before sending NOTIFY_IME_OF_FOCUS (it's possible when
442 : // the editor is reframed before sending NOTIFY_IME_OF_FOCUS asynchronously),
443 : // the notification requests of mWidget may be different from after the widget
444 : // receives NOTIFY_IME_OF_FOCUS. So, this should be called again by
445 : // OnIMEReceivedFocus() which is called after sending NOTIFY_IME_OF_FOCUS.
446 0 : if (!mIMEHasFocus) {
447 0 : MOZ_ASSERT(!mWidget || mNeedsToNotifyIMEOfFocusSet ||
448 : mSendingNotification == NOTIFY_IME_OF_FOCUS,
449 : "Wow, OnIMEReceivedFocus() won't be called?");
450 0 : return;
451 : }
452 :
453 0 : mIsObserving = true;
454 0 : if (mEditorBase) {
455 0 : mEditorBase->AddEditorObserver(this);
456 : }
457 :
458 0 : if (!WasInitializedWithPlugin()) {
459 : // Add selection change listener only when this starts to observe
460 : // non-plugin content since we cannot detect selection changes in
461 : // plugins.
462 0 : nsCOMPtr<nsISelectionPrivate> selPrivate(do_QueryInterface(mSelection));
463 0 : NS_ENSURE_TRUE_VOID(selPrivate);
464 0 : nsresult rv = selPrivate->AddSelectionListener(this);
465 0 : NS_ENSURE_SUCCESS_VOID(rv);
466 : // Add text change observer only when this starts to observe
467 : // non-plugin content since we cannot detect text changes in
468 : // plugins.
469 0 : mRootContent->AddMutationObserver(this);
470 : // If it's in a document (should be so), we can use document observer to
471 : // reduce redundant computation of text change offsets.
472 0 : nsIDocument* doc = mRootContent->GetComposedDoc();
473 0 : if (doc) {
474 0 : RefPtr<DocumentObserver> documentObserver = mDocumentObserver;
475 0 : documentObserver->Observe(doc);
476 : }
477 : }
478 :
479 0 : if (mDocShell) {
480 : // Add scroll position listener and reflow observer to detect position
481 : // and size changes
482 0 : mDocShell->AddWeakScrollObserver(this);
483 0 : mDocShell->AddWeakReflowObserver(this);
484 : }
485 : }
486 :
487 : void
488 0 : IMEContentObserver::NotifyIMEOfBlur()
489 : {
490 : // Prevent any notifications to be sent IME.
491 0 : nsCOMPtr<nsIWidget> widget;
492 0 : mWidget.swap(widget);
493 0 : mIMENotificationRequests = nullptr;
494 :
495 : // If we hasn't been set focus, we shouldn't send blur notification to IME.
496 0 : if (!mIMEHasFocus) {
497 0 : return;
498 : }
499 :
500 : // mWidget must have been non-nullptr if IME has focus.
501 0 : MOZ_RELEASE_ASSERT(widget);
502 :
503 0 : RefPtr<IMEContentObserver> kungFuDeathGrip(this);
504 :
505 0 : MOZ_LOG(sIMECOLog, LogLevel::Info,
506 : ("0x%p IMEContentObserver::NotifyIMEOfBlur(), "
507 : "sending NOTIFY_IME_OF_BLUR", this));
508 :
509 : // For now, we need to send blur notification in any condition because
510 : // we don't have any simple ways to send blur notification asynchronously.
511 : // After this call, Destroy() or Unlink() will stop observing the content
512 : // and forget everything. Therefore, if it's not safe to send notification
513 : // when script blocker is unlocked, we cannot send blur notification after
514 : // that and before next focus notification.
515 : // Anyway, as far as we know, IME doesn't try to query content when it loses
516 : // focus. So, this may not cause any problem.
517 0 : mIMEHasFocus = false;
518 0 : IMEStateManager::NotifyIME(IMENotification(NOTIFY_IME_OF_BLUR), widget);
519 :
520 0 : MOZ_LOG(sIMECOLog, LogLevel::Debug,
521 : ("0x%p IMEContentObserver::NotifyIMEOfBlur(), "
522 : "sent NOTIFY_IME_OF_BLUR", this));
523 : }
524 :
525 : void
526 0 : IMEContentObserver::UnregisterObservers()
527 : {
528 0 : if (!mIsObserving) {
529 0 : return;
530 : }
531 0 : mIsObserving = false;
532 :
533 0 : if (mEditorBase) {
534 0 : mEditorBase->RemoveEditorObserver(this);
535 : }
536 :
537 0 : if (mSelection) {
538 0 : nsCOMPtr<nsISelectionPrivate> selPrivate(do_QueryInterface(mSelection));
539 0 : if (selPrivate) {
540 0 : selPrivate->RemoveSelectionListener(this);
541 : }
542 0 : mSelectionData.Clear();
543 0 : mFocusedWidget = nullptr;
544 : }
545 :
546 0 : if (mRootContent) {
547 0 : mRootContent->RemoveMutationObserver(this);
548 : }
549 :
550 0 : if (mDocumentObserver) {
551 0 : RefPtr<DocumentObserver> documentObserver = mDocumentObserver;
552 0 : documentObserver->StopObserving();
553 : }
554 :
555 0 : if (mDocShell) {
556 0 : mDocShell->RemoveWeakScrollObserver(this);
557 0 : mDocShell->RemoveWeakReflowObserver(this);
558 : }
559 : }
560 :
561 : nsPresContext*
562 0 : IMEContentObserver::GetPresContext() const
563 : {
564 0 : return mESM ? mESM->GetPresContext() : nullptr;
565 : }
566 :
567 : void
568 0 : IMEContentObserver::Destroy()
569 : {
570 : // WARNING: When you change this method, you have to check Unlink() too.
571 :
572 0 : NotifyIMEOfBlur();
573 0 : UnregisterObservers();
574 0 : Clear();
575 :
576 0 : mWidget = nullptr;
577 0 : mIMENotificationRequests = nullptr;
578 :
579 0 : if (mESM) {
580 0 : mESM->OnStopObservingContent(this);
581 0 : mESM = nullptr;
582 : }
583 0 : }
584 :
585 : bool
586 0 : IMEContentObserver::Destroyed() const
587 : {
588 0 : return !mWidget;
589 : }
590 :
591 : void
592 0 : IMEContentObserver::DisconnectFromEventStateManager()
593 : {
594 0 : mESM = nullptr;
595 0 : }
596 :
597 : bool
598 0 : IMEContentObserver::MaybeReinitialize(nsIWidget* aWidget,
599 : nsPresContext* aPresContext,
600 : nsIContent* aContent,
601 : EditorBase* aEditorBase)
602 : {
603 0 : if (!IsObservingContent(aPresContext, aContent)) {
604 0 : return false;
605 : }
606 :
607 0 : if (GetState() == eState_StoppedObserving) {
608 0 : Init(aWidget, aPresContext, aContent, aEditorBase);
609 : }
610 0 : return IsManaging(aPresContext, aContent);
611 : }
612 :
613 : bool
614 0 : IMEContentObserver::IsManaging(nsPresContext* aPresContext,
615 : nsIContent* aContent) const
616 : {
617 0 : return GetState() == eState_Observing &&
618 0 : IsObservingContent(aPresContext, aContent);
619 : }
620 :
621 : bool
622 0 : IMEContentObserver::IsManaging(const TextComposition* aComposition) const
623 : {
624 0 : if (GetState() != eState_Observing) {
625 0 : return false;
626 : }
627 0 : nsPresContext* presContext = aComposition->GetPresContext();
628 0 : if (NS_WARN_IF(!presContext)) {
629 0 : return false;
630 : }
631 0 : if (presContext != GetPresContext()) {
632 0 : return false; // observing different document
633 : }
634 0 : nsINode* targetNode = aComposition->GetEventTargetNode();
635 : nsIContent* targetContent =
636 0 : targetNode && targetNode->IsContent() ? targetNode->AsContent() : nullptr;
637 0 : return IsObservingContent(presContext, targetContent);
638 : }
639 :
640 : IMEContentObserver::State
641 0 : IMEContentObserver::GetState() const
642 : {
643 0 : if (!mSelection || !mRootContent || !mEditableNode) {
644 0 : return eState_NotObserving; // failed to initialize or finalized.
645 : }
646 0 : if (!mRootContent->IsInComposedDoc()) {
647 : // the focused editor has already been reframed.
648 0 : return eState_StoppedObserving;
649 : }
650 0 : return mIsObserving ? eState_Observing : eState_Initializing;
651 : }
652 :
653 : bool
654 0 : IMEContentObserver::IsObservingContent(nsPresContext* aPresContext,
655 : nsIContent* aContent) const
656 : {
657 0 : return IsInitializedWithPlugin() ?
658 0 : mRootContent == aContent && mRootContent != nullptr :
659 0 : mEditableNode == IMEStateManager::GetRootEditableNode(aPresContext,
660 0 : aContent);
661 : }
662 :
663 : bool
664 0 : IMEContentObserver::IsEditorHandlingEventForComposition() const
665 : {
666 0 : if (!mWidget) {
667 0 : return false;
668 : }
669 : RefPtr<TextComposition> composition =
670 0 : IMEStateManager::GetTextCompositionFor(mWidget);
671 0 : if (!composition) {
672 0 : return false;
673 : }
674 0 : return composition->IsEditorHandlingEvent();
675 : }
676 :
677 : bool
678 0 : IMEContentObserver::IsEditorComposing() const
679 : {
680 : // Note that don't use TextComposition here. The important thing is,
681 : // whether the editor already started to handle composition because
682 : // web contents can change selection, text content and/or something from
683 : // compositionstart event listener which is run before EditorBase handles it.
684 0 : if (NS_WARN_IF(!mEditorBase)) {
685 0 : return false;
686 : }
687 0 : return mEditorBase->IsIMEComposing();
688 : }
689 :
690 : nsresult
691 0 : IMEContentObserver::GetSelectionAndRoot(nsISelection** aSelection,
692 : nsIContent** aRootContent) const
693 : {
694 0 : if (!mEditableNode || !mSelection) {
695 0 : return NS_ERROR_NOT_AVAILABLE;
696 : }
697 :
698 0 : NS_ASSERTION(mSelection && mRootContent, "uninitialized content observer");
699 0 : NS_ADDREF(*aSelection = mSelection);
700 0 : NS_ADDREF(*aRootContent = mRootContent);
701 0 : return NS_OK;
702 : }
703 :
704 : nsresult
705 0 : IMEContentObserver::NotifySelectionChanged(nsIDOMDocument* aDOMDocument,
706 : nsISelection* aSelection,
707 : int16_t aReason)
708 : {
709 0 : int32_t count = 0;
710 0 : nsresult rv = aSelection->GetRangeCount(&count);
711 0 : NS_ENSURE_SUCCESS(rv, rv);
712 0 : if (count > 0 && mWidget) {
713 0 : bool causedByComposition = IsEditorHandlingEventForComposition();
714 0 : bool causedBySelectionEvent = TextComposition::IsHandlingSelectionEvent();
715 0 : bool duringComposition = IsEditorComposing();
716 0 : MaybeNotifyIMEOfSelectionChange(causedByComposition,
717 : causedBySelectionEvent,
718 0 : duringComposition);
719 : }
720 0 : return NS_OK;
721 : }
722 :
723 : void
724 0 : IMEContentObserver::ScrollPositionChanged()
725 : {
726 0 : if (!NeedsPositionChangeNotification()) {
727 0 : return;
728 : }
729 :
730 0 : MaybeNotifyIMEOfPositionChange();
731 : }
732 :
733 : NS_IMETHODIMP
734 0 : IMEContentObserver::Reflow(DOMHighResTimeStamp aStart,
735 : DOMHighResTimeStamp aEnd)
736 : {
737 0 : if (!NeedsPositionChangeNotification()) {
738 0 : return NS_OK;
739 : }
740 :
741 0 : MaybeNotifyIMEOfPositionChange();
742 0 : return NS_OK;
743 : }
744 :
745 : NS_IMETHODIMP
746 0 : IMEContentObserver::ReflowInterruptible(DOMHighResTimeStamp aStart,
747 : DOMHighResTimeStamp aEnd)
748 : {
749 0 : if (!NeedsPositionChangeNotification()) {
750 0 : return NS_OK;
751 : }
752 :
753 0 : MaybeNotifyIMEOfPositionChange();
754 0 : return NS_OK;
755 : }
756 :
757 : nsresult
758 0 : IMEContentObserver::HandleQueryContentEvent(WidgetQueryContentEvent* aEvent)
759 : {
760 : // If the instance has normal selection cache and the query event queries
761 : // normal selection's range, it should use the cached selection which was
762 : // sent to the widget. However, if this instance has already received new
763 : // selection change notification but hasn't updated the cache yet (i.e.,
764 : // not sending selection change notification to IME, don't use the cached
765 : // value. Note that don't update selection cache here since if you update
766 : // selection cache here, IMENotificationSender won't notify IME of selection
767 : // change because it looks like that the selection isn't actually changed.
768 : bool isSelectionCacheAvailable =
769 0 : aEvent->mUseNativeLineBreak && mSelectionData.IsValid() &&
770 0 : !mNeedsToNotifyIMEOfSelectionChange;
771 0 : if (isSelectionCacheAvailable &&
772 0 : aEvent->mMessage == eQuerySelectedText &&
773 0 : aEvent->mInput.mSelectionType == SelectionType::eNormal) {
774 0 : aEvent->mReply.mContentsRoot = mRootContent;
775 0 : aEvent->mReply.mHasSelection = !mSelectionData.IsCollapsed();
776 0 : aEvent->mReply.mOffset = mSelectionData.mOffset;
777 0 : aEvent->mReply.mString = mSelectionData.String();
778 0 : aEvent->mReply.mWritingMode = mSelectionData.GetWritingMode();
779 0 : aEvent->mReply.mReversed = mSelectionData.mReversed;
780 0 : aEvent->mSucceeded = true;
781 0 : MOZ_LOG(sIMECOLog, LogLevel::Debug,
782 : ("0x%p IMEContentObserver::HandleQueryContentEvent(aEvent={ "
783 : "mMessage=%s })", this, ToChar(aEvent->mMessage)));
784 0 : return NS_OK;
785 : }
786 :
787 0 : MOZ_LOG(sIMECOLog, LogLevel::Debug,
788 : ("0x%p IMEContentObserver::HandleQueryContentEvent(aEvent={ "
789 : "mMessage=%s })", this, ToChar(aEvent->mMessage)));
790 :
791 : // If we can make the event's input offset absolute with TextComposition or
792 : // mSelection, we should set it here for reducing the cost of computing
793 : // selection start offset. If ContentEventHandler receives a
794 : // WidgetQueryContentEvent whose input offset is relative to insertion point,
795 : // it computes current selection start offset (this may be expensive) and
796 : // make the offset absolute value itself.
797 : // Note that calling MakeOffsetAbsolute() makes the event a query event with
798 : // absolute offset. So, ContentEventHandler doesn't pay any additional cost
799 : // after calling MakeOffsetAbsolute() here.
800 0 : if (aEvent->mInput.mRelativeToInsertionPoint &&
801 0 : aEvent->mInput.IsValidEventMessage(aEvent->mMessage)) {
802 : RefPtr<TextComposition> composition =
803 0 : IMEStateManager::GetTextCompositionFor(aEvent->mWidget);
804 0 : if (composition) {
805 0 : uint32_t compositionStart = composition->NativeOffsetOfStartComposition();
806 0 : if (NS_WARN_IF(!aEvent->mInput.MakeOffsetAbsolute(compositionStart))) {
807 0 : return NS_ERROR_FAILURE;
808 : }
809 0 : } else if (isSelectionCacheAvailable) {
810 0 : uint32_t selectionStart = mSelectionData.mOffset;
811 0 : if (NS_WARN_IF(!aEvent->mInput.MakeOffsetAbsolute(selectionStart))) {
812 0 : return NS_ERROR_FAILURE;
813 : }
814 : }
815 : }
816 :
817 0 : AutoRestore<bool> handling(mIsHandlingQueryContentEvent);
818 0 : mIsHandlingQueryContentEvent = true;
819 0 : ContentEventHandler handler(GetPresContext());
820 0 : nsresult rv = handler.HandleQueryContentEvent(aEvent);
821 0 : if (NS_WARN_IF(Destroyed())) {
822 : // If this has already destroyed during querying the content, the query
823 : // is outdated even if it's succeeded. So, make the query fail.
824 0 : aEvent->mSucceeded = false;
825 0 : MOZ_LOG(sIMECOLog, LogLevel::Warning,
826 : ("0x%p IMEContentObserver::HandleQueryContentEvent(), WARNING, "
827 : "IMEContentObserver has been destroyed during the query, "
828 : "making the query fail", this));
829 0 : return rv;
830 : }
831 :
832 0 : if (!IsInitializedWithPlugin() &&
833 0 : NS_WARN_IF(aEvent->mReply.mContentsRoot != mRootContent)) {
834 : // Focus has changed unexpectedly, so make the query fail.
835 0 : aEvent->mSucceeded = false;
836 : }
837 0 : return rv;
838 : }
839 :
840 : bool
841 0 : IMEContentObserver::OnMouseButtonEvent(nsPresContext* aPresContext,
842 : WidgetMouseEvent* aMouseEvent)
843 : {
844 0 : if (!mIMENotificationRequests ||
845 0 : !mIMENotificationRequests->WantMouseButtonEventOnChar()) {
846 0 : return false;
847 : }
848 0 : if (!aMouseEvent->IsTrusted() ||
849 0 : aMouseEvent->DefaultPrevented() ||
850 0 : !aMouseEvent->mWidget) {
851 0 : return false;
852 : }
853 : // Now, we need to notify only mouse down and mouse up event.
854 0 : switch (aMouseEvent->mMessage) {
855 : case eMouseUp:
856 : case eMouseDown:
857 0 : break;
858 : default:
859 0 : return false;
860 : }
861 0 : if (NS_WARN_IF(!mWidget) || NS_WARN_IF(mWidget->Destroyed())) {
862 0 : return false;
863 : }
864 :
865 0 : RefPtr<IMEContentObserver> kungFuDeathGrip(this);
866 :
867 : WidgetQueryContentEvent charAtPt(true, eQueryCharacterAtPoint,
868 0 : aMouseEvent->mWidget);
869 0 : charAtPt.mRefPoint = aMouseEvent->mRefPoint;
870 0 : ContentEventHandler handler(aPresContext);
871 0 : handler.OnQueryCharacterAtPoint(&charAtPt);
872 0 : if (NS_WARN_IF(!charAtPt.mSucceeded) ||
873 0 : charAtPt.mReply.mOffset == WidgetQueryContentEvent::NOT_FOUND) {
874 0 : return false;
875 : }
876 :
877 : // The widget might be destroyed during querying the content since it
878 : // causes flushing layout.
879 0 : if (!mWidget || NS_WARN_IF(mWidget->Destroyed())) {
880 0 : return false;
881 : }
882 :
883 : // The result character rect is relative to the top level widget.
884 : // We should notify it with offset in the widget.
885 0 : nsIWidget* topLevelWidget = mWidget->GetTopLevelWidget();
886 0 : if (topLevelWidget && topLevelWidget != mWidget) {
887 : charAtPt.mReply.mRect.MoveBy(
888 0 : topLevelWidget->WidgetToScreenOffset() -
889 0 : mWidget->WidgetToScreenOffset());
890 : }
891 : // The refPt is relative to its widget.
892 : // We should notify it with offset in the widget.
893 0 : if (aMouseEvent->mWidget != mWidget) {
894 0 : charAtPt.mRefPoint += aMouseEvent->mWidget->WidgetToScreenOffset() -
895 0 : mWidget->WidgetToScreenOffset();
896 : }
897 :
898 0 : IMENotification notification(NOTIFY_IME_OF_MOUSE_BUTTON_EVENT);
899 0 : notification.mMouseButtonEventData.mEventMessage = aMouseEvent->mMessage;
900 0 : notification.mMouseButtonEventData.mOffset = charAtPt.mReply.mOffset;
901 : notification.mMouseButtonEventData.mCursorPos.Set(
902 0 : charAtPt.mRefPoint.ToUnknownPoint());
903 : notification.mMouseButtonEventData.mCharRect.Set(
904 0 : charAtPt.mReply.mRect.ToUnknownRect());
905 0 : notification.mMouseButtonEventData.mButton = aMouseEvent->button;
906 0 : notification.mMouseButtonEventData.mButtons = aMouseEvent->buttons;
907 0 : notification.mMouseButtonEventData.mModifiers = aMouseEvent->mModifiers;
908 :
909 0 : nsresult rv = IMEStateManager::NotifyIME(notification, mWidget);
910 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
911 0 : return false;
912 : }
913 :
914 0 : bool consumed = (rv == NS_SUCCESS_EVENT_CONSUMED);
915 0 : if (consumed) {
916 0 : aMouseEvent->PreventDefault();
917 : }
918 0 : return consumed;
919 : }
920 :
921 : void
922 0 : IMEContentObserver::CharacterDataWillChange(nsIDocument* aDocument,
923 : nsIContent* aContent,
924 : CharacterDataChangeInfo* aInfo)
925 : {
926 0 : NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT),
927 : "character data changed for non-text node");
928 0 : MOZ_ASSERT(mPreCharacterDataChangeLength < 0,
929 : "CharacterDataChanged() should've reset "
930 : "mPreCharacterDataChangeLength");
931 :
932 0 : if (!NeedsTextChangeNotification()) {
933 0 : return;
934 : }
935 :
936 0 : mEndOfAddedTextCache.Clear();
937 0 : mStartOfRemovingTextRangeCache.Clear();
938 :
939 : // Although we don't assume this change occurs while this is storing
940 : // the range of added consecutive nodes, if it actually happens, we need to
941 : // flush them since this change may occur before or in the range. So, it's
942 : // safe to flush pending computation of mTextChangeData before handling this.
943 0 : MaybeNotifyIMEOfAddedTextDuringDocumentChange();
944 :
945 0 : mPreCharacterDataChangeLength =
946 0 : ContentEventHandler::GetNativeTextLength(aContent, aInfo->mChangeStart,
947 : aInfo->mChangeEnd);
948 0 : MOZ_ASSERT(mPreCharacterDataChangeLength >=
949 : aInfo->mChangeEnd - aInfo->mChangeStart,
950 : "The computed length must be same as or larger than XP length");
951 : }
952 :
953 : void
954 0 : IMEContentObserver::CharacterDataChanged(nsIDocument* aDocument,
955 : nsIContent* aContent,
956 : CharacterDataChangeInfo* aInfo)
957 : {
958 0 : NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT),
959 : "character data changed for non-text node");
960 :
961 0 : if (!NeedsTextChangeNotification()) {
962 0 : return;
963 : }
964 :
965 0 : mEndOfAddedTextCache.Clear();
966 0 : mStartOfRemovingTextRangeCache.Clear();
967 0 : MOZ_ASSERT(!HasAddedNodesDuringDocumentChange(),
968 : "The stored range should be flushed before actually the data is changed");
969 :
970 0 : int64_t removedLength = mPreCharacterDataChangeLength;
971 0 : mPreCharacterDataChangeLength = -1;
972 :
973 0 : MOZ_ASSERT(removedLength >= 0,
974 : "mPreCharacterDataChangeLength should've been set by "
975 : "CharacterDataWillChange()");
976 :
977 0 : uint32_t offset = 0;
978 : // get offsets of change and fire notification
979 : nsresult rv =
980 0 : ContentEventHandler::GetFlatTextLengthInRange(
981 0 : NodePosition(mRootContent, 0),
982 0 : NodePosition(aContent, aInfo->mChangeStart),
983 0 : mRootContent, &offset, LINE_BREAK_TYPE_NATIVE);
984 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
985 0 : return;
986 : }
987 :
988 : uint32_t newLength =
989 0 : ContentEventHandler::GetNativeTextLength(aContent, aInfo->mChangeStart,
990 0 : aInfo->mChangeStart +
991 0 : aInfo->mReplaceLength);
992 :
993 0 : uint32_t oldEnd = offset + static_cast<uint32_t>(removedLength);
994 0 : uint32_t newEnd = offset + newLength;
995 :
996 : TextChangeData data(offset, oldEnd, newEnd,
997 0 : IsEditorHandlingEventForComposition(),
998 0 : IsEditorComposing());
999 0 : MaybeNotifyIMEOfTextChange(data);
1000 : }
1001 :
1002 : void
1003 0 : IMEContentObserver::NotifyContentAdded(nsINode* aContainer,
1004 : int32_t aStartIndex,
1005 : int32_t aEndIndex)
1006 : {
1007 0 : if (!NeedsTextChangeNotification()) {
1008 0 : return;
1009 : }
1010 :
1011 0 : mStartOfRemovingTextRangeCache.Clear();
1012 :
1013 : // If it's in a document change, nodes are added consecutively. Therefore,
1014 : // if we cache the first node and the last node, we need to compute the
1015 : // range once.
1016 : // FYI: This is not true if the change caused by an operation in the editor.
1017 0 : if (IsInDocumentChange()) {
1018 : // Now, mEndOfAddedTextCache may be invalid if node is added before
1019 : // the last node in mEndOfAddedTextCache. Clear it.
1020 0 : mEndOfAddedTextCache.Clear();
1021 0 : if (!HasAddedNodesDuringDocumentChange()) {
1022 0 : mFirstAddedNodeContainer = mLastAddedNodeContainer = aContainer;
1023 0 : mFirstAddedNodeOffset = aStartIndex;
1024 0 : mLastAddedNodeOffset = aEndIndex;
1025 0 : MOZ_LOG(sIMECOLog, LogLevel::Debug,
1026 : ("0x%p IMEContentObserver::NotifyContentAdded(), starts to store "
1027 : "consecutive added nodes", this));
1028 0 : return;
1029 : }
1030 : // If first node being added is not next node of the last node,
1031 : // notify IME of the previous range first, then, restart to cache the
1032 : // range.
1033 0 : if (NS_WARN_IF(!IsNextNodeOfLastAddedNode(aContainer, aStartIndex))) {
1034 : // Flush the old range first.
1035 0 : MaybeNotifyIMEOfAddedTextDuringDocumentChange();
1036 0 : mFirstAddedNodeContainer = aContainer;
1037 0 : mFirstAddedNodeOffset = aStartIndex;
1038 0 : MOZ_LOG(sIMECOLog, LogLevel::Debug,
1039 : ("0x%p IMEContentObserver::NotifyContentAdded(), starts to store "
1040 : "consecutive added nodes", this));
1041 : }
1042 0 : mLastAddedNodeContainer = aContainer;
1043 0 : mLastAddedNodeOffset = aEndIndex;
1044 0 : return;
1045 : }
1046 0 : MOZ_ASSERT(!HasAddedNodesDuringDocumentChange(),
1047 : "The cache should be cleared when document change finished");
1048 :
1049 0 : uint32_t offset = 0;
1050 0 : nsresult rv = NS_OK;
1051 0 : if (!mEndOfAddedTextCache.Match(aContainer, aStartIndex)) {
1052 0 : mEndOfAddedTextCache.Clear();
1053 0 : rv = ContentEventHandler::GetFlatTextLengthInRange(
1054 0 : NodePosition(mRootContent, 0),
1055 0 : NodePositionBefore(aContainer, aStartIndex),
1056 0 : mRootContent, &offset, LINE_BREAK_TYPE_NATIVE);
1057 0 : if (NS_WARN_IF(NS_FAILED((rv)))) {
1058 0 : return;
1059 : }
1060 : } else {
1061 0 : offset = mEndOfAddedTextCache.mFlatTextLength;
1062 : }
1063 :
1064 : // get offset at the end of the last added node
1065 0 : uint32_t addingLength = 0;
1066 0 : rv = ContentEventHandler::GetFlatTextLengthInRange(
1067 0 : NodePositionBefore(aContainer, aStartIndex),
1068 0 : NodePosition(aContainer, aEndIndex),
1069 : mRootContent, &addingLength,
1070 0 : LINE_BREAK_TYPE_NATIVE);
1071 0 : if (NS_WARN_IF(NS_FAILED((rv)))) {
1072 0 : mEndOfAddedTextCache.Clear();
1073 0 : return;
1074 : }
1075 :
1076 : // If multiple lines are being inserted in an HTML editor, next call of
1077 : // NotifyContentAdded() is for adding next node. Therefore, caching the text
1078 : // length can skip to compute the text length before the adding node and
1079 : // before of it.
1080 0 : mEndOfAddedTextCache.Cache(aContainer, aEndIndex, offset + addingLength);
1081 :
1082 0 : if (!addingLength) {
1083 0 : return;
1084 : }
1085 :
1086 : TextChangeData data(offset, offset, offset + addingLength,
1087 0 : IsEditorHandlingEventForComposition(),
1088 0 : IsEditorComposing());
1089 0 : MaybeNotifyIMEOfTextChange(data);
1090 : }
1091 :
1092 : void
1093 0 : IMEContentObserver::ContentAppended(nsIDocument* aDocument,
1094 : nsIContent* aContainer,
1095 : nsIContent* aFirstNewContent,
1096 : int32_t aNewIndexInContainer)
1097 : {
1098 0 : NotifyContentAdded(aContainer, aNewIndexInContainer,
1099 0 : aContainer->GetChildCount());
1100 0 : }
1101 :
1102 : void
1103 0 : IMEContentObserver::ContentInserted(nsIDocument* aDocument,
1104 : nsIContent* aContainer,
1105 : nsIContent* aChild,
1106 : int32_t aIndexInContainer)
1107 : {
1108 0 : NotifyContentAdded(NODE_FROM(aContainer, aDocument),
1109 0 : aIndexInContainer, aIndexInContainer + 1);
1110 0 : }
1111 :
1112 : void
1113 0 : IMEContentObserver::ContentRemoved(nsIDocument* aDocument,
1114 : nsIContent* aContainer,
1115 : nsIContent* aChild,
1116 : int32_t aIndexInContainer,
1117 : nsIContent* aPreviousSibling)
1118 : {
1119 0 : if (!NeedsTextChangeNotification()) {
1120 0 : return;
1121 : }
1122 :
1123 0 : mEndOfAddedTextCache.Clear();
1124 0 : MaybeNotifyIMEOfAddedTextDuringDocumentChange();
1125 :
1126 0 : nsINode* containerNode = NODE_FROM(aContainer, aDocument);
1127 :
1128 0 : uint32_t offset = 0;
1129 0 : nsresult rv = NS_OK;
1130 0 : if (!mStartOfRemovingTextRangeCache.Match(containerNode, aIndexInContainer)) {
1131 : // At removing a child node of aContainer, we need the line break caused
1132 : // by open tag of aContainer. Be careful when aIndexInContainer is 0.
1133 0 : rv = ContentEventHandler::GetFlatTextLengthInRange(
1134 0 : NodePosition(mRootContent, 0),
1135 0 : NodePosition(containerNode, aIndexInContainer),
1136 0 : mRootContent, &offset, LINE_BREAK_TYPE_NATIVE);
1137 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1138 0 : mStartOfRemovingTextRangeCache.Clear();
1139 0 : return;
1140 : }
1141 0 : mStartOfRemovingTextRangeCache.Cache(containerNode, aIndexInContainer,
1142 0 : offset);
1143 : } else {
1144 0 : offset = mStartOfRemovingTextRangeCache.mFlatTextLength;
1145 : }
1146 :
1147 : // get offset at the end of the deleted node
1148 0 : uint32_t textLength = 0;
1149 0 : if (aChild->IsNodeOfType(nsINode::eTEXT)) {
1150 0 : textLength = ContentEventHandler::GetNativeTextLength(aChild);
1151 : } else {
1152 0 : uint32_t nodeLength = static_cast<int32_t>(aChild->GetChildCount());
1153 0 : rv = ContentEventHandler::GetFlatTextLengthInRange(
1154 0 : NodePositionBefore(aChild, 0),
1155 0 : NodePosition(aChild, nodeLength),
1156 : mRootContent, &textLength,
1157 0 : LINE_BREAK_TYPE_NATIVE, true);
1158 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1159 0 : mStartOfRemovingTextRangeCache.Clear();
1160 0 : return;
1161 : }
1162 : }
1163 :
1164 0 : if (!textLength) {
1165 0 : return;
1166 : }
1167 :
1168 : TextChangeData data(offset, offset + textLength, offset,
1169 0 : IsEditorHandlingEventForComposition(),
1170 0 : IsEditorComposing());
1171 0 : MaybeNotifyIMEOfTextChange(data);
1172 : }
1173 :
1174 : void
1175 0 : IMEContentObserver::AttributeWillChange(nsIDocument* aDocument,
1176 : dom::Element* aElement,
1177 : int32_t aNameSpaceID,
1178 : nsIAtom* aAttribute,
1179 : int32_t aModType,
1180 : const nsAttrValue* aNewValue)
1181 : {
1182 0 : if (!NeedsTextChangeNotification()) {
1183 0 : return;
1184 : }
1185 :
1186 0 : mPreAttrChangeLength =
1187 0 : ContentEventHandler::GetNativeTextLengthBefore(aElement, mRootContent);
1188 : }
1189 :
1190 : void
1191 0 : IMEContentObserver::AttributeChanged(nsIDocument* aDocument,
1192 : dom::Element* aElement,
1193 : int32_t aNameSpaceID,
1194 : nsIAtom* aAttribute,
1195 : int32_t aModType,
1196 : const nsAttrValue* aOldValue)
1197 : {
1198 0 : if (!NeedsTextChangeNotification()) {
1199 0 : return;
1200 : }
1201 :
1202 0 : mEndOfAddedTextCache.Clear();
1203 0 : mStartOfRemovingTextRangeCache.Clear();
1204 :
1205 : uint32_t postAttrChangeLength =
1206 0 : ContentEventHandler::GetNativeTextLengthBefore(aElement, mRootContent);
1207 0 : if (postAttrChangeLength == mPreAttrChangeLength) {
1208 0 : return;
1209 : }
1210 : // First, compute text range which were added during a document change.
1211 0 : MaybeNotifyIMEOfAddedTextDuringDocumentChange();
1212 : // Then, compute the new text changed caused by this attribute change.
1213 : uint32_t start;
1214 : nsresult rv =
1215 0 : ContentEventHandler::GetFlatTextLengthInRange(
1216 0 : NodePosition(mRootContent, 0),
1217 0 : NodePositionBefore(aElement, 0),
1218 0 : mRootContent, &start, LINE_BREAK_TYPE_NATIVE);
1219 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1220 0 : return;
1221 : }
1222 :
1223 0 : TextChangeData data(start, start + mPreAttrChangeLength,
1224 : start + postAttrChangeLength,
1225 0 : IsEditorHandlingEventForComposition(),
1226 0 : IsEditorComposing());
1227 0 : MaybeNotifyIMEOfTextChange(data);
1228 : }
1229 :
1230 : void
1231 0 : IMEContentObserver::ClearAddedNodesDuringDocumentChange()
1232 : {
1233 0 : mFirstAddedNodeContainer = mLastAddedNodeContainer = nullptr;
1234 0 : mFirstAddedNodeOffset = mLastAddedNodeOffset = 0;
1235 0 : MOZ_LOG(sIMECOLog, LogLevel::Debug,
1236 : ("0x%p IMEContentObserver::ClearAddedNodesDuringDocumentChange()"
1237 : ", finished storing consecutive nodes", this));
1238 0 : }
1239 :
1240 : // static
1241 : nsIContent*
1242 0 : IMEContentObserver::GetChildNode(nsINode* aParent, int32_t aOffset)
1243 : {
1244 0 : if (!aParent->HasChildren() || aOffset < 0 ||
1245 0 : aOffset >= static_cast<int32_t>(aParent->Length())) {
1246 0 : return nullptr;
1247 : }
1248 0 : if (!aOffset) {
1249 0 : return aParent->GetFirstChild();
1250 : }
1251 0 : if (aOffset == static_cast<int32_t>(aParent->Length() - 1)) {
1252 0 : return aParent->GetLastChild();
1253 : }
1254 0 : return aParent->GetChildAt(aOffset);
1255 : }
1256 :
1257 : bool
1258 0 : IMEContentObserver::IsNextNodeOfLastAddedNode(nsINode* aParent,
1259 : int32_t aOffset) const
1260 : {
1261 0 : MOZ_ASSERT(aParent);
1262 0 : MOZ_ASSERT(aOffset >= 0 &&
1263 : aOffset <= static_cast<int32_t>(aParent->Length()));
1264 0 : MOZ_ASSERT(mRootContent);
1265 0 : MOZ_ASSERT(HasAddedNodesDuringDocumentChange());
1266 :
1267 : // If the parent node isn't changed, we can check it only with offset.
1268 0 : if (aParent == mLastAddedNodeContainer) {
1269 0 : if (NS_WARN_IF(mLastAddedNodeOffset != aOffset)) {
1270 0 : return false;
1271 : }
1272 0 : return true;
1273 : }
1274 :
1275 : // If the parent node is changed, that means that given offset should be the
1276 : // last added node not having next sibling.
1277 0 : if (NS_WARN_IF(mLastAddedNodeOffset !=
1278 : static_cast<int32_t>(mLastAddedNodeContainer->Length()))) {
1279 0 : return false;
1280 : }
1281 :
1282 : // If the node is aParent is a descendant of mLastAddedNodeContainer,
1283 : // aOffset should be 0.
1284 0 : if (mLastAddedNodeContainer == aParent->GetParent()) {
1285 0 : if (NS_WARN_IF(aOffset)) {
1286 0 : return false;
1287 : }
1288 0 : return true;
1289 : }
1290 :
1291 : // Otherwise, we need to check it even with slow path.
1292 : nsIContent* lastAddedContent =
1293 0 : GetChildNode(mLastAddedNodeContainer, mLastAddedNodeOffset - 1);
1294 0 : if (NS_WARN_IF(!lastAddedContent)) {
1295 0 : return false;
1296 : }
1297 :
1298 : nsIContent* nextContentOfLastAddedContent =
1299 0 : lastAddedContent->GetNextNode(mRootContent->GetParentNode());
1300 0 : if (NS_WARN_IF(!nextContentOfLastAddedContent)) {
1301 0 : return false;
1302 : }
1303 :
1304 0 : nsIContent* startContent = GetChildNode(aParent, aOffset);
1305 0 : if (NS_WARN_IF(!startContent) ||
1306 0 : NS_WARN_IF(nextContentOfLastAddedContent != startContent)) {
1307 0 : return false;
1308 : }
1309 : #ifdef DEBUG
1310 0 : NS_WARNING_ASSERTION(
1311 : !aOffset || aOffset == static_cast<int32_t>(aParent->Length() - 1),
1312 : "Used slow path for aParent");
1313 0 : NS_WARNING_ASSERTION(
1314 : !(mLastAddedNodeOffset - 1) ||
1315 : mLastAddedNodeOffset ==
1316 : static_cast<int32_t>(mLastAddedNodeContainer->Length()),
1317 : "Used slow path for mLastAddedNodeContainer");
1318 : #endif // #ifdef DEBUG
1319 0 : return true;
1320 : }
1321 :
1322 : void
1323 0 : IMEContentObserver::MaybeNotifyIMEOfAddedTextDuringDocumentChange()
1324 : {
1325 0 : if (!HasAddedNodesDuringDocumentChange()) {
1326 0 : return;
1327 : }
1328 :
1329 0 : MOZ_LOG(sIMECOLog, LogLevel::Debug,
1330 : ("0x%p IMEContentObserver::MaybeNotifyIMEOfAddedTextDuringDocumentChange()"
1331 : ", flushing stored consecutive nodes", this));
1332 :
1333 : // Notify IME of text change which is caused by added nodes now.
1334 :
1335 : // First, compute offset of start of first added node from start of the
1336 : // editor.
1337 : uint32_t offset;
1338 : nsresult rv =
1339 0 : ContentEventHandler::GetFlatTextLengthInRange(
1340 0 : NodePosition(mRootContent, 0),
1341 0 : NodePosition(mFirstAddedNodeContainer,
1342 : mFirstAddedNodeOffset),
1343 0 : mRootContent, &offset, LINE_BREAK_TYPE_NATIVE);
1344 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1345 0 : ClearAddedNodesDuringDocumentChange();
1346 0 : return;
1347 : }
1348 :
1349 : // Next, compute the text length of added nodes.
1350 : uint32_t length;
1351 : rv =
1352 0 : ContentEventHandler::GetFlatTextLengthInRange(
1353 0 : NodePosition(mFirstAddedNodeContainer,
1354 : mFirstAddedNodeOffset),
1355 0 : NodePosition(mLastAddedNodeContainer,
1356 : mLastAddedNodeOffset),
1357 0 : mRootContent, &length, LINE_BREAK_TYPE_NATIVE);
1358 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1359 0 : ClearAddedNodesDuringDocumentChange();
1360 0 : return;
1361 : }
1362 :
1363 : // Finally, try to notify IME of the range.
1364 : TextChangeData data(offset, offset, offset + length,
1365 0 : IsEditorHandlingEventForComposition(),
1366 0 : IsEditorComposing());
1367 0 : MaybeNotifyIMEOfTextChange(data);
1368 0 : ClearAddedNodesDuringDocumentChange();
1369 : }
1370 :
1371 : void
1372 0 : IMEContentObserver::BeginDocumentUpdate()
1373 : {
1374 0 : MOZ_LOG(sIMECOLog, LogLevel::Debug,
1375 : ("0x%p IMEContentObserver::BeginDocumentUpdate(), "
1376 : "HasAddedNodesDuringDocumentChange()=%s",
1377 : this, ToChar(HasAddedNodesDuringDocumentChange())));
1378 :
1379 0 : MOZ_ASSERT(!HasAddedNodesDuringDocumentChange());
1380 0 : }
1381 :
1382 : void
1383 0 : IMEContentObserver::EndDocumentUpdate()
1384 : {
1385 0 : MOZ_LOG(sIMECOLog, LogLevel::Debug,
1386 : ("0x%p IMEContentObserver::EndDocumentUpdate(), "
1387 : "HasAddedNodesDuringDocumentChange()=%s",
1388 : this, ToChar(HasAddedNodesDuringDocumentChange())));
1389 :
1390 0 : MaybeNotifyIMEOfAddedTextDuringDocumentChange();
1391 0 : }
1392 :
1393 : void
1394 0 : IMEContentObserver::SuppressNotifyingIME()
1395 : {
1396 0 : mSuppressNotifications++;
1397 :
1398 0 : MOZ_LOG(sIMECOLog, LogLevel::Debug,
1399 : ("0x%p IMEContentObserver::SuppressNotifyingIME(), "
1400 : "mSuppressNotifications=%u", this, mSuppressNotifications));
1401 0 : }
1402 :
1403 : void
1404 0 : IMEContentObserver::UnsuppressNotifyingIME()
1405 : {
1406 0 : MOZ_LOG(sIMECOLog, LogLevel::Debug,
1407 : ("0x%p IMEContentObserver::UnsuppressNotifyingIME(), "
1408 : "mSuppressNotifications=%u", this, mSuppressNotifications));
1409 :
1410 0 : if (!mSuppressNotifications || --mSuppressNotifications) {
1411 0 : return;
1412 : }
1413 0 : FlushMergeableNotifications();
1414 : }
1415 :
1416 : NS_IMETHODIMP
1417 0 : IMEContentObserver::EditAction()
1418 : {
1419 0 : MOZ_LOG(sIMECOLog, LogLevel::Debug,
1420 : ("0x%p IMEContentObserver::EditAction()", this));
1421 :
1422 0 : mEndOfAddedTextCache.Clear();
1423 0 : mStartOfRemovingTextRangeCache.Clear();
1424 0 : FlushMergeableNotifications();
1425 0 : return NS_OK;
1426 : }
1427 :
1428 : NS_IMETHODIMP
1429 0 : IMEContentObserver::BeforeEditAction()
1430 : {
1431 0 : MOZ_LOG(sIMECOLog, LogLevel::Debug,
1432 : ("0x%p IMEContentObserver::BeforeEditAction()", this));
1433 :
1434 0 : mEndOfAddedTextCache.Clear();
1435 0 : mStartOfRemovingTextRangeCache.Clear();
1436 0 : return NS_OK;
1437 : }
1438 :
1439 : NS_IMETHODIMP
1440 0 : IMEContentObserver::CancelEditAction()
1441 : {
1442 0 : MOZ_LOG(sIMECOLog, LogLevel::Debug,
1443 : ("0x%p IMEContentObserver::CancelEditAction()", this));
1444 :
1445 0 : mEndOfAddedTextCache.Clear();
1446 0 : mStartOfRemovingTextRangeCache.Clear();
1447 0 : FlushMergeableNotifications();
1448 0 : return NS_OK;
1449 : }
1450 :
1451 : void
1452 0 : IMEContentObserver::PostFocusSetNotification()
1453 : {
1454 0 : MOZ_LOG(sIMECOLog, LogLevel::Debug,
1455 : ("0x%p IMEContentObserver::PostFocusSetNotification()", this));
1456 :
1457 0 : mNeedsToNotifyIMEOfFocusSet = true;
1458 0 : }
1459 :
1460 : void
1461 0 : IMEContentObserver::PostTextChangeNotification()
1462 : {
1463 0 : MOZ_LOG(sIMECOLog, LogLevel::Debug,
1464 : ("0x%p IMEContentObserver::PostTextChangeNotification("
1465 : "mTextChangeData=%s)",
1466 : this, TextChangeDataToString(mTextChangeData).get()));
1467 :
1468 0 : MOZ_ASSERT(mTextChangeData.IsValid(),
1469 : "mTextChangeData must have text change data");
1470 0 : mNeedsToNotifyIMEOfTextChange = true;
1471 0 : }
1472 :
1473 : void
1474 0 : IMEContentObserver::PostSelectionChangeNotification()
1475 : {
1476 0 : MOZ_LOG(sIMECOLog, LogLevel::Debug,
1477 : ("0x%p IMEContentObserver::PostSelectionChangeNotification(), "
1478 : "mSelectionData={ mCausedByComposition=%s, mCausedBySelectionEvent=%s }",
1479 : this, ToChar(mSelectionData.mCausedByComposition),
1480 : ToChar(mSelectionData.mCausedBySelectionEvent)));
1481 :
1482 0 : mNeedsToNotifyIMEOfSelectionChange = true;
1483 0 : }
1484 :
1485 : void
1486 0 : IMEContentObserver::MaybeNotifyIMEOfFocusSet()
1487 : {
1488 0 : MOZ_LOG(sIMECOLog, LogLevel::Debug,
1489 : ("0x%p IMEContentObserver::MaybeNotifyIMEOfFocusSet()", this));
1490 :
1491 0 : PostFocusSetNotification();
1492 0 : FlushMergeableNotifications();
1493 0 : }
1494 :
1495 : void
1496 0 : IMEContentObserver::MaybeNotifyIMEOfTextChange(
1497 : const TextChangeDataBase& aTextChangeData)
1498 : {
1499 0 : MOZ_LOG(sIMECOLog, LogLevel::Debug,
1500 : ("0x%p IMEContentObserver::MaybeNotifyIMEOfTextChange("
1501 : "aTextChangeData=%s)",
1502 : this, TextChangeDataToString(aTextChangeData).get()));
1503 :
1504 0 : mTextChangeData += aTextChangeData;
1505 0 : PostTextChangeNotification();
1506 0 : FlushMergeableNotifications();
1507 0 : }
1508 :
1509 : void
1510 0 : IMEContentObserver::CancelNotifyingIMEOfTextChange()
1511 : {
1512 0 : MOZ_LOG(sIMECOLog, LogLevel::Debug,
1513 : ("0x%p IMEContentObserver::CancelNotifyingIMEOfTextChange()", this));
1514 0 : mTextChangeData.Clear();
1515 0 : mNeedsToNotifyIMEOfTextChange = false;
1516 0 : }
1517 :
1518 : void
1519 0 : IMEContentObserver::MaybeNotifyIMEOfSelectionChange(
1520 : bool aCausedByComposition,
1521 : bool aCausedBySelectionEvent,
1522 : bool aOccurredDuringComposition)
1523 : {
1524 0 : MOZ_LOG(sIMECOLog, LogLevel::Debug,
1525 : ("0x%p IMEContentObserver::MaybeNotifyIMEOfSelectionChange("
1526 : "aCausedByComposition=%s, aCausedBySelectionEvent=%s, "
1527 : "aOccurredDuringComposition)",
1528 : this, ToChar(aCausedByComposition), ToChar(aCausedBySelectionEvent)));
1529 :
1530 0 : mSelectionData.AssignReason(aCausedByComposition,
1531 : aCausedBySelectionEvent,
1532 0 : aOccurredDuringComposition);
1533 0 : PostSelectionChangeNotification();
1534 0 : FlushMergeableNotifications();
1535 0 : }
1536 :
1537 : void
1538 0 : IMEContentObserver::MaybeNotifyIMEOfPositionChange()
1539 : {
1540 0 : MOZ_LOG(sIMECOLog, LogLevel::Debug,
1541 : ("0x%p IMEContentObserver::MaybeNotifyIMEOfPositionChange()", this));
1542 : // If reflow is caused by ContentEventHandler during PositionChangeEvent
1543 : // sending NOTIFY_IME_OF_POSITION_CHANGE, we don't need to notify IME of it
1544 : // again since ContentEventHandler returns the result including this reflow's
1545 : // result.
1546 0 : if (mIsHandlingQueryContentEvent &&
1547 0 : mSendingNotification == NOTIFY_IME_OF_POSITION_CHANGE) {
1548 0 : MOZ_LOG(sIMECOLog, LogLevel::Debug,
1549 : ("0x%p IMEContentObserver::MaybeNotifyIMEOfPositionChange(), "
1550 : "ignored since caused by ContentEventHandler during sending "
1551 : "NOTIY_IME_OF_POSITION_CHANGE", this));
1552 0 : return;
1553 : }
1554 0 : PostPositionChangeNotification();
1555 0 : FlushMergeableNotifications();
1556 : }
1557 :
1558 : void
1559 0 : IMEContentObserver::CancelNotifyingIMEOfPositionChange()
1560 : {
1561 0 : MOZ_LOG(sIMECOLog, LogLevel::Debug,
1562 : ("0x%p IMEContentObserver::CancelNotifyIMEOfPositionChange()", this));
1563 0 : mNeedsToNotifyIMEOfPositionChange = false;
1564 0 : }
1565 :
1566 : void
1567 0 : IMEContentObserver::MaybeNotifyCompositionEventHandled()
1568 : {
1569 0 : MOZ_LOG(sIMECOLog, LogLevel::Debug,
1570 : ("0x%p IMEContentObserver::MaybeNotifyCompositionEventHandled()",
1571 : this));
1572 :
1573 0 : PostCompositionEventHandledNotification();
1574 0 : FlushMergeableNotifications();
1575 0 : }
1576 :
1577 : bool
1578 0 : IMEContentObserver::UpdateSelectionCache()
1579 : {
1580 0 : MOZ_ASSERT(IsSafeToNotifyIME());
1581 :
1582 0 : if (WasInitializedWithPlugin()) {
1583 0 : return false;
1584 : }
1585 :
1586 0 : mSelectionData.ClearSelectionData();
1587 :
1588 : // XXX Cannot we cache some information for reducing the cost to compute
1589 : // selection offset and writing mode?
1590 0 : WidgetQueryContentEvent selection(true, eQuerySelectedText, mWidget);
1591 0 : ContentEventHandler handler(GetPresContext());
1592 0 : handler.OnQuerySelectedText(&selection);
1593 0 : if (NS_WARN_IF(!selection.mSucceeded) ||
1594 0 : NS_WARN_IF(selection.mReply.mContentsRoot != mRootContent)) {
1595 0 : return false;
1596 : }
1597 :
1598 0 : mFocusedWidget = selection.mReply.mFocusedWidget;
1599 0 : mSelectionData.mOffset = selection.mReply.mOffset;
1600 0 : *mSelectionData.mString = selection.mReply.mString;
1601 0 : mSelectionData.SetWritingMode(selection.GetWritingMode());
1602 0 : mSelectionData.mReversed = selection.mReply.mReversed;
1603 :
1604 : // WARNING: Don't modify the reason of selection change here.
1605 :
1606 0 : MOZ_LOG(sIMECOLog, LogLevel::Debug,
1607 : ("0x%p IMEContentObserver::UpdateSelectionCache(), "
1608 : "mSelectionData=%s",
1609 : this, SelectionChangeDataToString(mSelectionData).get()));
1610 :
1611 0 : return mSelectionData.IsValid();
1612 : }
1613 :
1614 : void
1615 0 : IMEContentObserver::PostPositionChangeNotification()
1616 : {
1617 0 : MOZ_LOG(sIMECOLog, LogLevel::Debug,
1618 : ("0x%p IMEContentObserver::PostPositionChangeNotification()", this));
1619 :
1620 0 : mNeedsToNotifyIMEOfPositionChange = true;
1621 0 : }
1622 :
1623 : void
1624 0 : IMEContentObserver::PostCompositionEventHandledNotification()
1625 : {
1626 0 : MOZ_LOG(sIMECOLog, LogLevel::Debug,
1627 : ("0x%p IMEContentObserver::"
1628 : "PostCompositionEventHandledNotification()", this));
1629 :
1630 0 : mNeedsToNotifyIMEOfCompositionEventHandled = true;
1631 0 : }
1632 :
1633 : bool
1634 0 : IMEContentObserver::IsReflowLocked() const
1635 : {
1636 0 : nsPresContext* presContext = GetPresContext();
1637 0 : if (NS_WARN_IF(!presContext)) {
1638 0 : return false;
1639 : }
1640 0 : nsIPresShell* presShell = presContext->GetPresShell();
1641 0 : if (NS_WARN_IF(!presShell)) {
1642 0 : return false;
1643 : }
1644 : // During reflow, we shouldn't notify IME because IME may query content
1645 : // synchronously. Then, it causes ContentEventHandler will try to flush
1646 : // pending notifications during reflow.
1647 0 : return presShell->IsReflowLocked();
1648 : }
1649 :
1650 : bool
1651 0 : IMEContentObserver::IsSafeToNotifyIME() const
1652 : {
1653 : // If this is already detached from the widget, this doesn't need to notify
1654 : // anything.
1655 0 : if (!mWidget) {
1656 0 : return false;
1657 : }
1658 :
1659 : // Don't notify IME of anything if it's not good time to do it.
1660 0 : if (mSuppressNotifications) {
1661 0 : return false;
1662 : }
1663 :
1664 0 : if (!mESM || NS_WARN_IF(!GetPresContext())) {
1665 0 : return false;
1666 : }
1667 :
1668 : // If it's in reflow, we should wait to finish the reflow.
1669 : // FYI: This should be called again from Reflow() or ReflowInterruptible().
1670 0 : if (IsReflowLocked()) {
1671 0 : return false;
1672 : }
1673 :
1674 : // If we're in handling an edit action, this method will be called later.
1675 0 : if (mEditorBase && mEditorBase->IsInEditAction()) {
1676 0 : return false;
1677 : }
1678 :
1679 0 : return true;
1680 : }
1681 :
1682 : void
1683 0 : IMEContentObserver::FlushMergeableNotifications()
1684 : {
1685 0 : if (!IsSafeToNotifyIME()) {
1686 : // So, if this is already called, this should do nothing.
1687 0 : MOZ_LOG(sIMECOLog, LogLevel::Debug,
1688 : ("0x%p IMEContentObserver::FlushMergeableNotifications(), "
1689 : "FAILED, due to unsafe to notify IME", this));
1690 0 : return;
1691 : }
1692 :
1693 : // Notifying something may cause nested call of this method. For example,
1694 : // when somebody notified one of the notifications may dispatch query content
1695 : // event. Then, it causes flushing layout which may cause another layout
1696 : // change notification.
1697 :
1698 0 : if (mQueuedSender) {
1699 : // So, if this is already called, this should do nothing.
1700 0 : MOZ_LOG(sIMECOLog, LogLevel::Debug,
1701 : ("0x%p IMEContentObserver::FlushMergeableNotifications(), "
1702 : "FAILED, due to already flushing pending notifications", this));
1703 0 : return;
1704 : }
1705 :
1706 : // If text change notification and/or position change notification becomes
1707 : // unnecessary, let's cancel them.
1708 0 : if (mNeedsToNotifyIMEOfTextChange && !NeedsTextChangeNotification()) {
1709 0 : CancelNotifyingIMEOfTextChange();
1710 : }
1711 0 : if (mNeedsToNotifyIMEOfPositionChange && !NeedsPositionChangeNotification()) {
1712 0 : CancelNotifyingIMEOfPositionChange();
1713 : }
1714 :
1715 0 : if (!NeedsToNotifyIMEOfSomething()) {
1716 0 : MOZ_LOG(sIMECOLog, LogLevel::Debug,
1717 : ("0x%p IMEContentObserver::FlushMergeableNotifications(), "
1718 : "FAILED, due to no pending notifications", this));
1719 0 : return;
1720 : }
1721 :
1722 : // NOTE: Reset each pending flag because sending notification may cause
1723 : // another change.
1724 :
1725 0 : MOZ_LOG(sIMECOLog, LogLevel::Debug,
1726 : ("0x%p IMEContentObserver::FlushMergeableNotifications(), "
1727 : "creating IMENotificationSender...", this));
1728 :
1729 : // If contents in selection range is modified, the selection range still
1730 : // has removed node from the tree. In such case, nsContentIterator won't
1731 : // work well. Therefore, we shouldn't use AddScriptRunnder() here since
1732 : // it may kick runnable event immediately after DOM tree is changed but
1733 : // the selection range isn't modified yet.
1734 0 : mQueuedSender = new IMENotificationSender(this);
1735 0 : mQueuedSender->Dispatch(mDocShell);
1736 0 : MOZ_LOG(sIMECOLog, LogLevel::Debug,
1737 : ("0x%p IMEContentObserver::FlushMergeableNotifications(), "
1738 : "finished", this));
1739 : }
1740 :
1741 : void
1742 0 : IMEContentObserver::TryToFlushPendingNotifications()
1743 : {
1744 0 : if (!mQueuedSender || mSendingNotification != NOTIFY_IME_OF_NOTHING ||
1745 0 : XRE_IsContentProcess()) {
1746 0 : return;
1747 : }
1748 :
1749 0 : MOZ_LOG(sIMECOLog, LogLevel::Debug,
1750 : ("0x%p IMEContentObserver::TryToFlushPendingNotifications(), "
1751 : "performing queued IMENotificationSender forcibly", this));
1752 0 : RefPtr<IMENotificationSender> queuedSender = mQueuedSender;
1753 0 : queuedSender->Run();
1754 : }
1755 :
1756 : /******************************************************************************
1757 : * mozilla::IMEContentObserver::AChangeEvent
1758 : ******************************************************************************/
1759 :
1760 : bool
1761 0 : IMEContentObserver::AChangeEvent::CanNotifyIME(
1762 : ChangeEventType aChangeEventType) const
1763 : {
1764 0 : RefPtr<IMEContentObserver> observer = GetObserver();
1765 0 : if (NS_WARN_IF(!observer)) {
1766 0 : return false;
1767 : }
1768 :
1769 0 : if (aChangeEventType == eChangeEventType_CompositionEventHandled) {
1770 0 : return observer->mWidget != nullptr;
1771 : }
1772 0 : State state = observer->GetState();
1773 : // If it's not initialized, we should do nothing.
1774 0 : if (state == eState_NotObserving) {
1775 0 : return false;
1776 : }
1777 : // If setting focus, just check the state.
1778 0 : if (aChangeEventType == eChangeEventType_Focus) {
1779 0 : return !NS_WARN_IF(observer->mIMEHasFocus);
1780 : }
1781 : // If we've not notified IME of focus yet, we shouldn't notify anything.
1782 0 : if (!observer->mIMEHasFocus) {
1783 0 : return false;
1784 : }
1785 :
1786 : // If IME has focus, IMEContentObserver must hold the widget.
1787 0 : MOZ_ASSERT(observer->mWidget);
1788 :
1789 0 : return true;
1790 : }
1791 :
1792 : bool
1793 0 : IMEContentObserver::AChangeEvent::IsSafeToNotifyIME(
1794 : ChangeEventType aChangeEventType) const
1795 : {
1796 0 : if (NS_WARN_IF(!nsContentUtils::IsSafeToRunScript())) {
1797 0 : return false;
1798 : }
1799 :
1800 0 : RefPtr<IMEContentObserver> observer = GetObserver();
1801 0 : if (!observer) {
1802 0 : return false;
1803 : }
1804 :
1805 : // While we're sending a notification, we shouldn't send another notification
1806 : // recursively.
1807 0 : if (observer->mSendingNotification != NOTIFY_IME_OF_NOTHING) {
1808 0 : MOZ_LOG(sIMECOLog, LogLevel::Debug,
1809 : ("0x%p IMEContentObserver::AChangeEvent::IsSafeToNotifyIME(), "
1810 : "putting off sending notification due to detecting recursive call, "
1811 : "mIMEContentObserver={ mSendingNotification=%s }",
1812 : this, ToChar(observer->mSendingNotification)));
1813 0 : return false;
1814 : }
1815 0 : State state = observer->GetState();
1816 0 : if (aChangeEventType == eChangeEventType_Focus) {
1817 0 : if (NS_WARN_IF(state != eState_Initializing && state != eState_Observing)) {
1818 0 : return false;
1819 : }
1820 0 : } else if (aChangeEventType == eChangeEventType_CompositionEventHandled) {
1821 : // It doesn't need to check the observing status.
1822 0 : } else if (state != eState_Observing) {
1823 0 : return false;
1824 : }
1825 0 : return observer->IsSafeToNotifyIME();
1826 : }
1827 :
1828 : /******************************************************************************
1829 : * mozilla::IMEContentObserver::IMENotificationSender
1830 : ******************************************************************************/
1831 :
1832 : void
1833 0 : IMEContentObserver::IMENotificationSender::Dispatch(nsIDocShell* aDocShell)
1834 : {
1835 0 : if (XRE_IsContentProcess() && aDocShell) {
1836 0 : RefPtr<nsPresContext> presContext;
1837 0 : aDocShell->GetPresContext(getter_AddRefs(presContext));
1838 0 : if (presContext) {
1839 0 : nsRefreshDriver* refreshDriver = presContext->RefreshDriver();
1840 0 : if (refreshDriver) {
1841 0 : refreshDriver->AddEarlyRunner(this);
1842 0 : return;
1843 : }
1844 : }
1845 : }
1846 :
1847 : nsIScriptGlobalObject* globalObject =
1848 0 : aDocShell ? aDocShell->GetScriptGlobalObject() : nullptr;
1849 0 : if (globalObject) {
1850 0 : RefPtr<IMENotificationSender> queuedSender = this;
1851 0 : globalObject->Dispatch(nullptr, TaskCategory::Other,
1852 0 : queuedSender.forget());
1853 : } else {
1854 0 : NS_DispatchToCurrentThread(this);
1855 : }
1856 : }
1857 :
1858 : NS_IMETHODIMP
1859 0 : IMEContentObserver::IMENotificationSender::Run()
1860 : {
1861 0 : if (NS_WARN_IF(mIsRunning)) {
1862 0 : MOZ_LOG(sIMECOLog, LogLevel::Error,
1863 : ("0x%p IMEContentObserver::IMENotificationSender::Run(), FAILED, "
1864 : "called recursively", this));
1865 0 : return NS_OK;
1866 : }
1867 :
1868 0 : RefPtr<IMEContentObserver> observer = GetObserver();
1869 0 : if (!observer) {
1870 0 : return NS_OK;
1871 : }
1872 :
1873 0 : AutoRestore<bool> running(mIsRunning);
1874 0 : mIsRunning = true;
1875 :
1876 : // This instance was already performed forcibly.
1877 0 : if (observer->mQueuedSender != this) {
1878 0 : return NS_OK;
1879 : }
1880 :
1881 : // NOTE: Reset each pending flag because sending notification may cause
1882 : // another change.
1883 :
1884 0 : if (observer->mNeedsToNotifyIMEOfFocusSet) {
1885 0 : observer->mNeedsToNotifyIMEOfFocusSet = false;
1886 0 : SendFocusSet();
1887 0 : observer->mQueuedSender = nullptr;
1888 : // If it's not safe to notify IME of focus, SendFocusSet() sets
1889 : // mNeedsToNotifyIMEOfFocusSet true again. For guaranteeing to send the
1890 : // focus notification later, we should put a new sender into the queue but
1891 : // this case must be rare. Note that if mIMEContentObserver is already
1892 : // destroyed, mNeedsToNotifyIMEOfFocusSet is never set true again.
1893 0 : if (observer->mNeedsToNotifyIMEOfFocusSet) {
1894 0 : MOZ_ASSERT(!observer->mIMEHasFocus);
1895 0 : MOZ_LOG(sIMECOLog, LogLevel::Debug,
1896 : ("0x%p IMEContentObserver::IMENotificationSender::Run(), "
1897 : "posting IMENotificationSender to current thread", this));
1898 0 : observer->mQueuedSender = new IMENotificationSender(observer);
1899 0 : observer->mQueuedSender->Dispatch(observer->mDocShell);
1900 0 : return NS_OK;
1901 : }
1902 : // This is the first notification to IME. So, we don't need to notify
1903 : // anymore since IME starts to query content after it gets focus.
1904 0 : observer->ClearPendingNotifications();
1905 0 : return NS_OK;
1906 : }
1907 :
1908 0 : if (observer->mNeedsToNotifyIMEOfTextChange) {
1909 0 : observer->mNeedsToNotifyIMEOfTextChange = false;
1910 0 : SendTextChange();
1911 : }
1912 :
1913 : // If a text change notification causes another text change again, we should
1914 : // notify IME of that before sending a selection change notification.
1915 0 : if (!observer->mNeedsToNotifyIMEOfTextChange) {
1916 : // Be aware, PuppetWidget depends on the order of this. A selection change
1917 : // notification should not be sent before a text change notification because
1918 : // PuppetWidget shouldn't query new text content every selection change.
1919 0 : if (observer->mNeedsToNotifyIMEOfSelectionChange) {
1920 0 : observer->mNeedsToNotifyIMEOfSelectionChange = false;
1921 0 : SendSelectionChange();
1922 : }
1923 : }
1924 :
1925 : // If a text change notification causes another text change again or a
1926 : // selection change notification causes either a text change or another
1927 : // selection change, we should notify IME of those before sending a position
1928 : // change notification.
1929 0 : if (!observer->mNeedsToNotifyIMEOfTextChange &&
1930 0 : !observer->mNeedsToNotifyIMEOfSelectionChange) {
1931 0 : if (observer->mNeedsToNotifyIMEOfPositionChange) {
1932 0 : observer->mNeedsToNotifyIMEOfPositionChange = false;
1933 0 : SendPositionChange();
1934 : }
1935 : }
1936 :
1937 : // Composition event handled notification should be sent after all the
1938 : // other notifications because this notifies widget of finishing all pending
1939 : // events are handled completely.
1940 0 : if (!observer->mNeedsToNotifyIMEOfTextChange &&
1941 0 : !observer->mNeedsToNotifyIMEOfSelectionChange &&
1942 0 : !observer->mNeedsToNotifyIMEOfPositionChange) {
1943 0 : if (observer->mNeedsToNotifyIMEOfCompositionEventHandled) {
1944 0 : observer->mNeedsToNotifyIMEOfCompositionEventHandled = false;
1945 0 : SendCompositionEventHandled();
1946 : }
1947 : }
1948 :
1949 0 : observer->mQueuedSender = nullptr;
1950 :
1951 : // If notifications caused some new change, we should notify them now.
1952 0 : if (observer->NeedsToNotifyIMEOfSomething()) {
1953 0 : if (observer->GetState() == eState_StoppedObserving) {
1954 0 : MOZ_LOG(sIMECOLog, LogLevel::Debug,
1955 : ("0x%p IMEContentObserver::IMENotificationSender::Run(), "
1956 : "waiting IMENotificationSender to be reinitialized", this));
1957 : } else {
1958 0 : MOZ_LOG(sIMECOLog, LogLevel::Debug,
1959 : ("0x%p IMEContentObserver::IMENotificationSender::Run(), "
1960 : "posting IMENotificationSender to current thread", this));
1961 0 : observer->mQueuedSender = new IMENotificationSender(observer);
1962 0 : observer->mQueuedSender->Dispatch(observer->mDocShell);
1963 : }
1964 : }
1965 0 : return NS_OK;
1966 : }
1967 :
1968 : void
1969 0 : IMEContentObserver::IMENotificationSender::SendFocusSet()
1970 : {
1971 0 : RefPtr<IMEContentObserver> observer = GetObserver();
1972 0 : if (!observer) {
1973 0 : return;
1974 : }
1975 :
1976 0 : if (!CanNotifyIME(eChangeEventType_Focus)) {
1977 : // If IMEContentObserver has already gone, we don't need to notify IME of
1978 : // focus.
1979 0 : MOZ_LOG(sIMECOLog, LogLevel::Debug,
1980 : ("0x%p IMEContentObserver::IMENotificationSender::"
1981 : "SendFocusSet(), FAILED, due to impossible to notify IME of focus",
1982 : this));
1983 0 : observer->ClearPendingNotifications();
1984 0 : return;
1985 : }
1986 :
1987 0 : if (!IsSafeToNotifyIME(eChangeEventType_Focus)) {
1988 0 : MOZ_LOG(sIMECOLog, LogLevel::Debug,
1989 : ("0x%p IMEContentObserver::IMENotificationSender::"
1990 : "SendFocusSet(), retrying to send NOTIFY_IME_OF_FOCUS...", this));
1991 0 : observer->PostFocusSetNotification();
1992 0 : return;
1993 : }
1994 :
1995 0 : observer->mIMEHasFocus = true;
1996 : // Initialize selection cache with the first selection data.
1997 0 : observer->UpdateSelectionCache();
1998 :
1999 0 : MOZ_LOG(sIMECOLog, LogLevel::Info,
2000 : ("0x%p IMEContentObserver::IMENotificationSender::"
2001 : "SendFocusSet(), sending NOTIFY_IME_OF_FOCUS...", this));
2002 :
2003 0 : MOZ_RELEASE_ASSERT(observer->mSendingNotification ==
2004 : NOTIFY_IME_OF_NOTHING);
2005 0 : observer->mSendingNotification = NOTIFY_IME_OF_FOCUS;
2006 0 : IMEStateManager::NotifyIME(IMENotification(NOTIFY_IME_OF_FOCUS),
2007 0 : observer->mWidget);
2008 0 : observer->mSendingNotification = NOTIFY_IME_OF_NOTHING;
2009 :
2010 : // IMENotificationRequests referred by ObserveEditableNode() may be different
2011 : // before or after widget receives NOTIFY_IME_OF_FOCUS. Therefore, we need
2012 : // to guarantee to call ObserveEditableNode() after sending
2013 : // NOTIFY_IME_OF_FOCUS.
2014 0 : observer->OnIMEReceivedFocus();
2015 :
2016 0 : MOZ_LOG(sIMECOLog, LogLevel::Debug,
2017 : ("0x%p IMEContentObserver::IMENotificationSender::"
2018 : "SendFocusSet(), sent NOTIFY_IME_OF_FOCUS", this));
2019 : }
2020 :
2021 : void
2022 0 : IMEContentObserver::IMENotificationSender::SendSelectionChange()
2023 : {
2024 0 : RefPtr<IMEContentObserver> observer = GetObserver();
2025 0 : if (!observer) {
2026 0 : return;
2027 : }
2028 :
2029 0 : if (!CanNotifyIME(eChangeEventType_Selection)) {
2030 0 : MOZ_LOG(sIMECOLog, LogLevel::Debug,
2031 : ("0x%p IMEContentObserver::IMENotificationSender::"
2032 : "SendSelectionChange(), FAILED, due to impossible to notify IME of "
2033 : "selection change", this));
2034 0 : return;
2035 : }
2036 :
2037 0 : if (!IsSafeToNotifyIME(eChangeEventType_Selection)) {
2038 0 : MOZ_LOG(sIMECOLog, LogLevel::Debug,
2039 : ("0x%p IMEContentObserver::IMENotificationSender::"
2040 : "SendSelectionChange(), retrying to send "
2041 : "NOTIFY_IME_OF_SELECTION_CHANGE...", this));
2042 0 : observer->PostSelectionChangeNotification();
2043 0 : return;
2044 : }
2045 :
2046 0 : SelectionChangeData lastSelChangeData = observer->mSelectionData;
2047 0 : if (NS_WARN_IF(!observer->UpdateSelectionCache())) {
2048 0 : MOZ_LOG(sIMECOLog, LogLevel::Error,
2049 : ("0x%p IMEContentObserver::IMENotificationSender::"
2050 : "SendSelectionChange(), FAILED, due to UpdateSelectionCache() failure",
2051 : this));
2052 0 : return;
2053 : }
2054 :
2055 : // The state may be changed since querying content causes flushing layout.
2056 0 : if (!CanNotifyIME(eChangeEventType_Selection)) {
2057 0 : MOZ_LOG(sIMECOLog, LogLevel::Debug,
2058 : ("0x%p IMEContentObserver::IMENotificationSender::"
2059 : "SendSelectionChange(), FAILED, due to flushing layout having changed "
2060 : "something", this));
2061 0 : return;
2062 : }
2063 :
2064 : // If the selection isn't changed actually, we shouldn't notify IME of
2065 : // selection change.
2066 0 : SelectionChangeData& newSelChangeData = observer->mSelectionData;
2067 0 : if (lastSelChangeData.IsValid() &&
2068 0 : lastSelChangeData.mOffset == newSelChangeData.mOffset &&
2069 0 : lastSelChangeData.String() == newSelChangeData.String() &&
2070 0 : lastSelChangeData.GetWritingMode() == newSelChangeData.GetWritingMode() &&
2071 0 : lastSelChangeData.mReversed == newSelChangeData.mReversed) {
2072 0 : MOZ_LOG(sIMECOLog, LogLevel::Debug,
2073 : ("0x%p IMEContentObserver::IMENotificationSender::"
2074 : "SendSelectionChange(), not notifying IME of "
2075 : "NOTIFY_IME_OF_SELECTION_CHANGE due to not changed actually", this));
2076 0 : return;
2077 : }
2078 :
2079 0 : MOZ_LOG(sIMECOLog, LogLevel::Info,
2080 : ("0x%p IMEContentObserver::IMENotificationSender::"
2081 : "SendSelectionChange(), sending NOTIFY_IME_OF_SELECTION_CHANGE... "
2082 : "newSelChangeData=%s",
2083 : this, SelectionChangeDataToString(newSelChangeData).get()));
2084 :
2085 0 : IMENotification notification(NOTIFY_IME_OF_SELECTION_CHANGE);
2086 0 : notification.SetData(observer->mSelectionData);
2087 :
2088 0 : MOZ_RELEASE_ASSERT(observer->mSendingNotification ==
2089 : NOTIFY_IME_OF_NOTHING);
2090 0 : observer->mSendingNotification = NOTIFY_IME_OF_SELECTION_CHANGE;
2091 0 : IMEStateManager::NotifyIME(notification, observer->mWidget);
2092 0 : observer->mSendingNotification = NOTIFY_IME_OF_NOTHING;
2093 :
2094 0 : MOZ_LOG(sIMECOLog, LogLevel::Debug,
2095 : ("0x%p IMEContentObserver::IMENotificationSender::"
2096 : "SendSelectionChange(), sent NOTIFY_IME_OF_SELECTION_CHANGE", this));
2097 : }
2098 :
2099 : void
2100 0 : IMEContentObserver::IMENotificationSender::SendTextChange()
2101 : {
2102 0 : RefPtr<IMEContentObserver> observer = GetObserver();
2103 0 : if (!observer) {
2104 0 : return;
2105 : }
2106 :
2107 0 : if (!CanNotifyIME(eChangeEventType_Text)) {
2108 0 : MOZ_LOG(sIMECOLog, LogLevel::Debug,
2109 : ("0x%p IMEContentObserver::IMENotificationSender::"
2110 : "SendTextChange(), FAILED, due to impossible to notify IME of text "
2111 : "change", this));
2112 0 : return;
2113 : }
2114 :
2115 0 : if (!IsSafeToNotifyIME(eChangeEventType_Text)) {
2116 0 : MOZ_LOG(sIMECOLog, LogLevel::Debug,
2117 : ("0x%p IMEContentObserver::IMENotificationSender::"
2118 : "SendTextChange(), retrying to send NOTIFY_IME_OF_TEXT_CHANGE...",
2119 : this));
2120 0 : observer->PostTextChangeNotification();
2121 0 : return;
2122 : }
2123 :
2124 : // If text change notification is unnecessary anymore, just cancel it.
2125 0 : if (!observer->NeedsTextChangeNotification()) {
2126 0 : MOZ_LOG(sIMECOLog, LogLevel::Warning,
2127 : ("0x%p IMEContentObserver::IMENotificationSender::"
2128 : "SendTextChange(), canceling sending NOTIFY_IME_OF_TEXT_CHANGE",
2129 : this));
2130 0 : observer->CancelNotifyingIMEOfTextChange();
2131 0 : return;
2132 : }
2133 :
2134 0 : MOZ_LOG(sIMECOLog, LogLevel::Info,
2135 : ("0x%p IMEContentObserver::IMENotificationSender::"
2136 : "SendTextChange(), sending NOTIFY_IME_OF_TEXT_CHANGE... "
2137 : "mIMEContentObserver={ mTextChangeData=%s }",
2138 : this, TextChangeDataToString(observer->mTextChangeData).get()));
2139 :
2140 0 : IMENotification notification(NOTIFY_IME_OF_TEXT_CHANGE);
2141 0 : notification.SetData(observer->mTextChangeData);
2142 0 : observer->mTextChangeData.Clear();
2143 :
2144 0 : MOZ_RELEASE_ASSERT(observer->mSendingNotification ==
2145 : NOTIFY_IME_OF_NOTHING);
2146 0 : observer->mSendingNotification = NOTIFY_IME_OF_TEXT_CHANGE;
2147 0 : IMEStateManager::NotifyIME(notification, observer->mWidget);
2148 0 : observer->mSendingNotification = NOTIFY_IME_OF_NOTHING;
2149 :
2150 0 : MOZ_LOG(sIMECOLog, LogLevel::Debug,
2151 : ("0x%p IMEContentObserver::IMENotificationSender::"
2152 : "SendTextChange(), sent NOTIFY_IME_OF_TEXT_CHANGE", this));
2153 : }
2154 :
2155 : void
2156 0 : IMEContentObserver::IMENotificationSender::SendPositionChange()
2157 : {
2158 0 : RefPtr<IMEContentObserver> observer = GetObserver();
2159 0 : if (!observer) {
2160 0 : return;
2161 : }
2162 :
2163 0 : if (!CanNotifyIME(eChangeEventType_Position)) {
2164 0 : MOZ_LOG(sIMECOLog, LogLevel::Debug,
2165 : ("0x%p IMEContentObserver::IMENotificationSender::"
2166 : "SendPositionChange(), FAILED, due to impossible to notify IME of "
2167 : "position change", this));
2168 0 : return;
2169 : }
2170 :
2171 0 : if (!IsSafeToNotifyIME(eChangeEventType_Position)) {
2172 0 : MOZ_LOG(sIMECOLog, LogLevel::Debug,
2173 : ("0x%p IMEContentObserver::IMENotificationSender::"
2174 : "SendPositionChange(), retrying to send "
2175 : "NOTIFY_IME_OF_POSITION_CHANGE...", this));
2176 0 : observer->PostPositionChangeNotification();
2177 0 : return;
2178 : }
2179 :
2180 : // If position change notification is unnecessary anymore, just cancel it.
2181 0 : if (!observer->NeedsPositionChangeNotification()) {
2182 0 : MOZ_LOG(sIMECOLog, LogLevel::Warning,
2183 : ("0x%p IMEContentObserver::IMENotificationSender::"
2184 : "SendPositionChange(), canceling sending NOTIFY_IME_OF_POSITION_CHANGE",
2185 : this));
2186 0 : observer->CancelNotifyingIMEOfPositionChange();
2187 0 : return;
2188 : }
2189 :
2190 0 : MOZ_LOG(sIMECOLog, LogLevel::Info,
2191 : ("0x%p IMEContentObserver::IMENotificationSender::"
2192 : "SendPositionChange(), sending NOTIFY_IME_OF_POSITION_CHANGE...", this));
2193 :
2194 0 : MOZ_RELEASE_ASSERT(observer->mSendingNotification ==
2195 : NOTIFY_IME_OF_NOTHING);
2196 0 : observer->mSendingNotification = NOTIFY_IME_OF_POSITION_CHANGE;
2197 0 : IMEStateManager::NotifyIME(IMENotification(NOTIFY_IME_OF_POSITION_CHANGE),
2198 0 : observer->mWidget);
2199 0 : observer->mSendingNotification = NOTIFY_IME_OF_NOTHING;
2200 :
2201 0 : MOZ_LOG(sIMECOLog, LogLevel::Debug,
2202 : ("0x%p IMEContentObserver::IMENotificationSender::"
2203 : "SendPositionChange(), sent NOTIFY_IME_OF_POSITION_CHANGE", this));
2204 : }
2205 :
2206 : void
2207 0 : IMEContentObserver::IMENotificationSender::SendCompositionEventHandled()
2208 : {
2209 0 : RefPtr<IMEContentObserver> observer = GetObserver();
2210 0 : if (!observer) {
2211 0 : return;
2212 : }
2213 :
2214 0 : if (!CanNotifyIME(eChangeEventType_CompositionEventHandled)) {
2215 0 : MOZ_LOG(sIMECOLog, LogLevel::Debug,
2216 : ("0x%p IMEContentObserver::IMENotificationSender::"
2217 : "SendCompositionEventHandled(), FAILED, due to impossible to notify "
2218 : "IME of composition event handled", this));
2219 0 : return;
2220 : }
2221 :
2222 0 : if (!IsSafeToNotifyIME(eChangeEventType_CompositionEventHandled)) {
2223 0 : MOZ_LOG(sIMECOLog, LogLevel::Debug,
2224 : ("0x%p IMEContentObserver::IMENotificationSender::"
2225 : "SendCompositionEventHandled(), retrying to send "
2226 : "NOTIFY_IME_OF_POSITION_CHANGE...", this));
2227 0 : observer->PostCompositionEventHandledNotification();
2228 0 : return;
2229 : }
2230 :
2231 0 : MOZ_LOG(sIMECOLog, LogLevel::Info,
2232 : ("0x%p IMEContentObserver::IMENotificationSender::"
2233 : "SendCompositionEventHandled(), sending "
2234 : "NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED...", this));
2235 :
2236 0 : MOZ_RELEASE_ASSERT(observer->mSendingNotification ==
2237 : NOTIFY_IME_OF_NOTHING);
2238 0 : observer->mSendingNotification =
2239 : NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED;
2240 0 : IMEStateManager::NotifyIME(
2241 0 : IMENotification(NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED),
2242 0 : observer->mWidget);
2243 0 : observer->mSendingNotification = NOTIFY_IME_OF_NOTHING;
2244 :
2245 0 : MOZ_LOG(sIMECOLog, LogLevel::Debug,
2246 : ("0x%p IMEContentObserver::IMENotificationSender::"
2247 : "SendCompositionEventHandled(), sent "
2248 : "NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED", this));
2249 : }
2250 :
2251 : /******************************************************************************
2252 : * mozilla::IMEContentObserver::DocumentObservingHelper
2253 : ******************************************************************************/
2254 :
2255 : NS_IMPL_CYCLE_COLLECTION_CLASS(IMEContentObserver::DocumentObserver)
2256 :
2257 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IMEContentObserver::DocumentObserver)
2258 : // StopObserving() releases mIMEContentObserver and mDocument.
2259 0 : tmp->StopObserving();
2260 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
2261 :
2262 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IMEContentObserver::DocumentObserver)
2263 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIMEContentObserver)
2264 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument)
2265 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
2266 :
2267 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IMEContentObserver::DocumentObserver)
2268 0 : NS_INTERFACE_MAP_ENTRY(nsIDocumentObserver)
2269 0 : NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
2270 0 : NS_INTERFACE_MAP_ENTRY(nsISupports)
2271 0 : NS_INTERFACE_MAP_END
2272 :
2273 0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(IMEContentObserver::DocumentObserver)
2274 0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(IMEContentObserver::DocumentObserver)
2275 :
2276 : void
2277 0 : IMEContentObserver::DocumentObserver::Observe(nsIDocument* aDocument)
2278 : {
2279 0 : MOZ_ASSERT(aDocument);
2280 :
2281 : // Guarantee that aDocument won't be destroyed during a call of
2282 : // StopObserving().
2283 0 : RefPtr<nsIDocument> newDocument = aDocument;
2284 :
2285 0 : StopObserving();
2286 :
2287 0 : mDocument = newDocument.forget();
2288 0 : mDocument->AddObserver(this);
2289 0 : }
2290 :
2291 : void
2292 0 : IMEContentObserver::DocumentObserver::StopObserving()
2293 : {
2294 0 : if (!IsObserving()) {
2295 0 : return;
2296 : }
2297 :
2298 : // Grab IMEContentObserver which could be destroyed during method calls.
2299 0 : RefPtr<IMEContentObserver> observer = mIMEContentObserver.forget();
2300 :
2301 : // Stop observing the document first.
2302 0 : RefPtr<nsIDocument> document = mDocument.forget();
2303 0 : document->RemoveObserver(this);
2304 :
2305 : // Notify IMEContentObserver of ending of document updates if this already
2306 : // notified it of beginning of document updates.
2307 0 : for (; IsUpdating(); --mDocumentUpdating) {
2308 : // FYI: IsUpdating() returns true until mDocumentUpdating becomes 0.
2309 : // However, IsObserving() returns false now because mDocument was
2310 : // already cleared above. Therefore, this method won't be called
2311 : // recursively.
2312 0 : observer->EndDocumentUpdate();
2313 : }
2314 : }
2315 :
2316 : void
2317 0 : IMEContentObserver::DocumentObserver::Destroy()
2318 : {
2319 0 : StopObserving();
2320 0 : mIMEContentObserver = nullptr;
2321 0 : }
2322 :
2323 : void
2324 0 : IMEContentObserver::DocumentObserver::BeginUpdate(nsIDocument* aDocument,
2325 : nsUpdateType aUpdateType)
2326 : {
2327 0 : if (NS_WARN_IF(Destroyed()) || NS_WARN_IF(!IsObserving())) {
2328 0 : return;
2329 : }
2330 0 : if (!(aUpdateType & UPDATE_CONTENT_MODEL)) {
2331 0 : return;
2332 : }
2333 0 : mDocumentUpdating++;
2334 0 : mIMEContentObserver->BeginDocumentUpdate();
2335 : }
2336 :
2337 : void
2338 0 : IMEContentObserver::DocumentObserver::EndUpdate(nsIDocument* aDocument,
2339 : nsUpdateType aUpdateType)
2340 : {
2341 0 : if (NS_WARN_IF(Destroyed()) || NS_WARN_IF(!IsObserving()) ||
2342 0 : NS_WARN_IF(!IsUpdating())) {
2343 0 : return;
2344 : }
2345 0 : if (!(aUpdateType & UPDATE_CONTENT_MODEL)) {
2346 0 : return;
2347 : }
2348 0 : mDocumentUpdating--;
2349 0 : mIMEContentObserver->EndDocumentUpdate();
2350 : }
2351 :
2352 : } // namespace mozilla
|