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