| 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 |