1 // Copyright (c) 2012 The Chromium Embedded Framework Authors.
2 // Portions copyright (c) 2012 The Chromium Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5
6 #include "libcef/browser/net/internal_scheme_handler.h"
7
8 #include <string>
9 #include <utility>
10
11 #include "libcef/common/app_manager.h"
12
13 #include "base/notreached.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "base/threading/thread_restrictions.h"
17 #include "net/base/mime_util.h"
18 #include "ui/base/resource/resource_bundle.h"
19
20 namespace scheme {
21
22 namespace {
23
FilePathFromASCII(const std::string & str)24 base::FilePath FilePathFromASCII(const std::string& str) {
25 #if BUILDFLAG(IS_WIN)
26 return base::FilePath(base::ASCIIToWide(str));
27 #else
28 return base::FilePath(str);
29 #endif
30 }
31
GetMimeType(const std::string & filename)32 std::string GetMimeType(const std::string& filename) {
33 // Requests should not block on the disk! On POSIX this goes to disk.
34 // http://code.google.com/p/chromium/issues/detail?id=59849
35 base::ThreadRestrictions::ScopedAllowIO allow_io;
36
37 std::string mime_type;
38 const base::FilePath& file_path = FilePathFromASCII(filename);
39 if (net::GetMimeTypeFromFile(file_path, &mime_type))
40 return mime_type;
41
42 // Check for newer extensions used by internal resources but not yet
43 // recognized by the mime type detector.
44 const std::string& extension = CefString(file_path.FinalExtension());
45 if (extension == ".md")
46 return "text/markdown";
47 if (extension == ".woff2")
48 return "application/font-woff2";
49
50 NOTREACHED() << "No known mime type for file: " << filename.c_str();
51 return "text/plain";
52 }
53
54 class RedirectHandler : public CefResourceHandler {
55 public:
RedirectHandler(const GURL & url)56 explicit RedirectHandler(const GURL& url) : url_(url) {}
57
58 RedirectHandler(const RedirectHandler&) = delete;
59 RedirectHandler& operator=(const RedirectHandler&) = delete;
60
Open(CefRefPtr<CefRequest> request,bool & handle_request,CefRefPtr<CefCallback> callback)61 bool Open(CefRefPtr<CefRequest> request,
62 bool& handle_request,
63 CefRefPtr<CefCallback> callback) override {
64 // Continue immediately.
65 handle_request = true;
66 return true;
67 }
68
GetResponseHeaders(CefRefPtr<CefResponse> response,int64 & response_length,CefString & redirectUrl)69 void GetResponseHeaders(CefRefPtr<CefResponse> response,
70 int64& response_length,
71 CefString& redirectUrl) override {
72 response_length = 0;
73 redirectUrl = url_.spec();
74 }
75
Read(void * data_out,int bytes_to_read,int & bytes_read,CefRefPtr<CefResourceReadCallback> callback)76 bool Read(void* data_out,
77 int bytes_to_read,
78 int& bytes_read,
79 CefRefPtr<CefResourceReadCallback> callback) override {
80 NOTREACHED();
81 return false;
82 }
83
Cancel()84 void Cancel() override {}
85
86 private:
87 GURL url_;
88
89 IMPLEMENT_REFCOUNTING(RedirectHandler);
90 };
91
92 class InternalHandler : public CefResourceHandler {
93 public:
InternalHandler(const std::string & mime_type,CefRefPtr<CefStreamReader> reader,int size)94 InternalHandler(const std::string& mime_type,
95 CefRefPtr<CefStreamReader> reader,
96 int size)
97 : mime_type_(mime_type), reader_(reader), size_(size) {}
98
99 InternalHandler(const InternalHandler&) = delete;
100 InternalHandler& operator=(const InternalHandler&) = delete;
101
Open(CefRefPtr<CefRequest> request,bool & handle_request,CefRefPtr<CefCallback> callback)102 bool Open(CefRefPtr<CefRequest> request,
103 bool& handle_request,
104 CefRefPtr<CefCallback> callback) override {
105 // Continue immediately.
106 handle_request = true;
107 return true;
108 }
109
GetResponseHeaders(CefRefPtr<CefResponse> response,int64 & response_length,CefString & redirectUrl)110 void GetResponseHeaders(CefRefPtr<CefResponse> response,
111 int64& response_length,
112 CefString& redirectUrl) override {
113 response_length = size_;
114
115 response->SetMimeType(mime_type_);
116 response->SetStatus(200);
117 }
118
Read(void * data_out,int bytes_to_read,int & bytes_read,CefRefPtr<CefResourceReadCallback> callback)119 bool Read(void* data_out,
120 int bytes_to_read,
121 int& bytes_read,
122 CefRefPtr<CefResourceReadCallback> callback) override {
123 // Read until the buffer is full or until Read() returns 0 to indicate no
124 // more data.
125 bytes_read = 0;
126 int read = 0;
127 do {
128 read = static_cast<int>(
129 reader_->Read(static_cast<char*>(data_out) + bytes_read, 1,
130 bytes_to_read - bytes_read));
131 bytes_read += read;
132 } while (read != 0 && bytes_read < bytes_to_read);
133
134 return (bytes_read > 0);
135 }
136
Cancel()137 void Cancel() override {}
138
139 private:
140 std::string mime_type_;
141 CefRefPtr<CefStreamReader> reader_;
142 int size_;
143
144 IMPLEMENT_REFCOUNTING(InternalHandler);
145 };
146
147 class InternalHandlerFactory : public CefSchemeHandlerFactory {
148 public:
InternalHandlerFactory(std::unique_ptr<InternalHandlerDelegate> delegate)149 explicit InternalHandlerFactory(
150 std::unique_ptr<InternalHandlerDelegate> delegate)
151 : delegate_(std::move(delegate)) {}
152
153 InternalHandlerFactory(const InternalHandlerFactory&) = delete;
154 InternalHandlerFactory& operator=(const InternalHandlerFactory&) = delete;
155
Create(CefRefPtr<CefBrowser> browser,CefRefPtr<CefFrame> frame,const CefString & scheme_name,CefRefPtr<CefRequest> request)156 CefRefPtr<CefResourceHandler> Create(CefRefPtr<CefBrowser> browser,
157 CefRefPtr<CefFrame> frame,
158 const CefString& scheme_name,
159 CefRefPtr<CefRequest> request) override {
160 GURL url = GURL(request->GetURL().ToString());
161
162 InternalHandlerDelegate::Action action;
163 if (delegate_->OnRequest(browser, request, &action)) {
164 if (!action.redirect_url.is_empty() && action.redirect_url.is_valid())
165 return new RedirectHandler(action.redirect_url);
166
167 if (action.mime_type.empty())
168 action.mime_type = GetMimeType(url.path());
169
170 if (!action.bytes && action.resource_id >= 0) {
171 std::string str =
172 ui::ResourceBundle::GetSharedInstance().LoadDataResourceString(
173 action.resource_id);
174 if (str.empty()) {
175 NOTREACHED() << "Failed to load internal resource for id: "
176 << action.resource_id << " URL: " << url.spec().c_str();
177 return nullptr;
178 }
179 action.bytes = base::RefCountedString::TakeString(&str);
180 }
181
182 if (action.bytes) {
183 action.stream = CefStreamReader::CreateForData(
184 const_cast<unsigned char*>(action.bytes->data()),
185 action.bytes->size());
186 action.stream_size = action.bytes->size();
187 }
188
189 if (action.stream.get()) {
190 return new InternalHandler(action.mime_type, action.stream,
191 action.stream_size);
192 }
193 }
194
195 return nullptr;
196 }
197
198 private:
199 std::unique_ptr<InternalHandlerDelegate> delegate_;
200
201 IMPLEMENT_REFCOUNTING(InternalHandlerFactory);
202 };
203
204 } // namespace
205
Action()206 InternalHandlerDelegate::Action::Action() : stream_size(-1), resource_id(-1) {}
207
CreateInternalHandlerFactory(std::unique_ptr<InternalHandlerDelegate> delegate)208 CefRefPtr<CefSchemeHandlerFactory> CreateInternalHandlerFactory(
209 std::unique_ptr<InternalHandlerDelegate> delegate) {
210 DCHECK(delegate.get());
211 return new InternalHandlerFactory(std::move(delegate));
212 }
213
214 } // namespace scheme
215