1 /* -*- Mode: C++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 #ifndef mozilla_widget_IMEData_h_ 7 #define mozilla_widget_IMEData_h_ 8 9 #include "nsPoint.h" 10 #include "nsRect.h" 11 #include "nsString.h" 12 #include "nsXULAppAPI.h" 13 #include "Units.h" 14 15 class nsIWidget; 16 17 namespace mozilla { 18 19 class WritingMode; 20 21 namespace widget { 22 23 /** 24 * Preference for receiving IME updates 25 * 26 * If mWantUpdates is not NOTIFY_NOTHING, nsTextStateManager will observe text 27 * change and/or selection change and call nsIWidget::NotifyIME() with 28 * NOTIFY_IME_OF_SELECTION_CHANGE and/or NOTIFY_IME_OF_TEXT_CHANGE. 29 * Please note that the text change observing cost is very expensive especially 30 * on an HTML editor has focus. 31 * If the IME implementation on a particular platform doesn't care about 32 * NOTIFY_IME_OF_SELECTION_CHANGE and/or NOTIFY_IME_OF_TEXT_CHANGE, 33 * they should set mWantUpdates to NOTIFY_NOTHING to avoid the cost. 34 * If the IME implementation needs notifications even while our process is 35 * deactive, it should also set NOTIFY_DURING_DEACTIVE. 36 */ 37 struct IMENotificationRequests final { 38 typedef uint8_t Notifications; 39 40 enum : Notifications { 41 NOTIFY_NOTHING = 0, 42 NOTIFY_TEXT_CHANGE = 1 << 1, 43 NOTIFY_POSITION_CHANGE = 1 << 2, 44 // NOTIFY_MOUSE_BUTTON_EVENT_ON_CHAR is used when mouse button is pressed 45 // or released on a character in the focused editor. The notification is 46 // notified to IME as a mouse event. If it's consumed by IME, NotifyIME() 47 // returns NS_SUCCESS_EVENT_CONSUMED. Otherwise, it returns NS_OK if it's 48 // handled without any error. 49 NOTIFY_MOUSE_BUTTON_EVENT_ON_CHAR = 1 << 3, 50 // NOTE: NOTIFY_DURING_DEACTIVE isn't supported in environments where two 51 // or more compositions are possible. E.g., Mac and Linux (GTK). 52 NOTIFY_DURING_DEACTIVE = 1 << 7, 53 54 NOTIFY_ALL = NOTIFY_TEXT_CHANGE | NOTIFY_POSITION_CHANGE | 55 NOTIFY_MOUSE_BUTTON_EVENT_ON_CHAR, 56 }; 57 IMENotificationRequestsfinal58 IMENotificationRequests() : mWantUpdates(NOTIFY_NOTHING) {} 59 IMENotificationRequestsfinal60 explicit IMENotificationRequests(Notifications aWantUpdates) 61 : mWantUpdates(aWantUpdates) {} 62 63 IMENotificationRequests operator|( 64 const IMENotificationRequests& aOther) const { 65 return IMENotificationRequests(aOther.mWantUpdates | mWantUpdates); 66 } 67 IMENotificationRequests& operator|=(const IMENotificationRequests& aOther) { 68 mWantUpdates |= aOther.mWantUpdates; 69 return *this; 70 } 71 bool operator==(const IMENotificationRequests& aOther) const { 72 return mWantUpdates == aOther.mWantUpdates; 73 } 74 WantTextChangefinal75 bool WantTextChange() const { return !!(mWantUpdates & NOTIFY_TEXT_CHANGE); } 76 WantPositionChangedfinal77 bool WantPositionChanged() const { 78 return !!(mWantUpdates & NOTIFY_POSITION_CHANGE); 79 } 80 WantChangesfinal81 bool WantChanges() const { return WantTextChange(); } 82 WantMouseButtonEventOnCharfinal83 bool WantMouseButtonEventOnChar() const { 84 return !!(mWantUpdates & NOTIFY_MOUSE_BUTTON_EVENT_ON_CHAR); 85 } 86 WantDuringDeactivefinal87 bool WantDuringDeactive() const { 88 return !!(mWantUpdates & NOTIFY_DURING_DEACTIVE); 89 } 90 91 Notifications mWantUpdates; 92 }; 93 94 /** 95 * Contains IMEStatus plus information about the current 96 * input context that the IME can use as hints if desired. 97 */ 98 99 struct IMEState final { 100 /** 101 * IME enabled states, the mEnabled value of 102 * SetInputContext()/GetInputContext() should be one value of following 103 * values. 104 * 105 * WARNING: If you change these values, you also need to edit: 106 * nsIDOMWindowUtils.idl 107 * nsContentUtils::GetWidgetStatusFromIMEStatus 108 */ 109 enum Enabled { 110 /** 111 * 'Disabled' means the user cannot use IME. So, the IME open state should 112 * be 'closed' during 'disabled'. 113 */ 114 DISABLED, 115 /** 116 * 'Enabled' means the user can use IME. 117 */ 118 ENABLED, 119 /** 120 * 'Password' state is a special case for the password editors. 121 * E.g., on mac, the password editors should disable the non-Roman 122 * keyboard layouts at getting focus. Thus, the password editor may have 123 * special rules on some platforms. 124 */ 125 PASSWORD, 126 /** 127 * This state is used when a plugin is focused. 128 * When a plug-in is focused content, we should send native events 129 * directly. Because we don't process some native events, but they may 130 * be needed by the plug-in. 131 */ 132 PLUGIN, 133 /** 134 * 'Unknown' is useful when you cache this enum. So, this shouldn't be 135 * used with nsIWidget::SetInputContext(). 136 */ 137 UNKNOWN 138 }; 139 Enabled mEnabled; 140 141 /** 142 * IME open states the mOpen value of SetInputContext() should be one value of 143 * OPEN, CLOSE or DONT_CHANGE_OPEN_STATE. GetInputContext() should return 144 * OPEN, CLOSE or OPEN_STATE_NOT_SUPPORTED. 145 */ 146 enum Open { 147 /** 148 * 'Unsupported' means the platform cannot return actual IME open state. 149 * This value is used only by GetInputContext(). 150 */ 151 OPEN_STATE_NOT_SUPPORTED, 152 /** 153 * 'Don't change' means the widget shouldn't change IME open state when 154 * SetInputContext() is called. 155 */ 156 DONT_CHANGE_OPEN_STATE = OPEN_STATE_NOT_SUPPORTED, 157 /** 158 * 'Open' means that IME should compose in its primary language (or latest 159 * input mode except direct ASCII character input mode). Even if IME is 160 * opened by this value, users should be able to close IME by theirselves. 161 * Web contents can specify this value by |ime-mode: active;|. 162 */ 163 OPEN, 164 /** 165 * 'Closed' means that IME shouldn't handle key events (or should handle 166 * as ASCII character inputs on mobile device). Even if IME is closed by 167 * this value, users should be able to open IME by theirselves. 168 * Web contents can specify this value by |ime-mode: inactive;|. 169 */ 170 CLOSED 171 }; 172 Open mOpen; 173 IMEStatefinal174 IMEState() : mEnabled(ENABLED), mOpen(DONT_CHANGE_OPEN_STATE) {} 175 176 explicit IMEState(Enabled aEnabled, Open aOpen = DONT_CHANGE_OPEN_STATE) mEnabledfinal177 : mEnabled(aEnabled), mOpen(aOpen) {} 178 179 // Returns true if the user can input characters. 180 // This means that a plain text editor, an HTML editor, a password editor or 181 // a plain text editor whose ime-mode is "disabled". IsEditablefinal182 bool IsEditable() const { 183 return mEnabled == ENABLED || mEnabled == PASSWORD; 184 } 185 // Returns true if the user might be able to input characters. 186 // This means that a plain text editor, an HTML editor, a password editor, 187 // a plain text editor whose ime-mode is "disabled" or a windowless plugin 188 // has focus. MaybeEditablefinal189 bool MaybeEditable() const { return IsEditable() || mEnabled == PLUGIN; } 190 }; 191 192 // NS_ONLY_ONE_NATIVE_IME_CONTEXT is a special value of native IME context. 193 // If there can be only one IME composition in a process, this can be used. 194 #define NS_ONLY_ONE_NATIVE_IME_CONTEXT \ 195 (reinterpret_cast<void*>(static_cast<intptr_t>(-1))) 196 197 struct NativeIMEContext final { 198 // Pointer to native IME context. Typically this is the result of 199 // nsIWidget::GetNativeData(NS_RAW_NATIVE_IME_CONTEXT) in the parent process. 200 // See also NS_ONLY_ONE_NATIVE_IME_CONTEXT. 201 uintptr_t mRawNativeIMEContext; 202 // Process ID of the origin of mNativeIMEContext. 203 uint64_t mOriginProcessID; 204 NativeIMEContextfinal205 NativeIMEContext() { Init(nullptr); } 206 NativeIMEContextfinal207 explicit NativeIMEContext(nsIWidget* aWidget) { Init(aWidget); } 208 IsValidfinal209 bool IsValid() const { 210 return mRawNativeIMEContext && 211 mOriginProcessID != static_cast<uintptr_t>(-1); 212 } 213 214 void Init(nsIWidget* aWidget); InitWithRawNativeIMEContextfinal215 void InitWithRawNativeIMEContext(const void* aRawNativeIMEContext) { 216 InitWithRawNativeIMEContext(const_cast<void*>(aRawNativeIMEContext)); 217 } 218 void InitWithRawNativeIMEContext(void* aRawNativeIMEContext); 219 220 bool operator==(const NativeIMEContext& aOther) const { 221 return mRawNativeIMEContext == aOther.mRawNativeIMEContext && 222 mOriginProcessID == aOther.mOriginProcessID; 223 } 224 bool operator!=(const NativeIMEContext& aOther) const { 225 return !(*this == aOther); 226 } 227 }; 228 229 struct InputContext final { InputContextfinal230 InputContext() 231 : mOrigin(XRE_IsParentProcess() ? ORIGIN_MAIN : ORIGIN_CONTENT), 232 mMayBeIMEUnaware(false), 233 mHasHandledUserInput(false), 234 mInPrivateBrowsing(false) {} 235 236 // If InputContext instance is a static variable, any heap allocated stuff 237 // of its members need to be deleted at XPCOM shutdown. Otherwise, it's 238 // detected as memory leak. ShutDownfinal239 void ShutDown() { 240 // The buffer for nsString will be released with a call of SetCapacity(0). 241 // Truncate() isn't enough because it just sets length to 0. 242 mHTMLInputType.SetCapacity(0); 243 mHTMLInputInputmode.SetCapacity(0); 244 mActionHint.SetCapacity(0); 245 } 246 IsPasswordEditorfinal247 bool IsPasswordEditor() const { 248 return mHTMLInputType.LowerCaseEqualsLiteral("password"); 249 } 250 251 IMEState mIMEState; 252 253 /* The type of the input if the input is a html input field */ 254 nsString mHTMLInputType; 255 256 /* The type of the inputmode */ 257 nsString mHTMLInputInputmode; 258 259 /* A hint for the action that is performed when the input is submitted */ 260 nsString mActionHint; 261 262 /** 263 * mOrigin indicates whether this focus event refers to main or remote 264 * content. 265 */ 266 enum Origin { 267 // Adjusting focus of content on the main process 268 ORIGIN_MAIN, 269 // Adjusting focus of content in a remote process 270 ORIGIN_CONTENT 271 }; 272 Origin mOrigin; 273 274 /* True if the webapp may be unaware of IME events such as input event or 275 * composiion events. This enables a key-events-only mode on Android for 276 * compatibility with webapps relying on key listeners. */ 277 bool mMayBeIMEUnaware; 278 279 /** 280 * True if the document has ever received user input 281 */ 282 bool mHasHandledUserInput; 283 284 /* Whether the owning document of the input element has been loaded 285 * in private browsing mode. */ 286 bool mInPrivateBrowsing; 287 IsOriginMainProcessfinal288 bool IsOriginMainProcess() const { return mOrigin == ORIGIN_MAIN; } 289 IsOriginContentProcessfinal290 bool IsOriginContentProcess() const { return mOrigin == ORIGIN_CONTENT; } 291 IsOriginCurrentProcessfinal292 bool IsOriginCurrentProcess() const { 293 if (XRE_IsParentProcess()) { 294 return IsOriginMainProcess(); 295 } 296 return IsOriginContentProcess(); 297 } 298 }; 299 300 // FYI: Implemented in nsBaseWidget.cpp 301 const char* ToChar(InputContext::Origin aOrigin); 302 303 struct InputContextAction final { 304 /** 305 * mCause indicates what action causes calling nsIWidget::SetInputContext(). 306 * It must be one of following values. 307 */ 308 enum Cause { 309 // The cause is unknown but originated from content. Focus might have been 310 // changed by content script. 311 CAUSE_UNKNOWN, 312 // The cause is unknown but originated from chrome. Focus might have been 313 // changed by chrome script. 314 CAUSE_UNKNOWN_CHROME, 315 // The cause is user's keyboard operation. 316 CAUSE_KEY, 317 // The cause is user's mouse operation. 318 CAUSE_MOUSE, 319 // The cause is user's touch operation (implies mouse) 320 CAUSE_TOUCH, 321 // The cause is unknown but it occurs during user input except keyboard 322 // input. E.g., an event handler of a user input event moves focus. 323 CAUSE_UNKNOWN_DURING_NON_KEYBOARD_INPUT, 324 // The cause is unknown but it occurs during keyboard input. 325 CAUSE_UNKNOWN_DURING_KEYBOARD_INPUT, 326 }; 327 Cause mCause; 328 329 /** 330 * mFocusChange indicates what happened for focus. 331 */ 332 enum FocusChange { 333 FOCUS_NOT_CHANGED, 334 // A content got focus. 335 GOT_FOCUS, 336 // Focused content lost focus. 337 LOST_FOCUS, 338 // Menu got pseudo focus that means focused content isn't changed but 339 // keyboard events will be handled by menu. 340 MENU_GOT_PSEUDO_FOCUS, 341 // Menu lost pseudo focus that means focused content will handle keyboard 342 // events. 343 MENU_LOST_PSEUDO_FOCUS, 344 // The widget is created. When a widget is crated, it may need to notify 345 // IME module to initialize its native IME context. In such case, this is 346 // used. I.e., this isn't used by IMEStateManager. 347 WIDGET_CREATED 348 }; 349 FocusChange mFocusChange; 350 ContentGotFocusByTrustedCausefinal351 bool ContentGotFocusByTrustedCause() const { 352 return (mFocusChange == GOT_FOCUS && mCause != CAUSE_UNKNOWN); 353 } 354 UserMightRequestOpenVKBfinal355 bool UserMightRequestOpenVKB() const { 356 // If focus is changed, user must not request to open VKB. 357 if (mFocusChange != FOCUS_NOT_CHANGED) { 358 return false; 359 } 360 switch (mCause) { 361 // If user clicks or touches focused editor, user must request to open 362 // VKB. 363 case CAUSE_MOUSE: 364 case CAUSE_TOUCH: 365 // If script does something during a user input and that causes changing 366 // input context, user might request to open VKB. E.g., user clicks 367 // dummy editor and JS moves focus to an actual editable node. However, 368 // this should return false if the user input is a keyboard event since 369 // physical keyboard operation shouldn't cause opening VKB. 370 case CAUSE_UNKNOWN_DURING_NON_KEYBOARD_INPUT: 371 return true; 372 default: 373 return false; 374 } 375 } 376 377 /** 378 * IsHandlingUserInput() returns true if it's caused by a user action directly 379 * or it's caused by script or something but it occurred while we're handling 380 * a user action. E.g., when it's caused by Element.focus() in an event 381 * handler of a user input, this returns true. 382 */ IsHandlingUserInputfinal383 static bool IsHandlingUserInput(Cause aCause) { 384 switch (aCause) { 385 case CAUSE_KEY: 386 case CAUSE_MOUSE: 387 case CAUSE_TOUCH: 388 case CAUSE_UNKNOWN_DURING_NON_KEYBOARD_INPUT: 389 case CAUSE_UNKNOWN_DURING_KEYBOARD_INPUT: 390 return true; 391 default: 392 return false; 393 } 394 } 395 IsHandlingUserInputfinal396 bool IsHandlingUserInput() const { return IsHandlingUserInput(mCause); } 397 InputContextActionfinal398 InputContextAction() 399 : mCause(CAUSE_UNKNOWN), mFocusChange(FOCUS_NOT_CHANGED) {} 400 401 explicit InputContextAction(Cause aCause, 402 FocusChange aFocusChange = FOCUS_NOT_CHANGED) mCausefinal403 : mCause(aCause), mFocusChange(aFocusChange) {} 404 }; 405 406 // IMEMessage is shared by IMEStateManager and TextComposition. 407 // Update values in GeckoEditable.java if you make changes here. 408 // XXX Negative values are used in Android... 409 typedef int8_t IMEMessageType; 410 enum IMEMessage : IMEMessageType { 411 // This is used by IMENotification internally. This means that the instance 412 // hasn't been initialized yet. 413 NOTIFY_IME_OF_NOTHING, 414 // An editable content is getting focus 415 NOTIFY_IME_OF_FOCUS, 416 // An editable content is losing focus 417 NOTIFY_IME_OF_BLUR, 418 // Selection in the focused editable content is changed 419 NOTIFY_IME_OF_SELECTION_CHANGE, 420 // Text in the focused editable content is changed 421 NOTIFY_IME_OF_TEXT_CHANGE, 422 // Notified when a dispatched composition event is handled by the 423 // contents. This must be notified after the other notifications. 424 // Note that if a remote process has focus, this is notified only once when 425 // all dispatched events are handled completely. So, the receiver shouldn't 426 // count number of received this notification for comparing with the number 427 // of dispatched events. 428 // NOTE: If a composition event causes moving focus from the focused editor, 429 // this notification may not be notified as usual. Even in such case, 430 // NOTIFY_IME_OF_BLUR is always sent. So, notification listeners 431 // should tread the blur notification as including this if there is 432 // pending composition events. 433 NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED, 434 // Position or size of focused element may be changed. 435 NOTIFY_IME_OF_POSITION_CHANGE, 436 // Mouse button event is fired on a character in focused editor 437 NOTIFY_IME_OF_MOUSE_BUTTON_EVENT, 438 // Request to commit current composition to IME 439 // (some platforms may not support) 440 REQUEST_TO_COMMIT_COMPOSITION, 441 // Request to cancel current composition to IME 442 // (some platforms may not support) 443 REQUEST_TO_CANCEL_COMPOSITION 444 }; 445 446 // FYI: Implemented in nsBaseWidget.cpp 447 const char* ToChar(IMEMessage aIMEMessage); 448 449 struct IMENotification final { IMENotificationfinal450 IMENotification() : mMessage(NOTIFY_IME_OF_NOTHING) {} 451 IMENotificationfinal452 IMENotification(const IMENotification& aOther) 453 : mMessage(NOTIFY_IME_OF_NOTHING) { 454 Assign(aOther); 455 } 456 ~IMENotificationfinal457 ~IMENotification() { Clear(); } 458 IMENotificationfinal459 MOZ_IMPLICIT IMENotification(IMEMessage aMessage) : mMessage(aMessage) { 460 switch (aMessage) { 461 case NOTIFY_IME_OF_SELECTION_CHANGE: 462 mSelectionChangeData.mString = new nsString(); 463 mSelectionChangeData.Clear(); 464 break; 465 case NOTIFY_IME_OF_TEXT_CHANGE: 466 mTextChangeData.Clear(); 467 break; 468 case NOTIFY_IME_OF_MOUSE_BUTTON_EVENT: 469 mMouseButtonEventData.mEventMessage = eVoidEvent; 470 mMouseButtonEventData.mOffset = UINT32_MAX; 471 mMouseButtonEventData.mCursorPos.Set(nsIntPoint(0, 0)); 472 mMouseButtonEventData.mCharRect.Set(nsIntRect(0, 0, 0, 0)); 473 mMouseButtonEventData.mButton = -1; 474 mMouseButtonEventData.mButtons = 0; 475 mMouseButtonEventData.mModifiers = 0; 476 break; 477 default: 478 break; 479 } 480 } 481 Assignfinal482 void Assign(const IMENotification& aOther) { 483 bool changingMessage = mMessage != aOther.mMessage; 484 if (changingMessage) { 485 Clear(); 486 mMessage = aOther.mMessage; 487 } 488 switch (mMessage) { 489 case NOTIFY_IME_OF_SELECTION_CHANGE: 490 if (changingMessage) { 491 mSelectionChangeData.mString = new nsString(); 492 } 493 mSelectionChangeData.Assign(aOther.mSelectionChangeData); 494 break; 495 case NOTIFY_IME_OF_TEXT_CHANGE: 496 mTextChangeData = aOther.mTextChangeData; 497 break; 498 case NOTIFY_IME_OF_MOUSE_BUTTON_EVENT: 499 mMouseButtonEventData = aOther.mMouseButtonEventData; 500 break; 501 default: 502 break; 503 } 504 } 505 506 IMENotification& operator=(const IMENotification& aOther) { 507 Assign(aOther); 508 return *this; 509 } 510 Clearfinal511 void Clear() { 512 if (mMessage == NOTIFY_IME_OF_SELECTION_CHANGE) { 513 MOZ_ASSERT(mSelectionChangeData.mString); 514 delete mSelectionChangeData.mString; 515 mSelectionChangeData.mString = nullptr; 516 } 517 mMessage = NOTIFY_IME_OF_NOTHING; 518 } 519 HasNotificationfinal520 bool HasNotification() const { return mMessage != NOTIFY_IME_OF_NOTHING; } 521 MergeWithfinal522 void MergeWith(const IMENotification& aNotification) { 523 switch (mMessage) { 524 case NOTIFY_IME_OF_NOTHING: 525 MOZ_ASSERT(aNotification.mMessage != NOTIFY_IME_OF_NOTHING); 526 Assign(aNotification); 527 break; 528 case NOTIFY_IME_OF_SELECTION_CHANGE: 529 MOZ_ASSERT(aNotification.mMessage == NOTIFY_IME_OF_SELECTION_CHANGE); 530 mSelectionChangeData.Assign(aNotification.mSelectionChangeData); 531 break; 532 case NOTIFY_IME_OF_TEXT_CHANGE: 533 MOZ_ASSERT(aNotification.mMessage == NOTIFY_IME_OF_TEXT_CHANGE); 534 mTextChangeData += aNotification.mTextChangeData; 535 break; 536 case NOTIFY_IME_OF_POSITION_CHANGE: 537 case NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED: 538 MOZ_ASSERT(aNotification.mMessage == mMessage); 539 break; 540 default: 541 MOZ_CRASH("Merging notification isn't supported"); 542 break; 543 } 544 } 545 546 IMEMessage mMessage; 547 548 struct Point { 549 int32_t mX; 550 int32_t mY; 551 Setfinal::Point552 void Set(const nsIntPoint& aPoint) { 553 mX = aPoint.x; 554 mY = aPoint.y; 555 } AsIntPointfinal::Point556 nsIntPoint AsIntPoint() const { return nsIntPoint(mX, mY); } 557 }; 558 559 struct Rect { 560 int32_t mX; 561 int32_t mY; 562 int32_t mWidth; 563 int32_t mHeight; 564 Setfinal::Rect565 void Set(const nsIntRect& aRect) { 566 aRect.GetRect(&mX, &mY, &mWidth, &mHeight); 567 } AsIntRectfinal::Rect568 nsIntRect AsIntRect() const { return nsIntRect(mX, mY, mWidth, mHeight); } 569 }; 570 571 // NOTIFY_IME_OF_SELECTION_CHANGE specific data 572 struct SelectionChangeDataBase { 573 // Selection range. 574 uint32_t mOffset; 575 576 // Selected string 577 nsString* mString; 578 579 // Writing mode at the selection. 580 uint8_t mWritingMode; 581 582 bool mReversed; 583 bool mCausedByComposition; 584 bool mCausedBySelectionEvent; 585 bool mOccurredDuringComposition; 586 587 void SetWritingMode(const WritingMode& aWritingMode); 588 WritingMode GetWritingMode() const; 589 StartOffsetfinal::SelectionChangeDataBase590 uint32_t StartOffset() const { 591 return mOffset + (mReversed ? Length() : 0); 592 } EndOffsetfinal::SelectionChangeDataBase593 uint32_t EndOffset() const { return mOffset + (mReversed ? 0 : Length()); } Stringfinal::SelectionChangeDataBase594 const nsString& String() const { return *mString; } Lengthfinal::SelectionChangeDataBase595 uint32_t Length() const { return mString->Length(); } IsInInt32Rangefinal::SelectionChangeDataBase596 bool IsInInt32Range() const { return mOffset + Length() <= INT32_MAX; } IsCollapsedfinal::SelectionChangeDataBase597 bool IsCollapsed() const { return mString->IsEmpty(); } ClearSelectionDatafinal::SelectionChangeDataBase598 void ClearSelectionData() { 599 mOffset = UINT32_MAX; 600 mString->Truncate(); 601 mWritingMode = 0; 602 mReversed = false; 603 } Clearfinal::SelectionChangeDataBase604 void Clear() { 605 ClearSelectionData(); 606 mCausedByComposition = false; 607 mCausedBySelectionEvent = false; 608 mOccurredDuringComposition = false; 609 } IsValidfinal::SelectionChangeDataBase610 bool IsValid() const { return mOffset != UINT32_MAX; } Assignfinal::SelectionChangeDataBase611 void Assign(const SelectionChangeDataBase& aOther) { 612 mOffset = aOther.mOffset; 613 *mString = aOther.String(); 614 mWritingMode = aOther.mWritingMode; 615 mReversed = aOther.mReversed; 616 AssignReason(aOther.mCausedByComposition, aOther.mCausedBySelectionEvent, 617 aOther.mOccurredDuringComposition); 618 } AssignReasonfinal::SelectionChangeDataBase619 void AssignReason(bool aCausedByComposition, bool aCausedBySelectionEvent, 620 bool aOccurredDuringComposition) { 621 mCausedByComposition = aCausedByComposition; 622 mCausedBySelectionEvent = aCausedBySelectionEvent; 623 mOccurredDuringComposition = aOccurredDuringComposition; 624 } 625 }; 626 627 // SelectionChangeDataBase cannot have constructors because it's used in 628 // the union. Therefore, SelectionChangeData should only implement 629 // constructors. In other words, add other members to 630 // SelectionChangeDataBase. 631 struct SelectionChangeData final : public SelectionChangeDataBase { SelectionChangeDatafinal::final632 SelectionChangeData() { 633 mString = &mStringInstance; 634 Clear(); 635 } SelectionChangeDatafinal::final636 explicit SelectionChangeData(const SelectionChangeDataBase& aOther) { 637 mString = &mStringInstance; 638 Assign(aOther); 639 } SelectionChangeDatafinal::final640 SelectionChangeData(const SelectionChangeData& aOther) { 641 mString = &mStringInstance; 642 Assign(aOther); 643 } 644 SelectionChangeData& operator=(const SelectionChangeDataBase& aOther) { 645 mString = &mStringInstance; 646 Assign(aOther); 647 return *this; 648 } 649 SelectionChangeData& operator=(const SelectionChangeData& aOther) { 650 mString = &mStringInstance; 651 Assign(aOther); 652 return *this; 653 } 654 655 private: 656 // When SelectionChangeData is used outside of union, it shouldn't create 657 // nsString instance in the heap as far as possible. 658 nsString mStringInstance; 659 }; 660 661 struct TextChangeDataBase { 662 // mStartOffset is the start offset of modified or removed text in 663 // original content and inserted text in new content. 664 uint32_t mStartOffset; 665 // mRemovalEndOffset is the end offset of modified or removed text in 666 // original content. If the value is same as mStartOffset, no text hasn't 667 // been removed yet. 668 uint32_t mRemovedEndOffset; 669 // mAddedEndOffset is the end offset of inserted text or same as 670 // mStartOffset if just removed. The vlaue is offset in the new content. 671 uint32_t mAddedEndOffset; 672 673 // Note that TextChangeDataBase may be the result of merging two or more 674 // changes especially in e10s mode. 675 676 // mCausedOnlyByComposition is true only when *all* merged changes are 677 // caused by composition. 678 bool mCausedOnlyByComposition; 679 // mIncludingChangesDuringComposition is true if at least one change which 680 // is not caused by composition occurred during the last composition. 681 // Note that if after the last composition is finished and there are some 682 // changes not caused by composition, this is set to false. 683 bool mIncludingChangesDuringComposition; 684 // mIncludingChangesWithoutComposition is true if there is at least one 685 // change which did occur when there wasn't a composition ongoing. 686 bool mIncludingChangesWithoutComposition; 687 OldLengthfinal::TextChangeDataBase688 uint32_t OldLength() const { 689 MOZ_ASSERT(IsValid()); 690 return mRemovedEndOffset - mStartOffset; 691 } NewLengthfinal::TextChangeDataBase692 uint32_t NewLength() const { 693 MOZ_ASSERT(IsValid()); 694 return mAddedEndOffset - mStartOffset; 695 } 696 697 // Positive if text is added. Negative if text is removed. Differencefinal::TextChangeDataBase698 int64_t Difference() const { return mAddedEndOffset - mRemovedEndOffset; } 699 IsInInt32Rangefinal::TextChangeDataBase700 bool IsInInt32Range() const { 701 MOZ_ASSERT(IsValid()); 702 return mStartOffset <= INT32_MAX && mRemovedEndOffset <= INT32_MAX && 703 mAddedEndOffset <= INT32_MAX; 704 } 705 IsValidfinal::TextChangeDataBase706 bool IsValid() const { 707 return !(mStartOffset == UINT32_MAX && !mRemovedEndOffset && 708 !mAddedEndOffset); 709 } 710 Clearfinal::TextChangeDataBase711 void Clear() { 712 mStartOffset = UINT32_MAX; 713 mRemovedEndOffset = mAddedEndOffset = 0; 714 } 715 716 void MergeWith(const TextChangeDataBase& aOther); 717 TextChangeDataBase& operator+=(const TextChangeDataBase& aOther) { 718 MergeWith(aOther); 719 return *this; 720 } 721 722 #ifdef DEBUG 723 void Test(); 724 #endif // #ifdef DEBUG 725 }; 726 727 // TextChangeDataBase cannot have constructors because they are used in union. 728 // Therefore, TextChangeData should only implement constructor. In other 729 // words, add other members to TextChangeDataBase. 730 struct TextChangeData : public TextChangeDataBase { TextChangeDatafinal::TextChangeData731 TextChangeData() { Clear(); } 732 TextChangeDatafinal::TextChangeData733 TextChangeData(uint32_t aStartOffset, uint32_t aRemovedEndOffset, 734 uint32_t aAddedEndOffset, bool aCausedByComposition, 735 bool aOccurredDuringComposition) { 736 MOZ_ASSERT(aRemovedEndOffset >= aStartOffset, 737 "removed end offset must not be smaller than start offset"); 738 MOZ_ASSERT(aAddedEndOffset >= aStartOffset, 739 "added end offset must not be smaller than start offset"); 740 mStartOffset = aStartOffset; 741 mRemovedEndOffset = aRemovedEndOffset; 742 mAddedEndOffset = aAddedEndOffset; 743 mCausedOnlyByComposition = aCausedByComposition; 744 mIncludingChangesDuringComposition = 745 !aCausedByComposition && aOccurredDuringComposition; 746 mIncludingChangesWithoutComposition = 747 !aCausedByComposition && !aOccurredDuringComposition; 748 } 749 }; 750 751 struct MouseButtonEventData { 752 // The value of WidgetEvent::mMessage 753 EventMessage mEventMessage; 754 // Character offset from the start of the focused editor under the cursor 755 uint32_t mOffset; 756 // Cursor position in pixels relative to the widget 757 Point mCursorPos; 758 // Character rect in pixels under the cursor relative to the widget 759 Rect mCharRect; 760 // The value of WidgetMouseEventBase::button and buttons 761 int16_t mButton; 762 int16_t mButtons; 763 // The value of WidgetInputEvent::modifiers 764 Modifiers mModifiers; 765 }; 766 767 union { 768 // NOTIFY_IME_OF_SELECTION_CHANGE specific data 769 SelectionChangeDataBase mSelectionChangeData; 770 771 // NOTIFY_IME_OF_TEXT_CHANGE specific data 772 TextChangeDataBase mTextChangeData; 773 774 // NOTIFY_IME_OF_MOUSE_BUTTON_EVENT specific data 775 MouseButtonEventData mMouseButtonEventData; 776 }; 777 SetDatafinal778 void SetData(const SelectionChangeDataBase& aSelectionChangeData) { 779 MOZ_RELEASE_ASSERT(mMessage == NOTIFY_IME_OF_SELECTION_CHANGE); 780 mSelectionChangeData.Assign(aSelectionChangeData); 781 } 782 SetDatafinal783 void SetData(const TextChangeDataBase& aTextChangeData) { 784 MOZ_RELEASE_ASSERT(mMessage == NOTIFY_IME_OF_TEXT_CHANGE); 785 mTextChangeData = aTextChangeData; 786 } 787 }; 788 789 struct CandidateWindowPosition { 790 // Upper left corner of the candidate window if mExcludeRect is false. 791 // Otherwise, the position currently interested. E.g., caret position. 792 LayoutDeviceIntPoint mPoint; 793 // Rect which shouldn't be overlapped with the candidate window. 794 // This is valid only when mExcludeRect is true. 795 LayoutDeviceIntRect mRect; 796 // See explanation of mPoint and mRect. 797 bool mExcludeRect; 798 }; 799 800 } // namespace widget 801 } // namespace mozilla 802 803 #endif // #ifndef mozilla_widget_IMEData_h_ 804