| OLD | NEW |
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include <math.h> | 5 #include "ppapi/c/dev/ppb_file_chooser_dev.h" |
| 6 #include <stdio.h> // FIXME(brettw) erase me. | |
| 7 #ifndef _WIN32 | |
| 8 #include <sys/time.h> | |
| 9 #endif | |
| 10 #include <time.h> | |
| 11 | |
| 12 #include <algorithm> | |
| 13 | |
| 14 #include "ppapi/c/dev/ppp_printing_dev.h" | |
| 15 #include "ppapi/c/pp_errors.h" | |
| 16 #include "ppapi/c/pp_input_event.h" | 6 #include "ppapi/c/pp_input_event.h" |
| 17 #include "ppapi/c/pp_rect.h" | |
| 18 #include "ppapi/cpp/completion_callback.h" | 7 #include "ppapi/cpp/completion_callback.h" |
| 19 #include "ppapi/cpp/dev/scriptable_object_deprecated.h" | 8 #include "ppapi/cpp/dev/file_chooser_dev.h" |
| 20 #include "ppapi/cpp/graphics_2d.h" | 9 #include "ppapi/cpp/dev/file_ref_dev.h" |
| 21 #include "ppapi/cpp/image_data.h" | |
| 22 #include "ppapi/cpp/instance.h" | 10 #include "ppapi/cpp/instance.h" |
| 23 #include "ppapi/cpp/module.h" | 11 #include "ppapi/cpp/module.h" |
| 24 #include "ppapi/cpp/rect.h" | |
| 25 #include "ppapi/cpp/url_loader.h" | |
| 26 #include "ppapi/cpp/url_request_info.h" | |
| 27 #include "ppapi/cpp/var.h" | 12 #include "ppapi/cpp/var.h" |
| 28 | 13 |
| 29 static const int kStepsPerCircle = 800; | 14 class MyInstance : public pp::Instance { |
| 30 | |
| 31 void FlushCallback(void* data, int32_t result); | |
| 32 | |
| 33 void FillRect(pp::ImageData* image, int left, int top, int width, int height, | |
| 34 uint32_t color) { | |
| 35 for (int y = std::max(0, top); | |
| 36 y < std::min(image->size().height() - 1, top + height); | |
| 37 y++) { | |
| 38 for (int x = std::max(0, left); | |
| 39 x < std::min(image->size().width() - 1, left + width); | |
| 40 x++) | |
| 41 *image->GetAddr32(pp::Point(x, y)) = color; | |
| 42 } | |
| 43 } | |
| 44 | |
| 45 class MyScriptableObject : public pp::deprecated::ScriptableObject { | |
| 46 public: | |
| 47 explicit MyScriptableObject(pp::Instance* instance) : instance_(instance) {} | |
| 48 | |
| 49 virtual bool HasMethod(const pp::Var& method, pp::Var* exception) { | |
| 50 return method.AsString() == "toString"; | |
| 51 } | |
| 52 | |
| 53 virtual bool HasProperty(const pp::Var& name, pp::Var* exception) { | |
| 54 if (name.is_string() && name.AsString() == "blah") | |
| 55 return true; | |
| 56 return false; | |
| 57 } | |
| 58 | |
| 59 virtual pp::Var GetProperty(const pp::Var& name, pp::Var* exception) { | |
| 60 if (name.is_string() && name.AsString() == "blah") | |
| 61 return pp::Var(instance_, new MyScriptableObject(instance_)); | |
| 62 return pp::Var(); | |
| 63 } | |
| 64 | |
| 65 virtual void GetAllPropertyNames(std::vector<pp::Var>* names, | |
| 66 pp::Var* exception) { | |
| 67 names->push_back("blah"); | |
| 68 } | |
| 69 | |
| 70 virtual pp::Var Call(const pp::Var& method, | |
| 71 const std::vector<pp::Var>& args, | |
| 72 pp::Var* exception) { | |
| 73 if (method.AsString() == "toString") | |
| 74 return pp::Var("hello world"); | |
| 75 return pp::Var(); | |
| 76 } | |
| 77 | |
| 78 private: | |
| 79 pp::Instance* instance_; | |
| 80 }; | |
| 81 | |
| 82 class MyFetcherClient { | |
| 83 public: | |
| 84 virtual void DidFetch(bool success, const std::string& data) = 0; | |
| 85 }; | |
| 86 | |
| 87 class MyFetcher { | |
| 88 public: | |
| 89 MyFetcher() : client_(NULL) { | |
| 90 callback_factory_.Initialize(this); | |
| 91 } | |
| 92 | |
| 93 void Start(const pp::Instance& instance, | |
| 94 const pp::Var& url, | |
| 95 MyFetcherClient* client) { | |
| 96 pp::URLRequestInfo request; | |
| 97 request.SetURL(url); | |
| 98 request.SetMethod("GET"); | |
| 99 | |
| 100 loader_ = pp::URLLoader(instance); | |
| 101 client_ = client; | |
| 102 | |
| 103 pp::CompletionCallback callback = | |
| 104 callback_factory_.NewCallback(&MyFetcher::DidOpen); | |
| 105 int rv = loader_.Open(request, callback); | |
| 106 if (rv != PP_ERROR_WOULDBLOCK) | |
| 107 callback.Run(rv); | |
| 108 } | |
| 109 | |
| 110 void StartWithOpenedLoader(const pp::URLLoader& loader, | |
| 111 MyFetcherClient* client) { | |
| 112 loader_ = loader; | |
| 113 client_ = client; | |
| 114 | |
| 115 ReadMore(); | |
| 116 } | |
| 117 | |
| 118 private: | |
| 119 void ReadMore() { | |
| 120 pp::CompletionCallback callback = | |
| 121 callback_factory_.NewCallback(&MyFetcher::DidRead); | |
| 122 int rv = loader_.ReadResponseBody(buf_, sizeof(buf_), callback); | |
| 123 if (rv != PP_ERROR_WOULDBLOCK) | |
| 124 callback.Run(rv); | |
| 125 } | |
| 126 | |
| 127 void DidOpen(int32_t result) { | |
| 128 if (result == PP_OK) { | |
| 129 ReadMore(); | |
| 130 } else { | |
| 131 DidFinish(result); | |
| 132 } | |
| 133 } | |
| 134 | |
| 135 void DidRead(int32_t result) { | |
| 136 if (result > 0) { | |
| 137 data_.append(buf_, result); | |
| 138 ReadMore(); | |
| 139 } else { | |
| 140 DidFinish(result); | |
| 141 } | |
| 142 } | |
| 143 | |
| 144 void DidFinish(int32_t result) { | |
| 145 if (client_) | |
| 146 client_->DidFetch(result == PP_OK, data_); | |
| 147 } | |
| 148 | |
| 149 pp::CompletionCallbackFactory<MyFetcher> callback_factory_; | |
| 150 pp::URLLoader loader_; | |
| 151 MyFetcherClient* client_; | |
| 152 char buf_[4096]; | |
| 153 std::string data_; | |
| 154 }; | |
| 155 | |
| 156 class MyInstance : public pp::Instance, public MyFetcherClient { | |
| 157 public: | 15 public: |
| 158 MyInstance(PP_Instance instance) | 16 MyInstance(PP_Instance instance) |
| 159 : pp::Instance(instance), | 17 : pp::Instance(instance) { |
| 160 time_at_last_check_(0.0), | 18 callback_factory_.Initialize(this); |
| 161 fetcher_(NULL), | |
| 162 width_(0), | |
| 163 height_(0), | |
| 164 animation_counter_(0), | |
| 165 print_settings_valid_(false) {} | |
| 166 | |
| 167 virtual ~MyInstance() { | |
| 168 if (fetcher_) { | |
| 169 delete fetcher_; | |
| 170 fetcher_ = NULL; | |
| 171 } | |
| 172 } | |
| 173 | |
| 174 virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]) { | |
| 175 return true; | |
| 176 } | |
| 177 | |
| 178 virtual bool HandleDocumentLoad(const pp::URLLoader& loader) { | |
| 179 fetcher_ = new MyFetcher(); | |
| 180 fetcher_->StartWithOpenedLoader(loader, this); | |
| 181 return true; | |
| 182 } | 19 } |
| 183 | 20 |
| 184 virtual bool HandleInputEvent(const PP_InputEvent& event) { | 21 virtual bool HandleInputEvent(const PP_InputEvent& event) { |
| 185 switch (event.type) { | 22 switch (event.type) { |
| 186 case PP_INPUTEVENT_TYPE_MOUSEDOWN: | 23 case PP_INPUTEVENT_TYPE_MOUSEDOWN: { |
| 187 //SayHello(); | 24 const PP_InputEvent_Mouse& mouse_event = event.u.mouse; |
| 25 if (mouse_event.button == PP_INPUTEVENT_MOUSEBUTTON_LEFT) |
| 26 ShowFileChooser(false); |
| 27 else if (mouse_event.button == PP_INPUTEVENT_MOUSEBUTTON_RIGHT) |
| 28 ShowFileChooser(true); |
| 29 else |
| 30 return false; |
| 31 |
| 188 return true; | 32 return true; |
| 189 case PP_INPUTEVENT_TYPE_MOUSEMOVE: | 33 } |
| 190 return true; | |
| 191 case PP_INPUTEVENT_TYPE_KEYDOWN: | |
| 192 return true; | |
| 193 default: | 34 default: |
| 194 return false; | 35 return false; |
| 195 } | 36 } |
| 196 } | 37 } |
| 197 | 38 |
| 198 virtual pp::Var GetInstanceObject() { | 39 private: |
| 199 return pp::Var(this, new MyScriptableObject(this)); | 40 void ShowFileChooser(bool multi_select) { |
| 41 RecreateConsole(); |
| 42 |
| 43 PP_FileChooserOptions_Dev options; |
| 44 options.mode = (multi_select ? PP_FILECHOOSERMODE_OPENMULTIPLE : |
| 45 PP_FILECHOOSERMODE_OPEN); |
| 46 options.accept_mime_types = (multi_select ? "" : "plain/text"); |
| 47 |
| 48 // Deleted in ShowSelectedFileNames(). |
| 49 pp::FileChooser_Dev* file_chooser = new pp::FileChooser_Dev( |
| 50 *this, options); |
| 51 file_chooser->Show(callback_factory_.NewCallback( |
| 52 &MyInstance::ShowSelectedFileNames, file_chooser)); |
| 200 } | 53 } |
| 201 | 54 |
| 202 pp::ImageData PaintImage(int width, int height) { | 55 void ShowSelectedFileNames(int32_t, pp::FileChooser_Dev* file_chooser) { |
| 203 pp::ImageData image(this, PP_IMAGEDATAFORMAT_BGRA_PREMUL, | 56 if (!file_chooser) |
| 204 pp::Size(width, height), false); | 57 return; |
| 205 if (image.is_null()) { | 58 |
| 206 printf("Couldn't allocate the image data: %d, %d\n", width, height); | 59 pp::FileRef_Dev file_ref = file_chooser->GetNextChosenFile(); |
| 207 return image; | 60 while (!file_ref.is_null()) { |
| 61 Log(file_ref.GetName()); |
| 62 file_ref = file_chooser->GetNextChosenFile(); |
| 208 } | 63 } |
| 209 | 64 |
| 210 // Fill with semitransparent gradient. | 65 delete file_chooser; |
| 211 for (int y = 0; y < image.size().height(); y++) { | |
| 212 char* row = &static_cast<char*>(image.data())[y * image.stride()]; | |
| 213 for (int x = 0; x < image.size().width(); x++) { | |
| 214 row[x * 4 + 0] = y; | |
| 215 row[x * 4 + 1] = y; | |
| 216 row[x * 4 + 2] = 0; | |
| 217 row[x * 4 + 3] = y; | |
| 218 } | |
| 219 } | |
| 220 | |
| 221 // Draw the orbiting box. | |
| 222 float radians = static_cast<float>(animation_counter_) / kStepsPerCircle * | |
| 223 2 * 3.14159265358979F; | |
| 224 | |
| 225 float radius = static_cast<float>(std::min(width, height)) / 2.0f - 3.0f; | |
| 226 int x = static_cast<int>(cos(radians) * radius + radius + 2); | |
| 227 int y = static_cast<int>(sin(radians) * radius + radius + 2); | |
| 228 | |
| 229 const uint32_t box_bgra = 0x80000000; // Alpha 50%. | |
| 230 FillRect(&image, x - 3, y - 3, 7, 7, box_bgra); | |
| 231 return image; | |
| 232 } | 66 } |
| 233 | 67 |
| 234 void Paint() { | 68 void RecreateConsole() { |
| 235 pp::ImageData image = PaintImage(width_, height_); | 69 pp::Var doc = GetWindowObject().GetProperty("document"); |
| 236 if (!image.is_null()) { | 70 pp::Var body = doc.GetProperty("body"); |
| 237 device_context_.ReplaceContents(&image); | 71 if (!console_.is_undefined()) |
| 238 device_context_.Flush(pp::CompletionCallback(&FlushCallback, this)); | 72 body.Call("removeChild", console_); |
| 239 } else { | 73 |
| 240 printf("NullImage: %d, %d\n", width_, height_); | 74 console_ = doc.Call("createElement", "pre"); |
| 241 } | 75 console_.SetProperty("id", "console"); |
| 76 console_.GetProperty("style").SetProperty("backgroundColor", "lightgray"); |
| 77 body.Call("appendChild", console_); |
| 242 } | 78 } |
| 243 | 79 |
| 244 virtual void DidChangeView(const pp::Rect& position, const pp::Rect& clip) { | |
| 245 if (position.size().width() == width_ && | |
| 246 position.size().height() == height_) | |
| 247 return; // We don't care about the position, only the size. | |
| 248 | |
| 249 width_ = position.size().width(); | |
| 250 height_ = position.size().height(); | |
| 251 printf("DidChangeView relevant change: width=%d height:%d\n", | |
| 252 width_, height_); | |
| 253 | |
| 254 device_context_ = pp::Graphics2D(this, pp::Size(width_, height_), false); | |
| 255 if (!BindGraphics(device_context_)) { | |
| 256 printf("Couldn't bind the device context\n"); | |
| 257 return; | |
| 258 } | |
| 259 | |
| 260 Paint(); | |
| 261 } | |
| 262 | |
| 263 void UpdateFps() { | |
| 264 // Time code doesn't currently compile on Windows, just skip FPS for now. | |
| 265 #ifndef _WIN32 | |
| 266 pp::Var window = GetWindowObject(); | |
| 267 pp::Var doc = window.GetProperty("document"); | |
| 268 pp::Var fps = doc.Call("getElementById", "fps"); | |
| 269 | |
| 270 struct timeval tv; | |
| 271 struct timezone tz = {0, 0}; | |
| 272 gettimeofday(&tv, &tz); | |
| 273 | |
| 274 double time_now = tv.tv_sec + tv.tv_usec / 1000000.0; | |
| 275 | |
| 276 if (animation_counter_ > 0) { | |
| 277 char fps_text[64]; | |
| 278 sprintf(fps_text, "%g fps", | |
| 279 kStepsPerCircle / (time_now - time_at_last_check_)); | |
| 280 fps.SetProperty("innerHTML", fps_text); | |
| 281 } | |
| 282 | |
| 283 time_at_last_check_ = time_now; | |
| 284 #endif | |
| 285 } | |
| 286 | |
| 287 // Print interfaces. | |
| 288 virtual PP_PrintOutputFormat_Dev* QuerySupportedPrintOutputFormats( | |
| 289 uint32_t* format_count) { | |
| 290 PP_PrintOutputFormat_Dev* format = | |
| 291 reinterpret_cast<PP_PrintOutputFormat_Dev*>( | |
| 292 pp::Module::Get()->core()->MemAlloc( | |
| 293 sizeof(PP_PrintOutputFormat_Dev))); | |
| 294 *format = PP_PRINTOUTPUTFORMAT_RASTER; | |
| 295 *format_count = 1; | |
| 296 return format; | |
| 297 } | |
| 298 | |
| 299 virtual int32_t PrintBegin(const PP_PrintSettings_Dev& print_settings) { | |
| 300 if (print_settings_.format != PP_PRINTOUTPUTFORMAT_RASTER) | |
| 301 return 0; | |
| 302 | |
| 303 print_settings_ = print_settings; | |
| 304 print_settings_valid_ = true; | |
| 305 return 1; | |
| 306 } | |
| 307 | |
| 308 virtual pp::Resource PrintPages( | |
| 309 const PP_PrintPageNumberRange_Dev* page_ranges, | |
| 310 uint32_t page_range_count) { | |
| 311 if (!print_settings_valid_) | |
| 312 return pp::Resource(); | |
| 313 | |
| 314 if (page_range_count != 1) | |
| 315 return pp::Resource(); | |
| 316 | |
| 317 // Check if the page numbers are valid. We returned 1 in PrintBegin so we | |
| 318 // only have 1 page to print. | |
| 319 if (page_ranges[0].first_page_number || page_ranges[0].last_page_number) { | |
| 320 return pp::Resource(); | |
| 321 } | |
| 322 | |
| 323 int width = static_cast<int>( | |
| 324 (print_settings_.printable_area.size.width / 72.0) * | |
| 325 print_settings_.dpi); | |
| 326 int height = static_cast<int>( | |
| 327 (print_settings_.printable_area.size.height / 72.0) * | |
| 328 print_settings_.dpi); | |
| 329 | |
| 330 return PaintImage(width, height); | |
| 331 } | |
| 332 | |
| 333 virtual void PrintEnd() { | |
| 334 print_settings_valid_ = false; | |
| 335 } | |
| 336 | |
| 337 void OnFlush() { | |
| 338 if (animation_counter_ % kStepsPerCircle == 0) | |
| 339 UpdateFps(); | |
| 340 animation_counter_++; | |
| 341 Paint(); | |
| 342 } | |
| 343 | |
| 344 private: | |
| 345 void Log(const pp::Var& var) { | 80 void Log(const pp::Var& var) { |
| 346 pp::Var doc = GetWindowObject().GetProperty("document"); | 81 pp::Var doc = GetWindowObject().GetProperty("document"); |
| 347 if (console_.is_undefined()) { | |
| 348 pp::Var body = doc.GetProperty("body"); | |
| 349 console_ = doc.Call("createElement", "pre"); | |
| 350 console_.GetProperty("style").SetProperty("backgroundColor", "lightgray"); | |
| 351 body.Call("appendChild", console_); | |
| 352 } | |
| 353 console_.Call("appendChild", doc.Call("createTextNode", var)); | 82 console_.Call("appendChild", doc.Call("createTextNode", var)); |
| 354 console_.Call("appendChild", doc.Call("createTextNode", "\n")); | 83 console_.Call("appendChild", doc.Call("createTextNode", "\n")); |
| 355 } | 84 } |
| 356 | 85 |
| 357 void SayHello() { | 86 pp::CompletionCallbackFactory<MyInstance> callback_factory_; |
| 358 pp::Var window = GetWindowObject(); | |
| 359 pp::Var doc = window.GetProperty("document"); | |
| 360 pp::Var body = doc.GetProperty("body"); | |
| 361 | |
| 362 pp::Var obj(this, new MyScriptableObject(this)); | |
| 363 | |
| 364 // Our object should have its toString method called. | |
| 365 Log("Testing MyScriptableObject::toString():"); | |
| 366 Log(obj); | |
| 367 | |
| 368 // body.appendChild(body) should throw an exception | |
| 369 Log("\nCalling body.appendChild(body):"); | |
| 370 pp::Var exception; | |
| 371 body.Call("appendChild", body, &exception); | |
| 372 Log(exception); | |
| 373 | |
| 374 Log("\nEnumeration of window properties:"); | |
| 375 std::vector<pp::Var> props; | |
| 376 window.GetAllPropertyNames(&props); | |
| 377 for (size_t i = 0; i < props.size(); ++i) | |
| 378 Log(props[i]); | |
| 379 | |
| 380 pp::Var location = window.GetProperty("location"); | |
| 381 pp::Var href = location.GetProperty("href"); | |
| 382 | |
| 383 if (!fetcher_) { | |
| 384 fetcher_ = new MyFetcher(); | |
| 385 fetcher_->Start(*this, href, this); | |
| 386 } | |
| 387 } | |
| 388 | |
| 389 void DidFetch(bool success, const std::string& data) { | |
| 390 Log("\nDownloaded location.href:"); | |
| 391 if (success) { | |
| 392 Log(data); | |
| 393 } else { | |
| 394 Log("Failed to download."); | |
| 395 } | |
| 396 delete fetcher_; | |
| 397 fetcher_ = NULL; | |
| 398 } | |
| 399 | |
| 400 pp::Var console_; | 87 pp::Var console_; |
| 401 pp::Graphics2D device_context_; | |
| 402 | |
| 403 double time_at_last_check_; | |
| 404 | |
| 405 MyFetcher* fetcher_; | |
| 406 | |
| 407 int width_; | |
| 408 int height_; | |
| 409 | |
| 410 // Incremented for each flush we get. | |
| 411 int animation_counter_; | |
| 412 bool print_settings_valid_; | |
| 413 PP_PrintSettings_Dev print_settings_; | |
| 414 }; | 88 }; |
| 415 | 89 |
| 416 void FlushCallback(void* data, int32_t result) { | |
| 417 static_cast<MyInstance*>(data)->OnFlush(); | |
| 418 } | |
| 419 | |
| 420 class MyModule : public pp::Module { | 90 class MyModule : public pp::Module { |
| 421 public: | 91 public: |
| 422 MyModule() : pp::Module() {} | 92 MyModule() : pp::Module() {} |
| 423 virtual ~MyModule() {} | 93 virtual ~MyModule() {} |
| 424 | 94 |
| 425 virtual pp::Instance* CreateInstance(PP_Instance instance) { | 95 virtual pp::Instance* CreateInstance(PP_Instance instance) { |
| 426 return new MyInstance(instance); | 96 return new MyInstance(instance); |
| 427 } | 97 } |
| 428 }; | 98 }; |
| 429 | 99 |
| 430 namespace pp { | 100 namespace pp { |
| 431 | 101 |
| 432 // Factory function for your specialization of the Module object. | 102 // Factory function for your specialization of the Module object. |
| 433 Module* CreateModule() { | 103 Module* CreateModule() { |
| 434 return new MyModule(); | 104 return new MyModule(); |
| 435 } | 105 } |
| 436 | 106 |
| 437 } // namespace pp | 107 } // namespace pp |
| OLD | NEW |