| OLD | NEW |
| 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2013 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/common/extensions/extension.h" | 5 #include "chrome/common/extensions/extension.h" |
| 6 | 6 |
| 7 #include "base/base64.h" | 7 #include "base/base64.h" |
| 8 #include "base/basictypes.h" | 8 #include "base/basictypes.h" |
| 9 #include "base/command_line.h" | 9 #include "base/command_line.h" |
| 10 #include "base/file_util.h" | 10 #include "base/file_util.h" |
| (...skipping 199 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 210 #endif | 210 #endif |
| 211 | 211 |
| 212 // first 16 bytes of SHA256 hashed public key. | 212 // first 16 bytes of SHA256 hashed public key. |
| 213 const size_t Extension::kIdSize = 16; | 213 const size_t Extension::kIdSize = 16; |
| 214 | 214 |
| 215 const char Extension::kMimeType[] = "application/x-chrome-extension"; | 215 const char Extension::kMimeType[] = "application/x-chrome-extension"; |
| 216 | 216 |
| 217 const int Extension::kValidWebExtentSchemes = | 217 const int Extension::kValidWebExtentSchemes = |
| 218 URLPattern::SCHEME_HTTP | URLPattern::SCHEME_HTTPS; | 218 URLPattern::SCHEME_HTTP | URLPattern::SCHEME_HTTPS; |
| 219 | 219 |
| 220 const int Extension::kValidHostPermissionSchemes = | 220 const int Extension::kValidHostPermissionSchemes = URLPattern::SCHEME_CHROMEUI | |
| 221 UserScript::kValidUserScriptSchemes | URLPattern::SCHEME_CHROMEUI; | 221 URLPattern::SCHEME_HTTP | |
| 222 URLPattern::SCHEME_HTTPS | |
| 223 URLPattern::SCHEME_FILE | |
| 224 URLPattern::SCHEME_FTP; |
| 222 | 225 |
| 223 Extension::Requirements::Requirements() | 226 Extension::Requirements::Requirements() |
| 224 : webgl(false), | 227 : webgl(false), |
| 225 css3d(false), | 228 css3d(false), |
| 226 npapi(false) { | 229 npapi(false) { |
| 227 } | 230 } |
| 228 | 231 |
| 229 Extension::Requirements::~Requirements() {} | 232 Extension::Requirements::~Requirements() {} |
| 230 | 233 |
| 231 // | 234 // |
| (...skipping 351 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 583 URLPattern::SCHEME_ALL : kValidHostPermissionSchemes; | 586 URLPattern::SCHEME_ALL : kValidHostPermissionSchemes; |
| 584 | 587 |
| 585 for (std::vector<std::string>::const_iterator it = host_data.begin(); | 588 for (std::vector<std::string>::const_iterator it = host_data.begin(); |
| 586 it != host_data.end(); ++it) { | 589 it != host_data.end(); ++it) { |
| 587 const std::string& permission_str = *it; | 590 const std::string& permission_str = *it; |
| 588 | 591 |
| 589 // Check if it's a host pattern permission. | 592 // Check if it's a host pattern permission. |
| 590 URLPattern pattern = URLPattern(kAllowedSchemes); | 593 URLPattern pattern = URLPattern(kAllowedSchemes); |
| 591 URLPattern::ParseResult parse_result = pattern.Parse(permission_str); | 594 URLPattern::ParseResult parse_result = pattern.Parse(permission_str); |
| 592 if (parse_result == URLPattern::PARSE_SUCCESS) { | 595 if (parse_result == URLPattern::PARSE_SUCCESS) { |
| 596 // The path component is not used for host permissions, so we force it |
| 597 // to match all paths. |
| 598 pattern.SetPath("/*"); |
| 599 int valid_schemes = pattern.valid_schemes(); |
| 600 if (pattern.MatchesScheme(chrome::kFileScheme) && |
| 601 !CanExecuteScriptEverywhere()) { |
| 602 wants_file_access_ = true; |
| 603 if (!(creation_flags_ & ALLOW_FILE_ACCESS)) |
| 604 valid_schemes &= ~URLPattern::SCHEME_FILE; |
| 605 } |
| 606 |
| 607 if (pattern.scheme() != chrome::kChromeUIScheme && |
| 608 !CanExecuteScriptEverywhere()) { |
| 609 // Keep chrome:// in allowed schemes only if it's explicitly requested |
| 610 // or CanExecuteScriptEverywhere is true. If the |
| 611 // extensions_on_chrome_urls flag is not set, CanSpecifyHostPermission |
| 612 // will fail, so don't check the flag here. |
| 613 valid_schemes &= ~URLPattern::SCHEME_CHROMEUI; |
| 614 } |
| 615 pattern.SetValidSchemes(valid_schemes); |
| 616 |
| 593 if (!CanSpecifyHostPermission(pattern, *api_permissions)) { | 617 if (!CanSpecifyHostPermission(pattern, *api_permissions)) { |
| 618 // TODO(aboxhall): make a warning (see line 633) |
| 594 *error = ErrorUtils::FormatErrorMessageUTF16( | 619 *error = ErrorUtils::FormatErrorMessageUTF16( |
| 595 errors::kInvalidPermissionScheme, permission_str); | 620 errors::kInvalidPermissionScheme, permission_str); |
| 596 return false; | 621 return false; |
| 597 } | 622 } |
| 598 | 623 |
| 599 // The path component is not used for host permissions, so we force it | 624 host_permissions->AddPattern(pattern); |
| 600 // to match all paths. | |
| 601 pattern.SetPath("/*"); | |
| 602 | 625 |
| 603 if (pattern.MatchesScheme(chrome::kFileScheme) && | 626 // We need to make sure all_urls matches chrome://favicon and |
| 604 !CanExecuteScriptEverywhere()) { | 627 // (maybe) chrome://thumbnail, so add them back in to host_permissions |
| 605 wants_file_access_ = true; | 628 // separately. |
| 606 if (!(creation_flags_ & ALLOW_FILE_ACCESS)) { | 629 if (pattern.match_all_urls()) { |
| 607 pattern.SetValidSchemes( | 630 host_permissions->AddPattern( |
| 608 pattern.valid_schemes() & ~URLPattern::SCHEME_FILE); | 631 URLPattern(URLPattern::SCHEME_CHROMEUI, |
| 632 chrome::kChromeUIFaviconURL)); |
| 633 if (api_permissions->find(APIPermission::kExperimental) != |
| 634 api_permissions->end()) { |
| 635 host_permissions->AddPattern( |
| 636 URLPattern(URLPattern::SCHEME_CHROMEUI, |
| 637 chrome::kChromeUIThumbnailURL)); |
| 609 } | 638 } |
| 610 } | 639 } |
| 611 | |
| 612 host_permissions->AddPattern(pattern); | |
| 613 continue; | 640 continue; |
| 614 } | 641 } |
| 615 | 642 |
| 616 // It's probably an unknown API permission. Do not throw an error so | 643 // It's probably an unknown API permission. Do not throw an error so |
| 617 // extensions can retain backwards compatability (http://crbug.com/42742). | 644 // extensions can retain backwards compatability (http://crbug.com/42742). |
| 618 install_warnings_.push_back(InstallWarning( | 645 install_warnings_.push_back(InstallWarning( |
| 619 InstallWarning::FORMAT_TEXT, | 646 InstallWarning::FORMAT_TEXT, |
| 620 base::StringPrintf( | 647 base::StringPrintf( |
| 621 "Permission '%s' is unknown or URL pattern is malformed.", | 648 "Permission '%s' is unknown or URL pattern is malformed.", |
| 622 permission_str.c_str()))); | 649 permission_str.c_str()))); |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 657 const URLPatternSet& Extension::GetEffectiveHostPermissions() const { | 684 const URLPatternSet& Extension::GetEffectiveHostPermissions() const { |
| 658 base::AutoLock auto_lock(runtime_data_lock_); | 685 base::AutoLock auto_lock(runtime_data_lock_); |
| 659 return runtime_data_.GetActivePermissions()->effective_hosts(); | 686 return runtime_data_.GetActivePermissions()->effective_hosts(); |
| 660 } | 687 } |
| 661 | 688 |
| 662 bool Extension::CanSilentlyIncreasePermissions() const { | 689 bool Extension::CanSilentlyIncreasePermissions() const { |
| 663 return location() != Manifest::INTERNAL; | 690 return location() != Manifest::INTERNAL; |
| 664 } | 691 } |
| 665 | 692 |
| 666 bool Extension::HasHostPermission(const GURL& url) const { | 693 bool Extension::HasHostPermission(const GURL& url) const { |
| 667 if (url.SchemeIs(chrome::kChromeUIScheme) && | |
| 668 url.host() != chrome::kChromeUIFaviconHost && | |
| 669 url.host() != chrome::kChromeUIThumbnailHost && | |
| 670 location() != Manifest::COMPONENT) { | |
| 671 return false; | |
| 672 } | |
| 673 | |
| 674 base::AutoLock auto_lock(runtime_data_lock_); | 694 base::AutoLock auto_lock(runtime_data_lock_); |
| 675 return runtime_data_.GetActivePermissions()-> | 695 return runtime_data_.GetActivePermissions()-> |
| 676 HasExplicitAccessToOrigin(url); | 696 HasExplicitAccessToOrigin(url); |
| 677 } | 697 } |
| 678 | 698 |
| 679 bool Extension::HasEffectiveAccessToAllHosts() const { | 699 bool Extension::HasEffectiveAccessToAllHosts() const { |
| 680 base::AutoLock auto_lock(runtime_data_lock_); | 700 base::AutoLock auto_lock(runtime_data_lock_); |
| 681 return runtime_data_.GetActivePermissions()->HasEffectiveAccessToAllHosts(); | 701 return runtime_data_.GetActivePermissions()->HasEffectiveAccessToAllHosts(); |
| 682 } | 702 } |
| 683 | 703 |
| (...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 796 GURL store_url(extension_urls::GetWebstoreLaunchURL()); | 816 GURL store_url(extension_urls::GetWebstoreLaunchURL()); |
| 797 if ((document_url.host() == store_url.host()) && | 817 if ((document_url.host() == store_url.host()) && |
| 798 !CanExecuteScriptEverywhere() && | 818 !CanExecuteScriptEverywhere() && |
| 799 !CommandLine::ForCurrentProcess()->HasSwitch( | 819 !CommandLine::ForCurrentProcess()->HasSwitch( |
| 800 switches::kAllowScriptingGallery)) { | 820 switches::kAllowScriptingGallery)) { |
| 801 if (error) | 821 if (error) |
| 802 *error = errors::kCannotScriptGallery; | 822 *error = errors::kCannotScriptGallery; |
| 803 return false; | 823 return false; |
| 804 } | 824 } |
| 805 | 825 |
| 806 if (document_url.SchemeIs(chrome::kChromeUIScheme) && | 826 if (!CommandLine::ForCurrentProcess()->HasSwitch( |
| 807 !CanExecuteScriptEverywhere()) { | 827 switches::kExtensionsOnChromeURLs)) { |
| 808 return false; | 828 if (document_url.SchemeIs(chrome::kChromeUIScheme) && |
| 829 !CanExecuteScriptEverywhere()) { |
| 830 return false; |
| 831 } |
| 809 } | 832 } |
| 810 | 833 |
| 811 if (top_frame_url.SchemeIs(extensions::kExtensionScheme) && | 834 if (top_frame_url.SchemeIs(extensions::kExtensionScheme) && |
| 812 top_frame_url.GetOrigin() != | 835 top_frame_url.GetOrigin() != |
| 813 GetBaseURLFromExtensionId(id()).GetOrigin() && | 836 GetBaseURLFromExtensionId(id()).GetOrigin() && |
| 814 !CanExecuteScriptEverywhere()) { | 837 !CanExecuteScriptEverywhere()) { |
| 815 return false; | 838 return false; |
| 816 } | 839 } |
| 817 | 840 |
| 818 // If a tab ID is specified, try the tab-specific permissions. | 841 // If a tab ID is specified, try the tab-specific permissions. |
| (...skipping 1136 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1955 !LoadSystemIndicator(error) || | 1978 !LoadSystemIndicator(error) || |
| 1956 !LoadIncognitoMode(error)) | 1979 !LoadIncognitoMode(error)) |
| 1957 return false; | 1980 return false; |
| 1958 | 1981 |
| 1959 return true; | 1982 return true; |
| 1960 } | 1983 } |
| 1961 | 1984 |
| 1962 bool Extension::LoadContentScripts(string16* error) { | 1985 bool Extension::LoadContentScripts(string16* error) { |
| 1963 if (!manifest_->HasKey(keys::kContentScripts)) | 1986 if (!manifest_->HasKey(keys::kContentScripts)) |
| 1964 return true; | 1987 return true; |
| 1988 |
| 1965 const ListValue* list_value; | 1989 const ListValue* list_value; |
| 1966 if (!manifest_->GetList(keys::kContentScripts, &list_value)) { | 1990 if (!manifest_->GetList(keys::kContentScripts, &list_value)) { |
| 1967 *error = ASCIIToUTF16(errors::kInvalidContentScriptsList); | 1991 *error = ASCIIToUTF16(errors::kInvalidContentScriptsList); |
| 1968 return false; | 1992 return false; |
| 1969 } | 1993 } |
| 1970 | 1994 |
| 1971 for (size_t i = 0; i < list_value->GetSize(); ++i) { | 1995 for (size_t i = 0; i < list_value->GetSize(); ++i) { |
| 1972 const DictionaryValue* content_script = NULL; | 1996 const DictionaryValue* content_script = NULL; |
| 1973 if (!list_value->GetDictionary(i, &content_script)) { | 1997 if (!list_value->GetDictionary(i, &content_script)) { |
| 1974 *error = ErrorUtils::FormatErrorMessageUTF16( | 1998 *error = ErrorUtils::FormatErrorMessageUTF16( |
| 1975 errors::kInvalidContentScript, base::IntToString(i)); | 1999 errors::kInvalidContentScript, base::IntToString(i)); |
| 1976 return false; | 2000 return false; |
| 1977 } | 2001 } |
| 1978 | 2002 |
| 1979 UserScript script; | 2003 UserScript script; |
| 1980 if (!LoadUserScriptHelper(content_script, i, error, &script)) | 2004 if (!LoadUserScriptHelper(content_script, i, error, &script)) |
| 1981 return false; // Failed to parse script context definition. | 2005 return false; // Failed to parse script context definition. |
| 2006 |
| 1982 script.set_extension_id(id()); | 2007 script.set_extension_id(id()); |
| 1983 if (converted_from_user_script_) { | 2008 if (converted_from_user_script_) { |
| 1984 script.set_emulate_greasemonkey(true); | 2009 script.set_emulate_greasemonkey(true); |
| 1985 script.set_match_all_frames(true); // Greasemonkey matches all frames. | 2010 script.set_match_all_frames(true); // Greasemonkey matches all frames. |
| 1986 } | 2011 } |
| 1987 content_scripts_.push_back(script); | 2012 content_scripts_.push_back(script); |
| 1988 } | 2013 } |
| 1989 return true; | 2014 return true; |
| 1990 } | 2015 } |
| 1991 | 2016 |
| (...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2097 std::string match_str; | 2122 std::string match_str; |
| 2098 if (!matches->GetString(j, &match_str)) { | 2123 if (!matches->GetString(j, &match_str)) { |
| 2099 *error = ErrorUtils::FormatErrorMessageUTF16( | 2124 *error = ErrorUtils::FormatErrorMessageUTF16( |
| 2100 errors::kInvalidMatch, | 2125 errors::kInvalidMatch, |
| 2101 base::IntToString(definition_index), | 2126 base::IntToString(definition_index), |
| 2102 base::IntToString(j), | 2127 base::IntToString(j), |
| 2103 errors::kExpectString); | 2128 errors::kExpectString); |
| 2104 return false; | 2129 return false; |
| 2105 } | 2130 } |
| 2106 | 2131 |
| 2107 URLPattern pattern(UserScript::kValidUserScriptSchemes); | 2132 URLPattern pattern( |
| 2108 if (CanExecuteScriptEverywhere()) | 2133 UserScript::ValidUserScriptSchemes(CanExecuteScriptEverywhere())); |
| 2109 pattern.SetValidSchemes(URLPattern::SCHEME_ALL); | |
| 2110 | 2134 |
| 2111 URLPattern::ParseResult parse_result = pattern.Parse(match_str); | 2135 URLPattern::ParseResult parse_result = pattern.Parse(match_str); |
| 2112 if (parse_result != URLPattern::PARSE_SUCCESS) { | 2136 if (parse_result != URLPattern::PARSE_SUCCESS) { |
| 2113 *error = ErrorUtils::FormatErrorMessageUTF16( | 2137 *error = ErrorUtils::FormatErrorMessageUTF16( |
| 2114 errors::kInvalidMatch, | 2138 errors::kInvalidMatch, |
| 2115 base::IntToString(definition_index), | 2139 base::IntToString(definition_index), |
| 2116 base::IntToString(j), | 2140 base::IntToString(j), |
| 2117 URLPattern::GetParseResultString(parse_result)); | 2141 URLPattern::GetParseResultString(parse_result)); |
| 2118 return false; | 2142 return false; |
| 2119 } | 2143 } |
| 2120 | 2144 |
| 2145 // TODO(aboxhall): check for webstore |
| 2146 if (!CanExecuteScriptEverywhere() && |
| 2147 pattern.scheme() != chrome::kChromeUIScheme) { |
| 2148 // Exclude SCHEME_CHROMEUI unless it's been explicitly requested. |
| 2149 // If the --extensions-on-chrome-urls flag has not been passed, requesting |
| 2150 // a chrome:// url will cause a parse failure above, so there's no need to |
| 2151 // check the flag here. |
| 2152 pattern.SetValidSchemes( |
| 2153 pattern.valid_schemes() & ~URLPattern::SCHEME_CHROMEUI); |
| 2154 } |
| 2155 |
| 2121 if (pattern.MatchesScheme(chrome::kFileScheme) && | 2156 if (pattern.MatchesScheme(chrome::kFileScheme) && |
| 2122 !CanExecuteScriptEverywhere()) { | 2157 !CanExecuteScriptEverywhere()) { |
| 2123 wants_file_access_ = true; | 2158 wants_file_access_ = true; |
| 2124 if (!(creation_flags_ & ALLOW_FILE_ACCESS)) { | 2159 if (!(creation_flags_ & ALLOW_FILE_ACCESS)) { |
| 2125 pattern.SetValidSchemes( | 2160 pattern.SetValidSchemes( |
| 2126 pattern.valid_schemes() & ~URLPattern::SCHEME_FILE); | 2161 pattern.valid_schemes() & ~URLPattern::SCHEME_FILE); |
| 2127 } | 2162 } |
| 2128 } | 2163 } |
| 2129 | 2164 |
| 2130 result->add_url_pattern(pattern); | 2165 result->add_url_pattern(pattern); |
| (...skipping 13 matching lines...) Expand all Loading... |
| 2144 std::string match_str; | 2179 std::string match_str; |
| 2145 if (!exclude_matches->GetString(j, &match_str)) { | 2180 if (!exclude_matches->GetString(j, &match_str)) { |
| 2146 *error = ErrorUtils::FormatErrorMessageUTF16( | 2181 *error = ErrorUtils::FormatErrorMessageUTF16( |
| 2147 errors::kInvalidExcludeMatch, | 2182 errors::kInvalidExcludeMatch, |
| 2148 base::IntToString(definition_index), | 2183 base::IntToString(definition_index), |
| 2149 base::IntToString(j), | 2184 base::IntToString(j), |
| 2150 errors::kExpectString); | 2185 errors::kExpectString); |
| 2151 return false; | 2186 return false; |
| 2152 } | 2187 } |
| 2153 | 2188 |
| 2154 URLPattern pattern(UserScript::kValidUserScriptSchemes); | 2189 int valid_schemes = |
| 2155 if (CanExecuteScriptEverywhere()) | 2190 UserScript::ValidUserScriptSchemes(CanExecuteScriptEverywhere()); |
| 2156 pattern.SetValidSchemes(URLPattern::SCHEME_ALL); | 2191 URLPattern pattern(valid_schemes); |
| 2157 URLPattern::ParseResult parse_result = pattern.Parse(match_str); | 2192 URLPattern::ParseResult parse_result = pattern.Parse(match_str); |
| 2158 if (parse_result != URLPattern::PARSE_SUCCESS) { | 2193 if (parse_result != URLPattern::PARSE_SUCCESS) { |
| 2159 *error = ErrorUtils::FormatErrorMessageUTF16( | 2194 *error = ErrorUtils::FormatErrorMessageUTF16( |
| 2160 errors::kInvalidExcludeMatch, | 2195 errors::kInvalidExcludeMatch, |
| 2161 base::IntToString(definition_index), base::IntToString(j), | 2196 base::IntToString(definition_index), base::IntToString(j), |
| 2162 URLPattern::GetParseResultString(parse_result)); | 2197 URLPattern::GetParseResultString(parse_result)); |
| 2163 return false; | 2198 return false; |
| 2164 } | 2199 } |
| 2165 | 2200 |
| 2166 result->add_exclude_url_pattern(pattern); | 2201 result->add_exclude_url_pattern(pattern); |
| (...skipping 183 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2350 // Experimental extensions are also allowed chrome://thumb. | 2385 // Experimental extensions are also allowed chrome://thumb. |
| 2351 if (pattern.host() == chrome::kChromeUIThumbnailHost) { | 2386 if (pattern.host() == chrome::kChromeUIThumbnailHost) { |
| 2352 return permissions.find(APIPermission::kExperimental) != | 2387 return permissions.find(APIPermission::kExperimental) != |
| 2353 permissions.end(); | 2388 permissions.end(); |
| 2354 } | 2389 } |
| 2355 | 2390 |
| 2356 // Component extensions can have access to all of chrome://*. | 2391 // Component extensions can have access to all of chrome://*. |
| 2357 if (CanExecuteScriptEverywhere()) | 2392 if (CanExecuteScriptEverywhere()) |
| 2358 return true; | 2393 return true; |
| 2359 | 2394 |
| 2395 if (CommandLine::ForCurrentProcess()->HasSwitch( |
| 2396 switches::kExtensionsOnChromeURLs)) |
| 2397 return true; |
| 2398 |
| 2399 // TODO(aboxhall): return from_webstore() when webstore handles blocking |
| 2400 // extensions which request chrome:// urls |
| 2360 return false; | 2401 return false; |
| 2361 } | 2402 } |
| 2362 | 2403 |
| 2363 // Otherwise, the valid schemes were handled by URLPattern. | 2404 // Otherwise, the valid schemes were handled by URLPattern. |
| 2364 return true; | 2405 return true; |
| 2365 } | 2406 } |
| 2366 | 2407 |
| 2367 bool Extension::CheckMinimumChromeVersion(string16* error) const { | 2408 bool Extension::CheckMinimumChromeVersion(string16* error) const { |
| 2368 if (!manifest_->HasKey(keys::kMinimumChromeVersion)) | 2409 if (!manifest_->HasKey(keys::kMinimumChromeVersion)) |
| 2369 return true; | 2410 return true; |
| (...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2451 | 2492 |
| 2452 UpdatedExtensionPermissionsInfo::UpdatedExtensionPermissionsInfo( | 2493 UpdatedExtensionPermissionsInfo::UpdatedExtensionPermissionsInfo( |
| 2453 const Extension* extension, | 2494 const Extension* extension, |
| 2454 const PermissionSet* permissions, | 2495 const PermissionSet* permissions, |
| 2455 Reason reason) | 2496 Reason reason) |
| 2456 : reason(reason), | 2497 : reason(reason), |
| 2457 extension(extension), | 2498 extension(extension), |
| 2458 permissions(permissions) {} | 2499 permissions(permissions) {} |
| 2459 | 2500 |
| 2460 } // namespace extensions | 2501 } // namespace extensions |
| OLD | NEW |