| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #ifndef SYNC_INTERNAL_API_PUBLIC_SYNC_MANAGER_H_ | 5 #ifndef SYNC_INTERNAL_API_PUBLIC_SYNC_MANAGER_H_ |
| 6 #define SYNC_INTERNAL_API_PUBLIC_SYNC_MANAGER_H_ | 6 #define SYNC_INTERNAL_API_PUBLIC_SYNC_MANAGER_H_ |
| 7 | 7 |
| 8 #include <string> | 8 #include <string> |
| 9 #include <vector> | 9 #include <vector> |
| 10 | 10 |
| (...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 81 // SyncManager encapsulates syncable::Directory and serves as the parent of all | 81 // SyncManager encapsulates syncable::Directory and serves as the parent of all |
| 82 // other objects in the sync API. If multiple threads interact with the same | 82 // other objects in the sync API. If multiple threads interact with the same |
| 83 // local sync repository (i.e. the same sqlite database), they should share a | 83 // local sync repository (i.e. the same sqlite database), they should share a |
| 84 // single SyncManager instance. The caller should typically create one | 84 // single SyncManager instance. The caller should typically create one |
| 85 // SyncManager for the lifetime of a user session. | 85 // SyncManager for the lifetime of a user session. |
| 86 // | 86 // |
| 87 // Unless stated otherwise, all methods of SyncManager should be called on the | 87 // Unless stated otherwise, all methods of SyncManager should be called on the |
| 88 // same thread. | 88 // same thread. |
| 89 class SyncManager { | 89 class SyncManager { |
| 90 public: | 90 public: |
| 91 // SyncInternal contains the implementation of SyncManager, while abstracting | |
| 92 // internal types from clients of the interface. | |
| 93 class SyncInternal; | |
| 94 | |
| 95 // An interface the embedding application implements to be notified | 91 // An interface the embedding application implements to be notified |
| 96 // on change events. Note that these methods may be called on *any* | 92 // on change events. Note that these methods may be called on *any* |
| 97 // thread. | 93 // thread. |
| 98 class ChangeDelegate { | 94 class ChangeDelegate { |
| 99 public: | 95 public: |
| 100 // Notify the delegate that changes have been applied to the sync model. | 96 // Notify the delegate that changes have been applied to the sync model. |
| 101 // | 97 // |
| 102 // This will be invoked on the same thread as on which ApplyChanges was | 98 // This will be invoked on the same thread as on which ApplyChanges was |
| 103 // called. |changes| is an array of size |change_count|, and contains the | 99 // called. |changes| is an array of size |change_count|, and contains the |
| 104 // ID of each individual item that was changed. |changes| exists only for | 100 // ID of each individual item that was changed. |changes| exists only for |
| (...skipping 235 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 340 protected: | 336 protected: |
| 341 virtual ~Observer(); | 337 virtual ~Observer(); |
| 342 }; | 338 }; |
| 343 | 339 |
| 344 enum TestingMode { | 340 enum TestingMode { |
| 345 NON_TEST, | 341 NON_TEST, |
| 346 TEST_ON_DISK, | 342 TEST_ON_DISK, |
| 347 TEST_IN_MEMORY, | 343 TEST_IN_MEMORY, |
| 348 }; | 344 }; |
| 349 | 345 |
| 350 // Create an uninitialized SyncManager. Callers must Init() before using. | 346 SyncManager(); |
| 351 explicit SyncManager(const std::string& name); | |
| 352 virtual ~SyncManager(); | 347 virtual ~SyncManager(); |
| 353 | 348 |
| 354 // Initialize the sync manager. |database_location| specifies the path of | 349 // Initialize the sync manager. |database_location| specifies the path of |
| 355 // the directory in which to locate a sqlite repository storing the syncer | 350 // the directory in which to locate a sqlite repository storing the syncer |
| 356 // backend state. Initialization will open the database, or create it if it | 351 // backend state. Initialization will open the database, or create it if it |
| 357 // does not already exist. Returns false on failure. | 352 // does not already exist. Returns false on failure. |
| 358 // |event_handler| is the JsEventHandler used to propagate events to | 353 // |event_handler| is the JsEventHandler used to propagate events to |
| 359 // chrome://sync-internals. |event_handler| may be uninitialized. | 354 // chrome://sync-internals. |event_handler| may be uninitialized. |
| 360 // |sync_server_and_path| and |sync_server_port| represent the Chrome sync | 355 // |sync_server_and_path| and |sync_server_port| represent the Chrome sync |
| 361 // server to use, and |use_ssl| specifies whether to communicate securely; | 356 // server to use, and |use_ssl| specifies whether to communicate securely; |
| 362 // the default is false. | 357 // the default is false. |
| 363 // |blocking_task_runner| is a TaskRunner to be used for tasks that | 358 // |blocking_task_runner| is a TaskRunner to be used for tasks that |
| 364 // may block on disk I/O. | 359 // may block on disk I/O. |
| 365 // |post_factory| will be owned internally and used to create | 360 // |post_factory| will be owned internally and used to create |
| 366 // instances of an HttpPostProvider. | 361 // instances of an HttpPostProvider. |
| 367 // |model_safe_worker| ownership is given to the SyncManager. | 362 // |model_safe_worker| ownership is given to the SyncManager. |
| 368 // |user_agent| is a 7-bit ASCII string suitable for use as the User-Agent | 363 // |user_agent| is a 7-bit ASCII string suitable for use as the User-Agent |
| 369 // HTTP header. Used internally when collecting stats to classify clients. | 364 // HTTP header. Used internally when collecting stats to classify clients. |
| 370 // |sync_notifier| is owned and used to listen for notifications. | 365 // |sync_notifier| is owned and used to listen for notifications. |
| 371 // |report_unrecoverable_error_function| may be NULL. | 366 // |report_unrecoverable_error_function| may be NULL. |
| 372 // | 367 // |
| 373 // TODO(akalin): Replace the |post_factory| parameter with a | 368 // TODO(akalin): Replace the |post_factory| parameter with a |
| 374 // URLFetcher parameter. | 369 // URLFetcher parameter. |
| 375 bool Init(const FilePath& database_location, | 370 virtual bool Init( |
| 376 const syncer::WeakHandle<syncer::JsEventHandler>& | 371 const FilePath& database_location, |
| 377 event_handler, | 372 const syncer::WeakHandle<syncer::JsEventHandler>& event_handler, |
| 378 const std::string& sync_server_and_path, | 373 const std::string& sync_server_and_path, |
| 379 int sync_server_port, | 374 int sync_server_port, |
| 380 bool use_ssl, | 375 bool use_ssl, |
| 381 const scoped_refptr<base::TaskRunner>& blocking_task_runner, | 376 const scoped_refptr<base::TaskRunner>& blocking_task_runner, |
| 382 HttpPostProviderFactory* post_factory, | 377 scoped_ptr<HttpPostProviderFactory> post_factory, |
| 383 const syncer::ModelSafeRoutingInfo& model_safe_routing_info, | 378 const syncer::ModelSafeRoutingInfo& model_safe_routing_info, |
| 384 const std::vector<syncer::ModelSafeWorker*>& workers, | 379 const std::vector<syncer::ModelSafeWorker*>& workers, |
| 385 syncer::ExtensionsActivityMonitor* | 380 syncer::ExtensionsActivityMonitor* extensions_activity_monitor, |
| 386 extensions_activity_monitor, | 381 ChangeDelegate* change_delegate, |
| 387 ChangeDelegate* change_delegate, | 382 const SyncCredentials& credentials, |
| 388 const SyncCredentials& credentials, | 383 scoped_ptr<syncer::SyncNotifier> sync_notifier, |
| 389 syncer::SyncNotifier* sync_notifier, | 384 const std::string& restored_key_for_bootstrapping, |
| 390 const std::string& restored_key_for_bootstrapping, | 385 TestingMode testing_mode, |
| 391 TestingMode testing_mode, | 386 syncer::Encryptor* encryptor, |
| 392 syncer::Encryptor* encryptor, | 387 syncer::UnrecoverableErrorHandler* unrecoverable_error_handler, |
| 393 syncer::UnrecoverableErrorHandler* | 388 syncer::ReportUnrecoverableErrorFunction |
| 394 unrecoverable_error_handler, | 389 report_unrecoverable_error_function) = 0; |
| 395 syncer::ReportUnrecoverableErrorFunction | |
| 396 report_unrecoverable_error_function); | |
| 397 | 390 |
| 398 // Throw an unrecoverable error from a transaction (mostly used for | 391 // Throw an unrecoverable error from a transaction (mostly used for |
| 399 // testing). | 392 // testing). |
| 400 void ThrowUnrecoverableError(); | 393 virtual void ThrowUnrecoverableError() = 0; |
| 401 | 394 |
| 402 // Returns the set of types for which we have stored some sync data. | 395 virtual syncer::ModelTypeSet InitialSyncEndedTypes() = 0; |
| 403 syncer::ModelTypeSet InitialSyncEndedTypes(); | |
| 404 | 396 |
| 405 // Returns those types within |types| that have an empty progress marker | 397 // Returns those types within |types| that have an empty progress marker |
| 406 // token. | 398 // token. |
| 407 syncer::ModelTypeSet GetTypesWithEmptyProgressMarkerToken( | 399 virtual syncer::ModelTypeSet GetTypesWithEmptyProgressMarkerToken( |
| 408 syncer::ModelTypeSet types); | 400 syncer::ModelTypeSet types) = 0; |
| 409 | 401 |
| 410 // Purge from the directory those types with non-empty progress markers | 402 // Purge from the directory those types with non-empty progress markers |
| 411 // but without initial synced ended set. | 403 // but without initial synced ended set. |
| 412 // Returns false if an error occurred, true otherwise. | 404 // Returns false if an error occurred, true otherwise. |
| 413 bool PurgePartiallySyncedTypes(); | 405 virtual bool PurgePartiallySyncedTypes() = 0; |
| 414 | 406 |
| 415 // Update tokens that we're using in Sync. Email must stay the same. | 407 // Update tokens that we're using in Sync. Email must stay the same. |
| 416 void UpdateCredentials(const SyncCredentials& credentials); | 408 virtual void UpdateCredentials(const SyncCredentials& credentials) = 0; |
| 417 | 409 |
| 418 // Called when the user disables or enables a sync type. | 410 // Called when the user disables or enables a sync type. |
| 419 void UpdateEnabledTypes(const syncer::ModelTypeSet& enabled_types); | 411 virtual void UpdateEnabledTypes( |
| 412 const syncer::ModelTypeSet& enabled_types) = 0; |
| 420 | 413 |
| 421 // Put the syncer in normal mode ready to perform nudges and polls. | 414 // Put the syncer in normal mode ready to perform nudges and polls. |
| 422 void StartSyncingNormally( | 415 virtual void StartSyncingNormally( |
| 423 const syncer::ModelSafeRoutingInfo& routing_info); | 416 const syncer::ModelSafeRoutingInfo& routing_info) = 0; |
| 424 | 417 |
| 425 // Attempts to re-encrypt encrypted data types using the passphrase provided. | 418 // Attempts to re-encrypt encrypted data types using the passphrase provided. |
| 426 // Notifies observers of the result of the operation via OnPassphraseAccepted | 419 // Notifies observers of the result of the operation via OnPassphraseAccepted |
| 427 // or OnPassphraseRequired, updates the nigori node, and does re-encryption as | 420 // or OnPassphraseRequired, updates the nigori node, and does re-encryption as |
| 428 // appropriate. If an explicit password has been set previously, we drop | 421 // appropriate. If an explicit password has been set previously, we drop |
| 429 // subsequent requests to set a passphrase. If the cryptographer has pending | 422 // subsequent requests to set a passphrase. If the cryptographer has pending |
| 430 // keys, and a new implicit passphrase is provided, we try decrypting the | 423 // keys, and a new implicit passphrase is provided, we try decrypting the |
| 431 // pending keys with it, and if that fails, we cache the passphrase for | 424 // pending keys with it, and if that fails, we cache the passphrase for |
| 432 // re-encryption once the pending keys are decrypted. | 425 // re-encryption once the pending keys are decrypted. |
| 433 void SetEncryptionPassphrase(const std::string& passphrase, bool is_explicit); | 426 virtual void SetEncryptionPassphrase(const std::string& passphrase, |
| 427 bool is_explicit) = 0; |
| 434 | 428 |
| 435 // Provides a passphrase for decrypting the user's existing sync data. | 429 // Provides a passphrase for decrypting the user's existing sync data. |
| 436 // Notifies observers of the result of the operation via OnPassphraseAccepted | 430 // Notifies observers of the result of the operation via OnPassphraseAccepted |
| 437 // or OnPassphraseRequired, updates the nigori node, and does re-encryption as | 431 // or OnPassphraseRequired, updates the nigori node, and does re-encryption as |
| 438 // appropriate if there is a previously cached encryption passphrase. It is an | 432 // appropriate if there is a previously cached encryption passphrase. It is an |
| 439 // error to call this when we don't have pending keys. | 433 // error to call this when we don't have pending keys. |
| 440 void SetDecryptionPassphrase(const std::string& passphrase); | 434 virtual void SetDecryptionPassphrase(const std::string& passphrase) = 0; |
| 441 | 435 |
| 442 // Switches the mode of operation to CONFIGURATION_MODE and performs | 436 // Switches the mode of operation to CONFIGURATION_MODE and performs |
| 443 // any configuration tasks needed as determined by the params. Once complete, | 437 // any configuration tasks needed as determined by the params. Once complete, |
| 444 // syncer will remain in CONFIGURATION_MODE until StartSyncingNormally is | 438 // syncer will remain in CONFIGURATION_MODE until StartSyncingNormally is |
| 445 // called. | 439 // called. |
| 446 // |ready_task| is invoked when the configuration completes. | 440 // |ready_task| is invoked when the configuration completes. |
| 447 // |retry_task| is invoked if the configuration job could not immediately | 441 // |retry_task| is invoked if the configuration job could not immediately |
| 448 // execute. |ready_task| will still be called when it eventually | 442 // execute. |ready_task| will still be called when it eventually |
| 449 // does finish. | 443 // does finish. |
| 450 void ConfigureSyncer( | 444 virtual void ConfigureSyncer( |
| 451 ConfigureReason reason, | 445 ConfigureReason reason, |
| 452 const syncer::ModelTypeSet& types_to_config, | 446 const syncer::ModelTypeSet& types_to_config, |
| 453 const syncer::ModelSafeRoutingInfo& new_routing_info, | 447 const syncer::ModelSafeRoutingInfo& new_routing_info, |
| 454 const base::Closure& ready_task, | 448 const base::Closure& ready_task, |
| 455 const base::Closure& retry_task); | 449 const base::Closure& retry_task) = 0; |
| 456 | 450 |
| 457 // Adds a listener to be notified of sync events. | 451 // Adds a listener to be notified of sync events. |
| 458 // NOTE: It is OK (in fact, it's probably a good idea) to call this before | 452 // NOTE: It is OK (in fact, it's probably a good idea) to call this before |
| 459 // having received OnInitializationCompleted. | 453 // having received OnInitializationCompleted. |
| 460 void AddObserver(Observer* observer); | 454 virtual void AddObserver(Observer* observer) = 0; |
| 461 | 455 |
| 462 // Remove the given observer. Make sure to call this if the | 456 // Remove the given observer. Make sure to call this if the |
| 463 // Observer is being destroyed so the SyncManager doesn't | 457 // Observer is being destroyed so the SyncManager doesn't |
| 464 // potentially dereference garbage. | 458 // potentially dereference garbage. |
| 465 void RemoveObserver(Observer* observer); | 459 virtual void RemoveObserver(Observer* observer) = 0; |
| 466 | 460 |
| 467 // Status-related getter. May be called on any thread. | 461 // Status-related getter. May be called on any thread. |
| 468 SyncStatus GetDetailedStatus() const; | 462 virtual SyncStatus GetDetailedStatus() const = 0; |
| 469 | 463 |
| 470 // Whether or not the Nigori node is encrypted using an explicit passphrase. | 464 // Whether or not the Nigori node is encrypted using an explicit passphrase. |
| 471 // May be called on any thread. | 465 // May be called on any thread. |
| 472 bool IsUsingExplicitPassphrase(); | 466 virtual bool IsUsingExplicitPassphrase() = 0; |
| 473 | 467 |
| 474 // Call periodically from a database-safe thread to persist recent changes | 468 // Call periodically from a database-safe thread to persist recent changes |
| 475 // to the syncapi model. | 469 // to the syncapi model. |
| 476 void SaveChanges(); | 470 virtual void SaveChanges() = 0; |
| 477 | 471 |
| 478 // Initiates shutdown of various components in the sync engine. Must be | 472 // Initiates shutdown of various components in the sync engine. Must be |
| 479 // called from the main thread to allow preempting ongoing tasks on the sync | 473 // called from the main thread to allow preempting ongoing tasks on the sync |
| 480 // loop (that may be blocked on I/O). The semantics of |callback| are the | 474 // loop (that may be blocked on I/O). The semantics of |callback| are the |
| 481 // same as with StartConfigurationMode. If provided and a scheduler / sync | 475 // same as with StartConfigurationMode. If provided and a scheduler / sync |
| 482 // loop exists, it will be invoked from the sync loop by the scheduler to | 476 // loop exists, it will be invoked from the sync loop by the scheduler to |
| 483 // notify that all work has been flushed + cancelled, and it is idle. | 477 // notify that all work has been flushed + cancelled, and it is idle. |
| 484 // If no scheduler exists, the callback is run immediately (from the loop | 478 // If no scheduler exists, the callback is run immediately (from the loop |
| 485 // this was created on, which is the sync loop), as sync is effectively | 479 // this was created on, which is the sync loop), as sync is effectively |
| 486 // stopped. | 480 // stopped. |
| 487 void StopSyncingForShutdown(const base::Closure& callback); | 481 virtual void StopSyncingForShutdown(const base::Closure& callback) = 0; |
| 488 | 482 |
| 489 // Issue a final SaveChanges, and close sqlite handles. | 483 // Issue a final SaveChanges, and close sqlite handles. |
| 490 void ShutdownOnSyncThread(); | 484 virtual void ShutdownOnSyncThread() = 0; |
| 491 | 485 |
| 492 // May be called from any thread. | 486 // May be called from any thread. |
| 493 UserShare* GetUserShare() const; | 487 virtual UserShare* GetUserShare() const = 0; |
| 494 | 488 |
| 495 // Inform the cryptographer of the most recent passphrase and set of | 489 // Inform the cryptographer of the most recent passphrase and set of |
| 496 // encrypted types (from nigori node), then ensure all data that | 490 // encrypted types (from nigori node), then ensure all data that |
| 497 // needs encryption is encrypted with the appropriate passphrase. | 491 // needs encryption is encrypted with the appropriate passphrase. |
| 498 // | 492 // |
| 499 // May trigger OnPassphraseRequired(). Otherwise, it will trigger | 493 // May trigger OnPassphraseRequired(). Otherwise, it will trigger |
| 500 // OnEncryptedTypesChanged() if necessary (see comments for | 494 // OnEncryptedTypesChanged() if necessary (see comments for |
| 501 // OnEncryptedTypesChanged()), and then OnEncryptionComplete(). | 495 // OnEncryptedTypesChanged()), and then OnEncryptionComplete(). |
| 502 // | 496 // |
| 503 // Also updates or adds device information to the nigori node. | 497 // Also updates or adds device information to the nigori node. |
| 504 // | 498 // |
| 505 // Note: opens a transaction, so must only be called after syncapi | 499 // Note: opens a transaction, so must only be called after syncapi |
| 506 // has been initialized. | 500 // has been initialized. |
| 507 void RefreshNigori(const std::string& chrome_version, | 501 virtual void RefreshNigori(const std::string& chrome_version, |
| 508 const base::Closure& done_callback); | 502 const base::Closure& done_callback) = 0; |
| 509 | 503 |
| 510 // Enable encryption of all sync data. Once enabled, it can never be | 504 // Enable encryption of all sync data. Once enabled, it can never be |
| 511 // disabled without clearing the server data. | 505 // disabled without clearing the server data. |
| 512 // | 506 // |
| 513 // This will trigger OnEncryptedTypesChanged() if necessary (see | 507 // This will trigger OnEncryptedTypesChanged() if necessary (see |
| 514 // comments for OnEncryptedTypesChanged()). It then may trigger | 508 // comments for OnEncryptedTypesChanged()). It then may trigger |
| 515 // OnPassphraseRequired(), but otherwise it will trigger | 509 // OnPassphraseRequired(), but otherwise it will trigger |
| 516 // OnEncryptionComplete(). | 510 // OnEncryptionComplete(). |
| 517 void EnableEncryptEverything(); | 511 virtual void EnableEncryptEverything() = 0; |
| 518 | |
| 519 // Returns true if we are currently encrypting all sync data. May | |
| 520 // be called on any thread. | |
| 521 bool EncryptEverythingEnabledForTest() const; | |
| 522 | |
| 523 // Gets the set of encrypted types from the cryptographer | |
| 524 // Note: opens a transaction. May be called from any thread. | |
| 525 syncer::ModelTypeSet GetEncryptedDataTypesForTest() const; | |
| 526 | 512 |
| 527 // Reads the nigori node to determine if any experimental features should | 513 // Reads the nigori node to determine if any experimental features should |
| 528 // be enabled. | 514 // be enabled. |
| 529 // Note: opens a transaction. May be called on any thread. | 515 // Note: opens a transaction. May be called on any thread. |
| 530 bool ReceivedExperiment(syncer::Experiments* experiments) const; | 516 virtual bool ReceivedExperiment(syncer::Experiments* experiments) const = 0; |
| 531 | 517 |
| 532 // Uses a read-only transaction to determine if the directory being synced has | 518 // Uses a read-only transaction to determine if the directory being synced has |
| 533 // any remaining unsynced items. May be called on any thread. | 519 // any remaining unsynced items. May be called on any thread. |
| 534 bool HasUnsyncedItems() const; | 520 virtual bool HasUnsyncedItems() const = 0; |
| 535 | |
| 536 // Functions used for testing. | |
| 537 | |
| 538 void SimulateEnableNotificationsForTest(); | |
| 539 | |
| 540 void SimulateDisableNotificationsForTest(int reason); | |
| 541 | |
| 542 void TriggerOnIncomingNotificationForTest( | |
| 543 syncer::ModelTypeSet model_types); | |
| 544 | |
| 545 static const int kDefaultNudgeDelayMilliseconds; | |
| 546 static const int kPreferencesNudgeDelayMilliseconds; | |
| 547 static const int kPiggybackNudgeDelay; | |
| 548 | |
| 549 static const FilePath::CharType kSyncDatabaseFilename[]; | |
| 550 | |
| 551 private: | |
| 552 friend class SyncManagerTest; | |
| 553 FRIEND_TEST_ALL_PREFIXES(SyncManagerTest, NudgeDelayTest); | |
| 554 | |
| 555 // For unit tests. | |
| 556 base::TimeDelta GetNudgeDelayTimeDelta(const syncer::ModelType& model_type); | |
| 557 | |
| 558 // Set the internal scheduler for testing purposes. | |
| 559 // TODO(sync): Use dependency injection instead. crbug.com/133061 | |
| 560 void SetSyncSchedulerForTest( | |
| 561 scoped_ptr<syncer::SyncScheduler> scheduler); | |
| 562 | |
| 563 base::ThreadChecker thread_checker_; | |
| 564 | |
| 565 // An opaque pointer to the nested private class. | |
| 566 SyncInternal* data_; | |
| 567 | |
| 568 DISALLOW_COPY_AND_ASSIGN(SyncManager); | |
| 569 }; | 521 }; |
| 570 | 522 |
| 571 bool InitialSyncEndedForTypes(syncer::ModelTypeSet types, UserShare* share); | |
| 572 | |
| 573 const char* ConnectionStatusToString(ConnectionStatus status); | |
| 574 | |
| 575 // Returns the string representation of a PassphraseRequiredReason value. | |
| 576 const char* PassphraseRequiredReasonToString(PassphraseRequiredReason reason); | |
| 577 | |
| 578 } // namespace syncer | 523 } // namespace syncer |
| 579 | 524 |
| 580 #endif // SYNC_INTERNAL_API_PUBLIC_SYNC_MANAGER_H_ | 525 #endif // SYNC_INTERNAL_API_PUBLIC_SYNC_MANAGER_H_ |
| OLD | NEW |