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/browser/manifest/manifest_manager_host.h"
6
7#include "content/common/manifest_manager_messages.h"
8#include "content/public/browser/render_frame_host.h"
9#include "content/public/browser/render_process_host.h"
10#include "content/public/common/manifest.h"
11#include "content/public/common/result_codes.h"
12
13namespace content {
14
15namespace {
16
17void KillRenderer(RenderFrameHost* render_frame_host) {
18  base::ProcessHandle process_handle =
19      render_frame_host->GetProcess()->GetHandle();
20  if (process_handle == base::kNullProcessHandle)
21    return;
22  base::KillProcess(process_handle, RESULT_CODE_KILLED_BAD_MESSAGE, false);
23}
24
25} // anonymous namespace
26
27ManifestManagerHost::ManifestManagerHost(WebContents* web_contents)
28  : WebContentsObserver(web_contents) {
29}
30
31ManifestManagerHost::~ManifestManagerHost() {
32}
33
34ManifestManagerHost::CallbackMap* ManifestManagerHost::GetCallbackMapForFrame(
35    RenderFrameHost* render_frame_host) {
36  FrameCallbackMap::iterator it = pending_callbacks_.find(render_frame_host);
37  return it != pending_callbacks_.end() ? it->second : 0;
38}
39
40void ManifestManagerHost::RenderFrameDeleted(
41    RenderFrameHost* render_frame_host) {
42  CallbackMap* callbacks = GetCallbackMapForFrame(render_frame_host);
43  if (!callbacks)
44    return;
45
46  // Before deleting the callbacks, make sure they are called with a failure
47  // state.
48  CallbackMap::const_iterator it(callbacks);
49  for (; !it.IsAtEnd(); it.Advance())
50    it.GetCurrentValue()->Run(Manifest());
51
52  pending_callbacks_.erase(render_frame_host);
53}
54
55void ManifestManagerHost::GetManifest(RenderFrameHost* render_frame_host,
56                                      const GetManifestCallback& callback) {
57  CallbackMap* callbacks = GetCallbackMapForFrame(render_frame_host);
58  if (!callbacks) {
59    callbacks = new CallbackMap();
60    pending_callbacks_[render_frame_host] = callbacks;
61  }
62
63  int request_id = callbacks->Add(new GetManifestCallback(callback));
64
65  render_frame_host->Send(new ManifestManagerMsg_RequestManifest(
66      render_frame_host->GetRoutingID(), request_id));
67}
68
69bool ManifestManagerHost::OnMessageReceived(
70    const IPC::Message& message, RenderFrameHost* render_frame_host) {
71  bool handled = true;
72
73  IPC_BEGIN_MESSAGE_MAP_WITH_PARAM(ManifestManagerHost, message,
74                                   render_frame_host)
75    IPC_MESSAGE_HANDLER(ManifestManagerHostMsg_RequestManifestResponse,
76                        OnRequestManifestResponse)
77    IPC_MESSAGE_UNHANDLED(handled = false)
78  IPC_END_MESSAGE_MAP()
79
80  return handled;
81}
82
83void ManifestManagerHost::OnRequestManifestResponse(
84    RenderFrameHost* render_frame_host,
85    int request_id,
86    const Manifest& insecure_manifest) {
87  CallbackMap* callbacks = GetCallbackMapForFrame(render_frame_host);
88  if (!callbacks) {
89    DVLOG(1) << "Unexpected RequestManifestResponse to from renderer. "
90                "Killing renderer.";
91    KillRenderer(render_frame_host);
92    return;
93  }
94
95  GetManifestCallback* callback = callbacks->Lookup(request_id);
96  if (!callback) {
97    DVLOG(1) << "Received a request_id (" << request_id << ") from renderer "
98                "with no associated callback. Killing renderer.";
99    KillRenderer(render_frame_host);
100    return;
101  }
102
103  // When receiving a Manifest, the browser process can't trust that it is
104  // coming from a known and secure source. It must be processed accordingly.
105  Manifest manifest = insecure_manifest;
106  manifest.name = base::NullableString16(
107      manifest.name.string().substr(0, Manifest::kMaxIPCStringLength),
108      manifest.name.is_null());
109  manifest.short_name = base::NullableString16(
110        manifest.short_name.string().substr(0, Manifest::kMaxIPCStringLength),
111        manifest.short_name.is_null());
112  if (!manifest.start_url.is_valid())
113    manifest.start_url = GURL();
114  for (size_t i = 0; i < manifest.icons.size(); ++i) {
115    if (!manifest.icons[i].src.is_valid())
116      manifest.icons[i].src = GURL();
117    manifest.icons[i].type = base::NullableString16(
118        manifest.icons[i].type.string().substr(0,
119                                               Manifest::kMaxIPCStringLength),
120        manifest.icons[i].type.is_null());
121  }
122
123  callback->Run(manifest);
124  callbacks->Remove(request_id);
125  if (callbacks->IsEmpty()) {
126    delete callbacks;
127    pending_callbacks_.erase(render_frame_host);
128  }
129}
130
131} // namespace content
132