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 "content/renderer/manifest/manifest_manager.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 "content/renderer/manifest/manifest_uma_util.h"
14 #include "third_party/WebKit/public/platform/WebURLResponse.h"
15 #include "third_party/WebKit/public/web/WebConsoleMessage.h"
16 #include "third_party/WebKit/public/web/WebDocument.h"
17 #include "third_party/WebKit/public/web/WebLocalFrame.h"
21 ManifestManager::ManifestManager(RenderFrame
* render_frame
)
22 : RenderFrameObserver(render_frame
),
23 may_have_manifest_(false),
24 manifest_dirty_(true) {
27 ManifestManager::~ManifestManager() {
31 // Consumers in the browser process will not receive this message but they
32 // will be aware of the RenderFrame dying and should act on that. Consumers
33 // in the renderer process should be correctly notified.
34 ResolveCallbacks(ResolveStateFailure
);
37 bool ManifestManager::OnMessageReceived(const IPC::Message
& message
) {
40 IPC_BEGIN_MESSAGE_MAP(ManifestManager
, message
)
41 IPC_MESSAGE_HANDLER(ManifestManagerMsg_RequestManifest
, OnRequestManifest
)
42 IPC_MESSAGE_UNHANDLED(handled
= false)
48 void ManifestManager::OnRequestManifest(int request_id
) {
49 GetManifest(base::Bind(&ManifestManager::OnRequestManifestComplete
,
50 base::Unretained(this), request_id
));
53 void ManifestManager::OnRequestManifestComplete(
54 int request_id
, const Manifest
& manifest
) {
55 // When sent via IPC, the Manifest must follow certain security rules.
56 Manifest ipc_manifest
= manifest
;
57 ipc_manifest
.name
= base::NullableString16(
58 ipc_manifest
.name
.string().substr(0, Manifest::kMaxIPCStringLength
),
59 ipc_manifest
.name
.is_null());
60 ipc_manifest
.short_name
= base::NullableString16(
61 ipc_manifest
.short_name
.string().substr(0,
62 Manifest::kMaxIPCStringLength
),
63 ipc_manifest
.short_name
.is_null());
64 for (size_t i
= 0; i
< ipc_manifest
.icons
.size(); ++i
) {
65 ipc_manifest
.icons
[i
].type
= base::NullableString16(
66 ipc_manifest
.icons
[i
].type
.string().substr(
67 0, Manifest::kMaxIPCStringLength
),
68 ipc_manifest
.icons
[i
].type
.is_null());
70 ipc_manifest
.gcm_sender_id
= base::NullableString16(
71 ipc_manifest
.gcm_sender_id
.string().substr(
72 0, Manifest::kMaxIPCStringLength
),
73 ipc_manifest
.gcm_sender_id
.is_null());
75 Send(new ManifestManagerHostMsg_RequestManifestResponse(
76 routing_id(), request_id
, ipc_manifest
));
79 void ManifestManager::GetManifest(const GetManifestCallback
& callback
) {
80 if (!may_have_manifest_
) {
81 callback
.Run(Manifest());
85 if (!manifest_dirty_
) {
86 callback
.Run(manifest_
);
90 pending_callbacks_
.push_back(callback
);
92 // Just wait for the running call to be done if there are other callbacks.
93 if (pending_callbacks_
.size() > 1)
99 void ManifestManager::DidChangeManifest() {
100 may_have_manifest_
= true;
101 manifest_dirty_
= true;
104 void ManifestManager::DidCommitProvisionalLoad(
105 bool is_new_navigation
,
106 bool is_same_page_navigation
) {
107 if (is_same_page_navigation
)
110 may_have_manifest_
= false;
111 manifest_dirty_
= true;
114 void ManifestManager::FetchManifest() {
115 GURL
url(render_frame()->GetWebFrame()->document().manifestURL());
117 if (url
.is_empty()) {
118 ManifestUmaUtil::FetchFailed(ManifestUmaUtil::FETCH_EMPTY_URL
);
119 ResolveCallbacks(ResolveStateFailure
);
123 fetcher_
.reset(new ManifestFetcher(url
));
124 fetcher_
->Start(render_frame()->GetWebFrame(),
125 base::Bind(&ManifestManager::OnManifestFetchComplete
,
126 base::Unretained(this),
127 render_frame()->GetWebFrame()->document().url()));
130 void ManifestManager::OnManifestFetchComplete(
131 const GURL
& document_url
,
132 const blink::WebURLResponse
& response
,
133 const std::string
& data
) {
134 if (response
.isNull() && data
.empty()) {
135 ManifestUmaUtil::FetchFailed(ManifestUmaUtil::FETCH_UNSPECIFIED_REASON
);
136 ResolveCallbacks(ResolveStateFailure
);
140 ManifestUmaUtil::FetchSucceeded();
142 ManifestParser
parser(data
, response
.url(), document_url
);
147 for (const std::string
& msg
: parser
.errors()) {
148 blink::WebConsoleMessage message
;
149 message
.level
= blink::WebConsoleMessage::LevelError
;
150 message
.text
= blink::WebString::fromUTF8(msg
);
151 render_frame()->GetWebFrame()->addMessageToConsole(message
);
154 // Having errors while parsing the manifest doesn't mean the manifest parsing
155 // failed. Some properties might have been ignored but some others kept.
156 if (parser
.failed()) {
157 ResolveCallbacks(ResolveStateFailure
);
161 manifest_
= parser
.manifest();
162 ResolveCallbacks(ResolveStateSuccess
);
165 void ManifestManager::ResolveCallbacks(ResolveState state
) {
166 if (state
== ResolveStateFailure
)
167 manifest_
= Manifest();
169 manifest_dirty_
= state
!= ResolveStateSuccess
;
171 Manifest manifest
= manifest_
;
172 std::list
<GetManifestCallback
> callbacks
= pending_callbacks_
;
174 pending_callbacks_
.clear();
176 for (std::list
<GetManifestCallback
>::const_iterator it
= callbacks
.begin();
177 it
!= callbacks
.end(); ++it
) {
182 } // namespace content