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. 4 5#include "content/renderer/manifest/manifest_manager.h" 6 7#include "base/bind.h" 8#include "base/strings/nullable_string16.h" 9#include "content/common/manifest_manager_messages.h" 10#include "content/public/renderer/render_frame.h" 11#include "content/renderer/fetchers/manifest_fetcher.h" 12#include "content/renderer/manifest/manifest_parser.h" 13#include "third_party/WebKit/public/platform/WebURLResponse.h" 14#include "third_party/WebKit/public/web/WebDocument.h" 15#include "third_party/WebKit/public/web/WebLocalFrame.h" 16 17namespace content { 18 19ManifestManager::ManifestManager(RenderFrame* render_frame) 20 : RenderFrameObserver(render_frame), 21 may_have_manifest_(false), 22 manifest_dirty_(true) { 23} 24 25ManifestManager::~ManifestManager() { 26 if (fetcher_) 27 fetcher_->Cancel(); 28 29 // Consumers in the browser process will not receive this message but they 30 // will be aware of the RenderFrame dying and should act on that. Consumers 31 // in the renderer process should be correctly notified. 32 ResolveCallbacks(ResolveStateFailure); 33} 34 35bool ManifestManager::OnMessageReceived(const IPC::Message& message) { 36 bool handled = true; 37 38 IPC_BEGIN_MESSAGE_MAP(ManifestManager, message) 39 IPC_MESSAGE_HANDLER(ManifestManagerMsg_RequestManifest, OnRequestManifest) 40 IPC_MESSAGE_UNHANDLED(handled = false) 41 IPC_END_MESSAGE_MAP() 42 43 return handled; 44} 45 46void ManifestManager::OnRequestManifest(int request_id) { 47 GetManifest(base::Bind(&ManifestManager::OnRequestManifestComplete, 48 base::Unretained(this), request_id)); 49} 50 51void ManifestManager::OnRequestManifestComplete( 52 int request_id, const Manifest& manifest) { 53 // When sent via IPC, the Manifest must follow certain security rules. 54 Manifest ipc_manifest = manifest; 55 ipc_manifest.name = base::NullableString16( 56 ipc_manifest.name.string().substr(0, Manifest::kMaxIPCStringLength), 57 ipc_manifest.name.is_null()); 58 ipc_manifest.short_name = base::NullableString16( 59 ipc_manifest.short_name.string().substr(0, 60 Manifest::kMaxIPCStringLength), 61 ipc_manifest.short_name.is_null()); 62 for (size_t i = 0; i < ipc_manifest.icons.size(); ++i) { 63 ipc_manifest.icons[i].type = base::NullableString16( 64 ipc_manifest.icons[i].type.string().substr( 65 0, Manifest::kMaxIPCStringLength), 66 ipc_manifest.icons[i].type.is_null()); 67 } 68 69 Send(new ManifestManagerHostMsg_RequestManifestResponse( 70 routing_id(), request_id, ipc_manifest)); 71} 72 73void ManifestManager::GetManifest(const GetManifestCallback& callback) { 74 if (!may_have_manifest_) { 75 callback.Run(Manifest()); 76 return; 77 } 78 79 if (!manifest_dirty_) { 80 callback.Run(manifest_); 81 return; 82 } 83 84 pending_callbacks_.push_back(callback); 85 86 // Just wait for the running call to be done if there are other callbacks. 87 if (pending_callbacks_.size() > 1) 88 return; 89 90 FetchManifest(); 91} 92 93void ManifestManager::DidChangeManifest() { 94 may_have_manifest_ = true; 95 manifest_dirty_ = true; 96} 97 98void ManifestManager::FetchManifest() { 99 GURL url(render_frame()->GetWebFrame()->document().manifestURL()); 100 101 if (url.is_empty()) { 102 ResolveCallbacks(ResolveStateFailure); 103 return; 104 } 105 106 fetcher_.reset(new ManifestFetcher(url)); 107 108 // TODO(mlamouri,kenneth): this is not yet taking into account manifest-src 109 // CSP rule, see http://crbug.com/409996. 110 fetcher_->Start(render_frame()->GetWebFrame(), 111 base::Bind(&ManifestManager::OnManifestFetchComplete, 112 base::Unretained(this), 113 render_frame()->GetWebFrame()->document().url())); 114} 115 116void ManifestManager::OnManifestFetchComplete( 117 const GURL& document_url, 118 const blink::WebURLResponse& response, 119 const std::string& data) { 120 if (response.isNull() && data.empty()) { 121 ResolveCallbacks(ResolveStateFailure); 122 return; 123 } 124 125 manifest_ = ManifestParser::Parse(data, response.url(), document_url); 126 127 fetcher_.reset(); 128 ResolveCallbacks(ResolveStateSuccess); 129} 130 131void ManifestManager::ResolveCallbacks(ResolveState state) { 132 if (state == ResolveStateFailure) 133 manifest_ = Manifest(); 134 135 manifest_dirty_ = state != ResolveStateSuccess; 136 137 Manifest manifest = manifest_; 138 std::list<GetManifestCallback> callbacks = pending_callbacks_; 139 140 pending_callbacks_.clear(); 141 142 for (std::list<GetManifestCallback>::const_iterator it = callbacks.begin(); 143 it != callbacks.end(); ++it) { 144 it->Run(manifest); 145 } 146} 147 148} // namespace content 149