Commit 6edb6488 authored by zork@chromium.org's avatar zork@chromium.org

Forward MIME types to BrowserPlugin when a viewer is specified.

When the mime_type_handler key is available in an extension, this creates a
BrowserPlugin and gives the ID of it to the background page.

BUG=368514

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

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@276841 0039d316-1c4b-4281-b951-d872f2087c98
parent 03bbb697
......@@ -61,6 +61,7 @@ void StreamsPrivateAPI::ExecuteMimeTypeHandler(
const std::string& extension_id,
content::WebContents* web_contents,
scoped_ptr<content::StreamHandle> stream,
const std::string& view_id,
int64 expected_content_size) {
// Create the event's arguments value.
streams_private::StreamInfo info;
......@@ -69,6 +70,10 @@ void StreamsPrivateAPI::ExecuteMimeTypeHandler(
info.stream_url = stream->GetURL().spec();
info.tab_id = ExtensionTabUtil::GetTabId(web_contents);
if (!view_id.empty()) {
info.view_id.reset(new std::string(view_id));
}
int size = -1;
if (expected_content_size <= INT_MAX)
size = expected_content_size;
......
......@@ -30,9 +30,15 @@ class StreamsPrivateAPI : public BrowserContextKeyedAPI,
explicit StreamsPrivateAPI(content::BrowserContext* context);
virtual ~StreamsPrivateAPI();
// Send the onExecuteMimeTypeHandler event to |extension_id|.
// |web_contents| is used to determine the tabId where the document is being
// opened. The data for the document will be readable from |stream|, and
// should be |expected_content_size| bytes long. If the viewer is being opened
// in a BrowserPlugin, specify a non-empty |view_id| of the plugin.
void ExecuteMimeTypeHandler(const std::string& extension_id,
content::WebContents* web_contents,
scoped_ptr<content::StreamHandle> stream,
const std::string& view_id,
int64 expected_content_size);
void AbortStream(const std::string& extension_id,
......
......@@ -6,11 +6,13 @@
#include "base/lazy_instance.h"
#include "base/path_service.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/plugin_manager.h"
#include "chrome/browser/plugins/chrome_plugin_service_filter.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/extensions/api/plugins/plugins_handler.h"
#include "chrome/common/extensions/manifest_handlers/mime_types_handler.h"
#include "content/public/browser/plugin_service.h"
#include "content/public/common/pepper_plugin_info.h"
#include "extensions/browser/extension_registry.h"
......@@ -77,6 +79,13 @@ void PluginManager::OnExtensionLoaded(content::BrowserContext* browser_context,
UpdatePluginListWithNaClModules();
}
const MimeTypesHandler* handler = MimeTypesHandler::GetHandler(extension);
if (handler && !handler->handler_url().empty()) {
plugins_or_nacl_changed = true;
RegisterMimeTypeHandler(handler->extension_id());
UpdatePluginListWithNaClModules();
}
if (plugins_or_nacl_changed)
PluginService::GetInstance()->PurgePluginListCache(profile_, false);
}
......@@ -111,6 +120,12 @@ void PluginManager::OnExtensionUnloaded(
UpdatePluginListWithNaClModules();
}
const MimeTypesHandler* handler = MimeTypesHandler::GetHandler(extension);
if (handler && !handler->handler_url().empty()) {
plugins_or_nacl_changed = true;
UnregisterMimeTypeHandler(handler->extension_id());
}
if (plugins_or_nacl_changed)
PluginService::GetInstance()->PurgePluginListCache(profile_, false);
}
......@@ -172,6 +187,45 @@ void PluginManager::UpdatePluginListWithNaClModules() {
break;
}
}
for (std::set<std::string>::iterator ix =
mime_type_handler_extension_ids_.begin();
ix != mime_type_handler_extension_ids_.end(); ++ix) {
const std::string& extension_id = *ix;
const Extension* extension =
profile_->GetExtensionService()->GetExtensionById(extension_id, false);
const MimeTypesHandler* handler = MimeTypesHandler::GetHandler(extension);
if (handler && !handler->handler_url().empty()) {
PluginService::GetInstance()->UnregisterInternalPlugin(
base::FilePath::FromUTF8Unsafe(extension_id));
content::WebPluginInfo info;
info.type = content::WebPluginInfo::PLUGIN_TYPE_BROWSER_PLUGIN;
info.name = base::UTF8ToUTF16(extension_id);
info.path = base::FilePath::FromUTF8Unsafe(extension_id);
for (std::set<std::string>::const_iterator mime_type =
handler->mime_type_set().begin();
mime_type != handler->mime_type_set().end(); ++mime_type) {
content::WebPluginMimeType mime_type_info;
mime_type_info.mime_type = *mime_type;
info.mime_types.push_back(mime_type_info);
}
PluginService::GetInstance()->RefreshPlugins();
PluginService::GetInstance()->RegisterInternalPlugin(info, true);
}
}
}
void PluginManager::RegisterMimeTypeHandler(const std::string& extension_id) {
mime_type_handler_extension_ids_.insert(extension_id);
}
void PluginManager::UnregisterMimeTypeHandler(const std::string& extension_id) {
mime_type_handler_extension_ids_.erase(extension_id);
PluginService::GetInstance()->UnregisterInternalPlugin(
base::FilePath::FromUTF8Unsafe(extension_id));
}
NaClModuleInfo::List::iterator PluginManager::FindNaClModule(const GURL& url) {
......
......@@ -5,6 +5,9 @@
#ifndef CHROME_BROWSER_EXTENSIONS_PLUGIN_MANAGER_H_
#define CHROME_BROWSER_EXTENSIONS_PLUGIN_MANAGER_H_
#include <set>
#include <string>
#include "base/scoped_observer.h"
#include "chrome/common/extensions/manifest_handlers/nacl_modules_handler.h"
#include "extensions/browser/browser_context_keyed_api_factory.h"
......@@ -41,6 +44,9 @@ class PluginManager : public BrowserContextKeyedAPI,
void RegisterNaClModule(const NaClModuleInfo& info);
void UnregisterNaClModule(const NaClModuleInfo& info);
void RegisterMimeTypeHandler(const std::string& extension_id);
void UnregisterMimeTypeHandler(const std::string& extension_id);
// Call UpdatePluginListWithNaClModules() after registering or unregistering
// a NaCl module to see those changes reflected in the PluginList.
void UpdatePluginListWithNaClModules();
......@@ -61,6 +67,10 @@ class PluginManager : public BrowserContextKeyedAPI,
extensions::NaClModuleInfo::List nacl_module_list_;
// The set of extensions that are registered as the handler for at least one
// MIME type.
std::set<std::string> mime_type_handler_extension_ids_;
Profile* profile_;
// Listen to extension load, unloaded notifications.
......
......@@ -8,6 +8,7 @@
#include <vector>
#include "base/base64.h"
#include "base/guid.h"
#include "base/logging.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chrome_notification_types.h"
......@@ -156,23 +157,12 @@ void UpdatePrerenderNetworkBytesCallback(int render_process_id,
}
#if !defined(OS_ANDROID)
// Goes through the extension's file browser handlers and checks if there is one
// that can handle the |mime_type|.
// |extension| must not be NULL.
bool ExtensionCanHandleMimeType(const Extension* extension,
const std::string& mime_type) {
MimeTypesHandler* handler = MimeTypesHandler::GetHandler(extension);
if (!handler)
return false;
return handler->CanHandleMIMEType(mime_type);
}
void SendExecuteMimeTypeHandlerEvent(scoped_ptr<content::StreamHandle> stream,
int64 expected_content_size,
int render_process_id,
int render_view_id,
const std::string& extension_id) {
const std::string& extension_id,
const std::string& view_id) {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
content::WebContents* web_contents =
......@@ -196,7 +186,8 @@ void SendExecuteMimeTypeHandlerEvent(scoped_ptr<content::StreamHandle> stream,
if (!streams_private)
return;
streams_private->ExecuteMimeTypeHandler(
extension_id, web_contents, stream.Pass(), expected_content_size);
extension_id, web_contents, stream.Pass(), view_id,
expected_content_size);
}
void LaunchURL(const GURL& url, int render_process_id, int render_view_id,
......@@ -264,6 +255,7 @@ ChromeResourceDispatcherHostDelegate::ChromeResourceDispatcherHostDelegate(
}
ChromeResourceDispatcherHostDelegate::~ChromeResourceDispatcherHostDelegate() {
CHECK(stream_target_info_.empty());
}
bool ChromeResourceDispatcherHostDelegate::ShouldBeginRequest(
......@@ -549,14 +541,14 @@ bool ChromeResourceDispatcherHostDelegate::ShouldForceDownloadResource(
}
bool ChromeResourceDispatcherHostDelegate::ShouldInterceptResourceAsStream(
content::ResourceContext* resource_context,
const GURL& url,
net::URLRequest* request,
const std::string& mime_type,
GURL* origin,
std::string* target_id) {
std::string* payload) {
#if !defined(OS_ANDROID)
const ResourceRequestInfo* info = ResourceRequestInfo::ForRequest(request);
ProfileIOData* io_data =
ProfileIOData::FromResourceContext(resource_context);
ProfileIOData::FromResourceContext(info->GetContext());
bool profile_is_off_the_record = io_data->IsOffTheRecord();
const scoped_refptr<const extensions::InfoMap> extension_info_map(
io_data->GetExtensionInfoMap());
......@@ -575,9 +567,17 @@ bool ChromeResourceDispatcherHostDelegate::ShouldInterceptResourceAsStream(
continue;
}
if (ExtensionCanHandleMimeType(extension, mime_type)) {
MimeTypesHandler* handler = MimeTypesHandler::GetHandler(extension);
if (handler && handler->CanHandleMIMEType(mime_type)) {
StreamTargetInfo target_info;
*origin = Extension::GetBaseURLFromExtensionId(extension_id);
*target_id = extension_id;
target_info.extension_id = extension_id;
if (!handler->handler_url().empty()) {
target_info.view_id = base::GenerateGUID();
*payload = origin->spec() + handler->handler_url() +
"?id=" + target_info.view_id;
}
stream_target_info_[request] = target_info;
return true;
}
}
......@@ -586,18 +586,20 @@ bool ChromeResourceDispatcherHostDelegate::ShouldInterceptResourceAsStream(
}
void ChromeResourceDispatcherHostDelegate::OnStreamCreated(
content::ResourceContext* resource_context,
int render_process_id,
int render_view_id,
const std::string& target_id,
scoped_ptr<content::StreamHandle> stream,
int64 expected_content_size) {
net::URLRequest* request,
scoped_ptr<content::StreamHandle> stream) {
#if !defined(OS_ANDROID)
const ResourceRequestInfo* info = ResourceRequestInfo::ForRequest(request);
std::map<net::URLRequest*, StreamTargetInfo>::iterator ix =
stream_target_info_.find(request);
CHECK(ix != stream_target_info_.end());
content::BrowserThread::PostTask(
content::BrowserThread::UI, FROM_HERE,
base::Bind(&SendExecuteMimeTypeHandlerEvent, base::Passed(&stream),
expected_content_size, render_process_id, render_view_id,
target_id));
request->GetExpectedContentSize(),
info->GetChildID(), info->GetRouteID(),
ix->second.extension_id, ix->second.view_id));
stream_target_info_.erase(request);
#endif
}
......
......@@ -5,6 +5,7 @@
#ifndef CHROME_BROWSER_RENDERER_HOST_CHROME_RESOURCE_DISPATCHER_HOST_DELEGATE_H_
#define CHROME_BROWSER_RENDERER_HOST_CHROME_RESOURCE_DISPATCHER_HOST_DELEGATE_H_
#include <map>
#include <set>
#include "base/compiler_specific.h"
......@@ -70,18 +71,13 @@ class ChromeResourceDispatcherHostDelegate
virtual bool ShouldForceDownloadResource(
const GURL& url, const std::string& mime_type) OVERRIDE;
virtual bool ShouldInterceptResourceAsStream(
content::ResourceContext* resource_context,
const GURL& url,
net::URLRequest* request,
const std::string& mime_type,
GURL* origin,
std::string* target_id) OVERRIDE;
std::string* payload) OVERRIDE;
virtual void OnStreamCreated(
content::ResourceContext* resource_context,
int render_process_id,
int render_view_id,
const std::string& target_id,
scoped_ptr<content::StreamHandle> stream,
int64 expected_content_size) OVERRIDE;
net::URLRequest* request,
scoped_ptr<content::StreamHandle> stream) OVERRIDE;
virtual void OnResponseStarted(
net::URLRequest* request,
content::ResourceContext* resource_context,
......@@ -100,6 +96,11 @@ class ChromeResourceDispatcherHostDelegate
ExternalProtocolHandler::Delegate* delegate);
private:
struct StreamTargetInfo {
std::string extension_id;
std::string view_id;
};
void AppendStandardResourceThrottles(
net::URLRequest* request,
content::ResourceContext* resource_context,
......@@ -118,6 +119,7 @@ class ChromeResourceDispatcherHostDelegate
scoped_refptr<SafeBrowsingService> safe_browsing_;
scoped_refptr<extensions::UserScriptListener> user_script_listener_;
prerender::PrerenderTracker* prerender_tracker_;
std::map<net::URLRequest*, StreamTargetInfo> stream_target_info_;
DISALLOW_COPY_AND_ASSIGN(ChromeResourceDispatcherHostDelegate);
};
......
......@@ -18,6 +18,10 @@ namespace streamsPrivate {
// a tab, it will be -1.
long tabId;
// The ID of the view that will render the stream, if the viewer was opened
// in a plugin.
DOMString? viewId;
// The amount of data the Stream should contain, if known. If there is no
// information on the size it will be -1.
long expectedContentSize;
......
......@@ -41,6 +41,8 @@ class MimeTypesHandler {
// The URL that will be used to handle MIME type requests.
const std::string handler_url() const { return handler_url_; }
const std::set<std::string>& mime_type_set() const { return mime_type_set_; }
private:
// The id for the extension this action belongs to (as defined in the
// extension manifest).
......
......@@ -15,6 +15,7 @@
#include "content/browser/loader/certificate_resource_handler.h"
#include "content/browser/loader/resource_dispatcher_host_impl.h"
#include "content/browser/loader/resource_request_info_impl.h"
#include "content/browser/loader/stream_resource_handler.h"
#include "content/browser/plugin_service_impl.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/download_item.h"
......@@ -305,7 +306,7 @@ bool BufferedResourceHandler::SelectNextHandler(bool* defer) {
info->set_is_download(true);
scoped_ptr<ResourceHandler> handler(
new CertificateResourceHandler(request()));
return UseAlternateNextHandler(handler.Pass());
return UseAlternateNextHandler(handler.Pass(), std::string());
}
if (!info->allow_download())
......@@ -316,10 +317,12 @@ bool BufferedResourceHandler::SelectNextHandler(bool* defer) {
if (net::IsSupportedMimeType(mime_type))
return true;
std::string payload;
scoped_ptr<ResourceHandler> handler(
host_->MaybeInterceptAsStream(request(), response_.get()));
if (handler)
return UseAlternateNextHandler(handler.Pass());
host_->MaybeInterceptAsStream(request(), response_.get(), &payload));
if (handler) {
return UseAlternateNextHandler(handler.Pass(), payload);
}
#if defined(ENABLE_PLUGINS)
bool stale;
......@@ -348,11 +351,12 @@ bool BufferedResourceHandler::SelectNextHandler(bool* defer) {
content::DownloadItem::kInvalidId,
scoped_ptr<DownloadSaveInfo>(new DownloadSaveInfo()),
DownloadUrlParameters::OnStartedCallback()));
return UseAlternateNextHandler(handler.Pass());
return UseAlternateNextHandler(handler.Pass(), std::string());
}
bool BufferedResourceHandler::UseAlternateNextHandler(
scoped_ptr<ResourceHandler> new_handler) {
scoped_ptr<ResourceHandler> new_handler,
const std::string& payload_for_old_handler) {
if (response_->head.headers.get() && // Can be NULL if FTP.
response_->head.headers->response_code() / 100 != 2) {
// The response code indicates that this is an error page, but we don't
......@@ -373,10 +377,29 @@ bool BufferedResourceHandler::UseAlternateNextHandler(
// which does so is CrossSiteResourceHandler. Cross-site transitions should
// not trigger when switching handlers.
DCHECK(!defer_ignored);
if (payload_for_old_handler.empty()) {
net::URLRequestStatus status(net::URLRequestStatus::CANCELED,
net::ERR_ABORTED);
next_handler_->OnResponseCompleted(status, std::string(), &defer_ignored);
DCHECK(!defer_ignored);
} else {
scoped_refptr<net::IOBuffer> buf;
int size = 0;
next_handler_->OnWillRead(&buf, &size, payload_for_old_handler.length());
CHECK_GE(size, static_cast<int>(payload_for_old_handler.length()));
memcpy(buf->data(), payload_for_old_handler.c_str(),
payload_for_old_handler.length());
next_handler_->OnReadCompleted(payload_for_old_handler.length(),
&defer_ignored);
DCHECK(!defer_ignored);
net::URLRequestStatus status(net::URLRequestStatus::SUCCESS, 0);
next_handler_->OnResponseCompleted(status, std::string(), &defer_ignored);
DCHECK(!defer_ignored);
}
// This is handled entirely within the new ResourceHandler, so just reset the
// original ResourceHandler.
......
......@@ -54,7 +54,8 @@ class BufferedResourceHandler
bool ShouldSniffContent();
bool DetermineMimeType();
bool SelectNextHandler(bool* defer);
bool UseAlternateNextHandler(scoped_ptr<ResourceHandler> handler);
bool UseAlternateNextHandler(scoped_ptr<ResourceHandler> handler,
const std::string& payload_for_old_handler);
bool ReplayReadCompleted(bool* defer);
void CallReplayReadCompleted();
......
......@@ -608,18 +608,17 @@ ResourceDispatcherHostImpl::CreateResourceHandlerForDownload(
scoped_ptr<ResourceHandler>
ResourceDispatcherHostImpl::MaybeInterceptAsStream(net::URLRequest* request,
ResourceResponse* response) {
ResourceResponse* response,
std::string* payload) {
ResourceRequestInfoImpl* info = ResourceRequestInfoImpl::ForRequest(request);
const std::string& mime_type = response->head.mime_type;
GURL origin;
std::string target_id;
if (!delegate_ ||
!delegate_->ShouldInterceptResourceAsStream(info->GetContext(),
request->url(),
!delegate_->ShouldInterceptResourceAsStream(request,
mime_type,
&origin,
&target_id)) {
payload)) {
return scoped_ptr<ResourceHandler>();
}
......@@ -633,15 +632,11 @@ ResourceDispatcherHostImpl::MaybeInterceptAsStream(net::URLRequest* request,
info->set_is_stream(true);
delegate_->OnStreamCreated(
info->GetContext(),
info->GetChildID(),
info->GetRouteID(),
target_id,
request,
handler->stream()->CreateHandle(
request->url(),
mime_type,
response->head.headers),
request->GetExpectedContentSize());
response->head.headers));
return handler.PassAs<ResourceHandler>();
}
......
......@@ -210,10 +210,13 @@ class CONTENT_EXPORT ResourceDispatcherHostImpl
const DownloadUrlParameters::OnStartedCallback& started_cb);
// Must be called after the ResourceRequestInfo has been created
// and associated with the request.
// and associated with the request. If |payload| is set to a non-empty value,
// the value will be sent to the old resource handler instead of cancelling
// it, except on HTTP errors.
scoped_ptr<ResourceHandler> MaybeInterceptAsStream(
net::URLRequest* request,
ResourceResponse* response);
ResourceResponse* response,
std::string* payload);
void ClearSSLClientAuthHandlerForRequest(net::URLRequest* request);
......
......@@ -61,21 +61,16 @@ bool ResourceDispatcherHostDelegate::ShouldForceDownloadResource(
}
bool ResourceDispatcherHostDelegate::ShouldInterceptResourceAsStream(
content::ResourceContext* resource_context,
const GURL& url,
net::URLRequest* request,
const std::string& mime_type,
GURL* origin,
std::string* target_id) {
std::string* payload) {
return false;
}
void ResourceDispatcherHostDelegate::OnStreamCreated(
content::ResourceContext* resource_context,
int render_process_id,
int render_view_id,
const std::string& target_id,
scoped_ptr<StreamHandle> stream,
int64 expected_content_size) {
net::URLRequest* request,
scoped_ptr<content::StreamHandle> stream) {
}
void ResourceDispatcherHostDelegate::OnResponseStarted(
......
......@@ -96,32 +96,28 @@ class CONTENT_EXPORT ResourceDispatcherHostDelegate {
virtual bool ShouldForceDownloadResource(
const GURL& url, const std::string& mime_type);
// Returns true and sets |origin| and |target_id| if a Stream should be
// created for the resource.
// Returns true and sets |origin| if a Stream should be created for the
// resource.
// If true is returned, a new Stream will be created and OnStreamCreated()
// will be called with
// - the |target_id| returned by this function
// - a StreamHandle instance for the Stream. The handle contains the URL for
// reading the Stream etc.
// The Stream's origin will be set to |origin|.
//
// If the stream will be rendered in a BrowserPlugin, |payload| will contain
// the data that should be given to the old ResourceHandler to forward to the
// renderer process.
virtual bool ShouldInterceptResourceAsStream(
content::ResourceContext* resource_context,
const GURL& url,
net::URLRequest* request,
const std::string& mime_type,
GURL* origin,
std::string* target_id);
std::string* payload);
// Informs the delegate that a Stream was created. |target_id| will be filled
// with the parameter returned by ShouldInterceptResourceAsStream(). The
// Stream can be read from the blob URL of the Stream, but can only be read
// once.
// Informs the delegate that a Stream was created. The Stream can be read from
// the blob URL of the Stream, but can only be read once.
virtual void OnStreamCreated(
content::ResourceContext* resource_context,
int render_process_id,
int render_view_id,
const std::string& target_id,
scoped_ptr<StreamHandle> stream,
int64 expected_content_size);
net::URLRequest* request,
scoped_ptr<content::StreamHandle> stream);
// Informs the delegate that a response has started.
virtual void OnResponseStarted(
......
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