| OLD | NEW |
| 1 // Copyright 2017 The Chromium Authors. All rights reserved. | 1 // Copyright 2017 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 #include "chrome/browser/android/vr_shell/ui_scene_manager.h" | 5 #include "chrome/browser/android/vr_shell/ui_scene_manager.h" |
| 6 | 6 |
| 7 #include "base/callback.h" | 7 #include "base/callback.h" |
| 8 #include "base/memory/ptr_util.h" | 8 #include "base/memory/ptr_util.h" |
| 9 #include "chrome/browser/android/vr_shell/textures/ui_texture.h" | 9 #include "chrome/browser/android/vr_shell/textures/ui_texture.h" |
| 10 #include "chrome/browser/android/vr_shell/ui_elements/close_button.h" | 10 #include "chrome/browser/android/vr_shell/ui_elements/close_button.h" |
| 11 #include "chrome/browser/android/vr_shell/ui_elements/loading_indicator.h" |
| 11 #include "chrome/browser/android/vr_shell/ui_elements/permanent_security_warning
.h" | 12 #include "chrome/browser/android/vr_shell/ui_elements/permanent_security_warning
.h" |
| 12 #include "chrome/browser/android/vr_shell/ui_elements/transient_security_warning
.h" | 13 #include "chrome/browser/android/vr_shell/ui_elements/transient_security_warning
.h" |
| 13 #include "chrome/browser/android/vr_shell/ui_elements/ui_element.h" | 14 #include "chrome/browser/android/vr_shell/ui_elements/ui_element.h" |
| 14 #include "chrome/browser/android/vr_shell/ui_elements/url_bar.h" | 15 #include "chrome/browser/android/vr_shell/ui_elements/url_bar.h" |
| 15 #include "chrome/browser/android/vr_shell/ui_scene.h" | 16 #include "chrome/browser/android/vr_shell/ui_scene.h" |
| 16 #include "chrome/browser/android/vr_shell/vr_browser_interface.h" | 17 #include "chrome/browser/android/vr_shell/vr_browser_interface.h" |
| 17 #include "chrome/browser/android/vr_shell/vr_shell.h" | 18 #include "chrome/browser/android/vr_shell/vr_shell.h" |
| 18 | 19 |
| 19 namespace vr_shell { | 20 namespace vr_shell { |
| 20 | 21 |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 58 bool in_web_vr) | 59 bool in_web_vr) |
| 59 : browser_(browser), | 60 : browser_(browser), |
| 60 scene_(scene), | 61 scene_(scene), |
| 61 in_cct_(in_cct), | 62 in_cct_(in_cct), |
| 62 web_vr_mode_(in_web_vr), | 63 web_vr_mode_(in_web_vr), |
| 63 weak_ptr_factory_(this) { | 64 weak_ptr_factory_(this) { |
| 64 CreateBackground(); | 65 CreateBackground(); |
| 65 CreateContentQuad(); | 66 CreateContentQuad(); |
| 66 CreateSecurityWarnings(); | 67 CreateSecurityWarnings(); |
| 67 CreateUrlBar(); | 68 CreateUrlBar(); |
| 68 | |
| 69 if (in_cct_) | 69 if (in_cct_) |
| 70 CreateCloseButton(); | 70 CreateCloseButton(); |
| 71 |
| 72 ConfigureScene(); |
| 71 } | 73 } |
| 72 | 74 |
| 73 UiSceneManager::~UiSceneManager() {} | 75 UiSceneManager::~UiSceneManager() {} |
| 74 | 76 |
| 75 void UiSceneManager::CreateSecurityWarnings() { | 77 void UiSceneManager::CreateSecurityWarnings() { |
| 76 std::unique_ptr<UiElement> element; | 78 std::unique_ptr<UiElement> element; |
| 77 | 79 |
| 78 // TODO(mthiesse): Programatically compute the proper texture size for these | 80 // TODO(mthiesse): Programatically compute the proper texture size for these |
| 79 // textured UI elements. | 81 // textured UI elements. |
| 80 element = base::MakeUnique<PermanentSecurityWarning>(512); | 82 element = base::MakeUnique<PermanentSecurityWarning>(512); |
| (...skipping 27 matching lines...) Expand all Loading... |
| 108 void UiSceneManager::CreateContentQuad() { | 110 void UiSceneManager::CreateContentQuad() { |
| 109 std::unique_ptr<UiElement> element; | 111 std::unique_ptr<UiElement> element; |
| 110 | 112 |
| 111 element = base::MakeUnique<UiElement>(); | 113 element = base::MakeUnique<UiElement>(); |
| 112 element->set_id(AllocateId()); | 114 element->set_id(AllocateId()); |
| 113 element->set_fill(vr_shell::Fill::CONTENT); | 115 element->set_fill(vr_shell::Fill::CONTENT); |
| 114 element->set_size({kContentWidth, kContentHeight, 1}); | 116 element->set_size({kContentWidth, kContentHeight, 1}); |
| 115 element->set_translation({0, kContentVerticalOffset, -kContentDistance}); | 117 element->set_translation({0, kContentVerticalOffset, -kContentDistance}); |
| 116 element->set_visible(false); | 118 element->set_visible(false); |
| 117 main_content_ = element.get(); | 119 main_content_ = element.get(); |
| 118 browser_ui_elements_.push_back(element.get()); | 120 content_elements_.push_back(element.get()); |
| 119 scene_->AddUiElement(std::move(element)); | 121 scene_->AddUiElement(std::move(element)); |
| 120 | 122 |
| 121 // Place an invisible but hittable plane behind the content quad, to keep the | 123 // Place an invisible but hittable plane behind the content quad, to keep the |
| 122 // reticle roughly planar with the content if near content. | 124 // reticle roughly planar with the content if near content. |
| 123 element = base::MakeUnique<UiElement>(); | 125 element = base::MakeUnique<UiElement>(); |
| 124 element->set_id(AllocateId()); | 126 element->set_id(AllocateId()); |
| 125 element->set_fill(vr_shell::Fill::NONE); | 127 element->set_fill(vr_shell::Fill::NONE); |
| 126 element->set_size({kBackplaneSize, kBackplaneSize, 1.0}); | 128 element->set_size({kBackplaneSize, kBackplaneSize, 1.0}); |
| 127 element->set_translation({0.0, 0.0, -kTextureOffset}); | 129 element->set_translation({0.0, 0.0, -kTextureOffset}); |
| 128 element->set_parent_id(main_content_->id()); | 130 element->set_parent_id(main_content_->id()); |
| 129 browser_ui_elements_.push_back(element.get()); | 131 content_elements_.push_back(element.get()); |
| 130 scene_->AddUiElement(std::move(element)); | 132 scene_->AddUiElement(std::move(element)); |
| 131 | 133 |
| 132 // Limit reticle distance to a sphere based on content distance. | 134 // Limit reticle distance to a sphere based on content distance. |
| 133 scene_->SetBackgroundDistance(main_content_->translation().z() * | 135 scene_->SetBackgroundDistance(main_content_->translation().z() * |
| 134 -kBackgroundDistanceMultiplier); | 136 -kBackgroundDistanceMultiplier); |
| 135 } | 137 } |
| 136 | 138 |
| 137 void UiSceneManager::CreateBackground() { | 139 void UiSceneManager::CreateBackground() { |
| 138 std::unique_ptr<UiElement> element; | 140 std::unique_ptr<UiElement> element; |
| 139 | 141 |
| 140 // Floor. | 142 // Floor. |
| 141 element = base::MakeUnique<UiElement>(); | 143 element = base::MakeUnique<UiElement>(); |
| 142 element->set_id(AllocateId()); | 144 element->set_id(AllocateId()); |
| 143 element->set_size({kSceneSize, kSceneSize, 1.0}); | 145 element->set_size({kSceneSize, kSceneSize, 1.0}); |
| 144 element->set_translation({0.0, -kSceneHeight / 2, 0.0}); | 146 element->set_translation({0.0, -kSceneHeight / 2, 0.0}); |
| 145 element->set_rotation({1.0, 0.0, 0.0, -M_PI / 2.0}); | 147 element->set_rotation({1.0, 0.0, 0.0, -M_PI / 2.0}); |
| 146 element->set_fill(vr_shell::Fill::OPAQUE_GRADIENT); | 148 element->set_fill(vr_shell::Fill::OPAQUE_GRADIENT); |
| 147 element->set_edge_color(kBackgroundHorizonColor); | 149 element->set_edge_color(kBackgroundHorizonColor); |
| 148 element->set_center_color(kBackgroundCenterColor); | 150 element->set_center_color(kBackgroundCenterColor); |
| 149 element->set_draw_phase(0); | 151 element->set_draw_phase(0); |
| 150 browser_ui_elements_.push_back(element.get()); | 152 control_elements_.push_back(element.get()); |
| 151 scene_->AddUiElement(std::move(element)); | 153 scene_->AddUiElement(std::move(element)); |
| 152 | 154 |
| 153 // Ceiling. | 155 // Ceiling. |
| 154 element = base::MakeUnique<UiElement>(); | 156 element = base::MakeUnique<UiElement>(); |
| 155 element->set_id(AllocateId()); | 157 element->set_id(AllocateId()); |
| 156 element->set_fill(vr_shell::Fill::OPAQUE_GRADIENT); | 158 element->set_fill(vr_shell::Fill::OPAQUE_GRADIENT); |
| 157 element->set_size({kSceneSize, kSceneSize, 1.0}); | 159 element->set_size({kSceneSize, kSceneSize, 1.0}); |
| 158 element->set_translation({0.0, kSceneHeight / 2, 0.0}); | 160 element->set_translation({0.0, kSceneHeight / 2, 0.0}); |
| 159 element->set_rotation({1.0, 0.0, 0.0, M_PI / 2}); | 161 element->set_rotation({1.0, 0.0, 0.0, M_PI / 2}); |
| 160 element->set_fill(vr_shell::Fill::OPAQUE_GRADIENT); | 162 element->set_fill(vr_shell::Fill::OPAQUE_GRADIENT); |
| 161 element->set_edge_color(kBackgroundHorizonColor); | 163 element->set_edge_color(kBackgroundHorizonColor); |
| 162 element->set_center_color(kBackgroundCenterColor); | 164 element->set_center_color(kBackgroundCenterColor); |
| 163 element->set_draw_phase(0); | 165 element->set_draw_phase(0); |
| 164 browser_ui_elements_.push_back(element.get()); | 166 control_elements_.push_back(element.get()); |
| 165 scene_->AddUiElement(std::move(element)); | 167 scene_->AddUiElement(std::move(element)); |
| 166 | 168 |
| 167 // Floor grid. | 169 // Floor grid. |
| 168 element = base::MakeUnique<UiElement>(); | 170 element = base::MakeUnique<UiElement>(); |
| 169 element->set_id(AllocateId()); | 171 element->set_id(AllocateId()); |
| 170 element->set_fill(vr_shell::Fill::GRID_GRADIENT); | 172 element->set_fill(vr_shell::Fill::GRID_GRADIENT); |
| 171 element->set_size({kSceneSize, kSceneSize, 1.0}); | 173 element->set_size({kSceneSize, kSceneSize, 1.0}); |
| 172 element->set_translation({0.0, -kSceneHeight / 2 + kTextureOffset, 0.0}); | 174 element->set_translation({0.0, -kSceneHeight / 2 + kTextureOffset, 0.0}); |
| 173 element->set_rotation({1.0, 0.0, 0.0, -M_PI / 2}); | 175 element->set_rotation({1.0, 0.0, 0.0, -M_PI / 2}); |
| 174 element->set_fill(vr_shell::Fill::GRID_GRADIENT); | 176 element->set_fill(vr_shell::Fill::GRID_GRADIENT); |
| 175 element->set_center_color(kBackgroundHorizonColor); | 177 element->set_center_color(kBackgroundHorizonColor); |
| 176 vr::Colorf edge_color = kBackgroundHorizonColor; | 178 vr::Colorf edge_color = kBackgroundHorizonColor; |
| 177 edge_color.a = 0.0; | 179 edge_color.a = 0.0; |
| 178 element->set_edge_color(edge_color); | 180 element->set_edge_color(edge_color); |
| 179 element->set_gridline_count(kFloorGridlineCount); | 181 element->set_gridline_count(kFloorGridlineCount); |
| 180 element->set_draw_phase(0); | 182 element->set_draw_phase(0); |
| 181 browser_ui_elements_.push_back(element.get()); | 183 control_elements_.push_back(element.get()); |
| 182 scene_->AddUiElement(std::move(element)); | 184 scene_->AddUiElement(std::move(element)); |
| 183 | 185 |
| 184 scene_->SetBackgroundColor(kBackgroundHorizonColor); | 186 scene_->SetBackgroundColor(kBackgroundHorizonColor); |
| 185 } | 187 } |
| 186 | 188 |
| 187 void UiSceneManager::CreateUrlBar() { | 189 void UiSceneManager::CreateUrlBar() { |
| 188 // TODO(cjgrant): Incorporate final size and position. | 190 // TODO(cjgrant): Incorporate final size and position. |
| 189 // TODO(cjgrant): Add the loading progress indicator element. | 191 auto url_bar = base::MakeUnique<UrlBar>(512); |
| 190 std::unique_ptr<UrlBar> element = base::MakeUnique<UrlBar>(512); | 192 url_bar->set_id(AllocateId()); |
| 191 element->set_id(AllocateId()); | 193 url_bar->set_translation({0, -0.9, -1.8}); |
| 192 element->set_translation({0, -0.9, -1.8}); | 194 url_bar->set_size({0.9, 0, 1}); |
| 193 element->set_size({0.9, 0, 1}); | 195 url_bar->SetBackButtonCallback( |
| 194 element->SetBackButtonCallback( | |
| 195 base::Bind(&UiSceneManager::OnBackButtonClicked, base::Unretained(this))); | 196 base::Bind(&UiSceneManager::OnBackButtonClicked, base::Unretained(this))); |
| 196 url_bar_ = element.get(); | 197 url_bar_ = url_bar.get(); |
| 197 browser_ui_elements_.push_back(element.get()); | 198 control_elements_.push_back(url_bar.get()); |
| 198 scene_->AddUiElement(std::move(element)); | 199 scene_->AddUiElement(std::move(url_bar)); |
| 200 |
| 201 auto indicator = base::MakeUnique<LoadingIndicator>(256); |
| 202 indicator->set_id(AllocateId()); |
| 203 indicator->set_translation({0, -0.8, -1.8}); |
| 204 indicator->set_size({0.4, 0, 1}); |
| 205 loading_indicator_ = indicator.get(); |
| 206 control_elements_.push_back(indicator.get()); |
| 207 scene_->AddUiElement(std::move(indicator)); |
| 199 } | 208 } |
| 200 | 209 |
| 201 void UiSceneManager::CreateCloseButton() { | 210 void UiSceneManager::CreateCloseButton() { |
| 202 std::unique_ptr<CloseButton> element = | 211 std::unique_ptr<CloseButton> element = |
| 203 base::MakeUnique<CloseButton>(base::Bind( | 212 base::MakeUnique<CloseButton>(base::Bind( |
| 204 &UiSceneManager::OnCloseButtonClicked, base::Unretained(this))); | 213 &UiSceneManager::OnCloseButtonClicked, base::Unretained(this))); |
| 205 element->set_id(AllocateId()); | 214 element->set_id(AllocateId()); |
| 206 element->set_fill(vr_shell::Fill::NONE); | 215 element->set_fill(vr_shell::Fill::NONE); |
| 207 element->set_translation( | 216 element->set_translation( |
| 208 gfx::Vector3dF(0, kContentVerticalOffset - (kContentHeight / 2) - 0.3, | 217 gfx::Vector3dF(0, kContentVerticalOffset - (kContentHeight / 2) - 0.3, |
| 209 -kContentDistance + 0.4)); | 218 -kContentDistance + 0.4)); |
| 210 element->set_size(gfx::Vector3dF(0.2, 0.2, 1)); | 219 element->set_size(gfx::Vector3dF(0.2, 0.2, 1)); |
| 211 browser_ui_elements_.push_back(element.get()); | 220 control_elements_.push_back(element.get()); |
| 212 scene_->AddUiElement(std::move(element)); | 221 scene_->AddUiElement(std::move(element)); |
| 213 } | 222 } |
| 214 | 223 |
| 215 base::WeakPtr<UiSceneManager> UiSceneManager::GetWeakPtr() { | 224 base::WeakPtr<UiSceneManager> UiSceneManager::GetWeakPtr() { |
| 216 return weak_ptr_factory_.GetWeakPtr(); | 225 return weak_ptr_factory_.GetWeakPtr(); |
| 217 } | 226 } |
| 218 | 227 |
| 219 void UiSceneManager::SetWebVrMode(bool web_vr) { | 228 void UiSceneManager::SetWebVrMode(bool web_vr) { |
| 229 if (web_vr_mode_ == web_vr) |
| 230 return; |
| 220 web_vr_mode_ = web_vr; | 231 web_vr_mode_ = web_vr; |
| 232 ConfigureScene(); |
| 233 } |
| 221 | 234 |
| 222 // Make all VR scene UI elements visible if not in WebVR. | 235 void UiSceneManager::ConfigureScene() { |
| 223 for (UiElement* element : browser_ui_elements_) { | 236 // Controls (URL bar, loading progress, etc). |
| 224 element->set_visible(!web_vr_mode_); | 237 bool controls_visible = !web_vr_mode_ && !fullscreen_; |
| 238 for (UiElement* element : control_elements_) { |
| 239 element->SetEnabled(controls_visible); |
| 225 } | 240 } |
| 226 url_bar_->SetEnabled(!web_vr); | |
| 227 | 241 |
| 228 ConfigureSecurityWarnings(); | 242 // Content elements. |
| 243 for (UiElement* element : content_elements_) { |
| 244 element->SetEnabled(!web_vr_mode_); |
| 245 } |
| 246 |
| 247 // Update content quad parameters depending on fullscreen. |
| 248 // TODO(http://crbug.com/642937): Animate fullscreen transitions. |
| 249 if (fullscreen_) { |
| 250 scene_->SetBackgroundColor(kFullscreenBackgroundColor); |
| 251 main_content_->set_translation( |
| 252 {0, kFullscreenVerticalOffset, -kFullscreenDistance}); |
| 253 main_content_->set_size({kFullscreenWidth, kFullscreenHeight, 1}); |
| 254 } else { |
| 255 scene_->SetBackgroundColor(kBackgroundHorizonColor); |
| 256 // Note that main_content_ is already visible in this case. |
| 257 main_content_->set_translation( |
| 258 {0, kContentVerticalOffset, -kContentDistance}); |
| 259 main_content_->set_size({kContentWidth, kContentHeight, 1}); |
| 260 } |
| 261 |
| 262 scene_->SetBackgroundDistance(main_content_->translation().z() * |
| 263 -kBackgroundDistanceMultiplier); |
| 229 } | 264 } |
| 230 | 265 |
| 231 void UiSceneManager::SetWebVrSecureOrigin(bool secure) { | 266 void UiSceneManager::SetWebVrSecureOrigin(bool secure) { |
| 232 secure_origin_ = secure; | 267 secure_origin_ = secure; |
| 233 ConfigureSecurityWarnings(); | 268 ConfigureSecurityWarnings(); |
| 234 } | 269 } |
| 235 | 270 |
| 236 void UiSceneManager::OnAppButtonClicked() { | 271 void UiSceneManager::OnAppButtonClicked() { |
| 237 // App button click exits the WebVR presentation and fullscreen. | 272 // App button click exits the WebVR presentation and fullscreen. |
| 238 browser_->ExitPresent(); | 273 browser_->ExitPresent(); |
| 239 browser_->ExitFullscreen(); | 274 browser_->ExitFullscreen(); |
| 240 } | 275 } |
| 241 | 276 |
| 242 void UiSceneManager::OnAppButtonGesturePerformed( | 277 void UiSceneManager::OnAppButtonGesturePerformed( |
| 243 UiInterface::Direction direction) {} | 278 UiInterface::Direction direction) {} |
| 244 | 279 |
| 245 void UiSceneManager::SetFullscreen(bool fullscreen) { | 280 void UiSceneManager::SetFullscreen(bool fullscreen) { |
| 246 // Make all VR scene UI elements visible if not in fullscreen. | 281 if (fullscreen_ == fullscreen) |
| 247 for (UiElement* element : browser_ui_elements_) { | 282 return; |
| 248 element->set_visible(!fullscreen); | 283 fullscreen_ = fullscreen; |
| 249 } | 284 ConfigureScene(); |
| 250 | |
| 251 // Show the content quad in full screen. | |
| 252 if (fullscreen) { | |
| 253 scene_->SetBackgroundColor(kFullscreenBackgroundColor); | |
| 254 main_content_->set_visible(true); | |
| 255 main_content_->set_translation( | |
| 256 {0, kFullscreenVerticalOffset, -kFullscreenDistance}); | |
| 257 main_content_->set_size({kFullscreenWidth, kFullscreenHeight, 1}); | |
| 258 | |
| 259 // TODO(http://crbug.com/642937): Animate fullscreen transitions. | |
| 260 } else { | |
| 261 scene_->SetBackgroundColor(kBackgroundHorizonColor); | |
| 262 // Note that main_content_ is already visible in this case. | |
| 263 main_content_->set_translation( | |
| 264 {0, kContentVerticalOffset, -kContentDistance}); | |
| 265 main_content_->set_size({kContentWidth, kContentHeight, 1}); | |
| 266 } | |
| 267 | |
| 268 scene_->SetBackgroundDistance(main_content_->translation().z() * | |
| 269 -kBackgroundDistanceMultiplier); | |
| 270 } | 285 } |
| 271 | 286 |
| 272 void UiSceneManager::ConfigureSecurityWarnings() { | 287 void UiSceneManager::ConfigureSecurityWarnings() { |
| 273 bool enabled = web_vr_mode_ && !secure_origin_; | 288 bool enabled = web_vr_mode_ && !secure_origin_; |
| 274 permanent_security_warning_->set_visible(enabled); | 289 permanent_security_warning_->set_visible(enabled); |
| 275 transient_security_warning_->set_visible(enabled); | 290 transient_security_warning_->set_visible(enabled); |
| 276 if (enabled) { | 291 if (enabled) { |
| 277 security_warning_timer_.Start( | 292 security_warning_timer_.Start( |
| 278 FROM_HERE, base::TimeDelta::FromSeconds(kWarningTimeoutSeconds), this, | 293 FROM_HERE, base::TimeDelta::FromSeconds(kWarningTimeoutSeconds), this, |
| 279 &UiSceneManager::OnSecurityWarningTimer); | 294 &UiSceneManager::OnSecurityWarningTimer); |
| (...skipping 11 matching lines...) Expand all Loading... |
| 291 } | 306 } |
| 292 | 307 |
| 293 void UiSceneManager::SetURL(const GURL& gurl) { | 308 void UiSceneManager::SetURL(const GURL& gurl) { |
| 294 url_bar_->SetURL(gurl); | 309 url_bar_->SetURL(gurl); |
| 295 } | 310 } |
| 296 | 311 |
| 297 void UiSceneManager::SetSecurityLevel(int level) { | 312 void UiSceneManager::SetSecurityLevel(int level) { |
| 298 url_bar_->SetSecurityLevel(level); | 313 url_bar_->SetSecurityLevel(level); |
| 299 } | 314 } |
| 300 | 315 |
| 301 void UiSceneManager::SetLoading(bool loading) {} | 316 void UiSceneManager::SetLoading(bool loading) { |
| 317 loading_indicator_->SetLoading(loading); |
| 318 } |
| 302 | 319 |
| 303 void UiSceneManager::SetLoadProgress(double progress) {} | 320 void UiSceneManager::SetLoadProgress(float progress) { |
| 321 loading_indicator_->SetLoadProgress(progress); |
| 322 } |
| 304 | 323 |
| 305 void UiSceneManager::SetHistoryButtonsEnabled(bool can_go_back, | 324 void UiSceneManager::SetHistoryButtonsEnabled(bool can_go_back, |
| 306 bool can_go_forward) {} | 325 bool can_go_forward) {} |
| 307 | 326 |
| 308 void UiSceneManager::OnCloseButtonClicked() {} | 327 void UiSceneManager::OnCloseButtonClicked() {} |
| 309 | 328 |
| 310 int UiSceneManager::AllocateId() { | 329 int UiSceneManager::AllocateId() { |
| 311 return next_available_id_++; | 330 return next_available_id_++; |
| 312 } | 331 } |
| 313 | 332 |
| 314 } // namespace vr_shell | 333 } // namespace vr_shell |
| OLD | NEW |