Commit efdca9d8 authored by mlamouri's avatar mlamouri Committed by Commit bot

Implement ManifestManager to handle manifest in content/.

This can be used from the renderer process or the browser
process. Requesting the manifest can be done via one call, a
callback has to be passed and will run with the manifest
passed as parameter. A failure will return the empty
manifest.

Some more logic could be added like caching the manifest in
the browser process or keeping track of the manifest dirty
state in the browser process but those things can be added
transparently later.

BUG=366145

Review URL: https://codereview.chromium.org/537053002

Cr-Commit-Position: refs/heads/master@{#295085}
parent 59b96827
// Copyright 2014 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 "base/command_line.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/manifest.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/public/test/test_navigation_observer.h"
#include "content/shell/browser/shell.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
namespace content {
class ManifestBrowserTest : public ContentBrowserTest {
protected:
ManifestBrowserTest() {}
virtual ~ManifestBrowserTest() {}
void GetManifestAndWait() {
shell()->web_contents()->GetManifest(
base::Bind(&ManifestBrowserTest::OnGetManifest,
base::Unretained(this)));
message_loop_runner_ = new MessageLoopRunner();
message_loop_runner_->Run();
}
void OnGetManifest(const Manifest& manifest) {
manifest_ = manifest;
message_loop_runner_->Quit();
}
const Manifest& manifest() const {
return manifest_;
}
private:
scoped_refptr<MessageLoopRunner> message_loop_runner_;
Manifest manifest_;
DISALLOW_COPY_AND_ASSIGN(ManifestBrowserTest);
};
// If a page has no manifest, requesting a manifest should return the empty
// manifest.
IN_PROC_BROWSER_TEST_F(ManifestBrowserTest, NoManifest) {
GURL test_url = GetTestUrl("manifest", "no-manifest.html");
TestNavigationObserver navigation_observer(shell()->web_contents(), 1);
shell()->LoadURL(test_url);
navigation_observer.Wait();
GetManifestAndWait();
EXPECT_TRUE(manifest().IsEmpty());
}
// If a page manifest points to a 404 URL, requesting the manifest should return
// the empty manifest.
IN_PROC_BROWSER_TEST_F(ManifestBrowserTest, 404Manifest) {
GURL test_url = GetTestUrl("manifest", "404-manifest.html");
TestNavigationObserver navigation_observer(shell()->web_contents(), 1);
shell()->LoadURL(test_url);
navigation_observer.Wait();
GetManifestAndWait();
EXPECT_TRUE(manifest().IsEmpty());
}
// If a page has an empty manifest, requesting the manifest should return the
// empty manifest.
IN_PROC_BROWSER_TEST_F(ManifestBrowserTest, EmptyManifest) {
GURL test_url = GetTestUrl("manifest", "empty-manifest.html");
TestNavigationObserver navigation_observer(shell()->web_contents(), 1);
shell()->LoadURL(test_url);
navigation_observer.Wait();
GetManifestAndWait();
EXPECT_TRUE(manifest().IsEmpty());
}
// If a page's manifest can't be parsed correctly, requesting the manifest
// should return an empty manifest.
IN_PROC_BROWSER_TEST_F(ManifestBrowserTest, ParseErrorManifest) {
GURL test_url = GetTestUrl("manifest", "parse-error-manifest.html");
TestNavigationObserver navigation_observer(shell()->web_contents(), 1);
shell()->LoadURL(test_url);
navigation_observer.Wait();
GetManifestAndWait();
EXPECT_TRUE(manifest().IsEmpty());
}
// If a page has a manifest that can be fetched and parsed, requesting the
// manifest should return a properly filled manifest.
IN_PROC_BROWSER_TEST_F(ManifestBrowserTest, DummyManifest) {
GURL test_url = GetTestUrl("manifest", "dummy-manifest.html");
TestNavigationObserver navigation_observer(shell()->web_contents(), 1);
shell()->LoadURL(test_url);
navigation_observer.Wait();
GetManifestAndWait();
EXPECT_FALSE(manifest().IsEmpty());
}
// If a page changes manifest during its life-time, requesting the manifest
// should return the current manifest.
IN_PROC_BROWSER_TEST_F(ManifestBrowserTest, DynamicManifest) {
GURL test_url = GetTestUrl("manifest", "dynamic-manifest.html");
TestNavigationObserver navigation_observer(shell()->web_contents(), 1);
shell()->LoadURL(test_url);
navigation_observer.Wait();
{
GetManifestAndWait();
EXPECT_TRUE(manifest().IsEmpty());
}
{
std::string manifest_url =
GetTestUrl("manifest", "dummy-manifest.json").spec();
ASSERT_TRUE(content::ExecuteScript(
shell()->web_contents(), "setManifestTo('" + manifest_url + "')"));
GetManifestAndWait();
EXPECT_FALSE(manifest().IsEmpty());
}
{
std::string manifest_url =
GetTestUrl("manifest", "empty-manifest.json").spec();
ASSERT_TRUE(content::ExecuteScript(
shell()->web_contents(), "setManifestTo('" + manifest_url + "')"));
GetManifestAndWait();
EXPECT_TRUE(manifest().IsEmpty());
}
}
// If a page's manifest lives in a different origin, it should follow the CORS
// rules and requesting the manifest should return an empty manifest (unless the
// response contains CORS headers).
IN_PROC_BROWSER_TEST_F(ManifestBrowserTest, CORSManifest) {
scoped_ptr<net::test_server::EmbeddedTestServer> cors_embedded_test_server(
new net::test_server::EmbeddedTestServer);
ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
ASSERT_TRUE(cors_embedded_test_server->InitializeAndWaitUntilReady());
ASSERT_NE(embedded_test_server()->port(), cors_embedded_test_server->port());
GURL test_url =
embedded_test_server()->GetURL("/manifest/dynamic-manifest.html");
TestNavigationObserver navigation_observer(shell()->web_contents(), 1);
shell()->LoadURL(test_url);
navigation_observer.Wait();
std::string manifest_url =
cors_embedded_test_server->GetURL("/manifest/dummy-manifest.json").spec();
ASSERT_TRUE(content::ExecuteScript(shell()->web_contents(),
"setManifestTo('" + manifest_url + "')"));
GetManifestAndWait();
EXPECT_TRUE(manifest().IsEmpty());
}
// If a page's manifest is in an unsecure origin while the page is in a secure
// origin, requesting the manifest should return the empty manifest.
IN_PROC_BROWSER_TEST_F(ManifestBrowserTest, MixedContentManifest) {
scoped_ptr<net::SpawnedTestServer> https_server(new net::SpawnedTestServer(
net::SpawnedTestServer::TYPE_HTTPS,
net::BaseTestServer::SSLOptions(net::BaseTestServer::SSLOptions::CERT_OK),
base::FilePath(FILE_PATH_LITERAL("content/test/data"))));
ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
ASSERT_TRUE(https_server->Start());
GURL test_url =
embedded_test_server()->GetURL("/manifest/dynamic-manifest.html");
TestNavigationObserver navigation_observer(shell()->web_contents(), 1);
shell()->LoadURL(test_url);
navigation_observer.Wait();
std::string manifest_url =
https_server->GetURL("/manifest/dummy-manifest.json").spec();
ASSERT_TRUE(content::ExecuteScript(shell()->web_contents(),
"setManifestTo('" + manifest_url + "')"));
GetManifestAndWait();
EXPECT_TRUE(manifest().IsEmpty());
}
} // namespace content
// Copyright 2014 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 "content/browser/manifest/manifest_manager_host.h"
#include "content/common/manifest_manager_messages.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/common/manifest.h"
#include "content/public/common/result_codes.h"
namespace content {
namespace {
void KillRenderer(RenderFrameHost* render_frame_host) {
base::ProcessHandle process_handle =
render_frame_host->GetProcess()->GetHandle();
if (process_handle == base::kNullProcessHandle)
return;
base::KillProcess(process_handle, RESULT_CODE_KILLED_BAD_MESSAGE, false);
}
} // anonymous namespace
ManifestManagerHost::ManifestManagerHost(WebContents* web_contents)
: WebContentsObserver(web_contents) {
}
ManifestManagerHost::~ManifestManagerHost() {
}
ManifestManagerHost::CallbackMap* ManifestManagerHost::GetCallbackMapForFrame(
RenderFrameHost* render_frame_host) {
FrameCallbackMap::iterator it = pending_callbacks_.find(render_frame_host);
return it != pending_callbacks_.end() ? it->second : 0;
}
void ManifestManagerHost::RenderFrameDeleted(
RenderFrameHost* render_frame_host) {
CallbackMap* callbacks = GetCallbackMapForFrame(render_frame_host);
if (!callbacks)
return;
// Before deleting the callbacks, make sure they are called with a failure
// state.
CallbackMap::const_iterator it(callbacks);
for (; !it.IsAtEnd(); it.Advance())
it.GetCurrentValue()->Run(Manifest());
pending_callbacks_.erase(render_frame_host);
}
void ManifestManagerHost::GetManifest(RenderFrameHost* render_frame_host,
const GetManifestCallback& callback) {
CallbackMap* callbacks = GetCallbackMapForFrame(render_frame_host);
if (!callbacks) {
callbacks = new CallbackMap();
pending_callbacks_[render_frame_host] = callbacks;
}
int request_id = callbacks->Add(new GetManifestCallback(callback));
render_frame_host->Send(new ManifestManagerMsg_RequestManifest(
render_frame_host->GetRoutingID(), request_id));
}
bool ManifestManagerHost::OnMessageReceived(
const IPC::Message& message, RenderFrameHost* render_frame_host) {
bool handled = true;
IPC_BEGIN_MESSAGE_MAP_WITH_PARAM(ManifestManagerHost, message,
render_frame_host)
IPC_MESSAGE_HANDLER(ManifestManagerHostMsg_RequestManifestResponse,
OnRequestManifestResponse)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
}
void ManifestManagerHost::OnRequestManifestResponse(
RenderFrameHost* render_frame_host,
int request_id,
const Manifest& insecure_manifest) {
CallbackMap* callbacks = GetCallbackMapForFrame(render_frame_host);
if (!callbacks) {
DVLOG(1) << "Unexpected RequestManifestResponse to from renderer. "
"Killing renderer.";
KillRenderer(render_frame_host);
return;
}
GetManifestCallback* callback = callbacks->Lookup(request_id);
if (!callback) {
DVLOG(1) << "Received a request_id (" << request_id << ") from renderer "
"with no associated callback. Killing renderer.";
KillRenderer(render_frame_host);
return;
}
// When receiving a Manifest, the browser process can't trust that it is
// coming from a known and secure source. It must be processed accordingly.
Manifest manifest = insecure_manifest;
manifest.name = base::NullableString16(
manifest.name.string().substr(0, Manifest::kMaxIPCStringLength),
manifest.name.is_null());
manifest.short_name = base::NullableString16(
manifest.short_name.string().substr(0, Manifest::kMaxIPCStringLength),
manifest.short_name.is_null());
callback->Run(manifest);
callbacks->Remove(request_id);
if (callbacks->IsEmpty()) {
delete callbacks;
pending_callbacks_.erase(render_frame_host);
}
}
} // namespace content
// Copyright 2014 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.
#ifndef CONTENT_RENDERER_MANIFEST_MANIFEST_MANAGER_HOST_H_
#define CONTENT_RENDERER_MANIFEST_MANIFEST_MANAGER_HOST_H_
#include "base/callback_forward.h"
#include "base/id_map.h"
#include "content/public/browser/web_contents_observer.h"
#if defined(COMPILER_GCC)
namespace BASE_HASH_NAMESPACE {
template<>
struct hash<content::RenderFrameHost*> {
uint64 operator()(content::RenderFrameHost* ptr) const {
return hash<uint64>()(reinterpret_cast<uint64>(ptr));
}
};
}
#endif
namespace content {
class RenderFrameHost;
class WebContents;
struct Manifest;
// ManifestManagerHost is a helper class that allows callers to get the Manifest
// associated with a frame. It handles the IPC messaging with the child process.
// TODO(mlamouri): keep a cached version and a dirty bit here.
class ManifestManagerHost : public WebContentsObserver {
public:
explicit ManifestManagerHost(WebContents* web_contents);
virtual ~ManifestManagerHost();
typedef base::Callback<void(const Manifest&)> GetManifestCallback;
// Calls the given callback with the manifest associated with the
// given RenderFrameHost. If the frame has no manifest or if getting it failed
// the callback will have an empty manifest.
void GetManifest(RenderFrameHost*, const GetManifestCallback&);
// WebContentsObserver
virtual bool OnMessageReceived(const IPC::Message&,
RenderFrameHost*) OVERRIDE;
virtual void RenderFrameDeleted(RenderFrameHost*) OVERRIDE;
private:
typedef IDMap<GetManifestCallback, IDMapOwnPointer> CallbackMap;
typedef base::hash_map<RenderFrameHost*, CallbackMap*> FrameCallbackMap;
void OnRequestManifestResponse(
RenderFrameHost*, int request_id, const Manifest&);
// Returns the CallbackMap associated with the given RenderFrameHost, or null.
CallbackMap* GetCallbackMapForFrame(RenderFrameHost*);
FrameCallbackMap pending_callbacks_;
DISALLOW_COPY_AND_ASSIGN(ManifestManagerHost);
};
} // namespace content
#endif // CONTENT_RENDERER_MANIFEST_MANIFEST_MANAGER_HOST_H_
...@@ -38,6 +38,7 @@ ...@@ -38,6 +38,7 @@
#include "content/browser/geolocation/geolocation_dispatcher_host.h" #include "content/browser/geolocation/geolocation_dispatcher_host.h"
#include "content/browser/host_zoom_map_impl.h" #include "content/browser/host_zoom_map_impl.h"
#include "content/browser/loader/resource_dispatcher_host_impl.h" #include "content/browser/loader/resource_dispatcher_host_impl.h"
#include "content/browser/manifest/manifest_manager_host.h"
#include "content/browser/media/audio_stream_monitor.h" #include "content/browser/media/audio_stream_monitor.h"
#include "content/browser/media/midi_dispatcher_host.h" #include "content/browser/media/midi_dispatcher_host.h"
#include "content/browser/message_port_message_filter.h" #include "content/browser/message_port_message_filter.h"
...@@ -1206,6 +1207,8 @@ void WebContentsImpl::Init(const WebContents::CreateParams& params) { ...@@ -1206,6 +1207,8 @@ void WebContentsImpl::Init(const WebContents::CreateParams& params) {
screen_orientation_dispatcher_host_.reset( screen_orientation_dispatcher_host_.reset(
new ScreenOrientationDispatcherHostImpl(this)); new ScreenOrientationDispatcherHostImpl(this));
manifest_manager_host_.reset(new ManifestManagerHost(this));
#if defined(OS_ANDROID) #if defined(OS_ANDROID)
date_time_chooser_.reset(new DateTimeChooserAndroid()); date_time_chooser_.reset(new DateTimeChooserAndroid());
#endif #endif
...@@ -2425,6 +2428,10 @@ bool WebContentsImpl::WasRecentlyAudible() { ...@@ -2425,6 +2428,10 @@ bool WebContentsImpl::WasRecentlyAudible() {
return audio_stream_monitor_.WasRecentlyAudible(); return audio_stream_monitor_.WasRecentlyAudible();
} }
void WebContentsImpl::GetManifest(const GetManifestCallback& callback) {
manifest_manager_host_->GetManifest(GetMainFrame(), callback);
}
bool WebContentsImpl::FocusLocationBarByDefault() { bool WebContentsImpl::FocusLocationBarByDefault() {
NavigationEntry* entry = controller_.GetVisibleEntry(); NavigationEntry* entry = controller_.GetVisibleEntry();
if (entry && entry->GetURL() == GURL(url::kAboutBlankURL)) if (entry && entry->GetURL() == GURL(url::kAboutBlankURL))
......
...@@ -54,6 +54,7 @@ class DownloadItem; ...@@ -54,6 +54,7 @@ class DownloadItem;
class GeolocationDispatcherHost; class GeolocationDispatcherHost;
class InterstitialPageImpl; class InterstitialPageImpl;
class JavaScriptDialogManager; class JavaScriptDialogManager;
class ManifestManagerHost;
class MidiDispatcherHost; class MidiDispatcherHost;
class PowerSaveBlocker; class PowerSaveBlocker;
class RenderViewHost; class RenderViewHost;
...@@ -331,6 +332,7 @@ class CONTENT_EXPORT WebContentsImpl ...@@ -331,6 +332,7 @@ class CONTENT_EXPORT WebContentsImpl
virtual void StopFinding(StopFindAction action) OVERRIDE; virtual void StopFinding(StopFindAction action) OVERRIDE;
virtual void InsertCSS(const std::string& css) OVERRIDE; virtual void InsertCSS(const std::string& css) OVERRIDE;
virtual bool WasRecentlyAudible() OVERRIDE; virtual bool WasRecentlyAudible() OVERRIDE;
virtual void GetManifest(const GetManifestCallback&) OVERRIDE;
#if defined(OS_ANDROID) #if defined(OS_ANDROID)
virtual base::android::ScopedJavaLocalRef<jobject> GetJavaWebContents() virtual base::android::ScopedJavaLocalRef<jobject> GetJavaWebContents()
OVERRIDE; OVERRIDE;
...@@ -1230,6 +1232,8 @@ class CONTENT_EXPORT WebContentsImpl ...@@ -1230,6 +1232,8 @@ class CONTENT_EXPORT WebContentsImpl
scoped_ptr<ScreenOrientationDispatcherHost> scoped_ptr<ScreenOrientationDispatcherHost>
screen_orientation_dispatcher_host_; screen_orientation_dispatcher_host_;
scoped_ptr<ManifestManagerHost> manifest_manager_host_;
// The accessibility mode for all frames. This is queried when each frame // The accessibility mode for all frames. This is queried when each frame
// is created, and broadcast to all frames when it changes. // is created, and broadcast to all frames when it changes.
AccessibilityMode accessibility_mode_; AccessibilityMode accessibility_mode_;
......
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
#include "content/common/image_messages.h" #include "content/common/image_messages.h"
#include "content/common/indexed_db/indexed_db_messages.h" #include "content/common/indexed_db/indexed_db_messages.h"
#include "content/common/input_messages.h" #include "content/common/input_messages.h"
#include "content/common/manifest_manager_messages.h"
#include "content/common/media/aec_dump_messages.h" #include "content/common/media/aec_dump_messages.h"
#include "content/common/media/audio_messages.h" #include "content/common/media/audio_messages.h"
// TODO(xhwang): Move this to a new ifdef block. // TODO(xhwang): Move this to a new ifdef block.
......
// Copyright 2014 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.
// IPC messages for the web manifest manager.
// Multiply-included message file, hence no include guard.
#include "content/common/content_export.h"
#include "content/public/common/manifest.h"
#include "ipc/ipc_message_macros.h"
#undef IPC_MESSAGE_EXPORT
#define IPC_MESSAGE_EXPORT CONTENT_EXPORT
#define IPC_MESSAGE_START ManifestManagerMsgStart
IPC_STRUCT_TRAITS_BEGIN(content::Manifest)
IPC_STRUCT_TRAITS_MEMBER(name)
IPC_STRUCT_TRAITS_MEMBER(short_name)
IPC_STRUCT_TRAITS_END()
// The browser process requests for the manifest linked with the associated
// render frame. The renderer process will respond via a RequestManifestResponse
// IPC message with a Manifest object attached to it and the associated
// |request_id| that was initially given.
IPC_MESSAGE_ROUTED1(ManifestManagerMsg_RequestManifest,
int /* request_id */)
// The renderer process' response to a RequestManifest. The |request_id| will
// match the one that was initially received. The |manifest| object will be an
// empty manifest in case of any failure.
IPC_MESSAGE_ROUTED2(ManifestManagerHostMsg_RequestManifestResponse,
int, /* request_id */
content::Manifest /* manifest */)
...@@ -869,6 +869,8 @@ ...@@ -869,6 +869,8 @@
'browser/media/webrtc_identity_store.h', 'browser/media/webrtc_identity_store.h',
'browser/media/webrtc_identity_store_backend.cc', 'browser/media/webrtc_identity_store_backend.cc',
'browser/media/webrtc_identity_store_backend.h', 'browser/media/webrtc_identity_store_backend.h',
'browser/manifest/manifest_manager_host.cc',
'browser/manifest/manifest_manager_host.h',
'browser/message_port_message_filter.cc', 'browser/message_port_message_filter.cc',
'browser/message_port_message_filter.h', 'browser/message_port_message_filter.h',
'browser/message_port_service.cc', 'browser/message_port_service.cc',
......
...@@ -379,6 +379,7 @@ ...@@ -379,6 +379,7 @@
'common/mac/font_descriptor.mm', 'common/mac/font_descriptor.mm',
'common/mac/font_loader.h', 'common/mac/font_loader.h',
'common/mac/font_loader.mm', 'common/mac/font_loader.mm',
'common/manifest_manager_messages.h',
'common/media/aec_dump_messages.h', 'common/media/aec_dump_messages.h',
'common/media/audio_messages.h', 'common/media/audio_messages.h',
'common/media/cdm_messages.h', 'common/media/cdm_messages.h',
......
...@@ -214,6 +214,8 @@ ...@@ -214,6 +214,8 @@
'renderer/java/gin_java_bridge_object.h', 'renderer/java/gin_java_bridge_object.h',
'renderer/java/gin_java_bridge_value_converter.cc', 'renderer/java/gin_java_bridge_value_converter.cc',
'renderer/java/gin_java_bridge_value_converter.h', 'renderer/java/gin_java_bridge_value_converter.h',
'renderer/manifest/manifest_manager.cc',
'renderer/manifest/manifest_manager.h',
'renderer/manifest/manifest_parser.cc', 'renderer/manifest/manifest_parser.cc',
'renderer/manifest/manifest_parser.h', 'renderer/manifest/manifest_parser.h',
'renderer/media/aec_dump_message_filter.cc', 'renderer/media/aec_dump_message_filter.cc',
......
...@@ -1154,6 +1154,7 @@ ...@@ -1154,6 +1154,7 @@
'browser/indexed_db/mock_browsertest_indexed_db_class_factory.cc', 'browser/indexed_db/mock_browsertest_indexed_db_class_factory.cc',
'browser/indexed_db/mock_browsertest_indexed_db_class_factory.h', 'browser/indexed_db/mock_browsertest_indexed_db_class_factory.h',
'browser/loader/resource_dispatcher_host_browsertest.cc', 'browser/loader/resource_dispatcher_host_browsertest.cc',
'browser/manifest/manifest_browsertest.cc',
'browser/media/encrypted_media_browsertest.cc', 'browser/media/encrypted_media_browsertest.cc',
'browser/media/media_browsertest.cc', 'browser/media/media_browsertest.cc',
'browser/media/media_browsertest.h', 'browser/media/media_browsertest.h',
......
...@@ -57,6 +57,7 @@ class SiteInstance; ...@@ -57,6 +57,7 @@ class SiteInstance;
class WebContentsDelegate; class WebContentsDelegate;
struct CustomContextMenuContext; struct CustomContextMenuContext;
struct DropData; struct DropData;
struct Manifest;
struct RendererPreferences; struct RendererPreferences;
// WebContents is the core class in content/. A WebContents renders web content // WebContents is the core class in content/. A WebContents renders web content
...@@ -579,6 +580,11 @@ class WebContents : public PageNavigator, ...@@ -579,6 +580,11 @@ class WebContents : public PageNavigator,
// Returns true if audio has recently been audible from the WebContents. // Returns true if audio has recently been audible from the WebContents.
virtual bool WasRecentlyAudible() = 0; virtual bool WasRecentlyAudible() = 0;
typedef base::Callback<void(const Manifest&)> GetManifestCallback;
// Requests the Manifest of the main frame's document.
virtual void GetManifest(const GetManifestCallback&) = 0;
#if defined(OS_ANDROID) #if defined(OS_ANDROID)
CONTENT_EXPORT static WebContents* FromJavaWebContents( CONTENT_EXPORT static WebContents* FromJavaWebContents(
jobject jweb_contents_android); jobject jweb_contents_android);
......
...@@ -6,6 +6,8 @@ ...@@ -6,6 +6,8 @@
namespace content { namespace content {
const size_t Manifest::kMaxIPCStringLength = 4 * 1024;
Manifest::Manifest() { Manifest::Manifest() {
} }
......
...@@ -26,6 +26,11 @@ struct CONTENT_EXPORT Manifest { ...@@ -26,6 +26,11 @@ struct CONTENT_EXPORT Manifest {
// Null if the parsing failed or the field was not present. // Null if the parsing failed or the field was not present.
base::NullableString16 short_name; base::NullableString16 short_name;
// Maximum length for all the strings inside the Manifest when it is sent over
// IPC. The renderer process should truncate the strings before sending the
// Manifest and the browser process must do the same when receiving it.
static const size_t kMaxIPCStringLength;
}; };
} // namespace content } // namespace content
......
...@@ -53,6 +53,7 @@ class CONTENT_EXPORT RenderFrameObserver : public IPC::Listener, ...@@ -53,6 +53,7 @@ class CONTENT_EXPORT RenderFrameObserver : public IPC::Listener,
int world_id) {} int world_id) {}
virtual void DidClearWindowObject() {} virtual void DidClearWindowObject() {}
virtual void DidChangeName(const base::string16& name) {} virtual void DidChangeName(const base::string16& name) {}
virtual void DidChangeManifest() {}
// Called when the frame will soon be closed. This is the last opportunity to // Called when the frame will soon be closed. This is the last opportunity to
// send messages to the host (e.g., for clean-up, shutdown, etc.). // send messages to the host (e.g., for clean-up, shutdown, etc.).
......
// Copyright 2014 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 "content/renderer/manifest/manifest_manager.h"
#include "base/bind.h"
#include "base/strings/nullable_string16.h"
#include "content/common/manifest_manager_messages.h"
#include "content/public/renderer/render_frame.h"
#include "content/renderer/fetchers/manifest_fetcher.h"
#include "content/renderer/manifest/manifest_parser.h"
#include "third_party/WebKit/public/platform/WebURLResponse.h"
#include "third_party/WebKit/public/web/WebDocument.h"
#include "third_party/WebKit/public/web/WebLocalFrame.h"
namespace content {
ManifestManager::ManifestManager(RenderFrame* render_frame)
: RenderFrameObserver(render_frame),
may_have_manifest_(false),
manifest_dirty_(true) {
}
ManifestManager::~ManifestManager() {
if (fetcher_)
fetcher_->Cancel();
// Consumers in the browser process will not receive this message but they
// will be aware of the RenderFrame dying and should act on that. Consumers
// in the renderer process should be correctly notified.
ResolveCallbacks(ResolveStateFailure);
}
bool ManifestManager::OnMessageReceived(const IPC::Message& message) {
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(ManifestManager, message)
IPC_MESSAGE_HANDLER(ManifestManagerMsg_RequestManifest, OnRequestManifest)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
}
void ManifestManager::OnRequestManifest(int request_id) {
GetManifest(base::Bind(&ManifestManager::OnRequestManifestComplete,
base::Unretained(this), request_id));
}
void ManifestManager::OnRequestManifestComplete(
int request_id, const Manifest& manifest) {
// When sent via IPC, the Manifest must follow certain security rules.
Manifest ipc_manifest = manifest;
ipc_manifest.name = base::NullableString16(
ipc_manifest.name.string().substr(0, Manifest::kMaxIPCStringLength),
ipc_manifest.name.is_null());
ipc_manifest.short_name = base::NullableString16(
ipc_manifest.short_name.string().substr(0,
Manifest::kMaxIPCStringLength),
ipc_manifest.short_name.is_null());
Send(new ManifestManagerHostMsg_RequestManifestResponse(
routing_id(), request_id, ipc_manifest));
}
void ManifestManager::GetManifest(const GetManifestCallback& callback) {
if (!may_have_manifest_) {
callback.Run(Manifest());
return;
}
if (!manifest_dirty_) {
callback.Run(manifest_);
return;
}
pending_callbacks_.push_back(callback);
// Just wait for the running call to be done if there are other callbacks.
if (pending_callbacks_.size() > 1)
return;
FetchManifest();
}
void ManifestManager::DidChangeManifest() {
may_have_manifest_ = true;
manifest_dirty_ = true;
}
void ManifestManager::FetchManifest() {
GURL url(render_frame()->GetWebFrame()->document().manifestURL());
if (url.is_empty()) {
ResolveCallbacks(ResolveStateFailure);
return;
}
fetcher_.reset(new ManifestFetcher(url));
// TODO(mlamouri,kenneth): this is not yet taking into account manifest-src
// CSP rule, see http://crbug.com/409996.
fetcher_->Start(render_frame()->GetWebFrame(),
base::Bind(&ManifestManager::OnManifestFetchComplete,
base::Unretained(this)));
}
void ManifestManager::OnManifestFetchComplete(
const blink::WebURLResponse& response,
const std::string& data) {
if (response.isNull() && data.empty()) {
ResolveCallbacks(ResolveStateFailure);
return;
}
manifest_ = ManifestParser::Parse(data);
fetcher_.reset();
ResolveCallbacks(ResolveStateSuccess);
}
void ManifestManager::ResolveCallbacks(ResolveState state) {
if (state == ResolveStateFailure)
manifest_ = Manifest();
manifest_dirty_ = state != ResolveStateSuccess;
Manifest manifest = manifest_;
std::list<GetManifestCallback> callbacks = pending_callbacks_;
pending_callbacks_.clear();
for (std::list<GetManifestCallback>::const_iterator it = callbacks.begin();
it != callbacks.end(); ++it) {
it->Run(manifest);
}
}
} // namespace content
// Copyright 2014 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.
#ifndef CONTENT_RENDERER_MANIFEST_MANIFEST_MANAGER_H_
#define CONTENT_RENDERER_MANIFEST_MANIFEST_MANAGER_H_
#include <list>
#include "base/callback_forward.h"
#include "base/memory/scoped_ptr.h"
#include "content/public/common/manifest.h"
#include "content/public/renderer/render_frame_observer.h"
namespace blink {
class WebURLResponse;
}
namespace content {
class ManifestFetcher;
// The ManifestManager is a helper class that takes care of fetching and parsing
// the Manifest of the associated RenderFrame. It uses the ManifestFetcher and
// the ManifestParser in order to do so.
// There are two expected consumers of this helper: ManifestManagerHost, via IPC
// messages and callers inside the renderer process. The latter should use
// GetManifest().
class ManifestManager : public RenderFrameObserver {
public:
typedef base::Callback<void(const Manifest&)> GetManifestCallback;
explicit ManifestManager(RenderFrame* render_frame);
virtual ~ManifestManager();
// Will call the given |callback| with the Manifest associated with the
// RenderFrame if any. Will pass an empty Manifest in case of error.
void GetManifest(const GetManifestCallback& callback);
// RenderFrameObserver implementation.
virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
virtual void DidChangeManifest() OVERRIDE;
private:
enum ResolveState {
ResolveStateSuccess,
ResolveStateFailure
};
// Called when receiving a ManifestManagerMsg_RequestManifest from the browser
// process.
void OnRequestManifest(int request_id);
void OnRequestManifestComplete(int request_id, const Manifest&);
void FetchManifest();
void OnManifestFetchComplete(const blink::WebURLResponse& response,
const std::string& data);
void ResolveCallbacks(ResolveState state);
scoped_ptr<ManifestFetcher> fetcher_;
// Whether the RenderFrame may have an associated Manifest. If true, the frame
// may have a manifest, if false, it can't have one. This boolean is true when
// DidChangeManifest() is called, if it is never called, it means that the
// associated document has no <link rel='manifest'>.
bool may_have_manifest_;
// Whether the current Manifest is dirty.
bool manifest_dirty_;
// Current Manifest. Might be outdated if manifest_dirty_ is true.
Manifest manifest_;
std::list<GetManifestCallback> pending_callbacks_;
DISALLOW_COPY_AND_ASSIGN(ManifestManager);
};
} // namespace content
#endif // CONTENT_RENDERER_MANIFEST_MANIFEST_MANAGER_H_
...@@ -67,6 +67,7 @@ ...@@ -67,6 +67,7 @@
#include "content/renderer/image_loading_helper.h" #include "content/renderer/image_loading_helper.h"
#include "content/renderer/ime_event_guard.h" #include "content/renderer/ime_event_guard.h"
#include "content/renderer/internal_document_state_data.h" #include "content/renderer/internal_document_state_data.h"
#include "content/renderer/manifest/manifest_manager.h"
#include "content/renderer/media/audio_renderer_mixer_manager.h" #include "content/renderer/media/audio_renderer_mixer_manager.h"
#include "content/renderer/media/crypto/encrypted_media_player_support_impl.h" #include "content/renderer/media/crypto/encrypted_media_player_support_impl.h"
#include "content/renderer/media/media_stream_dispatcher.h" #include "content/renderer/media/media_stream_dispatcher.h"
...@@ -455,6 +456,7 @@ RenderFrameImpl::RenderFrameImpl(RenderViewImpl* render_view, int routing_id) ...@@ -455,6 +456,7 @@ RenderFrameImpl::RenderFrameImpl(RenderViewImpl* render_view, int routing_id)
geolocation_dispatcher_(NULL), geolocation_dispatcher_(NULL),
push_messaging_dispatcher_(NULL), push_messaging_dispatcher_(NULL),
screen_orientation_dispatcher_(NULL), screen_orientation_dispatcher_(NULL),
manifest_manager_(NULL),
accessibility_mode_(AccessibilityModeOff), accessibility_mode_(AccessibilityModeOff),
renderer_accessibility_(NULL), renderer_accessibility_(NULL),
weak_factory_(this) { weak_factory_(this) {
...@@ -473,6 +475,8 @@ RenderFrameImpl::RenderFrameImpl(RenderViewImpl* render_view, int routing_id) ...@@ -473,6 +475,8 @@ RenderFrameImpl::RenderFrameImpl(RenderViewImpl* render_view, int routing_id)
#if defined(ENABLE_NOTIFICATIONS) #if defined(ENABLE_NOTIFICATIONS)
notification_provider_ = new NotificationProvider(this); notification_provider_ = new NotificationProvider(this);
#endif #endif
manifest_manager_ = new ManifestManager(this);
} }
RenderFrameImpl::~RenderFrameImpl() { RenderFrameImpl::~RenderFrameImpl() {
...@@ -3214,6 +3218,13 @@ void RenderFrameImpl::postAccessibilityEvent(const blink::WebAXObject& obj, ...@@ -3214,6 +3218,13 @@ void RenderFrameImpl::postAccessibilityEvent(const blink::WebAXObject& obj,
HandleWebAccessibilityEvent(obj, event); HandleWebAccessibilityEvent(obj, event);
} }
void RenderFrameImpl::didChangeManifest(blink::WebLocalFrame* frame)
{
DCHECK(!frame_ || frame_ == frame);
FOR_EACH_OBSERVER(RenderFrameObserver, observers_, DidChangeManifest());
}
void RenderFrameImpl::DidPlay(blink::WebMediaPlayer* player) { void RenderFrameImpl::DidPlay(blink::WebMediaPlayer* player) {
Send(new FrameHostMsg_MediaPlayingNotification( Send(new FrameHostMsg_MediaPlayingNotification(
routing_id_, reinterpret_cast<int64>(player), player->hasVideo(), routing_id_, reinterpret_cast<int64>(player), player->hasVideo(),
......
...@@ -61,6 +61,7 @@ namespace content { ...@@ -61,6 +61,7 @@ namespace content {
class ChildFrameCompositingHelper; class ChildFrameCompositingHelper;
class ExternalPopupMenu; class ExternalPopupMenu;
class GeolocationDispatcher; class GeolocationDispatcher;
class ManifestManager;
class MediaStreamDispatcher; class MediaStreamDispatcher;
class MediaStreamRendererFactory; class MediaStreamRendererFactory;
class MidiDispatcher; class MidiDispatcher;
...@@ -440,6 +441,7 @@ class CONTENT_EXPORT RenderFrameImpl ...@@ -440,6 +441,7 @@ class CONTENT_EXPORT RenderFrameImpl
virtual bool isControlledByServiceWorker(); virtual bool isControlledByServiceWorker();
virtual void postAccessibilityEvent(const blink::WebAXObject& obj, virtual void postAccessibilityEvent(const blink::WebAXObject& obj,
blink::WebAXEvent event); blink::WebAXEvent event);
virtual void didChangeManifest(blink::WebLocalFrame*);
// WebMediaPlayerDelegate implementation: // WebMediaPlayerDelegate implementation:
virtual void DidPlay(blink::WebMediaPlayer* player) OVERRIDE; virtual void DidPlay(blink::WebMediaPlayer* player) OVERRIDE;
...@@ -729,6 +731,10 @@ class CONTENT_EXPORT RenderFrameImpl ...@@ -729,6 +731,10 @@ class CONTENT_EXPORT RenderFrameImpl
// initialized. // initialized.
ScreenOrientationDispatcher* screen_orientation_dispatcher_; ScreenOrientationDispatcher* screen_orientation_dispatcher_;
// The Manifest Manager handles the manifest requests from the browser
// process.
ManifestManager* manifest_manager_;
// The current accessibility mode. // The current accessibility mode.
AccessibilityMode accessibility_mode_; AccessibilityMode accessibility_mode_;
......
<!DOCTYPE HTML>
<html>
<head>
<link rel='manifest' href='thereisnomanifestthere.json'>
</head>
<body>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<link rel='manifest' href='dummy-manifest.json'>
</head>
<body>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
</head>
<body>
</body>
<script>
function setManifestTo(url) {
// Clear everything.
document.head.innerHTML = '';
var link = document.createElement('link');
link.rel = 'manifest';
link.href = url;
document.head.appendChild(link);
}
</script>
</html>
<!DOCTYPE html>
<html>
<head>
<link rel='manifest' href='empty-manifest.json'>
</head>
<body>
</body>
</html>
<!DOCTYPE HTML>
<html>
<head>
</head>
<body>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<link rel=manifest href='parse-error-manifest.json'>
</head>
<body>
</body>
</html>
...@@ -109,6 +109,7 @@ enum IPCMessageStart { ...@@ -109,6 +109,7 @@ enum IPCMessageStart {
CredentialManagerMsgStart, CredentialManagerMsgStart,
PDFMsgStart, PDFMsgStart,
WebCacheMsgStart, WebCacheMsgStart,
ManifestManagerMsgStart,
LastIPCMsgStart // Must come last. LastIPCMsgStart // Must come last.
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment