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 (auto& icon
: ipc_manifest
.icons
) {
65 icon
.type
= base::NullableString16(
66 icon
.type
.string().substr(0, Manifest::kMaxIPCStringLength
),
69 ipc_manifest
.gcm_sender_id
= base::NullableString16(
70 ipc_manifest
.gcm_sender_id
.string().substr(
71 0, Manifest::kMaxIPCStringLength
),
72 ipc_manifest
.gcm_sender_id
.is_null());
73 for (auto& related_application
: ipc_manifest
.related_applications
) {
74 related_application
.id
=
75 base::NullableString16(related_application
.id
.string().substr(
76 0, Manifest::kMaxIPCStringLength
),
77 related_application
.id
.is_null());
80 Send(new ManifestManagerHostMsg_RequestManifestResponse(
81 routing_id(), request_id
, ipc_manifest
));
84 void ManifestManager::GetManifest(const GetManifestCallback
& callback
) {
85 if (!may_have_manifest_
) {
86 callback
.Run(Manifest());
90 if (!manifest_dirty_
) {
91 callback
.Run(manifest_
);
95 pending_callbacks_
.push_back(callback
);
97 // Just wait for the running call to be done if there are other callbacks.
98 if (pending_callbacks_
.size() > 1)
104 void ManifestManager::DidChangeManifest() {
105 may_have_manifest_
= true;
106 manifest_dirty_
= true;
109 void ManifestManager::DidCommitProvisionalLoad(
110 bool is_new_navigation
,
111 bool is_same_page_navigation
) {
112 if (is_same_page_navigation
)
115 may_have_manifest_
= false;
116 manifest_dirty_
= true;
119 void ManifestManager::FetchManifest() {
120 GURL
url(render_frame()->GetWebFrame()->document().manifestURL());
122 if (url
.is_empty()) {
123 ManifestUmaUtil::FetchFailed(ManifestUmaUtil::FETCH_EMPTY_URL
);
124 ResolveCallbacks(ResolveStateFailure
);
128 fetcher_
.reset(new ManifestFetcher(url
));
130 render_frame()->GetWebFrame(),
131 render_frame()->GetWebFrame()->document().manifestUseCredentials(),
132 base::Bind(&ManifestManager::OnManifestFetchComplete
,
133 base::Unretained(this),
134 render_frame()->GetWebFrame()->document().url()));
137 void ManifestManager::OnManifestFetchComplete(
138 const GURL
& document_url
,
139 const blink::WebURLResponse
& response
,
140 const std::string
& data
) {
141 if (response
.isNull() && data
.empty()) {
142 ManifestUmaUtil::FetchFailed(ManifestUmaUtil::FETCH_UNSPECIFIED_REASON
);
143 ResolveCallbacks(ResolveStateFailure
);
147 ManifestUmaUtil::FetchSucceeded();
149 ManifestParser
parser(data
, response
.url(), document_url
);
154 for (const std::string
& msg
: parser
.errors()) {
155 blink::WebConsoleMessage message
;
156 message
.level
= blink::WebConsoleMessage::LevelError
;
157 message
.text
= blink::WebString::fromUTF8(msg
);
158 render_frame()->GetWebFrame()->addMessageToConsole(message
);
161 // Having errors while parsing the manifest doesn't mean the manifest parsing
162 // failed. Some properties might have been ignored but some others kept.
163 if (parser
.failed()) {
164 ResolveCallbacks(ResolveStateFailure
);
168 manifest_
= parser
.manifest();
169 ResolveCallbacks(ResolveStateSuccess
);
172 void ManifestManager::ResolveCallbacks(ResolveState state
) {
173 if (state
== ResolveStateFailure
)
174 manifest_
= Manifest();
176 manifest_dirty_
= state
!= ResolveStateSuccess
;
178 Manifest manifest
= manifest_
;
179 std::list
<GetManifestCallback
> callbacks
= pending_callbacks_
;
181 pending_callbacks_
.clear();
183 for (std::list
<GetManifestCallback
>::const_iterator it
= callbacks
.begin();
184 it
!= callbacks
.end(); ++it
) {
189 } // namespace content