OLD | NEW |
| (Empty) |
1 // Copyright 2015 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. | |
4 | |
5 #include <stddef.h> | |
6 #include <stdint.h> | |
7 #include <utility> | |
8 | |
9 #include "base/bind.h" | |
10 #include "base/callback.h" | |
11 #include "base/containers/hash_tables.h" | |
12 #include "base/macros.h" | |
13 #include "base/memory/scoped_ptr.h" | |
14 #include "components/bitmap_uploader/bitmap_uploader.h" | |
15 #include "components/mus/common/types.h" | |
16 #include "components/mus/public/cpp/input_event_handler.h" | |
17 #include "components/mus/public/cpp/scoped_window_ptr.h" | |
18 #include "components/mus/public/cpp/window.h" | |
19 #include "components/mus/public/cpp/window_observer.h" | |
20 #include "components/mus/public/cpp/window_tree_connection.h" | |
21 #include "components/mus/public/cpp/window_tree_delegate.h" | |
22 #include "components/mus/public/interfaces/input_events.mojom.h" | |
23 #include "components/mus/public/interfaces/input_key_codes.mojom.h" | |
24 #include "components/web_view/public/interfaces/frame.mojom.h" | |
25 #include "mojo/common/data_pipe_utils.h" | |
26 #include "mojo/public/c/system/main.h" | |
27 #include "mojo/public/cpp/bindings/binding.h" | |
28 #include "mojo/public/cpp/bindings/strong_binding.h" | |
29 #include "mojo/services/tracing/public/cpp/tracing_impl.h" | |
30 #include "mojo/shell/public/cpp/application_impl.h" | |
31 #include "mojo/shell/public/cpp/application_runner.h" | |
32 #include "mojo/shell/public/cpp/interface_factory_impl.h" | |
33 #include "mojo/shell/public/cpp/shell_client.h" | |
34 #include "mojo/shell/public/interfaces/content_handler.mojom.h" | |
35 #include "mojo/shell/public/interfaces/shell.mojom.h" | |
36 #include "third_party/pdfium/public/fpdf_ext.h" | |
37 #include "third_party/pdfium/public/fpdfview.h" | |
38 #include "ui/gfx/geometry/rect.h" | |
39 #include "v8/include/v8.h" | |
40 | |
41 const uint32_t g_background_color = 0xFF888888; | |
42 | |
43 namespace pdf_viewer { | |
44 namespace { | |
45 | |
46 // Responsible for managing a particlar view displaying a PDF document. | |
47 class PDFView : public mus::WindowTreeDelegate, | |
48 public mus::WindowObserver, | |
49 public mus::InputEventHandler, | |
50 public web_view::mojom::FrameClient, | |
51 public mojo::InterfaceFactory<web_view::mojom::FrameClient> { | |
52 public: | |
53 using DeleteCallback = base::Callback<void(PDFView*)>; | |
54 | |
55 PDFView(mojo::Shell* shell, | |
56 mojo::Connection* connection, | |
57 FPDF_DOCUMENT doc, | |
58 const DeleteCallback& delete_callback) | |
59 : app_ref_(shell->CreateAppRefCount()), | |
60 doc_(doc), | |
61 current_page_(0), | |
62 page_count_(FPDF_GetPageCount(doc_)), | |
63 shell_(shell), | |
64 root_(nullptr), | |
65 frame_client_binding_(this), | |
66 delete_callback_(delete_callback) { | |
67 connection->AddService(this); | |
68 } | |
69 | |
70 void Close() { | |
71 if (root_) | |
72 mus::ScopedWindowPtr::DeleteWindowOrWindowManager(root_); | |
73 else | |
74 delete this; | |
75 } | |
76 | |
77 private: | |
78 ~PDFView() override { | |
79 DCHECK(!root_); | |
80 if (!delete_callback_.is_null()) | |
81 delete_callback_.Run(this); | |
82 } | |
83 | |
84 void DrawBitmap() { | |
85 if (!doc_) | |
86 return; | |
87 | |
88 FPDF_PAGE page = FPDF_LoadPage(doc_, current_page_); | |
89 int width = static_cast<int>(FPDF_GetPageWidth(page)); | |
90 int height = static_cast<int>(FPDF_GetPageHeight(page)); | |
91 | |
92 scoped_ptr<std::vector<unsigned char>> bitmap; | |
93 bitmap.reset(new std::vector<unsigned char>); | |
94 bitmap->resize(width * height * 4); | |
95 | |
96 FPDF_BITMAP f_bitmap = FPDFBitmap_CreateEx(width, height, FPDFBitmap_BGRA, | |
97 &(*bitmap)[0], width * 4); | |
98 FPDFBitmap_FillRect(f_bitmap, 0, 0, width, height, 0xFFFFFFFF); | |
99 FPDF_RenderPageBitmap(f_bitmap, page, 0, 0, width, height, 0, 0); | |
100 FPDFBitmap_Destroy(f_bitmap); | |
101 | |
102 FPDF_ClosePage(page); | |
103 | |
104 bitmap_uploader_->SetBitmap(width, height, std::move(bitmap), | |
105 bitmap_uploader::BitmapUploader::BGRA); | |
106 } | |
107 | |
108 // WindowTreeDelegate: | |
109 void OnEmbed(mus::Window* root) override { | |
110 DCHECK(!root_); | |
111 root_ = root; | |
112 root_->AddObserver(this); | |
113 root_->set_input_event_handler(this); | |
114 bitmap_uploader_.reset(new bitmap_uploader::BitmapUploader(root_)); | |
115 bitmap_uploader_->Init(shell_); | |
116 bitmap_uploader_->SetColor(g_background_color); | |
117 DrawBitmap(); | |
118 } | |
119 | |
120 void OnConnectionLost(mus::WindowTreeConnection* connection) override { | |
121 root_ = nullptr; | |
122 delete this; | |
123 } | |
124 | |
125 // WindowObserver: | |
126 void OnWindowBoundsChanged(mus::Window* view, | |
127 const gfx::Rect& old_bounds, | |
128 const gfx::Rect& new_bounds) override { | |
129 DrawBitmap(); | |
130 } | |
131 | |
132 void OnWindowDestroyed(mus::Window* view) override { | |
133 DCHECK_EQ(root_, view); | |
134 root_ = nullptr; | |
135 bitmap_uploader_.reset(); | |
136 } | |
137 | |
138 // mus::InputEventHandler: | |
139 void OnWindowInputEvent(mus::Window* view, | |
140 mus::mojom::EventPtr event, | |
141 scoped_ptr<base::Closure>* ack_callback) override { | |
142 if (event->key_data && | |
143 (event->action != mus::mojom::EventType::KEY_PRESSED || | |
144 event->key_data->is_char)) { | |
145 return; | |
146 } | |
147 | |
148 // TODO(rjkroege): Make panning and scrolling more performant and | |
149 // responsive to gesture events. | |
150 if ((event->key_data && | |
151 event->key_data->windows_key_code == mus::mojom::KeyboardCode::DOWN) || | |
152 (event->pointer_data && event->pointer_data->wheel_data && | |
153 event->pointer_data->wheel_data->delta_y < 0)) { | |
154 if (current_page_ < (page_count_ - 1)) { | |
155 current_page_++; | |
156 DrawBitmap(); | |
157 } | |
158 } else if ((event->key_data && | |
159 event->key_data->windows_key_code == | |
160 mus::mojom::KeyboardCode::UP) || | |
161 (event->pointer_data && event->pointer_data->wheel_data && | |
162 event->pointer_data->wheel_data->delta_y > 0)) { | |
163 if (current_page_ > 0) { | |
164 current_page_--; | |
165 DrawBitmap(); | |
166 } | |
167 } | |
168 } | |
169 | |
170 // web_view::mojom::FrameClient: | |
171 void OnConnect(web_view::mojom::FramePtr frame, | |
172 uint32_t change_id, | |
173 uint32_t view_id, | |
174 web_view::mojom::WindowConnectType view_connect_type, | |
175 mojo::Array<web_view::mojom::FrameDataPtr> frame_data, | |
176 int64_t navigation_start_time_ticks, | |
177 const OnConnectCallback& callback) override { | |
178 callback.Run(); | |
179 | |
180 frame_ = std::move(frame); | |
181 frame_->DidCommitProvisionalLoad(); | |
182 } | |
183 void OnFrameAdded(uint32_t change_id, | |
184 web_view::mojom::FrameDataPtr frame_data) override {} | |
185 void OnFrameRemoved(uint32_t change_id, uint32_t frame_id) override {} | |
186 void OnFrameClientPropertyChanged(uint32_t frame_id, | |
187 const mojo::String& name, | |
188 mojo::Array<uint8_t> new_value) override {} | |
189 void OnPostMessageEvent(uint32_t source_frame_id, | |
190 uint32_t target_frame_id, | |
191 web_view::mojom::HTMLMessageEventPtr event) override { | |
192 } | |
193 void OnWillNavigate(const mojo::String& origin, | |
194 const OnWillNavigateCallback& callback) override {} | |
195 void OnFrameLoadingStateChanged(uint32_t frame_id, bool loading) override {} | |
196 void OnDispatchFrameLoadEvent(uint32_t frame_id) override {} | |
197 void Find(int32_t request_id, | |
198 const mojo::String& search_text, | |
199 web_view::mojom::FindOptionsPtr options, | |
200 bool wrap_within_frame, | |
201 const FindCallback& callback) override { | |
202 NOTIMPLEMENTED(); | |
203 bool found_results = false; | |
204 callback.Run(found_results); | |
205 } | |
206 void StopFinding(bool clear_selection) override {} | |
207 void HighlightFindResults(int32_t request_id, | |
208 const mojo::String& search_test, | |
209 web_view::mojom::FindOptionsPtr options, | |
210 bool reset) override { | |
211 NOTIMPLEMENTED(); | |
212 } | |
213 void StopHighlightingFindResults() override {} | |
214 | |
215 // mojo::InterfaceFactory<web_view::mojom::FrameClient>: | |
216 void Create( | |
217 mojo::Connection* connection, | |
218 mojo::InterfaceRequest<web_view::mojom::FrameClient> request) override { | |
219 frame_client_binding_.Bind(std::move(request)); | |
220 } | |
221 | |
222 scoped_ptr<mojo::AppRefCount> app_ref_; | |
223 FPDF_DOCUMENT doc_; | |
224 int current_page_; | |
225 int page_count_; | |
226 | |
227 scoped_ptr<bitmap_uploader::BitmapUploader> bitmap_uploader_; | |
228 | |
229 mojo::Shell* shell_; | |
230 mus::Window* root_; | |
231 | |
232 web_view::mojom::FramePtr frame_; | |
233 mojo::Binding<web_view::mojom::FrameClient> frame_client_binding_; | |
234 DeleteCallback delete_callback_; | |
235 | |
236 DISALLOW_COPY_AND_ASSIGN(PDFView); | |
237 }; | |
238 | |
239 // Responsible for managing all the views for displaying a PDF document. | |
240 class PDFViewerApplicationDelegate | |
241 : public mojo::ShellClient, | |
242 public mojo::InterfaceFactory<mus::mojom::WindowTreeClient> { | |
243 public: | |
244 PDFViewerApplicationDelegate( | |
245 mojo::ApplicationRequest request, | |
246 mojo::URLResponsePtr response, | |
247 const mojo::Callback<void()>& destruct_callback) | |
248 : app_(this, | |
249 std::move(request), | |
250 base::Bind(&PDFViewerApplicationDelegate::OnTerminate, | |
251 base::Unretained(this))), | |
252 doc_(nullptr), | |
253 is_destroying_(false), | |
254 destruct_callback_(destruct_callback) { | |
255 FetchPDF(std::move(response)); | |
256 } | |
257 | |
258 ~PDFViewerApplicationDelegate() override { | |
259 is_destroying_ = true; | |
260 if (doc_) | |
261 FPDF_CloseDocument(doc_); | |
262 while (!pdf_views_.empty()) | |
263 pdf_views_.front()->Close(); | |
264 destruct_callback_.Run(); | |
265 } | |
266 | |
267 private: | |
268 void FetchPDF(mojo::URLResponsePtr response) { | |
269 data_.clear(); | |
270 mojo::common::BlockingCopyToString(std::move(response->body), &data_); | |
271 if (data_.length() >= static_cast<size_t>(std::numeric_limits<int>::max())) | |
272 return; | |
273 doc_ = FPDF_LoadMemDocument(data_.data(), static_cast<int>(data_.length()), | |
274 nullptr); | |
275 } | |
276 | |
277 // Callback from the quit closure. We key off this rather than | |
278 // ShellClient::Quit() as we don't want to shut down the messageloop | |
279 // when we quit (the messageloop is shared among multiple PDFViews). | |
280 void OnTerminate() { delete this; } | |
281 | |
282 void OnPDFViewDestroyed(PDFView* pdf_view) { | |
283 DCHECK(std::find(pdf_views_.begin(), pdf_views_.end(), pdf_view) != | |
284 pdf_views_.end()); | |
285 pdf_views_.erase(std::find(pdf_views_.begin(), pdf_views_.end(), pdf_view)); | |
286 } | |
287 | |
288 // mojo::ShellClient: | |
289 bool AcceptConnection(mojo::Connection* connection) override { | |
290 connection->AddService<mus::mojom::WindowTreeClient>(this); | |
291 return true; | |
292 } | |
293 | |
294 // mojo::InterfaceFactory<mus::mojom::WindowTreeClient>: | |
295 void Create( | |
296 mojo::Connection* connection, | |
297 mojo::InterfaceRequest<mus::mojom::WindowTreeClient> request) override { | |
298 PDFView* pdf_view = new PDFView( | |
299 &app_, connection, doc_, | |
300 base::Bind(&PDFViewerApplicationDelegate::OnPDFViewDestroyed, | |
301 base::Unretained(this))); | |
302 pdf_views_.push_back(pdf_view); | |
303 mus::WindowTreeConnection::Create( | |
304 pdf_view, std::move(request), | |
305 mus::WindowTreeConnection::CreateType::DONT_WAIT_FOR_EMBED); | |
306 } | |
307 | |
308 mojo::ApplicationImpl app_; | |
309 std::string data_; | |
310 std::vector<PDFView*> pdf_views_; | |
311 FPDF_DOCUMENT doc_; | |
312 bool is_destroying_; | |
313 mojo::Callback<void()> destruct_callback_; | |
314 | |
315 DISALLOW_COPY_AND_ASSIGN(PDFViewerApplicationDelegate); | |
316 }; | |
317 | |
318 class ContentHandlerImpl : public mojo::shell::mojom::ContentHandler { | |
319 public: | |
320 ContentHandlerImpl( | |
321 mojo::InterfaceRequest<mojo::shell::mojom::ContentHandler> request) | |
322 : binding_(this, std::move(request)) {} | |
323 ~ContentHandlerImpl() override {} | |
324 | |
325 private: | |
326 // mojo::shell::mojom::ContentHandler: | |
327 void StartApplication( | |
328 mojo::ApplicationRequest request, | |
329 mojo::URLResponsePtr response, | |
330 const mojo::Callback<void()>& destruct_callback) override { | |
331 new PDFViewerApplicationDelegate(std::move(request), std::move(response), | |
332 destruct_callback); | |
333 } | |
334 | |
335 mojo::StrongBinding<mojo::shell::mojom::ContentHandler> binding_; | |
336 | |
337 DISALLOW_COPY_AND_ASSIGN(ContentHandlerImpl); | |
338 }; | |
339 | |
340 class PDFViewer | |
341 : public mojo::ShellClient, | |
342 public mojo::InterfaceFactory<mojo::shell::mojom::ContentHandler> { | |
343 public: | |
344 PDFViewer() { | |
345 v8::V8::InitializeICU(); | |
346 FPDF_InitLibrary(); | |
347 } | |
348 | |
349 ~PDFViewer() override { FPDF_DestroyLibrary(); } | |
350 | |
351 private: | |
352 // mojo::ShellClient: | |
353 void Initialize(mojo::Shell* shell, const std::string& url, | |
354 uint32_t id) override { | |
355 tracing_.Initialize(shell, url); | |
356 } | |
357 | |
358 bool AcceptConnection(mojo::Connection* connection) override { | |
359 connection->AddService(this); | |
360 return true; | |
361 } | |
362 | |
363 // InterfaceFactory<ContentHandler>: | |
364 void Create(mojo::Connection* connection, | |
365 mojo::InterfaceRequest<mojo::shell::mojom::ContentHandler> | |
366 request) override { | |
367 new ContentHandlerImpl(std::move(request)); | |
368 } | |
369 | |
370 mojo::TracingImpl tracing_; | |
371 | |
372 DISALLOW_COPY_AND_ASSIGN(PDFViewer); | |
373 }; | |
374 } // namespace | |
375 } // namespace pdf_viewer | |
376 | |
377 MojoResult MojoMain(MojoHandle application_request) { | |
378 mojo::ApplicationRunner runner(new pdf_viewer::PDFViewer()); | |
379 return runner.Run(application_request); | |
380 } | |
OLD | NEW |