diff options
author | chocobo@chromium.org <chocobo@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-07-28 07:03:55 +0000 |
---|---|---|
committer | chocobo@chromium.org <chocobo@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-07-28 07:03:55 +0000 |
commit | b2e23e306d03ef87a253573cb7dc0ac73c34cb05 (patch) | |
tree | 527e4a6f1fd87baf02667dd3f94d0f185c3ef2e5 /chrome/browser/chromeos | |
parent | 871c71501430e4cf4d083cf44999891c34b77038 (diff) | |
download | chromium_src-b2e23e306d03ef87a253573cb7dc0ac73c34cb05.zip chromium_src-b2e23e306d03ef87a253573cb7dc0ac73c34cb05.tar.gz chromium_src-b2e23e306d03ef87a253573cb7dc0ac73c34cb05.tar.bz2 |
Port network options to DOM UI. Hook up all the functionality. UI touchup work will be in another changelist.
BUG=chromium-os:4744
BUG=49030
TEST=manual
Review URL: http://codereview.chromium.org/3036020
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@53919 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/chromeos')
6 files changed, 473 insertions, 7 deletions
diff --git a/chrome/browser/chromeos/cros/mock_network_library.h b/chrome/browser/chromeos/cros/mock_network_library.h index 975a0f1..c329bd3 100644 --- a/chrome/browser/chromeos/cros/mock_network_library.h +++ b/chrome/browser/chromeos/cros/mock_network_library.h @@ -66,7 +66,7 @@ class MockNetworkLibrary : public NetworkLibrary { MOCK_METHOD1(DisconnectFromWirelessNetwork, void(const WirelessNetwork&)); MOCK_METHOD1(SaveCellularNetwork, void(const CellularNetwork&)); MOCK_METHOD1(SaveWifiNetwork, void(const WifiNetwork&)); - MOCK_METHOD1(ForgetWirelessNetwork, void(const WirelessNetwork&)); + MOCK_METHOD1(ForgetWirelessNetwork, void(const std::string&)); MOCK_CONST_METHOD0(ethernet_available, bool(void)); MOCK_CONST_METHOD0(wifi_available, bool(void)); diff --git a/chrome/browser/chromeos/cros/network_library.cc b/chrome/browser/chromeos/cros/network_library.cc index 09328e91..1651117 100644 --- a/chrome/browser/chromeos/cros/network_library.cc +++ b/chrome/browser/chromeos/cros/network_library.cc @@ -215,6 +215,8 @@ NetworkLibraryImpl::NetworkLibraryImpl() offline_mode_(false) { if (CrosLibrary::Get()->EnsureLoaded()) { Init(); + } else { + InitTestData(); } g_url_request_job_tracker.AddObserver(this); } @@ -460,9 +462,10 @@ void NetworkLibraryImpl::SaveWifiNetwork(const WifiNetwork& network) { } } -void NetworkLibraryImpl::ForgetWirelessNetwork(const WirelessNetwork& network) { +void NetworkLibraryImpl::ForgetWirelessNetwork( + const std::string& service_path) { if (CrosLibrary::Get()->EnsureLoaded()) { - DeleteRememberedService(network.service_path().c_str()); + DeleteRememberedService(service_path.c_str()); } } @@ -645,6 +648,77 @@ void NetworkLibraryImpl::Init() { this); } +void NetworkLibraryImpl::InitTestData() { + ethernet_.Clear(); + ethernet_.set_connected(true); + + wifi_networks_.clear(); + WifiNetwork wifi1 = WifiNetwork(); + wifi1.set_service_path("fw1"); + wifi1.set_name("Fake Wifi 1"); + wifi1.set_strength(90); + wifi1.set_connected(false); + wifi1.set_encryption(SECURITY_NONE); + wifi_networks_.push_back(wifi1); + + WifiNetwork wifi2 = WifiNetwork(); + wifi2.set_service_path("fw2"); + wifi2.set_name("Fake Wifi 2"); + wifi2.set_strength(70); + wifi2.set_connected(true); + wifi2.set_encryption(SECURITY_WEP); + wifi_networks_.push_back(wifi2); + + WifiNetwork wifi3 = WifiNetwork(); + wifi3.set_service_path("fw3"); + wifi3.set_name("Fake Wifi 3"); + wifi3.set_strength(50); + wifi3.set_connected(false); + wifi3.set_encryption(SECURITY_WEP); + wifi_networks_.push_back(wifi3); + + wifi_ = wifi2; + + cellular_networks_.clear(); + + cellular_networks_.clear(); + CellularNetwork cellular1 = CellularNetwork(); + cellular1.set_service_path("fc1"); + cellular1.set_name("Fake Cellular 1"); + cellular1.set_strength(90); + cellular1.set_connected(false); + cellular_networks_.push_back(cellular1); + + CellularNetwork cellular2 = CellularNetwork(); + cellular2.set_service_path("fc2"); + cellular2.set_name("Fake Cellular 2"); + cellular2.set_strength(70); + cellular2.set_connected(true); + cellular_networks_.push_back(cellular2); + + CellularNetwork cellular3 = CellularNetwork(); + cellular3.set_service_path("fc3"); + cellular3.set_name("Fake Cellular 3"); + cellular3.set_strength(50); + cellular3.set_connected(false); + cellular_networks_.push_back(cellular3); + + cellular_ = cellular2; + + remembered_wifi_networks_.clear(); + remembered_wifi_networks_.push_back(wifi2); + + remembered_cellular_networks_.clear(); + remembered_cellular_networks_.push_back(cellular2); + + int devices = (1 << TYPE_ETHERNET) | (1 << TYPE_WIFI) | + (1 << TYPE_CELLULAR); + available_devices_ = devices; + enabled_devices_ = devices; + connected_devices_ = devices; + offline_mode_ = false; +} + void NetworkLibraryImpl::UpdateSystemInfo() { if (CrosLibrary::Get()->EnsureLoaded()) { UpdateNetworkStatus(); diff --git a/chrome/browser/chromeos/cros/network_library.h b/chrome/browser/chromeos/cros/network_library.h index 645e6ae..0a7e5a2 100644 --- a/chrome/browser/chromeos/cros/network_library.h +++ b/chrome/browser/chromeos/cros/network_library.h @@ -32,6 +32,8 @@ class Network { bool failed() const { return state_ == STATE_FAILURE; } ConnectionError error() const { return error_; } + void set_service_path(const std::string& service_path) { + service_path_ = service_path; } void set_connecting(bool connecting) { state_ = (connecting ? STATE_ASSOCIATION : STATE_IDLE); } void set_connected(bool connected) { state_ = (connected ? @@ -92,6 +94,7 @@ class WirelessNetwork : public Network { bool auto_connect() const { return auto_connect_; } void set_name(const std::string& name) { name_ = name; } + void set_strength(int strength) { strength_ = strength; } void set_auto_connect(bool auto_connect) { auto_connect_ = auto_connect; } // Network overrides. @@ -339,7 +342,7 @@ class NetworkLibrary { virtual void SaveWifiNetwork(const WifiNetwork& network) = 0; // Forget the passed in wireless (either cellular or wifi) network. - virtual void ForgetWirelessNetwork(const WirelessNetwork& network) = 0; + virtual void ForgetWirelessNetwork(const std::string& service_path) = 0; virtual bool ethernet_available() const = 0; virtual bool wifi_available() const = 0; @@ -450,7 +453,7 @@ class NetworkLibraryImpl : public NetworkLibrary, virtual void DisconnectFromWirelessNetwork(const WirelessNetwork& network); virtual void SaveCellularNetwork(const CellularNetwork& network); virtual void SaveWifiNetwork(const WifiNetwork& network); - virtual void ForgetWirelessNetwork(const WirelessNetwork& network); + virtual void ForgetWirelessNetwork(const std::string& service_path); virtual bool ethernet_available() const { return available_devices_ & (1 << TYPE_ETHERNET); @@ -505,6 +508,8 @@ class NetworkLibraryImpl : public NetworkLibrary, // This methods loads the initial list of networks on startup and starts the // monitoring of network changes. void Init(); + // Initialize with test data. + void InitTestData(); // Returns the preferred wifi network. WifiNetwork* GetPreferredNetwork(); diff --git a/chrome/browser/chromeos/dom_ui/internet_options_handler.cc b/chrome/browser/chromeos/dom_ui/internet_options_handler.cc new file mode 100644 index 0000000..4a40d15 --- /dev/null +++ b/chrome/browser/chromeos/dom_ui/internet_options_handler.cc @@ -0,0 +1,328 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/chromeos/dom_ui/internet_options_handler.h" + +#include <string> +#include <vector> + +#include "app/l10n_util.h" +#include "app/resource_bundle.h" +#include "base/base64.h" +#include "base/basictypes.h" +#include "base/callback.h" +#include "base/values.h" +#include "chrome/browser/browser.h" +#include "chrome/browser/browser_list.h" +#include "chrome/browser/browser_window.h" +#include "chrome/browser/chromeos/cros/cros_library.h" +#include "chrome/browser/chromeos/status/network_menu_button.h" +#include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/browser/tab_contents/tab_contents_view.h" +#include "chrome/common/notification_service.h" +#include "gfx/codec/png_codec.h" +#include "grit/browser_resources.h" +#include "grit/chromium_strings.h" +#include "grit/generated_resources.h" +#include "grit/locale_settings.h" +#include "grit/theme_resources.h" +#include "third_party/skia/include/core/SkBitmap.h" +#include "views/window/window.h" + +InternetOptionsHandler::InternetOptionsHandler() { + chromeos::CrosLibrary::Get()->GetNetworkLibrary()->AddObserver(this); +} + +InternetOptionsHandler::~InternetOptionsHandler() { + chromeos::CrosLibrary::Get()->GetNetworkLibrary()->RemoveObserver(this); +} + +void InternetOptionsHandler::GetLocalizedValues( + DictionaryValue* localized_strings) { + DCHECK(localized_strings); + // Internet page - ChromeOS + localized_strings->SetString(L"internetPage", + l10n_util::GetString(IDS_OPTIONS_INTERNET_TAB_LABEL)); + + localized_strings->SetString(L"wired_title", + l10n_util::GetString(IDS_OPTIONS_SETTINGS_SECTION_TITLE_WIRED_NETWORK)); + localized_strings->SetString(L"wireless_title", + l10n_util::GetString( + IDS_OPTIONS_SETTINGS_SECTION_TITLE_WIRELESS_NETWORK)); + localized_strings->SetString(L"remembered_title", + l10n_util::GetString( + IDS_OPTIONS_SETTINGS_SECTION_TITLE_REMEMBERED_NETWORK)); + + localized_strings->SetString(L"connect_button", + l10n_util::GetString( + IDS_OPTIONS_SETTINGS_CONNECT)); + localized_strings->SetString(L"disconnect_button", + l10n_util::GetString( + IDS_OPTIONS_SETTINGS_DISCONNECT)); + localized_strings->SetString(L"options_button", + l10n_util::GetString( + IDS_OPTIONS_SETTINGS_OPTIONS)); + localized_strings->SetString(L"forget_button", + l10n_util::GetString( + IDS_OPTIONS_SETTINGS_FORGET)); + + localized_strings->Set(L"wiredList", GetWiredList()); + localized_strings->Set(L"wirelessList", GetWirelessList()); + localized_strings->Set(L"rememberedList", GetRememberedList()); +} + +void InternetOptionsHandler::RegisterMessages() { + // Setup handlers specific to this panel. + DCHECK(dom_ui_); + dom_ui_->RegisterMessageCallback("buttonClickCallback", + NewCallback(this, &InternetOptionsHandler::ButtonClickCallback)); +} + +void InternetOptionsHandler::NetworkChanged(chromeos::NetworkLibrary* cros) { + if (dom_ui_) { + DictionaryValue dictionary; + dictionary.Set(L"wiredList", GetWiredList()); + dictionary.Set(L"wirelessList", GetWirelessList()); + dictionary.Set(L"rememberedList", GetRememberedList()); + dom_ui_->CallJavascriptFunction(L"refreshNetworkData", dictionary); + } +} + +void InternetOptionsHandler::CreateModalPopup(views::WindowDelegate* view) { + Browser* browser = NULL; + TabContentsDelegate* delegate = dom_ui_->tab_contents()->delegate(); + if (delegate) + browser = delegate->GetBrowser(); + DCHECK(browser); + views::Window* window = views::Window::CreateChromeWindow( + browser->window()->GetNativeHandle(), gfx::Rect(), view); + window->SetIsAlwaysOnTop(true); + window->Show(); +} + +void InternetOptionsHandler::ButtonClickCallback(const Value* value) { + if (!value || !value->IsType(Value::TYPE_LIST)) { + NOTREACHED(); + return; + } + const ListValue* list_value = static_cast<const ListValue*>(value); + std::string str_type; + std::string service_path; + std::string command; + if (list_value->GetSize() != 3 || + !list_value->GetString(0, &str_type) || + !list_value->GetString(1, &service_path) || + !list_value->GetString(2, &command)) { + NOTREACHED(); + return; + } + + int type = atoi(str_type.c_str()); + chromeos::NetworkLibrary* cros = + chromeos::CrosLibrary::Get()->GetNetworkLibrary(); + if (type == chromeos::TYPE_ETHERNET) { + CreateModalPopup(new chromeos::NetworkConfigView(cros->ethernet_network())); + } else if (type == chromeos::TYPE_WIFI) { + chromeos::WifiNetwork network; + if (command == "forget") { + cros->ForgetWirelessNetwork(service_path); + } else if (cros->FindWifiNetworkByPath(service_path, &network)) { + if (command == "connect") { + // Connect to wifi here. Open password page if appropriate. + if (network.encrypted()) { + chromeos::NetworkConfigView* view = + new chromeos::NetworkConfigView(network, true); + CreateModalPopup(view); + view->SetLoginTextfieldFocus(); + } else { + cros->ConnectToWifiNetwork( + network, std::string(), std::string(), std::string()); + } + } else if (command == "disconnect") { + cros->DisconnectFromWirelessNetwork(network); + } else if (command == "options") { + CreateModalPopup(new chromeos::NetworkConfigView(network, false)); + } + } + } else if (type == chromeos::TYPE_CELLULAR) { + chromeos::CellularNetwork network; + if (command == "forget") { + cros->ForgetWirelessNetwork(service_path); + } else if (cros->FindCellularNetworkByPath(service_path, &network)) { + if (command == "connect") { + cros->ConnectToCellularNetwork(network); + } else if (command == "disconnect") { + cros->DisconnectFromWirelessNetwork(network); + } else if (command == "options") { + CreateModalPopup(new chromeos::NetworkConfigView(network)); + } + } + } else { + NOTREACHED(); + } +} + +// Helper function to convert an icon to a data: URL +// with the icon encoded as a PNG. +static std::string ConvertSkBitmapToDataURL(const SkBitmap& icon) { + DCHECK(!icon.isNull()); + + // Get the icon data. + std::vector<unsigned char> icon_data; + gfx::PNGCodec::EncodeBGRASkBitmap(icon, false, &icon_data); + + // Base64-encode it (to make it a data URL). + std::string icon_data_str(reinterpret_cast<char*>(&icon_data[0]), + icon_data.size()); + std::string icon_base64_encoded; + base::Base64Encode(icon_data_str, &icon_base64_encoded); + return std::string("data:image/png;base64," + icon_base64_encoded); +} + +ListValue* InternetOptionsHandler::GetNetwork(const std::string& service_path, + const SkBitmap& icon, const std::string& name, bool connecting, + bool connected, int connection_type, bool remembered) { + + ListValue* network = new ListValue(); + + int s = IDS_STATUSBAR_NETWORK_DEVICE_DISCONNECTED; + if (connecting) + s = IDS_STATUSBAR_NETWORK_DEVICE_CONNECTING; + else if (connected) + s = IDS_STATUSBAR_NETWORK_DEVICE_CONNECTED; + std::wstring status = l10n_util::GetString(s); + + // service path + network->Append(Value::CreateStringValue(service_path)); + // name + network->Append(Value::CreateStringValue(name)); + // status + network->Append(Value::CreateStringValue(l10n_util::GetString(s))); + // type + network->Append(Value::CreateIntegerValue(connection_type)); + // connected + network->Append(Value::CreateBooleanValue(connected)); + // connecting + network->Append(Value::CreateBooleanValue(connecting)); + // icon data url + network->Append(Value::CreateStringValue(ConvertSkBitmapToDataURL(icon))); + // remembered + network->Append(Value::CreateBooleanValue(remembered)); + return network; +} + +ListValue* InternetOptionsHandler::GetWiredList() { + chromeos::NetworkLibrary* cros = + chromeos::CrosLibrary::Get()->GetNetworkLibrary(); + ResourceBundle& rb = ResourceBundle::GetSharedInstance(); + ListValue* list = new ListValue(); + + const chromeos::EthernetNetwork& ethernet_network = cros->ethernet_network(); + SkBitmap icon = *rb.GetBitmapNamed(IDR_STATUSBAR_WIRED_BLACK); + if (!ethernet_network.connecting() && + !ethernet_network.connected()) { + icon = chromeos::NetworkMenuButton::IconForDisplay(icon, + *rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_DISCONNECTED)); + } + list->Append(GetNetwork( + ethernet_network.service_path(), + icon, + WideToASCII(l10n_util::GetString(IDS_STATUSBAR_NETWORK_DEVICE_ETHERNET)), + ethernet_network.connecting(), + ethernet_network.connected(), + chromeos::TYPE_ETHERNET, + false)); + return list; +} + +ListValue* InternetOptionsHandler::GetWirelessList() { + chromeos::NetworkLibrary* cros = + chromeos::CrosLibrary::Get()->GetNetworkLibrary(); + ResourceBundle& rb = ResourceBundle::GetSharedInstance(); + ListValue* list = new ListValue(); + + const chromeos::WifiNetworkVector& wifi_networks = cros->wifi_networks(); + for (chromeos::WifiNetworkVector::const_iterator it = + wifi_networks.begin(); it != wifi_networks.end(); ++it) { + SkBitmap icon = chromeos::NetworkMenuButton::IconForNetworkStrength( + it->strength(), true); + if (it->encrypted()) { + icon = chromeos::NetworkMenuButton::IconForDisplay(icon, + *rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_SECURE)); + } + list->Append(GetNetwork( + it->service_path(), + icon, + it->name(), + it->connecting(), + it->connected(), + chromeos::TYPE_WIFI, + false)); + } + + const chromeos::CellularNetworkVector& cellular_networks = + cros->cellular_networks(); + for (chromeos::CellularNetworkVector::const_iterator it = + cellular_networks.begin(); it != cellular_networks.end(); ++it) { + SkBitmap icon = chromeos::NetworkMenuButton::IconForNetworkStrength( + it->strength(), true); + SkBitmap badge = *rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_3G); + icon = chromeos::NetworkMenuButton::IconForDisplay(icon, badge); + list->Append(GetNetwork( + it->service_path(), + icon, + it->name(), + it->connecting(), + it->connected(), + chromeos::TYPE_CELLULAR, + false)); + } + + return list; +} + +ListValue* InternetOptionsHandler::GetRememberedList() { + chromeos::NetworkLibrary* cros = + chromeos::CrosLibrary::Get()->GetNetworkLibrary(); + ResourceBundle& rb = ResourceBundle::GetSharedInstance(); + ListValue* list = new ListValue(); + + const chromeos::WifiNetworkVector& wifi_networks = + cros->remembered_wifi_networks(); + for (chromeos::WifiNetworkVector::const_iterator it = + wifi_networks.begin(); it != wifi_networks.end(); ++it) { + SkBitmap icon = *rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_BARS0); + if (it->encrypted()) { + icon = chromeos::NetworkMenuButton::IconForDisplay(icon, + *rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_SECURE)); + } + list->Append(GetNetwork( + it->service_path(), + icon, + it->name(), + it->connecting(), + it->connected(), + chromeos::TYPE_WIFI, + true)); + } + + const chromeos::CellularNetworkVector& cellular_networks = + cros->remembered_cellular_networks(); + for (chromeos::CellularNetworkVector::const_iterator it = + cellular_networks.begin(); it != cellular_networks.end(); ++it) { + SkBitmap icon = *rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_BARS0); + SkBitmap badge = *rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_3G); + icon = chromeos::NetworkMenuButton::IconForDisplay(icon, badge); + list->Append(GetNetwork( + it->service_path(), + icon, + it->name(), + it->connecting(), + it->connected(), + chromeos::TYPE_CELLULAR, + true)); + } + + return list; +} diff --git a/chrome/browser/chromeos/dom_ui/internet_options_handler.h b/chrome/browser/chromeos/dom_ui/internet_options_handler.h new file mode 100644 index 0000000..31ebf43d --- /dev/null +++ b/chrome/browser/chromeos/dom_ui/internet_options_handler.h @@ -0,0 +1,59 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_CHROMEOS_DOM_UI_INTERNET_OPTIONS_HANDLER_H_ +#define CHROME_BROWSER_CHROMEOS_DOM_UI_INTERNET_OPTIONS_HANDLER_H_ + +#include <string> + +#include "chrome/browser/chromeos/cros/network_library.h" +#include "chrome/browser/dom_ui/options_ui.h" + +class SkBitmap; +namespace views { +class WindowDelegate; +} + +// ChromeOS internet options page UI handler. +class InternetOptionsHandler : public OptionsPageUIHandler, + public chromeos::NetworkLibrary::Observer { + public: + InternetOptionsHandler(); + virtual ~InternetOptionsHandler(); + + // OptionsUIHandler implementation. + virtual void GetLocalizedValues(DictionaryValue* localized_strings); + + // DOMMessageHandler implementation. + virtual void RegisterMessages(); + + // NetworkLibrary::Observer implementation. + virtual void NetworkChanged(chromeos::NetworkLibrary* obj); + virtual void NetworkTraffic(chromeos::NetworkLibrary* cros, + int traffic_type) {} + + private: + // Open a modal popup dialog. + void CreateModalPopup(views::WindowDelegate* view); + // Open options dialog for network. + // |value| will be [ network_type, service_path, command ] + // And command is one of 'options', 'connect', disconnect', or 'forget' + void ButtonClickCallback(const Value* value); + + // Creates the map of a network + ListValue* GetNetwork(const std::string& service_path, const SkBitmap& icon, + const std::string& name, bool connecting, bool connected, + int connection_type, bool remembered); + + // Creates the map of wired networks + ListValue* GetWiredList(); + // Creates the map of wireless networks + ListValue* GetWirelessList(); + // Creates the map of remembered networks + ListValue* GetRememberedList(); + + DISALLOW_COPY_AND_ASSIGN(InternetOptionsHandler); +}; + +#endif // CHROME_BROWSER_CHROMEOS_DOM_UI_INTERNET_OPTIONS_HANDLER_H_ diff --git a/chrome/browser/chromeos/options/internet_page_view.cc b/chrome/browser/chromeos/options/internet_page_view.cc index 71f3e55..437f70f 100644 --- a/chrome/browser/chromeos/options/internet_page_view.cc +++ b/chrome/browser/chromeos/options/internet_page_view.cc @@ -474,12 +474,12 @@ void RememberedSection::ButtonClicked(int button, int connection_type, int id) { if (connection_type == TYPE_CELLULAR) { if (static_cast<int>(celluar_networks_.size()) > id) { CrosLibrary::Get()->GetNetworkLibrary()->ForgetWirelessNetwork( - celluar_networks_[id]); + celluar_networks_[id].service_path()); } } else if (connection_type == TYPE_WIFI) { if (static_cast<int>(wifi_networks_.size()) > id) { CrosLibrary::Get()->GetNetworkLibrary()->ForgetWirelessNetwork( - wifi_networks_[id]); + wifi_networks_[id].service_path()); } } else { NOTREACHED(); |