1 // Copyright 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "webkit/common/user_agent/user_agent.h" 6 7 #include "base/lazy_instance.h" 8 #include "base/logging.h" 9 #include "base/strings/string_util.h" 10 #include "base/strings/stringprintf.h" 11 #include "base/synchronization/lock.h" 12 #include "webkit/common/user_agent/user_agent_util.h" 13 14 namespace webkit_glue { 15 16 namespace { 17 18 class UserAgentState { 19 public: 20 UserAgentState(); 21 ~UserAgentState(); 22 23 void Set(const std::string& user_agent, bool overriding); 24 const std::string& Get(const GURL& url) const; 25 26 private: 27 mutable std::string user_agent_; 28 // The UA string when we're pretending to be Mac Safari or Win Firefox. 29 mutable std::string user_agent_for_spoofing_hack_; 30 31 mutable bool user_agent_requested_; 32 bool user_agent_is_overridden_; 33 34 // This object can be accessed from multiple threads, so use a lock around 35 // accesses to the data members. 36 mutable base::Lock lock_; 37 }; 38 39 UserAgentState::UserAgentState() 40 : user_agent_requested_(false), 41 user_agent_is_overridden_(false) { 42 } 43 44 UserAgentState::~UserAgentState() { 45 } 46 47 void UserAgentState::Set(const std::string& user_agent, bool overriding) { 48 base::AutoLock auto_lock(lock_); 49 if (user_agent == user_agent_) { 50 // We allow the user agent to be set multiple times as long as it 51 // is set to the same value, in order to simplify unit testing 52 // given g_user_agent is a global. 53 return; 54 } 55 DCHECK(!user_agent.empty()); 56 DCHECK(!user_agent_requested_) << "Setting the user agent after someone has " 57 "already requested it can result in unexpected behavior."; 58 user_agent_is_overridden_ = overriding; 59 user_agent_ = user_agent; 60 } 61 62 bool IsMicrosoftSiteThatNeedsSpoofingForSilverlight(const GURL& url) { 63 #if defined(OS_MACOSX) && !defined(OS_IOS) 64 // The landing page for updating Silverlight gives a confusing experience 65 // in browsers that Silverlight doesn't officially support; spoof as 66 // Safari to reduce the chance that users won't complete updates. 67 // Should be removed if the sniffing is removed: http://crbug.com/88211 68 if (url.host() == "www.microsoft.com" && 69 StartsWithASCII(url.path(), "/getsilverlight", false)) { 70 return true; 71 } 72 #endif 73 return false; 74 } 75 76 bool IsYahooSiteThatNeedsSpoofingForSilverlight(const GURL& url) { 77 #if defined(OS_MACOSX) && !defined(OS_IOS) 78 if ((url.host() == "downloads.yahoo.co.jp" && 79 StartsWithASCII(url.path(), "/docs/silverlight/", true)) || 80 url.host() == "gyao.yahoo.co.jp") { 81 return true; 82 } 83 #elif defined(OS_WIN) 84 if (url.host() == "promotion.shopping.yahoo.co.jp") { 85 return true; 86 } 87 #endif 88 return false; 89 } 90 91 const std::string& UserAgentState::Get(const GURL& url) const { 92 base::AutoLock auto_lock(lock_); 93 user_agent_requested_ = true; 94 95 DCHECK(!user_agent_.empty()); 96 97 // Workarounds for sites that use misguided UA sniffing. 98 if (!user_agent_is_overridden_) { 99 if (IsMicrosoftSiteThatNeedsSpoofingForSilverlight(url) || 100 IsYahooSiteThatNeedsSpoofingForSilverlight(url)) { 101 if (user_agent_for_spoofing_hack_.empty()) { 102 #if defined(OS_MACOSX) && !defined(OS_IOS) 103 user_agent_for_spoofing_hack_ = 104 BuildUserAgentFromProduct("Version/5.1.1 Safari/534.51.22"); 105 #elif defined(OS_WIN) 106 // Pretend to be Firefox. Silverlight doesn't support Win Safari. 107 base::StringAppendF( 108 &user_agent_for_spoofing_hack_, 109 "Mozilla/5.0 (%s) Gecko/20100101 Firefox/8.0", 110 webkit_glue::BuildOSCpuInfo().c_str()); 111 #endif 112 } 113 DCHECK(!user_agent_for_spoofing_hack_.empty()); 114 return user_agent_for_spoofing_hack_; 115 } 116 } 117 118 return user_agent_; 119 } 120 121 base::LazyInstance<UserAgentState> g_user_agent = LAZY_INSTANCE_INITIALIZER; 122 123 } // namespace 124 125 void SetUserAgent(const std::string& user_agent, bool overriding) { 126 g_user_agent.Get().Set(user_agent, overriding); 127 } 128 129 const std::string& GetUserAgent(const GURL& url) { 130 return g_user_agent.Get().Get(url); 131 } 132 133 } // namespace webkit_glue 134