MacViews: Add a case for Mac in TextfieldTest.KeysWithModifiersTest
[chromium-blink-merge.git] / ios / crnet / crnet_environment.mm
blob9fa39da6b3f88a0c82faaa8d99da2da95f0c8f46
1 // Copyright 2014 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.
5 #include "ios/crnet/crnet_environment.h"
7 #import <Foundation/Foundation.h>
9 #include "base/at_exit.h"
10 #include "base/command_line.h"
11 #include "base/files/file_path.h"
12 #include "base/files/file_util.h"
13 #include "base/files/scoped_file.h"
14 #include "base/i18n/icu_util.h"
15 #include "base/json/json_writer.h"
16 #include "base/mac/bind_objc_block.h"
17 #include "base/mac/foundation_util.h"
18 #include "base/mac/scoped_block.h"
19 #include "base/metrics/statistics_recorder.h"
20 #include "base/path_service.h"
21 #include "base/prefs/json_pref_store.h"
22 #include "base/prefs/pref_filter.h"
23 #include "base/threading/worker_pool.h"
24 #import "components/webp_transcode/webp_network_client_factory.h"
25 #include "crypto/nss_util.h"
26 #include "ios/net/cookies/cookie_store_ios.h"
27 #include "ios/net/crn_http_protocol_handler.h"
28 #include "ios/net/empty_nsurlcache.h"
29 #include "ios/net/request_tracker.h"
30 #include "ios/web/public/user_agent.h"
31 #include "net/base/net_errors.h"
32 #include "net/base/network_change_notifier.h"
33 #include "net/base/sdch_manager.h"
34 #include "net/cert/cert_verifier.h"
35 #include "net/cert_net/nss_ocsp.h"
36 #include "net/disk_cache/disk_cache.h"
37 #include "net/http/http_auth_handler_factory.h"
38 #include "net/http/http_cache.h"
39 #include "net/http/http_server_properties_impl.h"
40 #include "net/http/http_stream_factory.h"
41 #include "net/http/http_util.h"
42 #include "net/log/net_log.h"
43 #include "net/log/write_to_file_net_log_observer.h"
44 #include "net/proxy/proxy_service.h"
45 #include "net/sdch/sdch_owner.h"
46 #include "net/socket/next_proto.h"
47 #include "net/ssl/channel_id_service.h"
48 #include "net/ssl/default_channel_id_store.h"
49 #include "net/ssl/ssl_config_service_defaults.h"
50 #include "net/url_request/data_protocol_handler.h"
51 #include "net/url_request/file_protocol_handler.h"
52 #include "net/url_request/sdch_dictionary_fetcher.h"
53 #include "net/url_request/static_http_user_agent_settings.h"
54 #include "net/url_request/url_request_context_getter.h"
55 #include "net/url_request/url_request_context_storage.h"
56 #include "net/url_request/url_request_job_factory_impl.h"
57 #include "url/url_util.h"
59 namespace {
61 base::AtExitManager* g_at_exit_ = nullptr;
63 // Request context getter for CrNet.
64 class CrNetURLRequestContextGetter : public net::URLRequestContextGetter {
65  public:
66   CrNetURLRequestContextGetter(
67       net::URLRequestContext* context,
68       const scoped_refptr<base::SingleThreadTaskRunner>& task_runner)
69       : context_(context), task_runner_(task_runner) {}
71   net::URLRequestContext* GetURLRequestContext() override { return context_; }
73   scoped_refptr<base::SingleThreadTaskRunner> GetNetworkTaskRunner()
74       const override {
75     return task_runner_;
76   }
77  private:
78   // Must be called on the IO thread.
79   ~CrNetURLRequestContextGetter() override {}
81   net::URLRequestContext* context_;
82   scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
83   DISALLOW_COPY_AND_ASSIGN(CrNetURLRequestContextGetter);
86 }  // namespace
88 // net::HTTPProtocolHandlerDelegate for CrNet.
89 class CrNetHttpProtocolHandlerDelegate
90     : public net::HTTPProtocolHandlerDelegate {
91  public:
92   CrNetHttpProtocolHandlerDelegate(net::URLRequestContextGetter* getter,
93                                    RequestFilterBlock filter)
94       : getter_(getter), filter_(filter, base::scoped_policy::RETAIN) {}
96  private:
97   // net::HTTPProtocolHandlerDelegate implementation:
98   bool CanHandleRequest(NSURLRequest* request) override {
99     // Don't advertise support for file:// URLs for now.
100     // This broke some time ago but it's not clear how to fix it at the moment.
101     // http://crbug.com/480620
102     if ([[[request URL] scheme] caseInsensitiveCompare:@"file"] ==
103         NSOrderedSame) {
104       return false;
105     }
106     if (filter_) {
107       RequestFilterBlock block = filter_.get();
108       return block(request);
109     }
110     return true;
111   }
113   bool IsRequestSupported(NSURLRequest* request) override {
114     NSString* scheme = [[request URL] scheme];
115     if (!scheme)
116       return false;
117     return [scheme caseInsensitiveCompare:@"data"] == NSOrderedSame ||
118            [scheme caseInsensitiveCompare:@"http"] == NSOrderedSame ||
119            [scheme caseInsensitiveCompare:@"https"] == NSOrderedSame;
120   }
122   net::URLRequestContextGetter* GetDefaultURLRequestContext() override {
123     return getter_.get();
124   }
126   scoped_refptr<net::URLRequestContextGetter> getter_;
127   base::mac::ScopedBlock<RequestFilterBlock> filter_;
130 void CrNetEnvironment::PostToNetworkThread(
131     const tracked_objects::Location& from_here,
132     const base::Closure& task) {
133   network_io_thread_->message_loop()->PostTask(from_here, task);
136 void CrNetEnvironment::PostToFileUserBlockingThread(
137     const tracked_objects::Location& from_here,
138     const base::Closure& task) {
139   file_user_blocking_thread_->message_loop()->PostTask(from_here, task);
142 // static
143 void CrNetEnvironment::Initialize() {
144   DCHECK_EQ([NSThread currentThread], [NSThread mainThread]);
145   if (!g_at_exit_)
146     g_at_exit_ = new base::AtExitManager;
148   CHECK(base::i18n::InitializeICU());
149   url::Initialize();
150   base::CommandLine::Init(0, nullptr);
151   // This needs to happen on the main thread. NSPR's initialization sets up its
152   // memory allocator; if this is not done before other threads are created,
153   // this initialization can race to cause accidental free/allocation
154   // mismatches.
155   crypto::EnsureNSPRInit();
156   // Without doing this, StatisticsRecorder::FactoryGet() leaks one histogram
157   // per call after the first for a given name.
158   base::StatisticsRecorder::Initialize();
160   // Create a message loop on the UI thread.
161   base::MessageLoop* main_message_loop =
162       new base::MessageLoop(base::MessageLoop::TYPE_UI);
163 #pragma unused(main_message_loop)
164   base::MessageLoopForUI::current()->Attach();
167 void CrNetEnvironment::StartNetLog(base::FilePath::StringType file_name,
168     bool log_bytes) {
169   DCHECK(file_name.length());
170   PostToFileUserBlockingThread(FROM_HERE,
171       base::Bind(&CrNetEnvironment::StartNetLogInternal,
172                  base::Unretained(this), file_name, log_bytes));
175 void CrNetEnvironment::StartNetLogInternal(
176     base::FilePath::StringType file_name, bool log_bytes) {
177   DCHECK(base::MessageLoop::current() ==
178          file_user_blocking_thread_->message_loop());
179   DCHECK(file_name.length());
180   DCHECK(net_log_);
182   if (net_log_observer_)
183     return;
185   base::FilePath temp_dir;
186   if (!base::GetTempDir(&temp_dir))
187     return;
189   base::FilePath full_path = temp_dir.Append(file_name);
190   base::ScopedFILE file(base::OpenFile(full_path, "w"));
191   if (!file)
192     return;
194   net::NetLogCaptureMode capture_mode = log_bytes ?
195       net::NetLogCaptureMode::IncludeSocketBytes() :
196       net::NetLogCaptureMode::Default();
198   net_log_observer_.reset(new net::WriteToFileNetLogObserver());
199   net_log_observer_->set_capture_mode(capture_mode);
200   net_log_observer_->StartObserving(net_log_.get(), file.Pass(), nullptr,
201                                     nullptr);
204 void CrNetEnvironment::StopNetLog() {
205   PostToFileUserBlockingThread(FROM_HERE,
206       base::Bind(&CrNetEnvironment::StopNetLogInternal,
207       base::Unretained(this)));
210 void CrNetEnvironment::StopNetLogInternal() {
211   DCHECK(base::MessageLoop::current() ==
212          file_user_blocking_thread_->message_loop());
213   if (net_log_observer_) {
214     net_log_observer_->StopObserving(nullptr);
215     net_log_observer_.reset();
216   }
219 void CrNetEnvironment::CloseAllSpdySessions() {
220   PostToNetworkThread(FROM_HERE,
221       base::Bind(&CrNetEnvironment::CloseAllSpdySessionsInternal,
222       base::Unretained(this)));
225 void CrNetEnvironment::SetRequestFilterBlock(RequestFilterBlock block) {
226   http_protocol_handler_delegate_.reset(
227       new CrNetHttpProtocolHandlerDelegate(main_context_getter_.get(), block));
228   net::HTTPProtocolHandlerDelegate::SetInstance(
229       http_protocol_handler_delegate_.get());
232 net::HttpNetworkSession* CrNetEnvironment::GetHttpNetworkSession(
233     net::URLRequestContext* context) {
234   DCHECK(context);
235   if (!context->http_transaction_factory())
236     return nullptr;
238   return context->http_transaction_factory()->GetSession();
241 void CrNetEnvironment::CloseAllSpdySessionsInternal() {
242   DCHECK(base::MessageLoop::current() ==
243          network_io_thread_->message_loop());
245   net::HttpNetworkSession* http_network_session =
246       GetHttpNetworkSession(GetMainContextGetter()->GetURLRequestContext());
248   if (http_network_session) {
249     net::SpdySessionPool *spdy_session_pool =
250         http_network_session->spdy_session_pool();
251     if (spdy_session_pool)
252       spdy_session_pool->CloseCurrentSessions(net::ERR_ABORTED);
253   }
256 CrNetEnvironment::CrNetEnvironment(const std::string& user_agent_product_name)
257     : spdy_enabled_(false),
258       quic_enabled_(false),
259       sdch_enabled_(false),
260       main_context_(new net::URLRequestContext),
261       user_agent_product_name_(user_agent_product_name),
262       net_log_(new net::NetLog) {}
264 void CrNetEnvironment::Install() {
265   // Threads setup.
266   network_cache_thread_.reset(new base::Thread("Chrome Network Cache Thread"));
267   network_cache_thread_->StartWithOptions(
268       base::Thread::Options(base::MessageLoop::TYPE_IO, 0));
269   network_io_thread_.reset(new base::Thread("Chrome Network IO Thread"));
270   network_io_thread_->StartWithOptions(
271       base::Thread::Options(base::MessageLoop::TYPE_IO, 0));
272   file_thread_.reset(new base::Thread("Chrome File Thread"));
273   file_thread_->StartWithOptions(
274       base::Thread::Options(base::MessageLoop::TYPE_IO, 0));
275   file_user_blocking_thread_.reset(
276       new base::Thread("Chrome File User Blocking Thread"));
277   file_user_blocking_thread_->StartWithOptions(
278       base::Thread::Options(base::MessageLoop::TYPE_IO, 0));
280   // The network change notifier must be initialized so that registered
281   // delegates will receive callbacks.
282   network_change_notifier_.reset(net::NetworkChangeNotifier::Create());
283   proxy_config_service_.reset(net::ProxyService::CreateSystemProxyConfigService(
284       network_io_thread_->task_runner(), nullptr));
286   PostToNetworkThread(FROM_HERE,
287       base::Bind(&CrNetEnvironment::InitializeOnNetworkThread,
288                  base::Unretained(this)));
290   net::SetURLRequestContextForNSSHttpIO(main_context_.get());
291   main_context_getter_ = new CrNetURLRequestContextGetter(
292       main_context_.get(), network_io_thread_->task_runner());
293   SetRequestFilterBlock(nil);
296 void CrNetEnvironment::InstallIntoSessionConfiguration(
297     NSURLSessionConfiguration* config) {
298   config.protocolClasses = @[ [CRNPauseableHTTPProtocolHandler class] ];
301 CrNetEnvironment::~CrNetEnvironment() {
302   net::HTTPProtocolHandlerDelegate::SetInstance(nullptr);
303   net::SetURLRequestContextForNSSHttpIO(nullptr);
306 net::URLRequestContextGetter* CrNetEnvironment::GetMainContextGetter() {
307   return main_context_getter_.get();
310 void CrNetEnvironment::SetHTTPProtocolHandlerRegistered(bool registered) {
311   if (registered) {
312     // Disable the default cache.
313     [NSURLCache setSharedURLCache:[EmptyNSURLCache emptyNSURLCache]];
314     // Register the chrome http protocol handler to replace the default one.
315     BOOL success =
316         [NSURLProtocol registerClass:[CRNPauseableHTTPProtocolHandler class]];
317     DCHECK(success);
318   } else {
319     // Set up an empty default cache, with default size.
320     // TODO(droger): If the NSURLCache is to be used, its size should most
321     // likely be changed. On an iPod2 with iOS4, the default size is 512k.
322     [NSURLCache setSharedURLCache:[[[NSURLCache alloc] init] autorelease]];
323     [NSURLProtocol unregisterClass:[CRNPauseableHTTPProtocolHandler class]];
324   }
327 void CrNetEnvironment::ConfigureSdchOnNetworkThread() {
328   DCHECK(base::MessageLoop::current() == network_io_thread_->message_loop());
329   net::URLRequestContext* context =
330       main_context_getter_->GetURLRequestContext();
332   if (!sdch_enabled_) {
333     DCHECK_EQ(static_cast<net::SdchManager*>(nullptr), context->sdch_manager());
334     return;
335   }
337   sdch_manager_.reset(new net::SdchManager());
338   sdch_owner_.reset(new net::SdchOwner(sdch_manager_.get(), context));
339   if (!sdch_pref_store_filename_.empty()) {
340     base::FilePath path(sdch_pref_store_filename_);
341     pref_store_worker_pool_ = file_user_blocking_thread_->task_runner();
342     net_pref_store_ = new JsonPrefStore(
343         path,
344         pref_store_worker_pool_.get(),
345         scoped_ptr<PrefFilter>());
346     net_pref_store_->ReadPrefsAsync(nullptr);
347     sdch_owner_->EnablePersistentStorage(net_pref_store_.get());
348   }
349   context->set_sdch_manager(sdch_manager_.get());
352 void CrNetEnvironment::InitializeOnNetworkThread() {
353   DCHECK(base::MessageLoop::current() == network_io_thread_->message_loop());
355   // Register network clients.
356   net::RequestTracker::AddGlobalNetworkClientFactory(
357       [[[WebPNetworkClientFactory alloc]
358           initWithTaskRunner:file_user_blocking_thread_
359                                  ->task_runner()] autorelease]);
361   ConfigureSdchOnNetworkThread();
363   NSString* bundlePath =
364       [[NSBundle mainBundle] pathForResource:@"crnet_resources"
365                                       ofType:@"bundle"];
366   NSBundle* bundle = [NSBundle bundleWithPath:bundlePath];
367   NSString* acceptableLanguages = NSLocalizedStringWithDefaultValue(
368       @"IDS_ACCEPT_LANGUAGES",
369       @"Localizable",
370       bundle,
371       @"en-US,en",
372       @"These values are copied from Chrome's .xtb files, so the same "
373        "values are used in the |Accept-Language| header. Key name matches "
374        "Chrome's.");
375   DCHECK(acceptableLanguages);
376   std::string acceptable_languages =
377       [acceptableLanguages cStringUsingEncoding:NSUTF8StringEncoding];
378   std::string user_agent =
379       web::BuildUserAgentFromProduct(user_agent_product_name_);
380   // Set the user agent through NSUserDefaults. This sets it for both
381   // UIWebViews and WKWebViews, and javascript calls to navigator.userAgent
382   // return this value.
383   [[NSUserDefaults standardUserDefaults] registerDefaults:@{
384     @"UserAgent" : [NSString stringWithUTF8String:user_agent.c_str()]
385   }];
386   main_context_->set_http_user_agent_settings(
387       new net::StaticHttpUserAgentSettings(acceptable_languages, user_agent));
389   main_context_->set_ssl_config_service(new net::SSLConfigServiceDefaults);
390   main_context_->set_transport_security_state(
391       new net::TransportSecurityState());
392   http_server_properties_.reset(new net::HttpServerPropertiesImpl());
393   main_context_->set_http_server_properties(
394       http_server_properties_->GetWeakPtr());
395   // TODO(rdsmith): Note that the ".release()" calls below are leaking
396   // the objects in question; this should be fixed by having an object
397   // corresponding to URLRequestContextStorage that actually owns those
398   // objects.  See http://crbug.com/523858.
399   main_context_->set_host_resolver(
400       net::HostResolver::CreateDefaultResolver(nullptr).release());
401   main_context_->set_cert_verifier(
402       net::CertVerifier::CreateDefault().release());
403   main_context_->set_http_auth_handler_factory(
404       net::HttpAuthHandlerRegistryFactory::CreateDefault(
405           main_context_->host_resolver())
406           .release());
407   main_context_->set_proxy_service(
408       net::ProxyService::CreateUsingSystemProxyResolver(
409           proxy_config_service_.get(), 0, nullptr)
410           .release());
412   // Cache
413   NSArray* dirs = NSSearchPathForDirectoriesInDomains(NSCachesDirectory,
414                                                       NSUserDomainMask,
415                                                       YES);
416   base::FilePath cache_path =
417       base::mac::NSStringToFilePath([dirs objectAtIndex:0]);
418   cache_path = cache_path.Append(FILE_PATH_LITERAL("crnet"));
419   net::HttpCache::DefaultBackend* main_backend =
420       new net::HttpCache::DefaultBackend(net::DISK_CACHE,
421                                          net::CACHE_BACKEND_DEFAULT, cache_path,
422                                          0,  // Default cache size.
423                                          network_cache_thread_->task_runner());
425   net::HttpNetworkSession::Params params;
426   params.host_resolver = main_context_->host_resolver();
427   params.cert_verifier = main_context_->cert_verifier();
428   params.channel_id_service = main_context_->channel_id_service();
429   params.transport_security_state = main_context_->transport_security_state();
430   params.proxy_service = main_context_->proxy_service();
431   params.ssl_session_cache_shard = "";
432   params.ssl_config_service = main_context_->ssl_config_service();
433   params.http_auth_handler_factory = main_context_->http_auth_handler_factory();
434   params.network_delegate = main_context_->network_delegate();
435   params.http_server_properties = main_context_->http_server_properties();
436   params.net_log = main_context_->net_log();
437   params.next_protos =
438       net::NextProtosWithSpdyAndQuic(spdy_enabled(), quic_enabled());
439   params.use_alternative_services = true;
440   params.enable_quic = quic_enabled();
441   params.alternative_service_probability_threshold =
442       alternate_protocol_threshold_;
444   if (!params.channel_id_service) {
445     // The main context may not have a ChannelIDService, since it is lazily
446     // constructed. If not, build an ephemeral ChannelIDService with no backing
447     // disk store.
448     // TODO(ellyjones): support persisting ChannelID.
449     params.channel_id_service = new net::ChannelIDService(
450         new net::DefaultChannelIDStore(NULL),
451         base::WorkerPool::GetTaskRunner(true));
452   }
454   net::HttpCache* main_cache = new net::HttpCache(params, main_backend);
455   main_context_->set_http_transaction_factory(main_cache);
457   // Cookies
458   scoped_refptr<net::CookieStore> cookie_store =
459       net::CookieStoreIOS::CreateCookieStoreFromNSHTTPCookieStorage();
460   main_context_->set_cookie_store(cookie_store.get());
462   net::URLRequestJobFactoryImpl* job_factory =
463       new net::URLRequestJobFactoryImpl;
464   job_factory->SetProtocolHandler(
465       "data", make_scoped_ptr(new net::DataProtocolHandler));
466   job_factory->SetProtocolHandler(
467       "file", make_scoped_ptr(
468                   new net::FileProtocolHandler(file_thread_->task_runner())));
469   main_context_->set_job_factory(job_factory);
471   main_context_->set_net_log(net_log_.get());
474 std::string CrNetEnvironment::user_agent() {
475   const net::HttpUserAgentSettings* user_agent_settings =
476       main_context_->http_user_agent_settings();
477   if (!user_agent_settings) {
478     return nullptr;
479   }
481   return user_agent_settings->GetUserAgent();
484 void CrNetEnvironment::ClearCache(ClearCacheCallback callback) {
485   PostToNetworkThread(FROM_HERE,
486       base::Bind(&CrNetEnvironment::ClearCacheOnNetworkThread,
487                  base::Unretained(this),
488                  callback));
491 void CrNetEnvironment::ClearCacheOnNetworkThread(ClearCacheCallback callback) {
492   DCHECK(base::MessageLoop::current() == network_io_thread_->message_loop());
493   __block disk_cache::Backend* backend = nullptr;
494   net::HttpCache* cache = main_context_->http_transaction_factory()->GetCache();
495   net::CompletionCallback client_callback = base::BindBlock(^(int error) {
496     if (callback != nil) {
497       callback(error);
498     }
499   });
500   net::CompletionCallback doom_callback = base::BindBlock(^(int error) {
501       if (backend)
502         backend->DoomAllEntries(client_callback);
503   });
504   int rc = cache->GetBackend(&backend, doom_callback);
505   if (rc != net::ERR_IO_PENDING) {
506     // GetBackend doesn't call the callback if it completes synchronously, so
507     // call it directly here.
508     doom_callback.Run(rc);
509   }