Coverage Report

Created: 2018-09-25 14:53

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