// Copyright (c) 2012 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/web_resource/json_asynchronous_unpacker.h" #include "base/command_line.h" #include "chrome/browser/web_resource/web_resource_service.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/chrome_utility_messages.h" #include "chrome/common/web_resource/web_resource_unpacker.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/resource_dispatcher_host.h" #include "content/public/browser/utility_process_host.h" #include "content/public/browser/utility_process_host_client.h" using content::BrowserThread; using content::UtilityProcessHost; using content::UtilityProcessHostClient; // This class coordinates a web resource unpack and parse task which is run in // a separate process. Results are sent back to this class and routed to // the WebResourceService. class JSONAsynchronousUnpackerImpl : public UtilityProcessHostClient, public JSONAsynchronousUnpacker { public: explicit JSONAsynchronousUnpackerImpl( JSONAsynchronousUnpackerDelegate* delegate) : JSONAsynchronousUnpacker(delegate), got_response_(false) { } void Start(const std::string& json_data) { AddRef(); // balanced in Cleanup. // TODO(willchan): Look for a better signal of whether we're in a unit test // or not. Using |ResourceDispatcherHost::Get()| for this is pretty lame. // If we don't have a ResourceDispatcherHost, assume we're in a test and // run the unpacker directly in-process. bool use_utility_process = content::ResourceDispatcherHost::Get() && !CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess); if (use_utility_process) { BrowserThread::ID thread_id; CHECK(BrowserThread::GetCurrentThreadIdentifier(&thread_id)); BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, base::Bind( &JSONAsynchronousUnpackerImpl::StartProcessOnIOThread, this, thread_id, json_data)); } else { WebResourceUnpacker unpacker(json_data); if (unpacker.Run()) { OnUnpackWebResourceSucceeded(*unpacker.parsed_json()); } else { OnUnpackWebResourceFailed(unpacker.error_message()); } } } private: virtual ~JSONAsynchronousUnpackerImpl() {} // UtilityProcessHostClient. virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE { bool handled = true; IPC_BEGIN_MESSAGE_MAP(JSONAsynchronousUnpackerImpl, message) IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_UnpackWebResource_Succeeded, OnUnpackWebResourceSucceeded) IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_UnpackWebResource_Failed, OnUnpackWebResourceFailed) IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() return handled; } virtual void OnProcessCrashed(int exit_code) OVERRIDE { if (got_response_) return; OnUnpackWebResourceFailed( "Utility process crashed while trying to retrieve web resources."); } void OnUnpackWebResourceSucceeded( const DictionaryValue& parsed_json) { if (delegate_) delegate_->OnUnpackFinished(parsed_json); Cleanup(); } void OnUnpackWebResourceFailed(const std::string& error_message) { if (delegate_) delegate_->OnUnpackError(error_message); Cleanup(); } // Release reference and set got_response_. void Cleanup() { DCHECK(!got_response_); got_response_ = true; Release(); } void StartProcessOnIOThread(BrowserThread::ID thread_id, const std::string& json_data) { UtilityProcessHost* host = UtilityProcessHost::Create(this, thread_id); host->EnableZygote(); // TODO(mrc): get proper file path when we start using web resources // that need to be unpacked. host->Send(new ChromeUtilityMsg_UnpackWebResource(json_data)); } // True if we got a response from the utility process and have cleaned up // already. bool got_response_; }; JSONAsynchronousUnpacker* JSONAsynchronousUnpacker::Create( JSONAsynchronousUnpackerDelegate* delegate) { return new JSONAsynchronousUnpackerImpl(delegate); }