summaryrefslogtreecommitdiffstats
path: root/webkit
diff options
context:
space:
mode:
authorBen Murdoch <benm@google.com>2010-07-29 17:14:53 +0100
committerBen Murdoch <benm@google.com>2010-08-04 14:29:45 +0100
commitc407dc5cd9bdc5668497f21b26b09d988ab439de (patch)
tree7eaf8707c0309516bdb042ad976feedaf72b0bb1 /webkit
parent0998b1cdac5733f299c12d88bc31ef9c8035b8fa (diff)
downloadexternal_chromium-c407dc5cd9bdc5668497f21b26b09d988ab439de.zip
external_chromium-c407dc5cd9bdc5668497f21b26b09d988ab439de.tar.gz
external_chromium-c407dc5cd9bdc5668497f21b26b09d988ab439de.tar.bz2
Merge Chromium src@r53293
Change-Id: Ia79acf8670f385cee48c45b0a75371d8e950af34
Diffstat (limited to 'webkit')
-rw-r--r--webkit/glue/DEPS11
-rw-r--r--webkit/glue/alt_error_page_resource_fetcher.cc51
-rw-r--r--webkit/glue/alt_error_page_resource_fetcher.h59
-rw-r--r--webkit/glue/bookmarklet_unittest.cc76
-rw-r--r--webkit/glue/context_menu.h129
-rw-r--r--webkit/glue/context_menu_unittest.cc67
-rw-r--r--webkit/glue/cpp_binding_example.cc123
-rw-r--r--webkit/glue/cpp_binding_example.h78
-rw-r--r--webkit/glue/cpp_bound_class.cc330
-rw-r--r--webkit/glue/cpp_bound_class.h181
-rw-r--r--webkit/glue/cpp_bound_class_unittest.cc288
-rw-r--r--webkit/glue/cpp_variant.cc268
-rw-r--r--webkit/glue/cpp_variant.h110
-rw-r--r--webkit/glue/cpp_variant_unittest.cc426
-rw-r--r--webkit/glue/devtools_message_data.cc30
-rw-r--r--webkit/glue/devtools_message_data.h25
-rw-r--r--webkit/glue/devtools_strings.grd154
-rw-r--r--webkit/glue/dom_operations.cc619
-rw-r--r--webkit/glue/dom_operations.h144
-rw-r--r--webkit/glue/dom_operations_unittest.cc184
-rw-r--r--webkit/glue/dom_serializer_unittest.cc850
-rw-r--r--webkit/glue/form_data.h41
-rw-r--r--webkit/glue/form_field.cc103
-rw-r--r--webkit/glue/form_field.h73
-rw-r--r--webkit/glue/ftp_directory_listing_response_delegate.cc159
-rw-r--r--webkit/glue/ftp_directory_listing_response_delegate.h68
-rw-r--r--webkit/glue/glue_serialize.cc473
-rw-r--r--webkit/glue/glue_serialize.h31
-rw-r--r--webkit/glue/glue_serialize_unittest.cc200
-rw-r--r--webkit/glue/iframe_redirect_unittest.cc48
-rw-r--r--webkit/glue/image_decoder.cc41
-rw-r--r--webkit/glue/image_decoder.h37
-rw-r--r--webkit/glue/image_resource_fetcher.cc54
-rw-r--r--webkit/glue/image_resource_fetcher.h63
-rw-r--r--webkit/glue/inspector_strings.grd640
-rw-r--r--webkit/glue/media/buffered_data_source.cc1052
-rw-r--r--webkit/glue/media/buffered_data_source.h413
-rw-r--r--webkit/glue/media/buffered_data_source_unittest.cc934
-rw-r--r--webkit/glue/media/media_resource_loader_bridge_factory.cc76
-rw-r--r--webkit/glue/media/media_resource_loader_bridge_factory.h76
-rw-r--r--webkit/glue/media/media_resource_loader_bridge_factory_unittest.cc44
-rw-r--r--webkit/glue/media/mock_media_resource_loader_bridge_factory.h36
-rw-r--r--webkit/glue/media/simple_data_source.cc236
-rw-r--r--webkit/glue/media/simple_data_source.h125
-rw-r--r--webkit/glue/media/simple_data_source_unittest.cc252
-rw-r--r--webkit/glue/media/video_renderer_impl.cc321
-rw-r--r--webkit/glue/media/video_renderer_impl.h122
-rw-r--r--webkit/glue/media/web_video_renderer.h39
-rw-r--r--webkit/glue/mimetype_unittest.cc91
-rw-r--r--webkit/glue/mock_resource_loader_bridge.h42
-rw-r--r--webkit/glue/multipart_response_delegate.cc373
-rw-r--r--webkit/glue/multipart_response_delegate.h147
-rw-r--r--webkit/glue/multipart_response_delegate_unittest.cc638
-rw-r--r--webkit/glue/npruntime_util.cc51
-rw-r--r--webkit/glue/npruntime_util.h21
-rw-r--r--webkit/glue/password_form.h168
-rw-r--r--webkit/glue/password_form_dom_manager.cc70
-rw-r--r--webkit/glue/password_form_dom_manager.h63
-rw-r--r--webkit/glue/plugins/DEPS4
-rw-r--r--webkit/glue/plugins/carbon_plugin_window_tracker_mac.cc55
-rw-r--r--webkit/glue/plugins/carbon_plugin_window_tracker_mac.h51
-rw-r--r--webkit/glue/plugins/coregraphics_private_symbols_mac.h27
-rw-r--r--webkit/glue/plugins/default_plugin_shared.h31
-rw-r--r--webkit/glue/plugins/gtk_plugin_container.cc85
-rw-r--r--webkit/glue/plugins/gtk_plugin_container.h26
-rw-r--r--webkit/glue/plugins/gtk_plugin_container_manager.cc147
-rw-r--r--webkit/glue/plugins/gtk_plugin_container_manager.h56
-rw-r--r--webkit/glue/plugins/mac_accelerated_surface_container.cc158
-rw-r--r--webkit/glue/plugins/mac_accelerated_surface_container.h110
-rw-r--r--webkit/glue/plugins/mac_accelerated_surface_container_manager.cc103
-rw-r--r--webkit/glue/plugins/mac_accelerated_surface_container_manager.h78
-rw-r--r--webkit/glue/plugins/npapi_extension_thunk.cc551
-rw-r--r--webkit/glue/plugins/npapi_extension_thunk.h23
-rw-r--r--webkit/glue/plugins/pepper_buffer.cc116
-rw-r--r--webkit/glue/plugins/pepper_buffer.h55
-rw-r--r--webkit/glue/plugins/pepper_device_context_2d.cc553
-rw-r--r--webkit/glue/plugins/pepper_device_context_2d.h175
-rw-r--r--webkit/glue/plugins/pepper_directory_reader.cc68
-rw-r--r--webkit/glue/plugins/pepper_directory_reader.h37
-rw-r--r--webkit/glue/plugins/pepper_event_conversion.cc235
-rw-r--r--webkit/glue/plugins/pepper_event_conversion.h26
-rw-r--r--webkit/glue/plugins/pepper_file_chooser.cc88
-rw-r--r--webkit/glue/plugins/pepper_file_chooser.h40
-rw-r--r--webkit/glue/plugins/pepper_file_io.cc255
-rw-r--r--webkit/glue/plugins/pepper_file_io.h69
-rw-r--r--webkit/glue/plugins/pepper_file_ref.cc169
-rw-r--r--webkit/glue/plugins/pepper_file_ref.h55
-rw-r--r--webkit/glue/plugins/pepper_file_system.cc59
-rw-r--r--webkit/glue/plugins/pepper_file_system.h21
-rw-r--r--webkit/glue/plugins/pepper_font.cc99
-rw-r--r--webkit/glue/plugins/pepper_font.h39
-rw-r--r--webkit/glue/plugins/pepper_image_data.cc159
-rw-r--r--webkit/glue/plugins/pepper_image_data.h116
-rw-r--r--webkit/glue/plugins/pepper_plugin_delegate.h59
-rw-r--r--webkit/glue/plugins/pepper_plugin_instance.cc828
-rw-r--r--webkit/glue/plugins/pepper_plugin_instance.h210
-rw-r--r--webkit/glue/plugins/pepper_plugin_module.cc362
-rw-r--r--webkit/glue/plugins/pepper_plugin_module.h102
-rw-r--r--webkit/glue/plugins/pepper_private.cc38
-rw-r--r--webkit/glue/plugins/pepper_private.h23
-rw-r--r--webkit/glue/plugins/pepper_resource.cc19
-rw-r--r--webkit/glue/plugins/pepper_resource.h135
-rw-r--r--webkit/glue/plugins/pepper_resource_tracker.cc61
-rw-r--r--webkit/glue/plugins/pepper_resource_tracker.h75
-rw-r--r--webkit/glue/plugins/pepper_scrollbar.cc228
-rw-r--r--webkit/glue/plugins/pepper_scrollbar.h64
-rw-r--r--webkit/glue/plugins/pepper_string.h30
-rw-r--r--webkit/glue/plugins/pepper_url_loader.cc299
-rw-r--r--webkit/glue/plugins/pepper_url_loader.h89
-rw-r--r--webkit/glue/plugins/pepper_url_request_info.cc220
-rw-r--r--webkit/glue/plugins/pepper_url_request_info.h83
-rw-r--r--webkit/glue/plugins/pepper_url_response_info.cc113
-rw-r--r--webkit/glue/plugins/pepper_url_response_info.h47
-rw-r--r--webkit/glue/plugins/pepper_var.cc857
-rw-r--r--webkit/glue/plugins/pepper_var.h46
-rw-r--r--webkit/glue/plugins/pepper_webplugin_impl.cc196
-rw-r--r--webkit/glue/plugins/pepper_webplugin_impl.h98
-rw-r--r--webkit/glue/plugins/pepper_widget.cc91
-rw-r--r--webkit/glue/plugins/pepper_widget.h53
-rw-r--r--webkit/glue/plugins/plugin_constants_win.h41
-rw-r--r--webkit/glue/plugins/plugin_host.cc1088
-rw-r--r--webkit/glue/plugins/plugin_host.h63
-rw-r--r--webkit/glue/plugins/plugin_instance.cc632
-rw-r--r--webkit/glue/plugins/plugin_instance.h360
-rw-r--r--webkit/glue/plugins/plugin_instance_mac.mm133
-rw-r--r--webkit/glue/plugins/plugin_lib.cc315
-rw-r--r--webkit/glue/plugins/plugin_lib.h120
-rw-r--r--webkit/glue/plugins/plugin_lib_mac.mm346
-rw-r--r--webkit/glue/plugins/plugin_lib_posix.cc255
-rw-r--r--webkit/glue/plugins/plugin_lib_unittest.cc151
-rw-r--r--webkit/glue/plugins/plugin_lib_win.cc41
-rw-r--r--webkit/glue/plugins/plugin_list.cc460
-rw-r--r--webkit/glue/plugins/plugin_list.h275
-rw-r--r--webkit/glue/plugins/plugin_list_mac.mm107
-rw-r--r--webkit/glue/plugins/plugin_list_posix.cc267
-rw-r--r--webkit/glue/plugins/plugin_list_win.cc404
-rw-r--r--webkit/glue/plugins/plugin_stream.cc254
-rw-r--r--webkit/glue/plugins/plugin_stream.h146
-rw-r--r--webkit/glue/plugins/plugin_stream_posix.cc74
-rw-r--r--webkit/glue/plugins/plugin_stream_url.cc106
-rw-r--r--webkit/glue/plugins/plugin_stream_url.h69
-rw-r--r--webkit/glue/plugins/plugin_stream_win.cc96
-rw-r--r--webkit/glue/plugins/plugin_string_stream.cc37
-rw-r--r--webkit/glue/plugins/plugin_string_stream.h39
-rw-r--r--webkit/glue/plugins/plugin_stubs.cc30
-rw-r--r--webkit/glue/plugins/plugin_switches.cc15
-rw-r--r--webkit/glue/plugins/plugin_switches.h15
-rw-r--r--webkit/glue/plugins/plugin_web_event_converter_mac.h66
-rw-r--r--webkit/glue/plugins/plugin_web_event_converter_mac.mm396
-rw-r--r--webkit/glue/plugins/ppb_private.h21
-rw-r--r--webkit/glue/plugins/quickdraw_drawing_manager_mac.cc154
-rw-r--r--webkit/glue/plugins/quickdraw_drawing_manager_mac.h83
-rw-r--r--webkit/glue/plugins/test/Info.plist46
-rw-r--r--webkit/glue/plugins/test/npapi_constants.cc10
-rw-r--r--webkit/glue/plugins/test/npapi_constants.h19
-rw-r--r--webkit/glue/plugins/test/npapi_test.cc80
-rw-r--r--webkit/glue/plugins/test/npapi_test.def6
-rw-r--r--webkit/glue/plugins/test/npapi_test.rc102
-rw-r--r--webkit/glue/plugins/test/plugin_arguments_test.cc68
-rw-r--r--webkit/glue/plugins/test/plugin_arguments_test.h43
-rw-r--r--webkit/glue/plugins/test/plugin_client.cc230
-rw-r--r--webkit/glue/plugins/test/plugin_client.h45
-rw-r--r--webkit/glue/plugins/test/plugin_create_instance_in_paint.cc76
-rw-r--r--webkit/glue/plugins/test/plugin_create_instance_in_paint.h33
-rw-r--r--webkit/glue/plugins/test/plugin_delete_plugin_in_stream_test.cc45
-rw-r--r--webkit/glue/plugins/test/plugin_delete_plugin_in_stream_test.h30
-rw-r--r--webkit/glue/plugins/test/plugin_get_javascript_url2_test.cc134
-rw-r--r--webkit/glue/plugins/test/plugin_get_javascript_url2_test.h38
-rw-r--r--webkit/glue/plugins/test/plugin_get_javascript_url_test.cc210
-rw-r--r--webkit/glue/plugins/test/plugin_get_javascript_url_test.h47
-rw-r--r--webkit/glue/plugins/test/plugin_geturl_test.cc374
-rw-r--r--webkit/glue/plugins/test/plugin_geturl_test.h55
-rw-r--r--webkit/glue/plugins/test/plugin_javascript_open_popup.cc103
-rw-r--r--webkit/glue/plugins/test/plugin_javascript_open_popup.h47
-rw-r--r--webkit/glue/plugins/test/plugin_new_fails_test.cc18
-rw-r--r--webkit/glue/plugins/test/plugin_new_fails_test.h21
-rw-r--r--webkit/glue/plugins/test/plugin_npobject_lifetime_test.cc170
-rw-r--r--webkit/glue/plugins/test/plugin_npobject_lifetime_test.h81
-rw-r--r--webkit/glue/plugins/test/plugin_npobject_proxy_test.cc51
-rw-r--r--webkit/glue/plugins/test/plugin_npobject_proxy_test.h27
-rw-r--r--webkit/glue/plugins/test/plugin_private_test.cc57
-rw-r--r--webkit/glue/plugins/test/plugin_private_test.h25
-rw-r--r--webkit/glue/plugins/test/plugin_schedule_timer_test.cc116
-rw-r--r--webkit/glue/plugins/test/plugin_schedule_timer_test.h70
-rw-r--r--webkit/glue/plugins/test/plugin_test.cc150
-rw-r--r--webkit/glue/plugins/test/plugin_test.h131
-rw-r--r--webkit/glue/plugins/test/plugin_test_factory.cc97
-rw-r--r--webkit/glue/plugins/test/plugin_test_factory.h22
-rw-r--r--webkit/glue/plugins/test/plugin_thread_async_call_test.cc116
-rw-r--r--webkit/glue/plugins/test/plugin_thread_async_call_test.h38
-rw-r--r--webkit/glue/plugins/test/plugin_window_size_test.cc55
-rw-r--r--webkit/glue/plugins/test/plugin_window_size_test.h24
-rw-r--r--webkit/glue/plugins/test/plugin_windowed_test.cc139
-rw-r--r--webkit/glue/plugins/test/plugin_windowed_test.h33
-rw-r--r--webkit/glue/plugins/test/plugin_windowless_test.cc260
-rw-r--r--webkit/glue/plugins/test/plugin_windowless_test.h35
-rw-r--r--webkit/glue/plugins/test/resource.h15
-rw-r--r--webkit/glue/plugins/webplugin.cc23
-rw-r--r--webkit/glue/plugins/webplugin.h195
-rw-r--r--webkit/glue/plugins/webplugin_2d_device_delegate.h57
-rw-r--r--webkit/glue/plugins/webplugin_3d_device_delegate.h101
-rw-r--r--webkit/glue/plugins/webplugin_audio_device_delegate.h56
-rw-r--r--webkit/glue/plugins/webplugin_delegate.h166
-rw-r--r--webkit/glue/plugins/webplugin_delegate_impl.cc268
-rw-r--r--webkit/glue/plugins/webplugin_delegate_impl.h490
-rw-r--r--webkit/glue/plugins/webplugin_delegate_impl_gtk.cc709
-rw-r--r--webkit/glue/plugins/webplugin_delegate_impl_mac.mm1160
-rw-r--r--webkit/glue/plugins/webplugin_delegate_impl_win.cc1397
-rw-r--r--webkit/glue/plugins/webplugin_file_delegate.h35
-rw-r--r--webkit/glue/plugins/webplugin_impl.cc1305
-rw-r--r--webkit/glue/plugins/webplugin_impl.h332
-rw-r--r--webkit/glue/plugins/webplugin_impl_unittest.cc232
-rw-r--r--webkit/glue/plugins/webplugin_page_delegate.h69
-rw-r--r--webkit/glue/plugins/webplugin_print_delegate.h49
-rw-r--r--webkit/glue/plugins/webplugininfo.h47
-rw-r--r--webkit/glue/plugins/webview_plugin.cc142
-rw-r--r--webkit/glue/plugins/webview_plugin.h107
-rw-r--r--webkit/glue/regular_expression_unittest.cc105
-rw-r--r--webkit/glue/resource_fetcher.cc129
-rw-r--r--webkit/glue/resource_fetcher.h117
-rw-r--r--webkit/glue/resource_fetcher_unittest.cc234
-rw-r--r--webkit/glue/resource_loader_bridge.cc68
-rw-r--r--webkit/glue/resource_loader_bridge.h345
-rw-r--r--webkit/glue/resource_type.h59
-rw-r--r--webkit/glue/resources/README.txt97
-rw-r--r--webkit/glue/resources/aliasb.curbin0 -> 326 bytes
-rw-r--r--webkit/glue/resources/broken-image.gifbin0 -> 165 bytes
-rw-r--r--webkit/glue/resources/cell.curbin0 -> 326 bytes
-rw-r--r--webkit/glue/resources/col_resize.curbin0 -> 326 bytes
-rw-r--r--webkit/glue/resources/copy.curbin0 -> 326 bytes
-rw-r--r--webkit/glue/resources/dash.pngbin0 -> 122 bytes
-rw-r--r--webkit/glue/resources/linux-checkbox-disabled-indeterminate.pngbin0 -> 3914 bytes
-rw-r--r--webkit/glue/resources/linux-checkbox-disabled-off.pngbin0 -> 2850 bytes
-rw-r--r--webkit/glue/resources/linux-checkbox-disabled-on.pngbin0 -> 3015 bytes
-rw-r--r--webkit/glue/resources/linux-checkbox-indeterminate.pngbin0 -> 3098 bytes
-rw-r--r--webkit/glue/resources/linux-checkbox-off.pngbin0 -> 3024 bytes
-rw-r--r--webkit/glue/resources/linux-checkbox-on.pngbin0 -> 3165 bytes
-rw-r--r--webkit/glue/resources/linux-progress-bar.pngbin0 -> 182 bytes
-rw-r--r--webkit/glue/resources/linux-progress-border-left.pngbin0 -> 148 bytes
-rw-r--r--webkit/glue/resources/linux-progress-border-right.pngbin0 -> 146 bytes
-rw-r--r--webkit/glue/resources/linux-progress-value.pngbin0 -> 167 bytes
-rw-r--r--webkit/glue/resources/linux-radio-disabled-off.pngbin0 -> 2910 bytes
-rw-r--r--webkit/glue/resources/linux-radio-disabled-on.pngbin0 -> 3015 bytes
-rw-r--r--webkit/glue/resources/linux-radio-off.pngbin0 -> 3133 bytes
-rw-r--r--webkit/glue/resources/linux-radio-on.pngbin0 -> 3148 bytes
-rw-r--r--webkit/glue/resources/media_pause.pngbin0 -> 153 bytes
-rw-r--r--webkit/glue/resources/media_play.pngbin0 -> 261 bytes
-rw-r--r--webkit/glue/resources/media_play_disabled.pngbin0 -> 237 bytes
-rw-r--r--webkit/glue/resources/media_slider_thumb.pngbin0 -> 324 bytes
-rw-r--r--webkit/glue/resources/media_sound_disabled.pngbin0 -> 307 bytes
-rw-r--r--webkit/glue/resources/media_sound_full.pngbin0 -> 461 bytes
-rw-r--r--webkit/glue/resources/media_sound_none.pngbin0 -> 301 bytes
-rw-r--r--webkit/glue/resources/media_volume_slider_thumb.pngbin0 -> 100 bytes
-rw-r--r--webkit/glue/resources/pan_east.curbin0 -> 4286 bytes
-rw-r--r--webkit/glue/resources/pan_icon.pngbin0 -> 238 bytes
-rw-r--r--webkit/glue/resources/pan_middle.curbin0 -> 4286 bytes
-rw-r--r--webkit/glue/resources/pan_north.curbin0 -> 4286 bytes
-rw-r--r--webkit/glue/resources/pan_north_east.curbin0 -> 4286 bytes
-rw-r--r--webkit/glue/resources/pan_north_west.curbin0 -> 4286 bytes
-rw-r--r--webkit/glue/resources/pan_south.curbin0 -> 4286 bytes
-rw-r--r--webkit/glue/resources/pan_south_east.curbin0 -> 4286 bytes
-rw-r--r--webkit/glue/resources/pan_south_west.curbin0 -> 4286 bytes
-rw-r--r--webkit/glue/resources/pan_west.curbin0 -> 4286 bytes
-rw-r--r--webkit/glue/resources/row_resize.curbin0 -> 326 bytes
-rw-r--r--webkit/glue/resources/search_cancel.pngbin0 -> 908 bytes
-rw-r--r--webkit/glue/resources/search_cancel_pressed.pngbin0 -> 895 bytes
-rw-r--r--webkit/glue/resources/search_magnifier.pngbin0 -> 1028 bytes
-rw-r--r--webkit/glue/resources/search_magnifier_results.pngbin0 -> 1224 bytes
-rw-r--r--webkit/glue/resources/textarea_resize_corner.pngbin0 -> 195 bytes
-rw-r--r--webkit/glue/resources/vertical_text.curbin0 -> 326 bytes
-rw-r--r--webkit/glue/resources/webkit_strings_am.xtb43
-rw-r--r--webkit/glue/resources/webkit_strings_ar.xtb43
-rw-r--r--webkit/glue/resources/webkit_strings_bg.xtb43
-rw-r--r--webkit/glue/resources/webkit_strings_bn.xtb43
-rw-r--r--webkit/glue/resources/webkit_strings_ca.xtb43
-rw-r--r--webkit/glue/resources/webkit_strings_cs.xtb43
-rw-r--r--webkit/glue/resources/webkit_strings_da.xtb43
-rw-r--r--webkit/glue/resources/webkit_strings_de.xtb43
-rw-r--r--webkit/glue/resources/webkit_strings_el.xtb43
-rw-r--r--webkit/glue/resources/webkit_strings_en-GB.xtb43
-rw-r--r--webkit/glue/resources/webkit_strings_es-419.xtb43
-rw-r--r--webkit/glue/resources/webkit_strings_es.xtb43
-rw-r--r--webkit/glue/resources/webkit_strings_et.xtb43
-rw-r--r--webkit/glue/resources/webkit_strings_fi.xtb43
-rw-r--r--webkit/glue/resources/webkit_strings_fil.xtb43
-rw-r--r--webkit/glue/resources/webkit_strings_fr.xtb43
-rw-r--r--webkit/glue/resources/webkit_strings_gu.xtb43
-rw-r--r--webkit/glue/resources/webkit_strings_hi.xtb43
-rw-r--r--webkit/glue/resources/webkit_strings_hr.xtb43
-rw-r--r--webkit/glue/resources/webkit_strings_hu.xtb43
-rw-r--r--webkit/glue/resources/webkit_strings_id.xtb43
-rw-r--r--webkit/glue/resources/webkit_strings_it.xtb43
-rw-r--r--webkit/glue/resources/webkit_strings_iw.xtb43
-rw-r--r--webkit/glue/resources/webkit_strings_ja.xtb43
-rw-r--r--webkit/glue/resources/webkit_strings_kn.xtb43
-rw-r--r--webkit/glue/resources/webkit_strings_ko.xtb43
-rw-r--r--webkit/glue/resources/webkit_strings_lt.xtb43
-rw-r--r--webkit/glue/resources/webkit_strings_lv.xtb43
-rw-r--r--webkit/glue/resources/webkit_strings_ml.xtb43
-rw-r--r--webkit/glue/resources/webkit_strings_mr.xtb43
-rw-r--r--webkit/glue/resources/webkit_strings_nl.xtb43
-rw-r--r--webkit/glue/resources/webkit_strings_no.xtb43
-rw-r--r--webkit/glue/resources/webkit_strings_pl.xtb43
-rw-r--r--webkit/glue/resources/webkit_strings_pt-BR.xtb43
-rw-r--r--webkit/glue/resources/webkit_strings_pt-PT.xtb43
-rw-r--r--webkit/glue/resources/webkit_strings_ro.xtb43
-rw-r--r--webkit/glue/resources/webkit_strings_ru.xtb43
-rw-r--r--webkit/glue/resources/webkit_strings_sk.xtb43
-rw-r--r--webkit/glue/resources/webkit_strings_sl.xtb43
-rw-r--r--webkit/glue/resources/webkit_strings_sr.xtb43
-rw-r--r--webkit/glue/resources/webkit_strings_sv.xtb43
-rw-r--r--webkit/glue/resources/webkit_strings_sw.xtb43
-rw-r--r--webkit/glue/resources/webkit_strings_ta.xtb43
-rw-r--r--webkit/glue/resources/webkit_strings_te.xtb43
-rw-r--r--webkit/glue/resources/webkit_strings_th.xtb43
-rw-r--r--webkit/glue/resources/webkit_strings_tr.xtb43
-rw-r--r--webkit/glue/resources/webkit_strings_uk.xtb43
-rw-r--r--webkit/glue/resources/webkit_strings_vi.xtb43
-rw-r--r--webkit/glue/resources/webkit_strings_zh-CN.xtb43
-rw-r--r--webkit/glue/resources/webkit_strings_zh-TW.xtb43
-rw-r--r--webkit/glue/resources/zoom_in.curbin0 -> 326 bytes
-rw-r--r--webkit/glue/resources/zoom_out.curbin0 -> 326 bytes
-rw-r--r--webkit/glue/scoped_clipboard_writer_glue.h32
-rw-r--r--webkit/glue/simple_webmimeregistry_impl.cc117
-rw-r--r--webkit/glue/simple_webmimeregistry_impl.h33
-rw-r--r--webkit/glue/site_isolation_metrics.cc231
-rw-r--r--webkit/glue/site_isolation_metrics.h41
-rw-r--r--webkit/glue/unittest_test_server.h65
-rw-r--r--webkit/glue/webaccessibility.cc300
-rw-r--r--webkit/glue/webaccessibility.h187
-rw-r--r--webkit/glue/webclipboard_impl.cc231
-rw-r--r--webkit/glue/webclipboard_impl.h55
-rw-r--r--webkit/glue/webcookie.h77
-rw-r--r--webkit/glue/webcursor.cc194
-rw-r--r--webkit/glue/webcursor.h149
-rw-r--r--webkit/glue/webcursor_gtk.cc218
-rw-r--r--webkit/glue/webcursor_gtk_data.h253
-rw-r--r--webkit/glue/webcursor_mac.mm384
-rw-r--r--webkit/glue/webcursor_unittest.cc82
-rw-r--r--webkit/glue/webcursor_win.cc236
-rw-r--r--webkit/glue/webdropdata.cc52
-rw-r--r--webkit/glue/webdropdata.h73
-rw-r--r--webkit/glue/webdropdata_win.cc33
-rw-r--r--webkit/glue/webfilesystem_impl.cc154
-rw-r--r--webkit/glue/webfilesystem_impl.h54
-rw-r--r--webkit/glue/webframe_unittest.cc95
-rw-r--r--webkit/glue/webkit_glue.cc509
-rw-r--r--webkit/glue/webkit_glue.gypi420
-rw-r--r--webkit/glue/webkit_glue.h293
-rw-r--r--webkit/glue/webkit_glue_dummy.cc20
-rw-r--r--webkit/glue/webkit_glue_unittest.cc37
-rw-r--r--webkit/glue/webkit_resources.grd64
-rw-r--r--webkit/glue/webkit_strings.grd364
-rw-r--r--webkit/glue/webkitclient_impl.cc437
-rw-r--r--webkit/glue/webkitclient_impl.h80
-rw-r--r--webkit/glue/webmediaplayer_impl.cc711
-rw-r--r--webkit/glue/webmediaplayer_impl.h340
-rw-r--r--webkit/glue/webmenuitem.h39
-rw-r--r--webkit/glue/webmenurunner_mac.h70
-rw-r--r--webkit/glue/webmenurunner_mac.mm204
-rw-r--r--webkit/glue/webpasswordautocompletelistener_impl.cc197
-rw-r--r--webkit/glue/webpasswordautocompletelistener_impl.h86
-rw-r--r--webkit/glue/webpasswordautocompletelistener_unittest.cc291
-rw-r--r--webkit/glue/webpreferences.cc110
-rw-r--r--webkit/glue/webpreferences.h120
-rw-r--r--webkit/glue/websocketstreamhandle_bridge.h47
-rw-r--r--webkit/glue/websocketstreamhandle_delegate.h37
-rw-r--r--webkit/glue/websocketstreamhandle_impl.cc169
-rw-r--r--webkit/glue/websocketstreamhandle_impl.h34
-rw-r--r--webkit/glue/webthemeengine_impl_win.cc142
-rw-r--r--webkit/glue/webthemeengine_impl_win.h48
-rw-r--r--webkit/glue/weburlloader_impl.cc688
-rw-r--r--webkit/glue/weburlloader_impl.h37
-rw-r--r--webkit/glue/webview_unittest.cc28
-rw-r--r--webkit/glue/window_open_disposition.cc30
-rw-r--r--webkit/glue/window_open_disposition.h28
376 files changed, 49510 insertions, 0 deletions
diff --git a/webkit/glue/DEPS b/webkit/glue/DEPS
new file mode 100644
index 0000000..e59afec
--- /dev/null
+++ b/webkit/glue/DEPS
@@ -0,0 +1,11 @@
+include_rules = [
+ "+app",
+ "+media",
+ "+skia/ext",
+ "+skia/include",
+ "+webkit/tools/test_shell", # Needed for test shell tests.
+
+ # This is not actually a directory, but npruntime_util.cc includes a file
+ # from WebKit starting with this path in JSCore mode.
+ "+bindings/c",
+]
diff --git a/webkit/glue/alt_error_page_resource_fetcher.cc b/webkit/glue/alt_error_page_resource_fetcher.cc
new file mode 100644
index 0000000..4e1867d
--- /dev/null
+++ b/webkit/glue/alt_error_page_resource_fetcher.cc
@@ -0,0 +1,51 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/alt_error_page_resource_fetcher.h"
+
+#include "base/callback.h"
+#include "webkit/glue/resource_fetcher.h"
+
+using WebKit::WebFrame;
+using WebKit::WebURLError;
+using WebKit::WebURLResponse;
+
+namespace webkit_glue {
+
+// Number of seconds to wait for the alternate error page server. If it takes
+// too long, just use the local error page.
+static const int kDownloadTimeoutSec = 3;
+
+AltErrorPageResourceFetcher::AltErrorPageResourceFetcher(
+ const GURL& url,
+ WebFrame* frame,
+ const WebURLError& original_error,
+ Callback* callback)
+ : frame_(frame),
+ callback_(callback),
+ original_error_(original_error) {
+ fetcher_.reset(new ResourceFetcherWithTimeout(
+ url, frame, kDownloadTimeoutSec,
+ NewCallback(this, &AltErrorPageResourceFetcher::OnURLFetchComplete)));
+}
+
+AltErrorPageResourceFetcher::~AltErrorPageResourceFetcher() {
+}
+
+void AltErrorPageResourceFetcher::Cancel() {
+ fetcher_->Cancel();
+}
+
+void AltErrorPageResourceFetcher::OnURLFetchComplete(
+ const WebURLResponse& response,
+ const std::string& data) {
+ // A null response indicates a network error.
+ if (!response.isNull() && response.httpStatusCode() == 200) {
+ callback_->Run(frame_, original_error_, data);
+ } else {
+ callback_->Run(frame_, original_error_, std::string());
+ }
+}
+
+} // namespace webkit_glue
diff --git a/webkit/glue/alt_error_page_resource_fetcher.h b/webkit/glue/alt_error_page_resource_fetcher.h
new file mode 100644
index 0000000..86671df
--- /dev/null
+++ b/webkit/glue/alt_error_page_resource_fetcher.h
@@ -0,0 +1,59 @@
+// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_ALT_ERROR_PAGE_RESOURCE_FETCHER_H_
+#define WEBKIT_GLUE_ALT_ERROR_PAGE_RESOURCE_FETCHER_H_
+
+#include "base/callback.h"
+#include "base/scoped_ptr.h"
+#include "googleurl/src/gurl.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURLError.h"
+
+namespace WebKit {
+class WebFrame;
+class WebURLResponse;
+}
+
+namespace webkit_glue {
+class ResourceFetcherWithTimeout;
+
+// Used for downloading alternate dns error pages. Once downloading is done
+// (or fails), the webview delegate is notified.
+class AltErrorPageResourceFetcher {
+ public:
+ // This will be called when the alternative error page has been fetched,
+ // successfully or not. If there is a failure, the third parameter (the
+ // data) will be empty.
+ typedef Callback3<WebKit::WebFrame*, const WebKit::WebURLError&,
+ const std::string&>::Type Callback;
+
+ AltErrorPageResourceFetcher(const GURL& url,
+ WebKit::WebFrame* frame,
+ const WebKit::WebURLError& original_error,
+ Callback* callback);
+ ~AltErrorPageResourceFetcher();
+
+ // Stop any pending loads.
+ void Cancel();
+
+ private:
+ void OnURLFetchComplete(const WebKit::WebURLResponse& response,
+ const std::string& data);
+
+ // Does the actual fetching.
+ scoped_ptr<ResourceFetcherWithTimeout> fetcher_;
+
+ WebKit::WebFrame* frame_;
+ scoped_ptr<Callback> callback_;
+
+ // The error associated with this load. If there's an error talking with the
+ // alt error page server, we need this to complete the original load.
+ WebKit::WebURLError original_error_;
+
+ DISALLOW_COPY_AND_ASSIGN(AltErrorPageResourceFetcher);
+};
+
+} // namespace webkit_glue
+
+#endif // WEBKIT_GLUE_ALT_ERROR_PAGE_RESOURCE_FETCHER_H_
diff --git a/webkit/glue/bookmarklet_unittest.cc b/webkit/glue/bookmarklet_unittest.cc
new file mode 100644
index 0000000..5d8a364
--- /dev/null
+++ b/webkit/glue/bookmarklet_unittest.cc
@@ -0,0 +1,76 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/file_util.h"
+#include "base/message_loop.h"
+#include "base/path_service.h"
+#include "base/string_util.h"
+#include "googleurl/src/gurl.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/tools/test_shell/test_shell_test.h"
+
+namespace {
+
+class BookmarkletTest : public TestShellTest {
+ public:
+ virtual void SetUp() {
+ TestShellTest::SetUp();
+
+ test_shell_->LoadURL(GURL("data:text/html,start page"));
+ test_shell_->WaitTestFinished();
+ }
+};
+
+TEST_F(BookmarkletTest, Redirect) {
+ test_shell_->LoadURL(
+ GURL("javascript:location.href='data:text/plain,SUCCESS'"));
+ test_shell_->WaitTestFinished();
+ std::wstring text = test_shell_->GetDocumentText();
+ EXPECT_EQ(L"SUCCESS", text);
+}
+
+TEST_F(BookmarkletTest, RedirectVoided) {
+ // This test should be redundant with the Redirect test above. The point
+ // here is to emphasize that in either case the assignment to location during
+ // the evaluation of the script should suppress loading the script result.
+ // Here, because of the void() wrapping there is no script result.
+ test_shell_->LoadURL(
+ GURL("javascript:void(location.href='data:text/plain,SUCCESS')"));
+ test_shell_->WaitTestFinished();
+ std::wstring text = test_shell_->GetDocumentText();
+ EXPECT_EQ(L"SUCCESS", text);
+}
+
+TEST_F(BookmarkletTest, NonEmptyResult) {
+ std::wstring text;
+
+ // TODO(darin): This test fails in a JSC build. WebCore+JSC does not really
+ // need to support this usage until WebCore supports javascript: URLs that
+ // generate content (https://bugs.webkit.org/show_bug.cgi?id=14959). It is
+ // important to note that Safari does not support bookmarklets, and this is
+ // really an edge case. Our behavior with V8 is consistent with FF and IE.
+#if 0
+ test_shell_->LoadURL(L"javascript:false");
+ MessageLoop::current()->RunAllPending();
+ text = test_shell_->GetDocumentText();
+ EXPECT_EQ(L"false", text);
+#endif
+
+ test_shell_->LoadURL(GURL("javascript:'hello world'"));
+ MessageLoop::current()->RunAllPending();
+ text = test_shell_->GetDocumentText();
+ EXPECT_EQ(L"hello world", text);
+}
+
+TEST_F(BookmarkletTest, DocumentWrite) {
+ test_shell_->LoadURL(GURL(
+ "javascript:document.open();"
+ "document.write('hello world');"
+ "document.close()"));
+ MessageLoop::current()->RunAllPending();
+ std::wstring text = test_shell_->GetDocumentText();
+ EXPECT_EQ(L"hello world", text);
+}
+
+} // namespace
diff --git a/webkit/glue/context_menu.h b/webkit/glue/context_menu.h
new file mode 100644
index 0000000..764fb9d
--- /dev/null
+++ b/webkit/glue/context_menu.h
@@ -0,0 +1,129 @@
+// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_CONTEXT_MENU_H_
+#define WEBKIT_GLUE_CONTEXT_MENU_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/utf_string_conversions.h"
+#include "googleurl/src/gurl.h"
+#include "webkit/glue/webmenuitem.h"
+
+#include "third_party/WebKit/WebKit/chromium/public/WebContextMenuData.h"
+
+// Parameters structure for ViewHostMsg_ContextMenu.
+// FIXME(beng): This would be more useful in the future and more efficient
+// if the parameters here weren't so literally mapped to what
+// they contain for the ContextMenu task. It might be better
+// to make the string fields more generic so that this object
+// could be used for more contextual actions.
+struct ContextMenuParams {
+ // This is the type of Context Node that the context menu was invoked on.
+ WebKit::WebContextMenuData::MediaType media_type;
+
+ // These values represent the coordinates of the mouse when the context menu
+ // was invoked. Coords are relative to the associated RenderView's origin.
+ int x;
+ int y;
+
+ // This is the URL of the link that encloses the node the context menu was
+ // invoked on.
+ GURL link_url;
+
+ // The link URL to be used ONLY for "copy link address". We don't validate
+ // this field in the frontend process.
+ GURL unfiltered_link_url;
+
+ // This is the source URL for the element that the context menu was
+ // invoked on. Example of elements with source URLs are img, audio, and
+ // video.
+ GURL src_url;
+
+ // This is true if the context menu was invoked on a blocked image.
+ bool is_image_blocked;
+
+ // This is the URL of the top level page that the context menu was invoked
+ // on.
+ GURL page_url;
+
+ // This is the URL of the subframe that the context menu was invoked on.
+ GURL frame_url;
+
+ // These are the parameters for the media element that the context menu
+ // was invoked on.
+ int media_flags;
+
+ // This is the text of the selection that the context menu was invoked on.
+ std::wstring selection_text;
+
+ // The misspelled word under the cursor, if any. Used to generate the
+ // |dictionary_suggestions| list.
+ string16 misspelled_word;
+
+ // Suggested replacements for a misspelled word under the cursor.
+ // This vector gets populated in the render process host
+ // by intercepting ViewHostMsg_ContextMenu in ResourceMessageFilter
+ // and populating dictionary_suggestions if the type is EDITABLE
+ // and the misspelled_word is not empty.
+ std::vector<string16> dictionary_suggestions;
+
+ // If editable, flag for whether spell check is enabled or not.
+ bool spellcheck_enabled;
+
+ // Whether context is editable.
+ bool is_editable;
+
+#if defined(OS_MACOSX)
+ // Writing direction menu items.
+ // Currently only used on OS X.
+ int writing_direction_default;
+ int writing_direction_left_to_right;
+ int writing_direction_right_to_left;
+#endif // OS_MACOSX
+
+ // These flags indicate to the browser whether the renderer believes it is
+ // able to perform the corresponding action.
+ int edit_flags;
+
+ // The security info for the resource we are showing the menu on.
+ std::string security_info;
+
+ // The character encoding of the frame on which the menu is invoked.
+ std::string frame_charset;
+
+ std::vector<WebMenuItem> custom_items;
+
+ ContextMenuParams() {}
+
+ ContextMenuParams(const WebKit::WebContextMenuData& data)
+ : media_type(data.mediaType),
+ x(data.mousePosition.x),
+ y(data.mousePosition.y),
+ link_url(data.linkURL),
+ unfiltered_link_url(data.linkURL),
+ src_url(data.srcURL),
+ is_image_blocked(data.isImageBlocked),
+ page_url(data.pageURL),
+ frame_url(data.frameURL),
+ media_flags(data.mediaFlags),
+ selection_text(UTF16ToWideHack(data.selectedText)),
+ misspelled_word(data.misspelledWord),
+ spellcheck_enabled(data.isSpellCheckingEnabled),
+ is_editable(data.isEditable),
+#if defined(OS_MACOSX)
+ writing_direction_default(data.writingDirectionDefault),
+ writing_direction_left_to_right(data.writingDirectionLeftToRight),
+ writing_direction_right_to_left(data.writingDirectionRightToLeft),
+#endif // OS_MACOSX
+ edit_flags(data.editFlags),
+ security_info(data.securityInfo),
+ frame_charset(data.frameEncoding.utf8()) {
+ for (size_t i = 0; i < data.customItems.size(); ++i)
+ custom_items.push_back(WebMenuItem(data.customItems[i]));
+ }
+};
+
+#endif // WEBKIT_GLUE_CONTEXT_MENU_H_
diff --git a/webkit/glue/context_menu_unittest.cc b/webkit/glue/context_menu_unittest.cc
new file mode 100644
index 0000000..9cefc9f
--- /dev/null
+++ b/webkit/glue/context_menu_unittest.cc
@@ -0,0 +1,67 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Tests for displaying context menus in corner cases (and swallowing context
+// menu events when appropriate)
+
+#include <vector>
+
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/message_loop.h"
+#include "googleurl/src/gurl.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebView.h"
+#include "webkit/glue/webkit_glue.h"
+#include "webkit/tools/test_shell/test_shell_test.h"
+#include "webkit/tools/test_shell/test_webview_delegate.h"
+
+using WebKit::WebInputEvent;
+using WebKit::WebMouseEvent;
+using WebKit::WebView;
+
+// Right clicking inside on an iframe should produce a context menu
+class ContextMenuCapturing : public TestShellTest {
+ protected:
+ void SetUp() {
+ TestShellTest::SetUp();
+
+ iframes_data_dir_ = data_dir_;
+ iframes_data_dir_ = iframes_data_dir_.AppendASCII("test_shell");
+ iframes_data_dir_ = iframes_data_dir_.AppendASCII("iframes");
+ ASSERT_TRUE(file_util::PathExists(iframes_data_dir_));
+ }
+
+ FilePath iframes_data_dir_;
+};
+
+
+TEST_F(ContextMenuCapturing, ContextMenuCapturing) {
+ // Make sure we have no stored mouse event state
+ TestWebViewDelegate* test_delegate = test_shell_->delegate();
+ test_delegate->clear_captured_context_menu_events();
+ EXPECT_EQ(0U, test_delegate->captured_context_menu_events().size());
+
+ GURL test_url = GetTestURL(iframes_data_dir_, "testiframe.html");
+ test_shell_->LoadURL(test_url);
+ test_shell_->WaitTestFinished();
+
+ // Create a right click in the center of the iframe. (I'm hoping this will
+ // make this a bit more robust in case of some other formatting or other bug.)
+ WebMouseEvent mouse_event;
+ mouse_event.type = WebInputEvent::MouseDown;
+ mouse_event.button = WebMouseEvent::ButtonRight;
+ mouse_event.x = 250;
+ mouse_event.y = 250;
+ mouse_event.globalX = 250;
+ mouse_event.globalY = 250;
+
+ WebView* webview = test_shell_->webView();
+ webview->handleInputEvent(mouse_event);
+
+ // Now simulate the corresponding up event which should display the menu
+ mouse_event.type = WebInputEvent::MouseUp;
+ webview->handleInputEvent(mouse_event);
+
+ EXPECT_EQ(1U, test_delegate->captured_context_menu_events().size());
+}
diff --git a/webkit/glue/cpp_binding_example.cc b/webkit/glue/cpp_binding_example.cc
new file mode 100644
index 0000000..79e96e4
--- /dev/null
+++ b/webkit/glue/cpp_binding_example.cc
@@ -0,0 +1,123 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file contains the definition for CppBindingExample, which is used in
+// cpp_bound_class_unittest.
+
+#include "cpp_binding_example.h"
+
+namespace {
+
+class PropertyCallbackExample : public CppBoundClass::PropertyCallback {
+ public:
+ virtual bool GetValue(CppVariant* value) {
+ value->Set(value_);
+ return true;
+ }
+
+ virtual bool SetValue(const CppVariant& value) {
+ value_.Set(value);
+ return true;
+ }
+
+ private:
+ CppVariant value_;
+};
+
+}
+
+CppBindingExample::CppBindingExample() {
+ // Map properties. It's recommended, but not required, that the JavaScript
+ // names (used as the keys in this map) match the names of the member
+ // variables exposed through those names.
+ BindProperty("my_value", &my_value);
+ BindProperty("my_other_value", &my_other_value);
+
+ // Bind property with a callback.
+ BindProperty("my_value_with_callback", new PropertyCallbackExample());
+ // Bind property with a getter callback.
+ BindProperty("same", &CppBindingExample::same);
+
+ // Map methods. See comment above about names.
+ BindMethod("echoValue", &CppBindingExample::echoValue);
+ BindMethod("echoType", &CppBindingExample::echoType);
+ BindMethod("plus", &CppBindingExample::plus);
+
+ // The fallback method is called when a nonexistent method is called on an
+ // object. If none is specified, calling a nonexistent method causes an
+ // exception to be thrown and the JavaScript execution is stopped.
+ BindFallbackMethod(&CppBindingExample::fallbackMethod);
+
+ my_value.Set(10);
+ my_other_value.Set("Reinitialized!");
+}
+
+void CppBindingExample::echoValue(const CppArgumentList& args,
+ CppVariant* result) {
+ if (args.size() < 1) {
+ result->SetNull();
+ return;
+ }
+ result->Set(args[0]);
+}
+
+void CppBindingExample::echoType(const CppArgumentList& args,
+ CppVariant* result) {
+ if (args.size() < 1) {
+ result->SetNull();
+ return;
+ }
+ // Note that if args[0] is a string, the following assignment implicitly
+ // makes a copy of that string, which may have an undesirable impact on
+ // performance.
+ CppVariant arg1 = args[0];
+ if (arg1.isBool())
+ result->Set(true);
+ else if (arg1.isInt32())
+ result->Set(7);
+ else if (arg1.isDouble())
+ result->Set(3.14159);
+ else if (arg1.isString())
+ result->Set("Success!");
+}
+
+void CppBindingExample::plus(const CppArgumentList& args,
+ CppVariant* result) {
+ if (args.size() < 2) {
+ result->SetNull();
+ return;
+ }
+
+ CppVariant arg1 = args[0];
+ CppVariant arg2 = args[1];
+
+ if (!arg1.isNumber() || !arg2.isNumber()) {
+ result->SetNull();
+ return;
+ }
+
+ // The value of a CppVariant may be read directly from its NPVariant struct.
+ // (However, it should only be set using one of the Set() functions.)
+ double sum = 0.;
+ if (arg1.isDouble())
+ sum += arg1.value.doubleValue;
+ else if (arg1.isInt32())
+ sum += arg1.value.intValue;
+
+ if (arg2.isDouble())
+ sum += arg2.value.doubleValue;
+ else if (arg2.isInt32())
+ sum += arg2.value.intValue;
+
+ result->Set(sum);
+}
+
+void CppBindingExample::same(CppVariant* result) {
+ result->Set(42);
+}
+
+void CppBindingExample::fallbackMethod(const CppArgumentList& args,
+ CppVariant* result) {
+ printf("Error: unknown JavaScript method invoked.\n");
+}
diff --git a/webkit/glue/cpp_binding_example.h b/webkit/glue/cpp_binding_example.h
new file mode 100644
index 0000000..a663682
--- /dev/null
+++ b/webkit/glue/cpp_binding_example.h
@@ -0,0 +1,78 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/*
+ CppBindingExample class:
+ This provides an example of how to use the CppBoundClass to create methods
+ and properties that can be exposed to JavaScript by an appropriately built
+ embedding client. It is also used by the CppBoundClass unit test.
+
+ Typically, a class intended to be bound to JavaScript will define a
+ constructor, any methods and properties to be exposed, and optionally a
+ destructor. An embedding client can then bind the class to a JavaScript
+ object in a frame's window using the CppBoundClass::BindToJavascript() method,
+ generally called from the WebView delegate's WindowObjectCleared().
+
+ Once this class has been bound, say to the name "example", it might be called
+ from JavaScript in the following way:
+
+ <script>
+ if (window.example) {
+ document.writeln(example.echoValue(false));
+ document.writeln(example.echoType("Hello world!"));
+ document.writeln(example.plus(2, 3.1));
+
+ example.my_value = 15;
+ example.my_other_value = 2.1;
+ document.writeln(example.plus(example.my_value, example.my_other_value));
+ }
+ </script>
+*/
+
+#ifndef CPP_BINDING_EXAMPLE_H__
+#define CPP_BINDING_EXAMPLE_H__
+
+#include "webkit/glue/cpp_bound_class.h"
+
+class CppBindingExample : public CppBoundClass {
+ public:
+ // The default constructor initializes the property and method lists needed
+ // to bind this class to a JS object.
+ CppBindingExample();
+
+ //
+ // These public member variables and methods implement the methods and
+ // properties that will be exposed to JavaScript. If needed, the class could
+ // also contain other methods or variables, which will be hidden from JS
+ // as long as they're not mapped in the property and method lists created in
+ // the constructor.
+ //
+ // The signatures of any methods to be bound must match
+ // CppBoundClass::Callback.
+ //
+
+ // Returns the value that was passed in as its first (only) argument.
+ void echoValue(const CppArgumentList& args, CppVariant* result);
+
+ // Returns a hard-coded value of the same type (bool, number (double),
+ // string, or null) that was passed in as an argument.
+ void echoType(const CppArgumentList& args, CppVariant* result);
+
+ // Returns the sum of the (first) two arguments as a double, if they are both
+ // numbers (integers or doubles). Otherwise returns null.
+ void plus(const CppArgumentList& args, CppVariant* result);
+
+ // Always returns the same value -- an example of a read-only property.
+ void same(CppVariant* result);
+
+ // Invoked when a nonexistent method is called on this example object, this
+ // prints an error message.
+ void fallbackMethod(const CppArgumentList& args, CppVariant* result);
+
+ // These properties will also be exposed to JavaScript.
+ CppVariant my_value;
+ CppVariant my_other_value;
+};
+
+#endif // CPP_BINDING_EXAMPLE_H__
diff --git a/webkit/glue/cpp_bound_class.cc b/webkit/glue/cpp_bound_class.cc
new file mode 100644
index 0000000..09c3f40
--- /dev/null
+++ b/webkit/glue/cpp_bound_class.cc
@@ -0,0 +1,330 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file contains definitions for CppBoundClass
+
+// Here's the control flow of a JS method getting forwarded to a class.
+// - Something calls our NPObject with a function like "Invoke".
+// - CppNPObject's static invoke() function forwards it to its attached
+// CppBoundClass's Invoke() method.
+// - CppBoundClass has then overridden Invoke() to look up the function
+// name in its internal map of methods, and then calls the appropriate
+// method.
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/utf_string_conversions.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebBindings.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebFrame.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebString.h"
+#include "webkit/glue/cpp_bound_class.h"
+
+using WebKit::WebBindings;
+using WebKit::WebFrame;
+
+namespace {
+
+class CppVariantPropertyCallback : public CppBoundClass::PropertyCallback {
+ public:
+ CppVariantPropertyCallback(CppVariant* value) : value_(value) { }
+
+ virtual bool GetValue(CppVariant* value) {
+ value->Set(*value_);
+ return true;
+ }
+ virtual bool SetValue(const CppVariant& value) {
+ value_->Set(value);
+ return true;
+ }
+
+ private:
+ CppVariant* value_;
+};
+
+class GetterPropertyCallback : public CppBoundClass::PropertyCallback {
+public:
+ GetterPropertyCallback(CppBoundClass::GetterCallback* callback)
+ : callback_(callback) { }
+
+ virtual bool GetValue(CppVariant* value) {
+ callback_->Run(value);
+ return true;
+ }
+
+ virtual bool SetValue(const CppVariant& value) {
+ return false;
+ }
+
+private:
+ scoped_ptr<CppBoundClass::GetterCallback> callback_;
+};
+
+}
+
+// Our special NPObject type. We extend an NPObject with a pointer to a
+// CppBoundClass, which is just a C++ interface that we forward all NPObject
+// callbacks to.
+struct CppNPObject {
+ NPObject parent; // This must be the first field in the struct.
+ CppBoundClass* bound_class;
+
+ //
+ // All following objects and functions are static, and just used to interface
+ // with NPObject/NPClass.
+ //
+
+ // An NPClass associates static functions of CppNPObject with the
+ // function pointers used by the JS runtime.
+ static NPClass np_class_;
+
+ // Allocate a new NPObject with the specified class.
+ static NPObject* allocate(NPP npp, NPClass* aClass);
+
+ // Free an object.
+ static void deallocate(NPObject* obj);
+
+ // Returns true if the C++ class associated with this NPObject exposes the
+ // given property. Called by the JS runtime.
+ static bool hasProperty(NPObject *obj, NPIdentifier ident);
+
+ // Returns true if the C++ class associated with this NPObject exposes the
+ // given method. Called by the JS runtime.
+ static bool hasMethod(NPObject *obj, NPIdentifier ident);
+
+ // If the given method is exposed by the C++ class associated with this
+ // NPObject, invokes it with the given args and returns a result. Otherwise,
+ // returns "undefined" (in the JavaScript sense). Called by the JS runtime.
+ static bool invoke(NPObject *obj, NPIdentifier ident,
+ const NPVariant *args, uint32_t arg_count,
+ NPVariant *result);
+
+ // If the given property is exposed by the C++ class associated with this
+ // NPObject, returns its value. Otherwise, returns "undefined" (in the
+ // JavaScript sense). Called by the JS runtime.
+ static bool getProperty(NPObject *obj, NPIdentifier ident,
+ NPVariant *result);
+
+ // If the given property is exposed by the C++ class associated with this
+ // NPObject, sets its value. Otherwise, does nothing. Called by the JS
+ // runtime.
+ static bool setProperty(NPObject *obj, NPIdentifier ident,
+ const NPVariant *value);
+};
+
+// Build CppNPObject's static function pointers into an NPClass, for use
+// in constructing NPObjects for the C++ classes.
+NPClass CppNPObject::np_class_ = {
+ NP_CLASS_STRUCT_VERSION,
+ CppNPObject::allocate,
+ CppNPObject::deallocate,
+ /* NPInvalidateFunctionPtr */ NULL,
+ CppNPObject::hasMethod,
+ CppNPObject::invoke,
+ /* NPInvokeDefaultFunctionPtr */ NULL,
+ CppNPObject::hasProperty,
+ CppNPObject::getProperty,
+ CppNPObject::setProperty,
+ /* NPRemovePropertyFunctionPtr */ NULL
+};
+
+/* static */ NPObject* CppNPObject::allocate(NPP npp, NPClass* aClass) {
+ CppNPObject* obj = new CppNPObject;
+ // obj->parent will be initialized by the NPObject code calling this.
+ obj->bound_class = NULL;
+ return &obj->parent;
+}
+
+/* static */ void CppNPObject::deallocate(NPObject* np_obj) {
+ CppNPObject* obj = reinterpret_cast<CppNPObject*>(np_obj);
+ delete obj;
+}
+
+/* static */ bool CppNPObject::hasMethod(NPObject* np_obj,
+ NPIdentifier ident) {
+ CppNPObject* obj = reinterpret_cast<CppNPObject*>(np_obj);
+ return obj->bound_class->HasMethod(ident);
+}
+
+/* static */ bool CppNPObject::hasProperty(NPObject* np_obj,
+ NPIdentifier ident) {
+ CppNPObject* obj = reinterpret_cast<CppNPObject*>(np_obj);
+ return obj->bound_class->HasProperty(ident);
+}
+
+/* static */ bool CppNPObject::invoke(NPObject* np_obj, NPIdentifier ident,
+ const NPVariant* args, uint32_t arg_count,
+ NPVariant* result) {
+ CppNPObject* obj = reinterpret_cast<CppNPObject*>(np_obj);
+ return obj->bound_class->Invoke(ident, args, arg_count, result);
+}
+
+/* static */ bool CppNPObject::getProperty(NPObject* np_obj,
+ NPIdentifier ident,
+ NPVariant* result) {
+ CppNPObject* obj = reinterpret_cast<CppNPObject*>(np_obj);
+ return obj->bound_class->GetProperty(ident, result);
+}
+
+/* static */ bool CppNPObject::setProperty(NPObject* np_obj,
+ NPIdentifier ident,
+ const NPVariant* value) {
+ CppNPObject* obj = reinterpret_cast<CppNPObject*>(np_obj);
+ return obj->bound_class->SetProperty(ident, value);
+}
+
+CppBoundClass::~CppBoundClass() {
+ for (MethodList::iterator i = methods_.begin(); i != methods_.end(); ++i)
+ delete i->second;
+
+ for (PropertyList::iterator i = properties_.begin(); i != properties_.end();
+ ++i) {
+ delete i->second;
+ }
+
+ // Unregister ourselves if we were bound to a frame.
+ if (bound_to_frame_)
+ WebBindings::unregisterObject(NPVARIANT_TO_OBJECT(self_variant_));
+}
+
+bool CppBoundClass::HasMethod(NPIdentifier ident) const {
+ return (methods_.find(ident) != methods_.end());
+}
+
+bool CppBoundClass::HasProperty(NPIdentifier ident) const {
+ return (properties_.find(ident) != properties_.end());
+}
+
+bool CppBoundClass::Invoke(NPIdentifier ident,
+ const NPVariant* args,
+ size_t arg_count,
+ NPVariant* result) {
+ MethodList::const_iterator method = methods_.find(ident);
+ Callback* callback;
+ if (method == methods_.end()) {
+ if (fallback_callback_.get()) {
+ callback = fallback_callback_.get();
+ } else {
+ VOID_TO_NPVARIANT(*result);
+ return false;
+ }
+ } else {
+ callback = (*method).second;
+ }
+
+ // Build a CppArgumentList argument vector from the NPVariants coming in.
+ CppArgumentList cpp_args(arg_count);
+ for (size_t i = 0; i < arg_count; i++)
+ cpp_args[i].Set(args[i]);
+
+ CppVariant cpp_result;
+ callback->Run(cpp_args, &cpp_result);
+
+ cpp_result.CopyToNPVariant(result);
+ return true;
+}
+
+bool CppBoundClass::GetProperty(NPIdentifier ident, NPVariant* result) const {
+ PropertyList::const_iterator callback = properties_.find(ident);
+ if (callback == properties_.end()) {
+ VOID_TO_NPVARIANT(*result);
+ return false;
+ }
+
+ CppVariant cpp_value;
+ if (!callback->second->GetValue(&cpp_value))
+ return false;
+ cpp_value.CopyToNPVariant(result);
+ return true;
+}
+
+bool CppBoundClass::SetProperty(NPIdentifier ident,
+ const NPVariant* value) {
+ PropertyList::iterator callback = properties_.find(ident);
+ if (callback == properties_.end())
+ return false;
+
+ CppVariant cpp_value;
+ cpp_value.Set(*value);
+ return (*callback).second->SetValue(cpp_value);
+}
+
+void CppBoundClass::BindCallback(const std::string& name, Callback* callback) {
+ NPIdentifier ident = WebBindings::getStringIdentifier(name.c_str());
+ MethodList::iterator old_callback = methods_.find(ident);
+ if (old_callback != methods_.end()) {
+ delete old_callback->second;
+ if (callback == NULL) {
+ methods_.erase(old_callback);
+ return;
+ }
+ }
+
+ methods_[ident] = callback;
+}
+
+void CppBoundClass::BindGetterCallback(const std::string& name,
+ GetterCallback* callback) {
+ PropertyCallback* property_callback = callback == NULL ?
+ NULL : new GetterPropertyCallback(callback);
+
+ BindProperty(name, property_callback);
+}
+
+void CppBoundClass::BindProperty(const std::string& name, CppVariant* prop) {
+ PropertyCallback* property_callback = prop == NULL ?
+ NULL : new CppVariantPropertyCallback(prop);
+
+ BindProperty(name, property_callback);
+}
+
+void CppBoundClass::BindProperty(const std::string& name,
+ PropertyCallback* callback) {
+ NPIdentifier ident = WebBindings::getStringIdentifier(name.c_str());
+ PropertyList::iterator old_callback = properties_.find(ident);
+ if (old_callback != properties_.end()) {
+ delete old_callback->second;
+ if (callback == NULL) {
+ properties_.erase(old_callback);
+ return;
+ }
+ }
+
+ properties_[ident] = callback;
+}
+
+bool CppBoundClass::IsMethodRegistered(const std::string& name) const {
+ NPIdentifier ident = WebBindings::getStringIdentifier(name.c_str());
+ MethodList::const_iterator callback = methods_.find(ident);
+ return (callback != methods_.end());
+}
+
+CppVariant* CppBoundClass::GetAsCppVariant() {
+ if (!self_variant_.isObject()) {
+ // Create an NPObject using our static NPClass. The first argument (a
+ // plugin's instance handle) is passed through to the allocate function
+ // directly, and we don't use it, so it's ok to be 0.
+ NPObject* np_obj = WebBindings::createObject(0, &CppNPObject::np_class_);
+ CppNPObject* obj = reinterpret_cast<CppNPObject*>(np_obj);
+ obj->bound_class = this;
+ self_variant_.Set(np_obj);
+ WebBindings::releaseObject(np_obj); // CppVariant takes the reference.
+ }
+ DCHECK(self_variant_.isObject());
+ return &self_variant_;
+}
+
+void CppBoundClass::BindToJavascript(WebFrame* frame,
+ const std::wstring& classname) {
+#if WEBKIT_USING_JSC
+#error "This is not going to work anymore...but it's not clear what the solution is...or if it's still necessary."
+ JSC::JSLock lock(false);
+#endif
+
+ // BindToWindowObject will take its own reference to the NPObject, and clean
+ // up after itself. It will also (indirectly) register the object with V8,
+ // so we must remember this so we can unregister it when we're destroyed.
+ frame->bindToWindowObject(WideToUTF16Hack(classname),
+ NPVARIANT_TO_OBJECT(*GetAsCppVariant()));
+ bound_to_frame_ = true;
+}
diff --git a/webkit/glue/cpp_bound_class.h b/webkit/glue/cpp_bound_class.h
new file mode 100644
index 0000000..dcd5c3e
--- /dev/null
+++ b/webkit/glue/cpp_bound_class.h
@@ -0,0 +1,181 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/*
+ CppBoundClass class:
+ This base class serves as a parent for C++ classes designed to be bound to
+ JavaScript objects.
+
+ Subclasses should define the constructor to build the property and method
+ lists needed to bind this class to a JS object. They should also declare
+ and define member variables and methods to be exposed to JS through
+ that object.
+
+ See cpp_binding_example.{h|cc} for an example.
+*/
+
+#ifndef WEBKIT_GLUE_CPP_BOUNDCLASS_H__
+#define WEBKIT_GLUE_CPP_BOUNDCLASS_H__
+
+#include <map>
+#include <vector>
+
+#include "webkit/glue/cpp_variant.h"
+
+#include "base/callback.h"
+#include "base/scoped_ptr.h"
+
+namespace WebKit {
+class WebFrame;
+}
+
+typedef std::vector<CppVariant> CppArgumentList;
+
+// CppBoundClass lets you map Javascript method calls and property accesses
+// directly to C++ method calls and CppVariant* variable access.
+class CppBoundClass {
+ public:
+ class PropertyCallback {
+ public:
+ virtual ~PropertyCallback() { }
+
+ // Sets |value| to the value of the property. Returns false in case of
+ // failure. |value| is always non-NULL.
+ virtual bool GetValue(CppVariant* value) = 0;
+
+ // sets the property value to |value|. Returns false in case of failure.
+ virtual bool SetValue(const CppVariant& value) = 0;
+ };
+
+ // The constructor should call BindMethod, BindProperty, and
+ // SetFallbackMethod as needed to set up the methods, properties, and
+ // fallback method.
+ CppBoundClass() : bound_to_frame_(false) { }
+ virtual ~CppBoundClass();
+
+ // Return a CppVariant representing this class, for use with BindProperty().
+ // The variant type is guaranteed to be NPVariantType_Object.
+ CppVariant* GetAsCppVariant();
+
+ // Given a WebFrame, BindToJavascript builds the NPObject that will represent
+ // the class and binds it to the frame's window under the given name. This
+ // should generally be called from the WebView delegate's
+ // WindowObjectCleared(). A class so bound will be accessible to JavaScript
+ // as window.<classname>. The owner of the CppBoundObject is responsible for
+ // keeping the object around while the frame is alive, and for destroying it
+ // afterwards.
+ void BindToJavascript(
+ WebKit::WebFrame* frame, const std::wstring& classname);
+
+ // The type of callbacks.
+ typedef Callback2<const CppArgumentList&, CppVariant*>::Type Callback;
+ typedef Callback1<CppVariant*>::Type GetterCallback;
+
+ // Used by a test. Returns true if a method with name |name| exists,
+ // regardless of whether a fallback is registered.
+ bool IsMethodRegistered(const std::string& name) const;
+
+ protected:
+ // Bind the Javascript method called |name| to the C++ callback |callback|.
+ void BindCallback(const std::string& name, Callback* callback);
+
+ // A wrapper for BindCallback, to simplify the common case of binding a
+ // method on the current object. Though not verified here, |method|
+ // must be a method of this CppBoundClass subclass.
+ template<typename T>
+ void BindMethod(const std::string& name,
+ void (T::*method)(const CppArgumentList&, CppVariant*)) {
+ Callback* callback =
+ NewCallback<T, const CppArgumentList&, CppVariant*>(
+ static_cast<T*>(this), method);
+ BindCallback(name, callback);
+ }
+
+ // Bind Javascript property |name| to the C++ getter callback |callback|.
+ // This can be used to create read-only properties.
+ void BindGetterCallback(const std::string& name, GetterCallback* callback);
+
+ // A wrapper for BindGetterCallback, to simplify the common case of binding a
+ // property on the current object. Though not verified here, |method|
+ // must be a method of this CppBoundClass subclass.
+ template<typename T>
+ void BindProperty(const std::string& name, void (T::*method)(CppVariant*)) {
+ GetterCallback* callback =
+ NewCallback<T, CppVariant*>(static_cast<T*>(this), method);
+ BindGetterCallback(name, callback);
+ }
+
+ // Bind the Javascript property called |name| to a CppVariant |prop|.
+ void BindProperty(const std::string& name, CppVariant* prop);
+
+ // Bind Javascript property called |name| to a PropertyCallback |callback|.
+ // CppBoundClass assumes control over the life time of the |callback|.
+ void BindProperty(const std::string& name, PropertyCallback* callback);
+
+ // Set the fallback callback, which is called when when a callback is
+ // invoked that isn't bound.
+ // If it is NULL (its default value), a JavaScript exception is thrown in
+ // that case (as normally expected). If non NULL, the fallback method is
+ // invoked and the script continues its execution.
+ // Passing NULL for |callback| clears out any existing binding.
+ // It is used for tests and should probably only be used in such cases
+ // as it may cause unexpected behaviors (a JavaScript object with a
+ // fallback always returns true when checked for a method's
+ // existence).
+ void BindFallbackCallback(Callback* fallback_callback) {
+ fallback_callback_.reset(fallback_callback);
+ }
+
+ // A wrapper for BindFallbackCallback, to simplify the common case of
+ // binding a method on the current object. Though not verified here,
+ // |method| must be a method of this CppBoundClass subclass.
+ // Passing NULL for |method| clears out any existing binding.
+ template<typename T>
+ void BindFallbackMethod(
+ void (T::*method)(const CppArgumentList&, CppVariant*)) {
+ if (method) {
+ Callback* callback =
+ NewCallback<T, const CppArgumentList&, CppVariant*>(
+ static_cast<T*>(this), method);
+ BindFallbackCallback(callback);
+ } else {
+ BindFallbackCallback(NULL);
+ }
+ }
+
+ // Some fields are protected because some tests depend on accessing them,
+ // but otherwise they should be considered private.
+
+ typedef std::map<NPIdentifier, PropertyCallback*> PropertyList;
+ typedef std::map<NPIdentifier, Callback*> MethodList;
+ // These maps associate names with property and method pointers to be
+ // exposed to JavaScript.
+ PropertyList properties_;
+ MethodList methods_;
+
+ // The callback gets invoked when a call is made to an nonexistent method.
+ scoped_ptr<Callback> fallback_callback_;
+
+ private:
+ // NPObject callbacks.
+ friend struct CppNPObject;
+ bool HasMethod(NPIdentifier ident) const;
+ bool Invoke(NPIdentifier ident, const NPVariant* args, size_t arg_count,
+ NPVariant* result);
+ bool HasProperty(NPIdentifier ident) const;
+ bool GetProperty(NPIdentifier ident, NPVariant* result) const;
+ bool SetProperty(NPIdentifier ident, const NPVariant* value);
+
+ // A lazily-initialized CppVariant representing this class. We retain 1
+ // reference to this object, and it is released on deletion.
+ CppVariant self_variant_;
+
+ // True if our np_object has been bound to a WebFrame, in which case it must
+ // be unregistered with V8 when we delete it.
+ bool bound_to_frame_;
+
+ DISALLOW_COPY_AND_ASSIGN(CppBoundClass);
+};
+
+#endif // CPP_BOUNDCLASS_H__
diff --git a/webkit/glue/cpp_bound_class_unittest.cc b/webkit/glue/cpp_bound_class_unittest.cc
new file mode 100644
index 0000000..71a56e6
--- /dev/null
+++ b/webkit/glue/cpp_bound_class_unittest.cc
@@ -0,0 +1,288 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Tests for CppBoundClass, in conjunction with CppBindingExample. Binds
+// a CppBindingExample class into JavaScript in a custom test shell and tests
+// the binding from the outside by loading JS into the shell.
+
+#include <vector>
+
+#include "base/message_loop.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebData.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebFrame.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURL.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebView.h"
+#include "webkit/glue/cpp_binding_example.h"
+#include "webkit/glue/webkit_glue.h"
+#include "webkit/tools/test_shell/test_shell_test.h"
+
+using WebKit::WebFrame;
+
+namespace {
+
+class CppBindingExampleSubObject : public CppBindingExample {
+ public:
+ CppBindingExampleSubObject() {
+ sub_value_.Set("sub!");
+ BindProperty("sub_value", &sub_value_);
+ }
+ private:
+ CppVariant sub_value_;
+};
+
+
+class CppBindingExampleWithOptionalFallback : public CppBindingExample {
+ public:
+ CppBindingExampleWithOptionalFallback() {
+ BindProperty("sub_object", sub_object_.GetAsCppVariant());
+ }
+
+ void set_fallback_method_enabled(bool state) {
+ BindFallbackMethod(state ?
+ &CppBindingExampleWithOptionalFallback::fallbackMethod
+ : NULL);
+ }
+
+ // The fallback method does nothing, but because of it the JavaScript keeps
+ // running when a nonexistent method is called on an object.
+ void fallbackMethod(const CppArgumentList& args, CppVariant* result) {
+ }
+
+ private:
+ CppBindingExampleSubObject sub_object_;
+};
+
+class ExampleTestShell : public TestShell {
+ public:
+
+ ExampleTestShell(bool use_fallback_method) {
+ example_bound_class_.set_fallback_method_enabled(use_fallback_method);
+ }
+
+ // When called by WebViewDelegate::WindowObjectCleared method, this binds a
+ // CppExampleObject to window.example.
+ virtual void BindJSObjectsToWindow(WebFrame* frame) {
+ example_bound_class_.BindToJavascript(frame, L"example");
+ // We use the layoutTestController binding for notifyDone.
+ TestShell::BindJSObjectsToWindow(frame);
+ }
+
+ // This is a public interface to TestShell's protected method, so it
+ // can be called by our CreateEmptyWindow.
+ bool PublicInitialize(const std::string& starting_url) {
+ return Initialize(GURL(starting_url));
+ }
+
+ CppBindingExampleWithOptionalFallback example_bound_class_;
+};
+
+class CppBoundClassTest : public TestShellTest {
+ protected:
+ // Adapted from TestShell::CreateNewWindow, this creates an
+ // ExampleTestShellWindow rather than a regular TestShell.
+ virtual void CreateEmptyWindow() {
+ ExampleTestShell* host = new ExampleTestShell(useFallback());
+ ASSERT_TRUE(host != NULL);
+ bool rv = host->PublicInitialize("about:blank");
+ if (rv) {
+ test_shell_ = host;
+ TestShell::windowList()->push_back(host->mainWnd());
+ webframe_ = test_shell_->webView()->mainFrame();
+ ASSERT_TRUE(webframe_ != NULL);
+ } else {
+ delete host;
+ }
+ }
+
+ // Wraps the given JavaScript snippet in <html><body><script> tags, then
+ // loads it into a webframe so it is executed.
+ void ExecuteJavaScript(const std::string& javascript) {
+ std::string html = "<html><body>";
+ html.append(TestShellTest::kJavascriptDelayExitScript);
+ html.append("<script>");
+ html.append(javascript);
+ html.append("</script></body></html>");
+ // The base URL doesn't matter.
+ webframe_->loadHTMLString(html, GURL("about:blank"));
+
+ test_shell_->WaitTestFinished();
+ }
+
+ // Executes the specified JavaScript and checks to be sure that the resulting
+ // document text is exactly "SUCCESS".
+ void CheckJavaScriptSuccess(const std::string& javascript) {
+ ExecuteJavaScript(javascript);
+ EXPECT_EQ(L"SUCCESS", webkit_glue::DumpDocumentText(webframe_));
+ }
+
+ // Executes the specified JavaScript and checks that the resulting document
+ // text is empty.
+ void CheckJavaScriptFailure(const std::string& javascript) {
+ ExecuteJavaScript(javascript);
+ EXPECT_EQ(L"", webkit_glue::DumpDocumentText(webframe_));
+ }
+
+ // Constructs a JavaScript snippet that evaluates and compares the left and
+ // right expressions, printing "SUCCESS" to the page if they are equal and
+ // printing their actual values if they are not. Any strings in the
+ // expressions should be enclosed in single quotes, and no double quotes
+ // should appear in either expression (even if escaped). (If a test case
+ // is added that needs fancier quoting, Json::valueToQuotedString could be
+ // used here. For now, it's not worth adding the dependency.)
+ std::string BuildJSCondition(std::string left, std::string right) {
+ return "var leftval = " + left + ";" +
+ "var rightval = " + right + ";" +
+ "if (leftval == rightval) {" +
+ " document.writeln('SUCCESS');" +
+ "} else {" +
+ " document.writeln(\"" +
+ left + " [\" + leftval + \"] != " +
+ right + " [\" + rightval + \"]\");" +
+ "}";
+ }
+
+protected:
+ virtual bool useFallback() {
+ return false;
+ }
+
+private:
+ WebFrame* webframe_;
+};
+
+class CppBoundClassWithFallbackMethodTest : public CppBoundClassTest {
+protected:
+ virtual bool useFallback() {
+ return true;
+ }
+};
+
+// Ensures that the example object has been bound to JS.
+TEST_F(CppBoundClassTest, ObjectExists) {
+ std::string js = BuildJSCondition("typeof window.example", "'object'");
+ CheckJavaScriptSuccess(js);
+
+ // An additional check to test our test.
+ js = BuildJSCondition("typeof window.invalid_object", "'undefined'");
+ CheckJavaScriptSuccess(js);
+}
+
+TEST_F(CppBoundClassTest, PropertiesAreInitialized) {
+ std::string js = BuildJSCondition("example.my_value", "10");
+ CheckJavaScriptSuccess(js);
+
+ js = BuildJSCondition("example.my_other_value", "'Reinitialized!'");
+ CheckJavaScriptSuccess(js);
+}
+
+TEST_F(CppBoundClassTest, SubOject) {
+ std::string js = BuildJSCondition("typeof window.example.sub_object",
+ "'object'");
+ CheckJavaScriptSuccess(js);
+
+ js = BuildJSCondition("example.sub_object.sub_value", "'sub!'");
+ CheckJavaScriptSuccess(js);
+}
+
+TEST_F(CppBoundClassTest, SetAndGetProperties) {
+ // The property on the left will be set to the value on the right, then
+ // checked to make sure it holds that same value.
+ static const std::string tests[] = {
+ "example.my_value", "7",
+ "example.my_value", "'test'",
+ "example.my_other_value", "3.14",
+ "example.my_other_value", "false",
+ "" // Array end marker: insert additional test pairs before this.
+ };
+
+ for (int i = 0; tests[i] != ""; i += 2) {
+ std::string left = tests[i];
+ std::string right = tests[i + 1];
+ // left = right;
+ std::string js = left;
+ js.append(" = ");
+ js.append(right);
+ js.append(";");
+ js.append(BuildJSCondition(left, right));
+ CheckJavaScriptSuccess(js);
+ }
+}
+
+TEST_F(CppBoundClassTest, SetAndGetPropertiesWithCallbacks) {
+ // TODO(dglazkov): fix NPObject issues around failing property setters and
+ // getters and add tests for situations when GetProperty or SetProperty fail.
+ std::string js = "var result = 'SUCCESS';\n"
+ "example.my_value_with_callback = 10;\n"
+ "if (example.my_value_with_callback != 10)\n"
+ " result = 'FAIL: unable to set property.';\n"
+ "example.my_value_with_callback = 11;\n"
+ "if (example.my_value_with_callback != 11)\n"
+ " result = 'FAIL: unable to set property again';\n"
+ "if (example.same != 42)\n"
+ " result = 'FAIL: same property should always be 42';\n"
+ "example.same = 24;\n"
+ "if (example.same != 42)\n"
+ " result = 'FAIL: same property should always be 42';\n"
+ "document.writeln(result);\n";
+ CheckJavaScriptSuccess(js);
+}
+
+TEST_F(CppBoundClassTest, InvokeMethods) {
+ // The expression on the left is expected to return the value on the right.
+ static const std::string tests[] = {
+ "example.echoValue(true)", "true",
+ "example.echoValue(13)", "13",
+ "example.echoValue(2.718)", "2.718",
+ "example.echoValue('yes')", "'yes'",
+ "example.echoValue()", "null", // Too few arguments
+
+ "example.echoType(false)", "true",
+ "example.echoType(19)", "7",
+ "example.echoType(9.876)", "3.14159",
+ "example.echoType('test string')", "'Success!'",
+ "example.echoType()", "null", // Too few arguments
+
+ // Comparing floats that aren't integer-valued is usually problematic due
+ // to rounding, but exact powers of 2 should also be safe.
+ "example.plus(2.5, 18.0)", "20.5",
+ "example.plus(2, 3.25)", "5.25",
+ "example.plus(2, 3)", "5",
+ "example.plus()", "null", // Too few arguments
+ "example.plus(1)", "null", // Too few arguments
+ "example.plus(1, 'test')", "null", // Wrong argument type
+ "example.plus('test', 2)", "null", // Wrong argument type
+ "example.plus('one', 'two')", "null", // Wrong argument type
+ "" // Array end marker: insert additional test pairs before this.
+ };
+
+ for (int i = 0; tests[i] != ""; i+= 2) {
+ std::string left = tests[i];
+ std::string right = tests[i + 1];
+ std::string js = BuildJSCondition(left, right);
+ CheckJavaScriptSuccess(js);
+ }
+
+ std::string js = "example.my_value = 3.25; example.my_other_value = 1.25;";
+ js.append(BuildJSCondition(
+ "example.plus(example.my_value, example.my_other_value)", "4.5"));
+ CheckJavaScriptSuccess(js);
+}
+
+// Tests that invoking a nonexistent method with no fallback method stops the
+// script's execution
+TEST_F(CppBoundClassTest,
+ InvokeNonexistentMethodNoFallback) {
+ std::string js = "example.nonExistentMethod();document.writeln('SUCCESS');";
+ CheckJavaScriptFailure(js);
+}
+
+// Ensures existent methods can be invoked successfully when the fallback method
+// is used
+TEST_F(CppBoundClassWithFallbackMethodTest,
+ InvokeExistentMethodsWithFallback) {
+ std::string js = BuildJSCondition("example.echoValue(34)", "34");
+ CheckJavaScriptSuccess(js);
+}
+
+} // namespace
diff --git a/webkit/glue/cpp_variant.cc b/webkit/glue/cpp_variant.cc
new file mode 100644
index 0000000..a254669
--- /dev/null
+++ b/webkit/glue/cpp_variant.cc
@@ -0,0 +1,268 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file contains definitions for CppVariant.
+
+#include <limits>
+#include "third_party/WebKit/WebKit/chromium/public/WebBindings.h"
+#include "webkit/glue/cpp_variant.h"
+#include "base/logging.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+
+using WebKit::WebBindings;
+
+CppVariant::CppVariant() {
+ type = NPVariantType_Null;
+}
+
+// Note that Set() performs a deep copy, which is necessary to safely
+// call FreeData() on the value in the destructor.
+CppVariant::CppVariant(const CppVariant& original) {
+ type = NPVariantType_Null;
+ Set(original);
+}
+
+// See comment for copy constructor, above.
+CppVariant& CppVariant::operator=(const CppVariant& original) {
+ if (&original != this)
+ Set(original);
+ return *this;
+}
+
+CppVariant::~CppVariant() {
+ FreeData();
+}
+
+void CppVariant::FreeData() {
+ WebBindings::releaseVariantValue(this);
+}
+
+bool CppVariant::isEqual(const CppVariant& other) const {
+ if (type != other.type)
+ return false;
+
+ switch (type) {
+ case NPVariantType_Bool: {
+ return (value.boolValue == other.value.boolValue);
+ }
+ case NPVariantType_Int32: {
+ return (value.intValue == other.value.intValue);
+ }
+ case NPVariantType_Double: {
+ return (value.doubleValue == other.value.doubleValue);
+ }
+ case NPVariantType_String: {
+ const NPString *this_value = &value.stringValue;
+ const NPString *other_value = &other.value.stringValue;
+ uint32_t len = this_value->UTF8Length;
+ return (len == other_value->UTF8Length &&
+ !strncmp(this_value->UTF8Characters, other_value->UTF8Characters,
+ len));
+ }
+ case NPVariantType_Null:
+ case NPVariantType_Void: {
+ return true;
+ }
+ case NPVariantType_Object: {
+ NPObject *this_value = value.objectValue;
+ NPObject *other_value = other.value.objectValue;
+ return (this_value->_class == other_value->_class &&
+ this_value->referenceCount == other_value->referenceCount);
+ }
+ }
+ return false;
+}
+
+void CppVariant::CopyToNPVariant(NPVariant* result) const {
+ result->type = type;
+ switch (type) {
+ case NPVariantType_Bool:
+ result->value.boolValue = value.boolValue;
+ break;
+ case NPVariantType_Int32:
+ result->value.intValue = value.intValue;
+ break;
+ case NPVariantType_Double:
+ result->value.doubleValue = value.doubleValue;
+ break;
+ case NPVariantType_String:
+ WebBindings::initializeVariantWithStringCopy(result, &value.stringValue);
+ break;
+ case NPVariantType_Null:
+ case NPVariantType_Void:
+ // Nothing to set.
+ break;
+ case NPVariantType_Object:
+ result->type = NPVariantType_Object;
+ result->value.objectValue = WebBindings::retainObject(value.objectValue);
+ break;
+ }
+}
+
+void CppVariant::Set(const NPVariant& new_value) {
+ FreeData();
+ switch (new_value.type) {
+ case NPVariantType_Bool:
+ Set(new_value.value.boolValue);
+ break;
+ case NPVariantType_Int32:
+ Set(new_value.value.intValue);
+ break;
+ case NPVariantType_Double:
+ Set(new_value.value.doubleValue);
+ break;
+ case NPVariantType_String:
+ Set(new_value.value.stringValue);
+ break;
+ case NPVariantType_Null:
+ case NPVariantType_Void:
+ type = new_value.type;
+ break;
+ case NPVariantType_Object:
+ Set(new_value.value.objectValue);
+ break;
+ }
+}
+
+void CppVariant::SetNull() {
+ FreeData();
+ type = NPVariantType_Null;
+}
+
+void CppVariant::Set(bool new_value) {
+ FreeData();
+ type = NPVariantType_Bool;
+ value.boolValue = new_value;
+}
+
+void CppVariant::Set(int32_t new_value) {
+ FreeData();
+ type = NPVariantType_Int32;
+ value.intValue = new_value;
+}
+
+void CppVariant::Set(double new_value) {
+ FreeData();
+ type = NPVariantType_Double;
+ value.doubleValue = new_value;
+}
+
+// The new_value must be a null-terminated string.
+void CppVariant::Set(const char* new_value) {
+ FreeData();
+ type = NPVariantType_String;
+ NPString new_string = {new_value,
+ static_cast<uint32_t>(strlen(new_value))};
+ WebBindings::initializeVariantWithStringCopy(this, &new_string);
+}
+
+void CppVariant::Set(const std::string& new_value) {
+ FreeData();
+ type = NPVariantType_String;
+ NPString new_string = {new_value.data(),
+ static_cast<uint32_t>(new_value.size())};
+ WebBindings::initializeVariantWithStringCopy(this, &new_string);
+}
+void CppVariant::Set(const NPString& new_value) {
+ FreeData();
+ type = NPVariantType_String;
+ WebBindings::initializeVariantWithStringCopy(this, &new_value);
+}
+
+void CppVariant::Set(NPObject* new_value) {
+ FreeData();
+ type = NPVariantType_Object;
+ value.objectValue = WebBindings::retainObject(new_value);
+}
+
+std::string CppVariant::ToString() const {
+ DCHECK(isString());
+ return std::string(value.stringValue.UTF8Characters,
+ value.stringValue.UTF8Length);
+}
+
+int32_t CppVariant::ToInt32() const {
+ if (isInt32()) {
+ return value.intValue;
+ } else if (isDouble()) {
+ return static_cast<int32_t>(value.doubleValue);
+ } else {
+ NOTREACHED();
+ return 0;
+ }
+}
+
+double CppVariant::ToDouble() const {
+ if (isInt32()) {
+ return static_cast<double>(value.intValue);
+ } else if (isDouble()) {
+ return value.doubleValue;
+ } else {
+ NOTREACHED();
+ return 0.0;
+ }
+}
+
+bool CppVariant::ToBoolean() const {
+ DCHECK(isBool());
+ return value.boolValue;
+}
+
+std::vector<std::wstring> CppVariant::ToStringVector() const {
+
+ DCHECK(isObject());
+ std::vector<std::wstring> wstring_vector;
+ NPObject* np_value = value.objectValue;
+ NPIdentifier length_id = WebBindings::getStringIdentifier("length");
+
+ if (WebBindings::hasProperty(NULL, np_value, length_id)) {
+ NPVariant length_value;
+ if (WebBindings::getProperty(NULL, np_value, length_id, &length_value)) {
+ int length = 0;
+ // The length is a double in some cases.
+ if (NPVARIANT_IS_DOUBLE(length_value))
+ length = static_cast<int>(NPVARIANT_TO_DOUBLE(length_value));
+ else if (NPVARIANT_IS_INT32(length_value))
+ length = NPVARIANT_TO_INT32(length_value);
+ WebBindings::releaseVariantValue(&length_value);
+
+ // For sanity, only allow 100 items.
+ length = std::min(100, length);
+ for (int i = 0; i < length; ++i) {
+ // Get each of the items.
+ std::string index = StringPrintf("%d", i);
+ NPIdentifier index_id = WebBindings::getStringIdentifier(index.c_str());
+ if (WebBindings::hasProperty(NULL, np_value, index_id)) {
+ NPVariant index_value;
+ if (WebBindings::getProperty(NULL, np_value, index_id, &index_value)) {
+ if (NPVARIANT_IS_STRING(index_value)) {
+ std::string string(
+ NPVARIANT_TO_STRING(index_value).UTF8Characters,
+ NPVARIANT_TO_STRING(index_value).UTF8Length);
+ wstring_vector.push_back(UTF8ToWide(string));
+ }
+ WebBindings::releaseVariantValue(&index_value);
+ }
+ }
+ }
+ }
+ }
+ return wstring_vector;
+}
+
+bool CppVariant::Invoke(const std::string& method, const CppVariant* args,
+ uint32 arg_count, CppVariant& result) const {
+ DCHECK(isObject());
+ NPIdentifier method_name = WebBindings::getStringIdentifier(method.c_str());
+ NPObject* np_object = value.objectValue;
+ if (WebBindings::hasMethod(NULL, np_object, method_name)) {
+ NPVariant r;
+ bool status = WebBindings::invoke(NULL, np_object, method_name, args, arg_count, &r);
+ result.Set(r);
+ return status;
+ } else {
+ return false;
+ }
+}
diff --git a/webkit/glue/cpp_variant.h b/webkit/glue/cpp_variant.h
new file mode 100644
index 0000000..71b3166
--- /dev/null
+++ b/webkit/glue/cpp_variant.h
@@ -0,0 +1,110 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/*
+ This file contains the declaration for CppVariant, a type used by C++ classes
+ that are to be bound to JavaScript objects.
+
+ CppVariant exists primarily as an interface between C++ callers and the
+ corresponding NPVariant type. CppVariant also provides a number of
+ convenience constructors and accessors, so that the NPVariantType values
+ don't need to be exposed, and a destructor to free any memory allocated for
+ string values.
+
+ For a usage example, see cpp_binding_example.{h|cc}.
+*/
+
+#ifndef WEBKIT_GLUE_CPP_VARIANT_H__
+#define WEBKIT_GLUE_CPP_VARIANT_H__
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "third_party/npapi/bindings/npruntime.h"
+
+class CppVariant : public NPVariant {
+ public:
+ CppVariant();
+ ~CppVariant();
+ void SetNull();
+ void Set(bool value);
+ void Set(int32_t value);
+ void Set(double value);
+
+ // Note that setting a CppVariant to a string value involves copying the
+ // string data, which must be freed with a call to FreeData() when the
+ // CppVariant is set to a different value or is no longer needed. Normally
+ // this is handled by the other Set() methods and by the destructor.
+ void Set(const char* value); // Must be a null-terminated string.
+ void Set(const std::string& value);
+ void Set(const NPString& new_value);
+ void Set(const NPVariant& new_value);
+
+ // Note that setting a CppVariant to an NPObject involves ref-counting
+ // the actual object. FreeData() should only be called if the CppVariant
+ // is no longer needed. The other Set() methods handle this internally.
+ // Also, the object's NPClass is expected to be a static object: neither
+ // the NP runtime nor CPPVariant will ever free it.
+ void Set(NPObject* new_value);
+
+ // These three methods all perform deep copies of any string data. This
+ // allows local CppVariants to be released by the destructor without
+ // corrupting their sources. In performance-critical code, or when strings
+ // are very long, avoid creating new CppVariants.
+ // In case of NPObject as the data, the copying involves ref-counting
+ // as opposed to deep-copying. The ref-counting ensures that sources don't
+ // get corrupted when the copies get destroyed.
+ void CopyToNPVariant(NPVariant* result) const;
+ CppVariant& operator=(const CppVariant& original);
+ CppVariant(const CppVariant& original);
+
+ // Calls NPN_ReleaseVariantValue, which frees any string data
+ // held by the object and sets its type to null.
+ // In case of NPObject, the NPN_ReleaseVariantValue decrements
+ // the ref-count (releases when ref-count becomes 0)
+ void FreeData();
+
+ // Compares this CppVariant's type and value to another's. They must be
+ // identical in both type and value to be considered equal. For string and
+ // object types, a deep comparison is performed; that is, the contents of the
+ // strings, or the classes and refcounts of the objects, must be the same,
+ // but they need not be the same pointers.
+ bool isEqual(const CppVariant& other) const;
+
+ // The value of a CppVariant may be read directly from its NPVariant (but
+ // should only be set using one of the Set() methods above). Although the
+ // type of a CppVariant is likewise public, it can be accessed through these
+ // functions rather than directly if a caller wishes to avoid dependence on
+ // the NPVariantType values.
+ bool isBool() const { return (type == NPVariantType_Bool); }
+ bool isInt32() const { return (type == NPVariantType_Int32); }
+ bool isDouble() const { return (type == NPVariantType_Double); }
+ bool isNumber() const { return (isInt32() || isDouble()); }
+ bool isString() const { return (type == NPVariantType_String); }
+ bool isVoid() const { return (type == NPVariantType_Void); }
+ bool isNull() const { return (type == NPVariantType_Null); }
+ bool isEmpty() const { return (isVoid() || isNull()); }
+ bool isObject() const { return (type == NPVariantType_Object); }
+
+ // Converters. The CppVariant must be of a type convertible to these values.
+ // For example, ToInteger() works only if isNumber() is true.
+ std::string ToString() const;
+ int32_t ToInt32() const;
+ double ToDouble() const;
+ bool ToBoolean() const;
+ // Returns a vector of strings for the specified argument. This is useful
+ // for converting a JavaScript array of strings into a vector of strings.
+ std::vector<std::wstring> ToStringVector() const;
+
+ // Invoke method of the given name on an object with the supplied arguments.
+ // The first argument should be the object on which the method is to be
+ // invoked. Returns whether the method was successfully invoked. If the
+ // method was invoked successfully, any return value is stored in the
+ // CppVariant specified by result.
+ bool Invoke(const std::string& method, const CppVariant* args,
+ uint32 arg_count, CppVariant& result) const;
+};
+
+#endif // WEBKIT_GLUE_CPP_VARIANT_H__
diff --git a/webkit/glue/cpp_variant_unittest.cc b/webkit/glue/cpp_variant_unittest.cc
new file mode 100644
index 0000000..43c78de
--- /dev/null
+++ b/webkit/glue/cpp_variant_unittest.cc
@@ -0,0 +1,426 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/compiler_specific.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebBindings.h"
+#include "webkit/glue/cpp_variant.h"
+
+using WebKit::WebBindings;
+
+// Creates a std::string from an NPVariant of string type. If the NPVariant
+// is not a string, empties the std::string.
+void MakeStdString(const NPVariant& np, std::string* std_string) {
+ if (np.type == NPVariantType_String) {
+ const char* chars =
+ reinterpret_cast<const char*>(np.value.stringValue.UTF8Characters);
+ (*std_string).assign(chars, np.value.stringValue.UTF8Length);
+ } else {
+ (*std_string).clear();
+ }
+}
+
+// Verifies that the actual NPVariant is a string and that its value matches
+// the expected_str.
+void CheckString(const std::string& expected_str, const NPVariant& actual) {
+ EXPECT_EQ(NPVariantType_String, actual.type);
+ std::string actual_str;
+ MakeStdString(actual, &actual_str);
+ EXPECT_EQ(expected_str, actual_str);
+}
+
+// Verifies that both the actual and the expected NPVariants are strings and
+// that their values match.
+void CheckString(const NPVariant& expected, const NPVariant& actual) {
+ EXPECT_EQ(NPVariantType_String, expected.type);
+ std::string expected_str;
+ MakeStdString(expected, &expected_str);
+ CheckString(expected_str, actual);
+}
+
+int g_allocate_call_count = 0;
+int g_deallocate_call_count = 0;
+
+void CheckObject(const NPVariant& actual) {
+ EXPECT_EQ(NPVariantType_Object, actual.type);
+ EXPECT_TRUE(actual.value.objectValue);
+ EXPECT_EQ(1U, actual.value.objectValue->referenceCount);
+ EXPECT_EQ(1, g_allocate_call_count);
+ EXPECT_EQ(0, g_deallocate_call_count);
+}
+
+NPObject* MockNPAllocate(NPP npp, NPClass* aClass) {
+ // This is a mock allocate method that mimics the behavior
+ // of WebBindings::createObject when allocate() is NULL
+
+ ++g_allocate_call_count;
+ // Ignore npp and NPClass
+ return reinterpret_cast<NPObject*>(malloc(sizeof(NPObject)));
+}
+
+void MockNPDeallocate(NPObject* npobj) {
+ // This is a mock deallocate method that mimics the behavior
+ // of NPN_DeallocateObject when deallocate() is NULL
+
+ ++g_deallocate_call_count;
+ free(npobj);
+}
+
+static NPClass void_class = { NP_CLASS_STRUCT_VERSION,
+ MockNPAllocate,
+ MockNPDeallocate,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+NPObject* MakeVoidObject() {
+ g_allocate_call_count = 0;
+ g_deallocate_call_count = 0;
+ return WebBindings::createObject(NULL, &void_class);
+}
+
+TEST(CppVariantTest, NewVariantHasNullType) {
+ CppVariant value;
+ EXPECT_EQ(NPVariantType_Null, value.type);
+}
+
+TEST(CppVariantTest, SetNullSetsType) {
+ CppVariant value;
+ value.Set(17);
+ value.SetNull();
+ EXPECT_EQ(NPVariantType_Null, value.type);
+}
+
+TEST(CppVariantTest, CopyConstructorDoesDeepCopy) {
+ CppVariant source;
+ source.Set("test string");
+ CppVariant dest = source;
+ EXPECT_EQ(NPVariantType_String, dest.type);
+ EXPECT_EQ(NPVariantType_String, source.type);
+
+ // Ensure that the string was copied, not just the pointer.
+ EXPECT_NE(source.value.stringValue.UTF8Characters,
+ dest.value.stringValue.UTF8Characters);
+
+ CheckString(source, dest);
+}
+
+TEST(CppVariantTest, CopyConstructorIncrementsRefCount) {
+ CppVariant source;
+ NPObject *object = MakeVoidObject();
+ source.Set(object);
+ // 2 references so far.
+ EXPECT_EQ(2U, source.value.objectValue->referenceCount);
+
+ CppVariant dest = source;
+ EXPECT_EQ(3U, dest.value.objectValue->referenceCount);
+ EXPECT_EQ(1, g_allocate_call_count);
+ WebBindings::releaseObject(object);
+ source.SetNull();
+ CheckObject(dest);
+}
+
+TEST(CppVariantTest, AssignmentDoesDeepCopy) {
+ CppVariant source;
+ source.Set("test string");
+ CppVariant dest;
+ dest = source;
+ EXPECT_EQ(NPVariantType_String, dest.type);
+ EXPECT_EQ(NPVariantType_String, source.type);
+
+ // Ensure that the string was copied, not just the pointer.
+ EXPECT_NE(source.value.stringValue.UTF8Characters,
+ dest.value.stringValue.UTF8Characters);
+
+ CheckString(source, dest);
+}
+
+TEST(CppVariantTest, AssignmentIncrementsRefCount) {
+ CppVariant source;
+ NPObject *object = MakeVoidObject();
+ source.Set(object);
+ // 2 references so far.
+ EXPECT_EQ(2U, source.value.objectValue->referenceCount);
+
+ CppVariant dest;
+ dest = source;
+ EXPECT_EQ(3U, dest.value.objectValue->referenceCount);
+ EXPECT_EQ(1, g_allocate_call_count);
+
+ WebBindings::releaseObject(object);
+ source.SetNull();
+ CheckObject(dest);
+}
+
+TEST(CppVariantTest, DestroyingCopyDoesNotCorruptSource) {
+ CppVariant source;
+ source.Set("test string");
+ std::string before;
+ MakeStdString(source, &before);
+ {
+ CppVariant dest = source;
+ }
+ CheckString(before, source);
+
+ NPObject *object = MakeVoidObject();
+ source.Set(object);
+ {
+ CppVariant dest2 = source;
+ }
+ WebBindings::releaseObject(object);
+ CheckObject(source);
+}
+
+TEST(CppVariantTest, CopiesTypeAndValueToNPVariant) {
+ NPVariant np;
+ CppVariant cpp;
+
+ cpp.Set(true);
+ cpp.CopyToNPVariant(&np);
+ EXPECT_EQ(cpp.type, np.type);
+ EXPECT_EQ(cpp.value.boolValue, np.value.boolValue);
+ WebBindings::releaseVariantValue(&np);
+
+ cpp.Set(17);
+ cpp.CopyToNPVariant(&np);
+ EXPECT_EQ(cpp.type, np.type);
+ EXPECT_EQ(cpp.value.intValue, np.value.intValue);
+ WebBindings::releaseVariantValue(&np);
+
+ cpp.Set(3.1415);
+ cpp.CopyToNPVariant(&np);
+ EXPECT_EQ(cpp.type, np.type);
+ EXPECT_EQ(cpp.value.doubleValue, np.value.doubleValue);
+ WebBindings::releaseVariantValue(&np);
+
+ cpp.Set("test value");
+ cpp.CopyToNPVariant(&np);
+ CheckString("test value", np);
+ WebBindings::releaseVariantValue(&np);
+
+ cpp.SetNull();
+ cpp.CopyToNPVariant(&np);
+ EXPECT_EQ(cpp.type, np.type);
+ WebBindings::releaseVariantValue(&np);
+
+ NPObject *object = MakeVoidObject();
+ cpp.Set(object);
+ cpp.CopyToNPVariant(&np);
+ WebBindings::releaseObject(object);
+ cpp.SetNull();
+ CheckObject(np);
+ WebBindings::releaseVariantValue(&np);
+}
+
+TEST(CppVariantTest, SetsTypeAndValueFromNPVariant) {
+ NPVariant np;
+ CppVariant cpp;
+
+ VOID_TO_NPVARIANT(np);
+ cpp.Set(np);
+ EXPECT_EQ(np.type, cpp.type);
+ WebBindings::releaseVariantValue(&np);
+
+ NULL_TO_NPVARIANT(np);
+ cpp.Set(np);
+ EXPECT_EQ(np.type, cpp.type);
+ WebBindings::releaseVariantValue(&np);
+
+ BOOLEAN_TO_NPVARIANT(true, np);
+ cpp.Set(np);
+ EXPECT_EQ(np.type, cpp.type);
+ EXPECT_EQ(np.value.boolValue, cpp.value.boolValue);
+ WebBindings::releaseVariantValue(&np);
+
+ INT32_TO_NPVARIANT(15, np);
+ cpp.Set(np);
+ EXPECT_EQ(np.type, cpp.type);
+ EXPECT_EQ(np.value.intValue, cpp.value.intValue);
+ WebBindings::releaseVariantValue(&np);
+
+ DOUBLE_TO_NPVARIANT(2.71828, np);
+ cpp.Set(np);
+ EXPECT_EQ(np.type, cpp.type);
+ EXPECT_EQ(np.value.doubleValue, cpp.value.doubleValue);
+ WebBindings::releaseVariantValue(&np);
+
+ NPString np_ascii_str = { "1st test value",
+ static_cast<uint32_t>(strlen("1st test value")) };
+ WebBindings::initializeVariantWithStringCopy(&np, &np_ascii_str);
+ cpp.Set(np);
+ CheckString("1st test value", cpp);
+ WebBindings::releaseVariantValue(&np);
+
+ // Test characters represented in 2/3/4 bytes in UTF-8
+ // Greek alpha, Chinese number 1 (horizontal bar),
+ // Deseret letter (similar to 'O')
+ NPString np_intl_str = { "\xce\xb1\xe4\xb8\x80\xf0\x90\x90\x84",
+ static_cast<uint32_t>(strlen(
+ "\xce\xb1\xe4\xb8\x80\xf0\x90\x90\x84")) };
+ WebBindings::initializeVariantWithStringCopy(&np, &np_intl_str);
+ cpp.Set(np);
+ CheckString("\xce\xb1\xe4\xb8\x80\xf0\x90\x90\x84", cpp);
+ WebBindings::releaseVariantValue(&np);
+
+ NPObject *obj = MakeVoidObject();
+ OBJECT_TO_NPVARIANT(obj, np); // Doesn't make a copy.
+ cpp.Set(np);
+ // Use this or WebBindings::releaseObject but NOT both.
+ WebBindings::releaseVariantValue(&np);
+ CheckObject(cpp);
+}
+
+TEST(CppVariantTest, SetsSimpleTypesAndValues) {
+ CppVariant cpp;
+ cpp.Set(true);
+ EXPECT_EQ(NPVariantType_Bool, cpp.type);
+ EXPECT_EQ(true, cpp.value.boolValue);
+
+ cpp.Set(5);
+ EXPECT_EQ(NPVariantType_Int32, cpp.type);
+ EXPECT_EQ(5, cpp.value.intValue);
+
+ cpp.Set(1.234);
+ EXPECT_EQ(NPVariantType_Double, cpp.type);
+ EXPECT_EQ(1.234, cpp.value.doubleValue);
+
+ // C string
+ cpp.Set("1st test string");
+ CheckString("1st test string", cpp);
+
+ // std::string
+ std::string source("std test string");
+ cpp.Set(source);
+ CheckString("std test string", cpp);
+
+ // NPString
+ NPString np_ascii_str = { "test NPString",
+ static_cast<uint32_t>(strlen("test NPString")) };
+ cpp.Set(np_ascii_str);
+ std::string expected("test NPString");
+ CheckString(expected, cpp);
+
+ // Test characters represented in 2/3/4 bytes in UTF-8
+ // Greek alpha, Chinese number 1 (horizontal bar),
+ // Deseret letter (similar to 'O')
+ NPString np_intl_str = { "\xce\xb1\xe4\xb8\x80\xf0\x90\x90\x84",
+ static_cast<uint32_t>(strlen(
+ "\xce\xb1\xe4\xb8\x80\xf0\x90\x90\x84")) };
+ cpp.Set(np_intl_str);
+ expected = std::string("\xce\xb1\xe4\xb8\x80\xf0\x90\x90\x84");
+ CheckString(expected, cpp);
+
+ NPObject* obj = MakeVoidObject();
+ cpp.Set(obj);
+ WebBindings::releaseObject(obj);
+ CheckObject(cpp);
+}
+
+TEST(CppVariantTest, FreeDataSetsToVoid) {
+ CppVariant cpp;
+ EXPECT_EQ(NPVariantType_Null, cpp.type);
+ cpp.Set(12);
+ EXPECT_EQ(NPVariantType_Int32, cpp.type);
+ cpp.FreeData();
+ EXPECT_EQ(NPVariantType_Void, cpp.type);
+}
+
+TEST(CppVariantTest, FreeDataReleasesObject) {
+ CppVariant cpp;
+ NPObject* object = MakeVoidObject();
+ cpp.Set(object);
+ EXPECT_EQ(2U, object->referenceCount);
+ cpp.FreeData();
+ EXPECT_EQ(1U, object->referenceCount);
+ EXPECT_EQ(0, g_deallocate_call_count);
+
+ cpp.Set(object);
+ WebBindings::releaseObject(object);
+ EXPECT_EQ(0, g_deallocate_call_count);
+ cpp.FreeData();
+ EXPECT_EQ(1, g_deallocate_call_count);
+}
+
+TEST(CppVariantTest, IsTypeFunctionsWork) {
+ CppVariant cpp;
+ // These should not happen in practice, since voids are not supported
+ // This test must be first since it just clobbers internal data without
+ // releasing.
+ VOID_TO_NPVARIANT(cpp);
+ EXPECT_FALSE(cpp.isBool());
+ EXPECT_FALSE(cpp.isInt32());
+ EXPECT_FALSE(cpp.isDouble());
+ EXPECT_FALSE(cpp.isNumber());
+ EXPECT_FALSE(cpp.isString());
+ EXPECT_TRUE(cpp.isVoid());
+ EXPECT_FALSE(cpp.isNull());
+ EXPECT_TRUE(cpp.isEmpty());
+
+ cpp.Set(true);
+ EXPECT_TRUE(cpp.isBool());
+ EXPECT_FALSE(cpp.isInt32());
+ EXPECT_FALSE(cpp.isDouble());
+ EXPECT_FALSE(cpp.isNumber());
+ EXPECT_FALSE(cpp.isString());
+ EXPECT_FALSE(cpp.isVoid());
+ EXPECT_FALSE(cpp.isNull());
+ EXPECT_FALSE(cpp.isEmpty());
+ EXPECT_FALSE(cpp.isObject());
+
+ cpp.Set(12);
+ EXPECT_FALSE(cpp.isBool());
+ EXPECT_TRUE(cpp.isInt32());
+ EXPECT_FALSE(cpp.isDouble());
+ EXPECT_TRUE(cpp.isNumber());
+ EXPECT_FALSE(cpp.isString());
+ EXPECT_FALSE(cpp.isVoid());
+ EXPECT_FALSE(cpp.isNull());
+ EXPECT_FALSE(cpp.isEmpty());
+ EXPECT_FALSE(cpp.isObject());
+
+ cpp.Set(3.1415);
+ EXPECT_FALSE(cpp.isBool());
+ EXPECT_FALSE(cpp.isInt32());
+ EXPECT_TRUE(cpp.isDouble());
+ EXPECT_TRUE(cpp.isNumber());
+ EXPECT_FALSE(cpp.isString());
+ EXPECT_FALSE(cpp.isVoid());
+ EXPECT_FALSE(cpp.isNull());
+ EXPECT_FALSE(cpp.isEmpty());
+ EXPECT_FALSE(cpp.isObject());
+
+ cpp.Set("a string");
+ EXPECT_FALSE(cpp.isBool());
+ EXPECT_FALSE(cpp.isInt32());
+ EXPECT_FALSE(cpp.isDouble());
+ EXPECT_FALSE(cpp.isNumber());
+ EXPECT_TRUE(cpp.isString());
+ EXPECT_FALSE(cpp.isVoid());
+ EXPECT_FALSE(cpp.isNull());
+ EXPECT_FALSE(cpp.isEmpty());
+ EXPECT_FALSE(cpp.isObject());
+
+ cpp.SetNull();
+ EXPECT_FALSE(cpp.isBool());
+ EXPECT_FALSE(cpp.isInt32());
+ EXPECT_FALSE(cpp.isDouble());
+ EXPECT_FALSE(cpp.isNumber());
+ EXPECT_FALSE(cpp.isString());
+ EXPECT_FALSE(cpp.isVoid());
+ EXPECT_TRUE(cpp.isNull());
+ EXPECT_TRUE(cpp.isEmpty());
+ EXPECT_FALSE(cpp.isObject());
+
+ NPObject *obj = MakeVoidObject();
+ cpp.Set(obj);
+ EXPECT_FALSE(cpp.isBool());
+ EXPECT_FALSE(cpp.isInt32());
+ EXPECT_FALSE(cpp.isDouble());
+ EXPECT_FALSE(cpp.isNumber());
+ EXPECT_FALSE(cpp.isString());
+ EXPECT_FALSE(cpp.isVoid());
+ EXPECT_FALSE(cpp.isNull());
+ EXPECT_FALSE(cpp.isEmpty());
+ EXPECT_TRUE(cpp.isObject());
+ WebBindings::releaseObject(obj);
+ CheckObject(cpp);
+}
diff --git a/webkit/glue/devtools_message_data.cc b/webkit/glue/devtools_message_data.cc
new file mode 100644
index 0000000..a9af3c5
--- /dev/null
+++ b/webkit/glue/devtools_message_data.cc
@@ -0,0 +1,30 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/devtools_message_data.h"
+
+#include "third_party/WebKit/WebKit/chromium/public/WebCString.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebDevToolsMessageData.h"
+
+using WebKit::WebDevToolsMessageData;
+using WebKit::WebString;
+using WebKit::WebVector;
+
+DevToolsMessageData::DevToolsMessageData(const WebDevToolsMessageData& data)
+ : class_name(data.className.utf8()),
+ method_name(data.methodName.utf8()) {
+ for (size_t i = 0; i < data.arguments.size(); i++)
+ arguments.push_back(data.arguments[i].utf8());
+}
+
+WebDevToolsMessageData DevToolsMessageData::ToWebDevToolsMessageData() const {
+ WebDevToolsMessageData result;
+ result.className = WebString::fromUTF8(class_name);
+ result.methodName = WebString::fromUTF8(method_name);
+ WebVector<WebString> web_args(arguments.size());
+ for (size_t i = 0; i < arguments.size(); i++)
+ web_args[i] = WebString::fromUTF8(arguments[i]);
+ result.arguments.swap(web_args);
+ return result;
+}
diff --git a/webkit/glue/devtools_message_data.h b/webkit/glue/devtools_message_data.h
new file mode 100644
index 0000000..31daa14
--- /dev/null
+++ b/webkit/glue/devtools_message_data.h
@@ -0,0 +1,25 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_DEVTOOLS_MESSAGE_DATA_H_
+#define WEBKIT_GLUE_DEVTOOLS_MESSAGE_DATA_H_
+
+#include <string>
+#include <vector>
+
+namespace WebKit {
+struct WebDevToolsMessageData;
+}
+
+struct DevToolsMessageData {
+ DevToolsMessageData() {}
+ explicit DevToolsMessageData(const WebKit::WebDevToolsMessageData&);
+ WebKit::WebDevToolsMessageData ToWebDevToolsMessageData() const;
+
+ std::string class_name;
+ std::string method_name;
+ std::vector<std::string> arguments;
+};
+
+#endif // WEBKIT_GLUE_DEVTOOLS_DEVTOOLS_MESSAGE_DATA_H_
diff --git a/webkit/glue/devtools_strings.grd b/webkit/glue/devtools_strings.grd
new file mode 100644
index 0000000..a21baf5
--- /dev/null
+++ b/webkit/glue/devtools_strings.grd
@@ -0,0 +1,154 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- This file contains definitions of strings that are specific to
+Google Chrome Developer Tools. -->
+
+<grit base_dir="." latest_public_release="0" current_release="1"
+ source_lang_id="en" enc_check="möl">
+ <outputs>
+ <output filename="devtoolsStrings_am.js" type="js_map_format" lang="am" />
+ <output filename="devtoolsStrings_ar.js" type="js_map_format" lang="ar" />
+ <output filename="devtoolsStrings_bg.js" type="js_map_format" lang="bg" />
+ <output filename="devtoolsStrings_bn.js" type="js_map_format" lang="bn" />
+ <output filename="devtoolsStrings_ca.js" type="js_map_format" lang="ca" />
+ <output filename="devtoolsStrings_cs.js" type="js_map_format" lang="cs" />
+ <output filename="devtoolsStrings_da.js" type="js_map_format" lang="da" />
+ <output filename="devtoolsStrings_de.js" type="js_map_format" lang="de" />
+ <output filename="devtoolsStrings_el.js" type="js_map_format" lang="el" />
+ <output filename="devtoolsStrings_en-GB.js" type="js_map_format" lang="en-GB" />
+ <output filename="devtoolsStrings_en-US.js" type="js_map_format" lang="en" />
+ <output filename="devtoolsStrings_es.js" type="js_map_format" lang="es" />
+ <output filename="devtoolsStrings_es-419.js" type="js_map_format" lang="es-419" />
+ <output filename="devtoolsStrings_et.js" type="js_map_format" lang="et" />
+ <output filename="devtoolsStrings_fi.js" type="js_map_format" lang="fi" />
+ <output filename="devtoolsStrings_fil.js" type="js_map_format" lang="fil" />
+ <output filename="devtoolsStrings_fr.js" type="js_map_format" lang="fr" />
+ <output filename="devtoolsStrings_gu.js" type="js_map_format" lang="gu" />
+ <output filename="devtoolsStrings_he.js" type="js_map_format" lang="he" />
+ <output filename="devtoolsStrings_hi.js" type="js_map_format" lang="hi" />
+ <output filename="devtoolsStrings_hr.js" type="js_map_format" lang="hr" />
+ <output filename="devtoolsStrings_hu.js" type="js_map_format" lang="hu" />
+ <output filename="devtoolsStrings_id.js" type="js_map_format" lang="id" />
+ <output filename="devtoolsStrings_it.js" type="js_map_format" lang="it" />
+ <output filename="devtoolsStrings_ja.js" type="js_map_format" lang="ja" />
+ <output filename="devtoolsStrings_kn.js" type="js_map_format" lang="kn" />
+ <output filename="devtoolsStrings_ko.js" type="js_map_format" lang="ko" />
+ <output filename="devtoolsStrings_lt.js" type="js_map_format" lang="lt" />
+ <output filename="devtoolsStrings_lv.js" type="js_map_format" lang="lv" />
+ <output filename="devtoolsStrings_ml.js" type="js_map_format" lang="ml" />
+ <output filename="devtoolsStrings_mr.js" type="js_map_format" lang="mr" />
+ <output filename="devtoolsStrings_nl.js" type="js_map_format" lang="nl" />
+ <!-- The translation console uses 'no' for Norwegian Bokmål. It should
+ be 'nb'. -->
+ <output filename="devtoolsStrings_nb.js" type="js_map_format" lang="no" />
+ <output filename="devtoolsStrings_or.js" type="js_map_format" lang="or" />
+ <output filename="devtoolsStrings_pl.js" type="js_map_format" lang="pl" />
+ <output filename="devtoolsStrings_pt-BR.js" type="js_map_format" lang="pt-BR" />
+ <output filename="devtoolsStrings_pt-PT.js" type="js_map_format" lang="pt-PT" />
+ <output filename="devtoolsStrings_ro.js" type="js_map_format" lang="ro" />
+ <output filename="devtoolsStrings_ru.js" type="js_map_format" lang="ru" />
+ <output filename="devtoolsStrings_sk.js" type="js_map_format" lang="sk" />
+ <output filename="devtoolsStrings_sl.js" type="js_map_format" lang="sl" />
+ <output filename="devtoolsStrings_sr.js" type="js_map_format" lang="sr" />
+ <output filename="devtoolsStrings_sv.js" type="js_map_format" lang="sv" />
+ <output filename="devtoolsStrings_sw.js" type="js_map_format" lang="sw" />
+ <output filename="devtoolsStrings_ta.js" type="js_map_format" lang="ta" />
+ <output filename="devtoolsStrings_te.js" type="js_map_format" lang="te" />
+ <output filename="devtoolsStrings_th.js" type="js_map_format" lang="th" />
+ <output filename="devtoolsStrings_tr.js" type="js_map_format" lang="tr" />
+ <output filename="devtoolsStrings_uk.js" type="js_map_format" lang="uk" />
+ <output filename="devtoolsStrings_vi.js" type="js_map_format" lang="vi" />
+ <output filename="devtoolsStrings_zh-CN.js" type="js_map_format" lang="zh-CN" />
+ <output filename="devtoolsStrings_zh-TW.js" type="js_map_format" lang="zh-TW" />
+ </outputs>
+ <translations>
+ <!-- TODO add references to each of the XTB files (from the Translation
+ Console) that contain translations of messages in your project. Each
+ takes a form like <file path="english.xtb" />. Remember that all file
+ references are relative to this .grd file. -->
+ </translations>
+ <release seq="1">
+ <messages fallback_to_english="true">
+ <message name="IDS_FAILED_TO_RESOLVE_CHILDREN" desc="Message indicating that resolving element children has failed.">
+ Failed to resolve children: <ph name="ERROR">%s<ex>Allocation failure</ex></ph>
+ </message>
+ <message name="IDS_CORRUPT_OBJECT" desc="Error message showing corrupt object.">
+ Corrupted object: <ph name="OBJECT">%s<ex>bad</ex></ph>
+ </message>
+ <message name="IDS_SCOPE_VARS_FAILURE" desc="Error message indicating failure to resolve scope variables.">
+ Failed to resolve scope variables: <ph name="ERROR">%s<ex>Undefined variable</ex></ph>
+ </message>
+ <message name="IDS_PROCESSING_PROFILE" desc="Message indicating that profile is being processed.">
+ Processing...
+ </message>
+ <message name="IDS_PROFILE_TICKS_PROCESSED" desc="Message showing how many ticks are already processed.">
+ <ph name="COUNT">%d<ex>1000</ex></ph> ticks processed
+ </message>
+ <message name="IDS_NO_SOURCE" desc="Label for a script with no source.">
+ &lt;source is not available&gt;
+ </message>
+ <message name="IDS_UNKNOWN_SCOPE_TYPE" desc="Label for an unknown scope type.">
+ &lt;Unknown scope type&gt;
+ </message>
+ <message name="IDS_WINDOW_HEADER" desc="DevTools window header.">
+ Developer Tools - <ph name="URL">%s<ex>http://www.example.com/</ex></ph>
+ </message>
+ <message name="IDS_HEAP_TAB_TITLE" desc="Title of the heap profiler tab.">
+ Heap
+ </message>
+ <message name="IDS_TAKE_HEAP_SNAPSHOT" desc="Title of a button that takes heap snapshot.">
+ Take heap snapshot.
+ </message>
+ <message name="IDS_HEAP_SNAPSHOT" desc="Heap snapshot title.">
+ Snapshot <ph name="COUNT">%d<ex>1</ex></ph>
+ </message>
+ <message name="IDS_HEAP_USAGE" desc="Heap usage.">
+ Used <ph name="USED">%1$s<ex>100MB</ex></ph> of <ph name="CAPACITY">%2$s<ex>200MB</ex></ph> (<ph name="PERCENT">%3$.0f<ex>50</ex></ph>%%)
+ </message>
+ <message name="IDS_HEAP_SNAPSHOT_GRID_CONSTRUCTOR" desc="Title of a heap snapshot grid column showing object's constructor name.">
+ Constructor
+ </message>
+ <message name="IDS_HEAP_SNAPSHOT_GRID_COUNT" desc="Title of a heap snapshot grid column showing objects count.">
+ Count
+ </message>
+ <message name="IDS_HEAP_SNAPSHOT_GRID_COUNT_DELTA" desc="Title of a heap snapshot grid column showing delta of objects count.">
+ ± Count
+ </message>
+ <message name="IDS_HEAP_SNAPSHOT_GRID_SIZE_DELTA" desc="Title of a heap snapshot grid column showing delta of objects size.">
+ ± Size
+ </message>
+ <message name="IDS_HEAP_SNAPSHOT_COMPARED_TO" desc="An option to choose a snapshot to compare current snapshot with.">
+ Compared to <ph name="BASE">%s<ex>Snapshot 1</ex></ph>
+ </message>
+ <message name="IDS_ABSOLUTE_OBJ_COUNTS_AND_SIZES" desc="An option to show absolute objects counts and sizes.">
+ Show absolute counts and sizes.
+ </message>
+ <message name="IDS_PERCENTAGE_OBJ_COUNTS_AND_SIZES" desc="An option to show objects counts and sizes as percentages.">
+ Show counts and sizes as percentages.
+ </message>
+ <message name="IDS_NEW_OBJECTS_IN_DELTA" desc="Indicates that object instances are new compared to base state.">
+ new
+ </message>
+ <message name="IDS_DELETED_OBJECTS_IN_DELTA" desc="Indicates that object instances have disappeared compared to base state.">
+ deleted
+ </message>
+ <message name="IDS_VERY_LARGE_DELTA" desc="Shows very large delta in percents">
+ <ph name="SIGN">%s<ex>+</ex></ph> >1000%%
+ </message>
+ <message name="IDS_SHARE_IN_PERCENTS_SIGNED" desc="Share in percents, with sign.">
+ <ph name="SIGN">%$1s<ex>+</ex></ph><ph name="SHARE">%$2.2f<ex>5</ex></ph>%%
+ </message>
+ <message name="IDS_SIGNED_DELTA" desc="Signed delta.">
+ %$1s%$2d
+ </message>
+ <message name="IDS_SIGNED_STRING_DELTA" desc="Signed string delta.">
+ %$1s%$2s
+ </message>
+ <message name="IDS_OBJECTS_AND_DATA_SIZE_LABEL" desc="Label for objects and data size legend item.">
+ Objects and Data
+ </message>
+ <message name="IDS_CODE_SIZE_LABEL" desc="Label for code size legend item.">
+ Code
+ </message>
+ </messages>
+ </release>
+</grit>
diff --git a/webkit/glue/dom_operations.cc b/webkit/glue/dom_operations.cc
new file mode 100644
index 0000000..82e5e3a
--- /dev/null
+++ b/webkit/glue/dom_operations.cc
@@ -0,0 +1,619 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <set>
+
+#include "base/compiler_specific.h"
+#include "base/string_util.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebAnimationController.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebDocument.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebElement.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebFormElement.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebFrame.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebInputElement.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebNode.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebNodeCollection.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebNodeList.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebVector.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebView.h"
+#include "webkit/glue/dom_operations.h"
+#include "webkit/glue/form_data.h"
+#include "webkit/glue/password_form_dom_manager.h"
+#include "webkit/glue/webpasswordautocompletelistener_impl.h"
+
+using WebKit::WebAnimationController;
+using WebKit::WebDocument;
+using WebKit::WebElement;
+using WebKit::WebFormElement;
+using WebKit::WebFrame;
+using WebKit::WebInputElement;
+using WebKit::WebNode;
+using WebKit::WebNodeCollection;
+using WebKit::WebNodeList;
+using WebKit::WebVector;
+using WebKit::WebView;
+
+namespace {
+
+// Structure for storage the unique set of all savable resource links for
+// making sure that no duplicated resource link in final result. The consumer
+// of the SavableResourcesUniqueCheck is responsible for keeping these pointers
+// valid for the lifetime of the SavableResourcesUniqueCheck instance.
+struct SavableResourcesUniqueCheck {
+ // Unique set of all sub resource links.
+ std::set<GURL>* resources_set;
+ // Unique set of all frame links.
+ std::set<GURL>* frames_set;
+ // Collection of all frames we go through when getting all savable resource
+ // links.
+ std::vector<WebFrame*>* frames;
+
+ SavableResourcesUniqueCheck()
+ : resources_set(NULL),
+ frames_set(NULL),
+ frames(NULL) {}
+
+ SavableResourcesUniqueCheck(std::set<GURL>* resources_set,
+ std::set<GURL>* frames_set, std::vector<WebFrame*>* frames)
+ : resources_set(resources_set),
+ frames_set(frames_set),
+ frames(frames) {}
+};
+
+// Get all savable resource links from current element. One element might
+// have more than one resource link. It is possible to have some links
+// in one CSS stylesheet.
+void GetSavableResourceLinkForElement(
+ const WebElement& element,
+ const WebDocument& current_doc,
+ SavableResourcesUniqueCheck* unique_check,
+ webkit_glue::SavableResourcesResult* result) {
+
+ // Handle frame and iframe tag.
+ if (element.hasTagName("iframe") ||
+ element.hasTagName("frame")) {
+ WebFrame* sub_frame = WebFrame::fromFrameOwnerElement(element);
+ if (sub_frame)
+ unique_check->frames->push_back(sub_frame);
+ return;
+ }
+
+ // Check whether the node has sub resource URL or not.
+ WebString value =
+ webkit_glue::GetSubResourceLinkFromElement(element);
+ if (value.isNull())
+ return;
+ // Get absolute URL.
+ GURL u = current_doc.completeURL(value);
+ // ignore invalid URL
+ if (!u.is_valid())
+ return;
+ // Ignore those URLs which are not standard protocols. Because FTP
+ // protocol does no have cache mechanism, we will skip all
+ // sub-resources if they use FTP protocol.
+ if (!u.SchemeIs("http") && !u.SchemeIs("https") && !u.SchemeIs("file"))
+ return;
+ // Ignore duplicated resource link.
+ if (!unique_check->resources_set->insert(u).second)
+ return;
+ result->resources_list->push_back(u);
+ // Insert referrer for above new resource link.
+ result->referrers_list->push_back(GURL());
+}
+
+// Get all savable resource links from current WebFrameImpl object pointer.
+void GetAllSavableResourceLinksForFrame(WebFrame* current_frame,
+ SavableResourcesUniqueCheck* unique_check,
+ webkit_glue::SavableResourcesResult* result,
+ const char** savable_schemes) {
+ // Get current frame's URL.
+ GURL current_frame_url = current_frame->url();
+
+ // If url of current frame is invalid, ignore it.
+ if (!current_frame_url.is_valid())
+ return;
+
+ // If url of current frame is not a savable protocol, ignore it.
+ bool is_valid_protocol = false;
+ for (int i = 0; savable_schemes[i] != NULL; ++i) {
+ if (current_frame_url.SchemeIs(savable_schemes[i])) {
+ is_valid_protocol = true;
+ break;
+ }
+ }
+ if (!is_valid_protocol)
+ return;
+
+ // If find same frame we have recorded, ignore it.
+ if (!unique_check->frames_set->insert(current_frame_url).second)
+ return;
+
+ // Get current using document.
+ WebDocument current_doc = current_frame->document();
+ // Go through all descent nodes.
+ WebNodeCollection all = current_doc.all();
+ // Go through all node in this frame.
+ for (WebNode node = all.firstItem(); !node.isNull();
+ node = all.nextItem()) {
+ // We only save HTML resources.
+ if (!node.isElementNode())
+ continue;
+ WebElement element = node.to<WebElement>();
+ GetSavableResourceLinkForElement(element,
+ current_doc,
+ unique_check,
+ result);
+ }
+}
+
+} // namespace
+
+namespace webkit_glue {
+
+// Map element name to a list of pointers to corresponding elements to simplify
+// form filling.
+typedef std::map<string16, WebKit::WebInputElement>
+ FormInputElementMap;
+
+// Utility struct for form lookup and autofill. When we parse the DOM to lookup
+// a form, in addition to action and origin URL's we have to compare all
+// necessary form elements. To avoid having to look these up again when we want
+// to fill the form, the FindFormElements function stores the pointers
+// in a FormElements* result, referenced to ensure they are safe to use.
+struct FormElements {
+ WebFormElement form_element;
+ FormInputElementMap input_elements;
+ FormElements() {
+ }
+};
+
+typedef std::vector<FormElements*> FormElementsList;
+
+// Internal implementation of FillForm API.
+static bool FillFormImpl(FormElements* fe, const FormData& data) {
+ if (!fe->form_element.autoComplete())
+ return false;
+
+ std::map<string16, string16> data_map;
+ for (size_t i = 0; i < data.fields.size(); i++)
+ data_map[data.fields[i].name()] = data.fields[i].value();
+
+ for (FormInputElementMap::iterator it = fe->input_elements.begin();
+ it != fe->input_elements.end(); ++it) {
+ WebKit::WebInputElement& element = it->second;
+ if (!element.value().isEmpty()) // Don't overwrite pre-filled values.
+ continue;
+ if (element.inputType() == WebInputElement::Password &&
+ (!element.isEnabledFormControl() || element.hasAttribute("readonly"))) {
+ continue; // Don't fill uneditable password fields.
+ }
+ element.setValue(data_map[it->first]);
+ element.setAutofilled(true);
+ element.dispatchFormControlChangeEvent();
+ }
+
+ return false;
+}
+
+// Helper to search the given form element for the specified input elements
+// in |data|, and add results to |result|.
+static bool FindFormInputElements(WebFormElement* fe,
+ const FormData& data,
+ FormElements* result) {
+ // Loop through the list of elements we need to find on the form in
+ // order to autofill it. If we don't find any one of them, abort
+ // processing this form; it can't be the right one.
+ for (size_t j = 0; j < data.fields.size(); j++) {
+ WebVector<WebNode> temp_elements;
+ fe->getNamedElements(data.fields[j].name(), temp_elements);
+ if (temp_elements.isEmpty()) {
+ // We didn't find a required element. This is not the right form.
+ // Make sure no input elements from a partially matched form
+ // in this iteration remain in the result set.
+ // Note: clear will remove a reference from each InputElement.
+ result->input_elements.clear();
+ return false;
+ }
+ // This element matched, add it to our temporary result. It's possible
+ // there are multiple matches, but for purposes of identifying the form
+ // one suffices and if some function needs to deal with multiple
+ // matching elements it can get at them through the FormElement*.
+ // Note: This assignment adds a reference to the InputElement.
+ result->input_elements[data.fields[j].name()] =
+ temp_elements[0].to<WebInputElement>();
+ }
+ return true;
+}
+
+// Helper to locate form elements identified by |data|.
+static void FindFormElements(WebView* view,
+ const FormData& data,
+ FormElementsList* results) {
+ DCHECK(view);
+ DCHECK(results);
+ WebFrame* main_frame = view->mainFrame();
+ if (!main_frame)
+ return;
+
+ GURL::Replacements rep;
+ rep.ClearQuery();
+ rep.ClearRef();
+
+ // Loop through each frame.
+ for (WebFrame* f = main_frame; f; f = f->traverseNext(false)) {
+ WebDocument doc = f->document();
+ if (!doc.isHTMLDocument())
+ continue;
+
+ GURL full_origin(f->url());
+ if (data.origin != full_origin.ReplaceComponents(rep))
+ continue;
+
+ WebVector<WebFormElement> forms;
+ f->forms(forms);
+
+ for (size_t i = 0; i < forms.size(); ++i) {
+ WebFormElement fe = forms[i];
+ // Action URL must match.
+ GURL full_action(f->document().completeURL(fe.action()));
+ if (data.action != full_action.ReplaceComponents(rep))
+ continue;
+
+ scoped_ptr<FormElements> curr_elements(new FormElements);
+ if (!FindFormInputElements(&fe, data, curr_elements.get()))
+ continue;
+
+ // We found the right element.
+ // Note: this assignment adds a reference to |fe|.
+ curr_elements->form_element = fe;
+ results->push_back(curr_elements.release());
+ }
+ }
+}
+
+void FillPasswordForm(WebView* view,
+ const PasswordFormFillData& data) {
+ FormElementsList forms;
+ // We own the FormElements* in forms.
+ FindFormElements(view, data.basic_data, &forms);
+ FormElementsList::iterator iter;
+ for (iter = forms.begin(); iter != forms.end(); ++iter) {
+ scoped_ptr<FormElements> form_elements(*iter);
+
+ // If wait_for_username is true, we don't want to initially fill the form
+ // until the user types in a valid username.
+ if (!data.wait_for_username)
+ FillFormImpl(form_elements.get(), data.basic_data);
+
+ // Attach autocomplete listener to enable selecting alternate logins.
+ // First, get pointers to username element.
+ WebInputElement username_element =
+ form_elements->input_elements[data.basic_data.fields[0].name()];
+
+ // Get pointer to password element. (We currently only support single
+ // password forms).
+ WebInputElement password_element =
+ form_elements->input_elements[data.basic_data.fields[1].name()];
+
+ username_element.document().frame()->registerPasswordListener(
+ username_element,
+ new WebPasswordAutocompleteListenerImpl(
+ new WebInputElementDelegate(username_element),
+ new WebInputElementDelegate(password_element),
+ data));
+ }
+}
+
+WebString GetSubResourceLinkFromElement(const WebElement& element) {
+ const char* attribute_name = NULL;
+ if (element.hasTagName("img") ||
+ element.hasTagName("script")) {
+ attribute_name = "src";
+ } else if (element.hasTagName("input")) {
+ const WebInputElement input = element.toConst<WebInputElement>();
+ if (input.inputType() == WebInputElement::Image) {
+ attribute_name = "src";
+ }
+ } else if (element.hasTagName("body") ||
+ element.hasTagName("table") ||
+ element.hasTagName("tr") ||
+ element.hasTagName("td")) {
+ attribute_name = "background";
+ } else if (element.hasTagName("blockquote") ||
+ element.hasTagName("q") ||
+ element.hasTagName("del") ||
+ element.hasTagName("ins")) {
+ attribute_name = "cite";
+ } else if (element.hasTagName("link")) {
+ // If the link element is not linked to css, ignore it.
+ if (LowerCaseEqualsASCII(element.getAttribute("type"), "text/css")) {
+ // TODO(jnd): Add support for extracting links of sub-resources which
+ // are inside style-sheet such as @import, url(), etc.
+ // See bug: http://b/issue?id=1111667.
+ attribute_name = "href";
+ }
+ }
+ if (!attribute_name)
+ return WebString();
+ WebString value = element.getAttribute(WebString::fromUTF8(attribute_name));
+ // If value has content and not start with "javascript:" then return it,
+ // otherwise return NULL.
+ if (!value.isNull() && !value.isEmpty() &&
+ !StartsWithASCII(value.utf8(), "javascript:", false))
+ return value;
+
+ return WebString();
+}
+
+// Get all savable resource links from current webview, include main
+// frame and sub-frame
+bool GetAllSavableResourceLinksForCurrentPage(WebView* view,
+ const GURL& page_url, SavableResourcesResult* result,
+ const char** savable_schemes) {
+ WebFrame* main_frame = view->mainFrame();
+ if (!main_frame)
+ return false;
+
+ std::set<GURL> resources_set;
+ std::set<GURL> frames_set;
+ std::vector<WebFrame*> frames;
+ SavableResourcesUniqueCheck unique_check(&resources_set,
+ &frames_set,
+ &frames);
+
+ GURL main_page_gurl(main_frame->url());
+
+ // Make sure we are saving same page between embedder and webkit.
+ // If page has being navigated, embedder will get three empty vector,
+ // which will make the saving page job ended.
+ if (page_url != main_page_gurl)
+ return true;
+
+ // First, process main frame.
+ frames.push_back(main_frame);
+
+ // Check all resource in this page, include sub-frame.
+ for (int i = 0; i < static_cast<int>(frames.size()); ++i) {
+ // Get current frame's all savable resource links.
+ GetAllSavableResourceLinksForFrame(frames[i], &unique_check, result,
+ savable_schemes);
+ }
+
+ // Since frame's src can also point to sub-resources link, so it is possible
+ // that some URLs in frames_list are also in resources_list. For those
+ // URLs, we will remove it from frame_list, only keep them in resources_list.
+ for (std::set<GURL>::iterator it = frames_set.begin();
+ it != frames_set.end(); ++it) {
+ // Append unique frame source to savable frame list.
+ if (resources_set.find(*it) == resources_set.end())
+ result->frames_list->push_back(*it);
+ }
+
+ return true;
+}
+
+// Sizes a single size (the width or height) from a 'sizes' attribute. A size
+// matches must match the following regex: [1-9][0-9]*.
+static int ParseSingleIconSize(const string16& text) {
+ // Size must not start with 0, and be between 0 and 9.
+ if (text.empty() || !(text[0] >= L'1' && text[0] <= L'9'))
+ return 0;
+ // Make sure all chars are from 0-9.
+ for (size_t i = 1; i < text.length(); ++i) {
+ if (!(text[i] >= L'0' && text[i] <= L'9'))
+ return 0;
+ }
+ int output;
+ if (!StringToInt(text, &output))
+ return 0;
+ return output;
+}
+
+// Parses an icon size. An icon size must match the following regex:
+// [1-9][0-9]*x[1-9][0-9]*.
+// If the input couldn't be parsed, a size with a width/height < 0 is returned.
+static gfx::Size ParseIconSize(const string16& text) {
+ std::vector<string16> sizes;
+ SplitStringDontTrim(text, L'x', &sizes);
+ if (sizes.size() != 2)
+ return gfx::Size();
+
+ return gfx::Size(ParseSingleIconSize(sizes[0]),
+ ParseSingleIconSize(sizes[1]));
+}
+
+bool ParseIconSizes(const string16& text,
+ std::vector<gfx::Size>* sizes,
+ bool* is_any) {
+ *is_any = false;
+ std::vector<string16> size_strings;
+ SplitStringAlongWhitespace(text, &size_strings);
+ for (size_t i = 0; i < size_strings.size(); ++i) {
+ if (EqualsASCII(size_strings[i], "any")) {
+ *is_any = true;
+ } else {
+ gfx::Size size = ParseIconSize(size_strings[i]);
+ if (size.width() <= 0 || size.height() <= 0)
+ return false; // Bogus size.
+ sizes->push_back(size);
+ }
+ }
+ if (*is_any && !sizes->empty()) {
+ // If is_any is true, it must occur by itself.
+ return false;
+ }
+ return (*is_any || !sizes->empty());
+}
+
+static void AddInstallIcon(const WebElement& link,
+ std::vector<WebApplicationInfo::IconInfo>* icons) {
+ WebString href = link.getAttribute("href");
+ if (href.isNull() || href.isEmpty())
+ return;
+
+ // Get complete url.
+ GURL url = link.document().completeURL(href);
+ if (!url.is_valid())
+ return;
+
+ if (!link.hasAttribute("sizes"))
+ return;
+
+ bool is_any = false;
+ std::vector<gfx::Size> icon_sizes;
+ if (!ParseIconSizes(link.getAttribute("sizes"), &icon_sizes, &is_any) ||
+ is_any ||
+ icon_sizes.size() != 1) {
+ return;
+ }
+ WebApplicationInfo::IconInfo icon_info;
+ icon_info.width = icon_sizes[0].width();
+ icon_info.height = icon_sizes[0].height();
+ icon_info.url = url;
+ icons->push_back(icon_info);
+}
+
+void GetApplicationInfo(WebView* view, WebApplicationInfo* app_info) {
+ WebFrame* main_frame = view->mainFrame();
+ if (!main_frame)
+ return;
+
+ WebDocument doc = main_frame->document();
+ if (doc.isNull())
+ return;
+
+ WebElement head = main_frame->document().head();
+ if (head.isNull())
+ return;
+
+ WebNodeList children = head.childNodes();
+ for (unsigned i = 0; i < children.length(); ++i) {
+ WebNode child = children.item(i);
+ if (!child.isElementNode())
+ continue;
+ WebElement elem = child.to<WebElement>();
+
+ if (elem.hasTagName("link")) {
+ std::string rel = elem.getAttribute("rel").utf8();
+ // "rel" attribute may use either "icon" or "shortcut icon".
+ // see also
+ // <http://en.wikipedia.org/wiki/Favicon>
+ // <http://dev.w3.org/html5/spec/Overview.html#rel-icon>
+ if (LowerCaseEqualsASCII(rel, "icon") ||
+ LowerCaseEqualsASCII(rel, "shortcut icon"))
+ AddInstallIcon(elem, &app_info->icons);
+ } else if (elem.hasTagName("meta") && elem.hasAttribute("name")) {
+ std::string name = elem.getAttribute("name").utf8();
+ WebString content = elem.getAttribute("content");
+ if (name == "application-name") {
+ app_info->title = content;
+ } else if (name == "description") {
+ app_info->description = content;
+ } else if (name == "application-url") {
+ std::string url = content.utf8();
+ GURL main_url = main_frame->url();
+ app_info->app_url = main_url.is_valid() ?
+ main_url.Resolve(url) : GURL(url);
+ if (!app_info->app_url.is_valid())
+ app_info->app_url = GURL();
+ }
+ }
+ }
+}
+
+bool PauseAnimationAtTimeOnElementWithId(WebView* view,
+ const std::string& animation_name,
+ double time,
+ const std::string& element_id) {
+ WebFrame* web_frame = view->mainFrame();
+ if (!web_frame)
+ return false;
+
+ WebAnimationController* controller = web_frame->animationController();
+ if (!controller)
+ return false;
+
+ WebElement element =
+ web_frame->document().getElementById(WebString::fromUTF8(element_id));
+ if (element.isNull())
+ return false;
+ return controller->pauseAnimationAtTime(element,
+ WebString::fromUTF8(animation_name),
+ time);
+}
+
+bool PauseTransitionAtTimeOnElementWithId(WebView* view,
+ const std::string& property_name,
+ double time,
+ const std::string& element_id) {
+ WebFrame* web_frame = view->mainFrame();
+ if (!web_frame)
+ return false;
+
+ WebAnimationController* controller = web_frame->animationController();
+ if (!controller)
+ return false;
+
+ WebElement element =
+ web_frame->document().getElementById(WebString::fromUTF8(element_id));
+ if (element.isNull())
+ return false;
+ return controller->pauseTransitionAtTime(element,
+ WebString::fromUTF8(property_name),
+ time);
+}
+
+bool ElementDoesAutoCompleteForElementWithId(WebView* view,
+ const std::string& element_id) {
+ WebFrame* web_frame = view->mainFrame();
+ if (!web_frame)
+ return false;
+
+ WebElement element = web_frame->document().getElementById(
+ WebString::fromUTF8(element_id));
+ if (element.isNull() || !element.hasTagName("input"))
+ return false;
+
+ WebInputElement input_element = element.to<WebInputElement>();
+ return input_element.autoComplete();
+}
+
+int NumberOfActiveAnimations(WebView* view) {
+ WebFrame* web_frame = view->mainFrame();
+ if (!web_frame)
+ return -1;
+
+ WebAnimationController* controller = web_frame->animationController();
+ if (!controller)
+ return -1;
+
+ return controller->numberOfActiveAnimations();
+}
+
+void GetMetaElementsWithName(WebDocument* document,
+ const string16& name,
+ std::vector<WebElement>* meta_elements) {
+ DCHECK(document);
+ DCHECK(meta_elements);
+ meta_elements->clear();
+ WebElement head = document->head();
+ if (head.isNull() || !head.hasChildNodes())
+ return;
+
+ WebNodeList children = head.childNodes();
+ for (size_t i = 0; i < children.length(); ++i) {
+ WebNode node = children.item(i);
+ if (!node.isElementNode())
+ continue;
+ WebElement element = node.to<WebElement>();
+ if (!element.hasTagName("meta"))
+ continue;
+ WebString meta_name = element.getAttribute("name");
+ if (meta_name.isNull() || meta_name != name)
+ continue;
+ meta_elements->push_back(element);
+ }
+}
+
+} // webkit_glue
diff --git a/webkit/glue/dom_operations.h b/webkit/glue/dom_operations.h
new file mode 100644
index 0000000..664add0
--- /dev/null
+++ b/webkit/glue/dom_operations.h
@@ -0,0 +1,144 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_DOM_OPERATIONS_H__
+#define WEBKIT_GLUE_DOM_OPERATIONS_H__
+
+#include <string>
+#include <map>
+#include <vector>
+
+#include "gfx/size.h"
+#include "googleurl/src/gurl.h"
+
+namespace WebKit {
+class WebDocument;
+class WebElement;
+class WebString;
+class WebView;
+}
+
+// A collection of operations that access the underlying WebKit DOM directly.
+namespace webkit_glue {
+
+struct PasswordFormFillData;
+
+// Fill matching password forms and trigger autocomplete in the case of multiple
+// matching logins.
+void FillPasswordForm(WebKit::WebView* view,
+ const PasswordFormFillData& data);
+
+// Structure for storage the result of getting all savable resource links
+// for current page. The consumer of the SavableResourcesResult is responsible
+// for keeping these pointers valid for the lifetime of the
+// SavableResourcesResult instance.
+struct SavableResourcesResult {
+ // vector which contains all savable links of sub resource.
+ std::vector<GURL>* resources_list;
+ // vector which contains corresponding all referral links of sub resource,
+ // it matched with links one by one.
+ std::vector<GURL>* referrers_list;
+ // vector which contains all savable links of main frame and sub frames.
+ std::vector<GURL>* frames_list;
+
+ // Constructor.
+ SavableResourcesResult(std::vector<GURL>* resources_list,
+ std::vector<GURL>* referrers_list,
+ std::vector<GURL>* frames_list)
+ : resources_list(resources_list),
+ referrers_list(referrers_list),
+ frames_list(frames_list) { }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SavableResourcesResult);
+};
+
+// Get all savable resource links from current webview, include main frame
+// and sub-frame. After collecting all savable resource links, this function
+// will send those links to embedder. Return value indicates whether we get
+// all saved resource links successfully.
+bool GetAllSavableResourceLinksForCurrentPage(WebKit::WebView* view,
+ const GURL& page_url, SavableResourcesResult* savable_resources_result,
+ const char** savable_schemes);
+
+// Structure used when installing a web page as an app. Populated via
+// GetApplicationInfo.
+struct WebApplicationInfo {
+ struct IconInfo {
+ GURL url;
+ int width;
+ int height;
+ };
+
+ // Title of the application. This is set from the meta tag whose name is
+ // 'application-name'.
+ string16 title;
+
+ // Description of the application. This is set from the meta tag whose name
+ // is 'description'.
+ string16 description;
+
+ // URL for the app. This is set from the meta tag whose name is
+ // 'application-url'.
+ GURL app_url;
+
+ // Set of available icons. This is set for all link tags whose rel=icon. Only
+ // icons that have a non-zero (width and/or height) are added.
+ std::vector<IconInfo> icons;
+};
+
+// Parses the icon's size attribute as defined in the HTML 5 spec. Returns true
+// on success, false on errors. On success either all the sizes specified in
+// the attribute are added to sizes, or is_any is set to true.
+//
+// You shouldn't have a need to invoke this directly, it's public for testing.
+bool ParseIconSizes(const string16& text,
+ std::vector<gfx::Size>* sizes,
+ bool* is_any);
+
+// Gets the application info for the specified page. See the description of
+// WebApplicationInfo for details as to where each field comes from.
+void GetApplicationInfo(WebKit::WebView* view, WebApplicationInfo* app_info);
+
+// Invokes pauseAnimationAtTime on the AnimationController associated with the
+// |view|s main frame.
+// This is used by test shell.
+bool PauseAnimationAtTimeOnElementWithId(WebKit::WebView* view,
+ const std::string& animation_name,
+ double time,
+ const std::string& element_id);
+
+// Invokes pauseTransitionAtTime on the AnimationController associated with the
+// |view|s main frame.
+// This is used by test shell.
+bool PauseTransitionAtTimeOnElementWithId(WebKit::WebView* view,
+ const std::string& property_name,
+ double time,
+ const std::string& element_id);
+
+// Returns true if the element with |element_id| as its id has autocomplete
+// on.
+bool ElementDoesAutoCompleteForElementWithId(WebKit::WebView* view,
+ const std::string& element_id);
+
+// Returns the number of animations currently running.
+int NumberOfActiveAnimations(WebKit::WebView* view);
+
+// Returns the value in an elements resource url attribute. For IMG, SCRIPT or
+// INPUT TYPE=image, returns the value in "src". For LINK TYPE=text/css, returns
+// the value in "href". For BODY, TABLE, TR, TD, returns the value in
+// "background". For BLOCKQUOTE, Q, DEL, INS, returns the value in "cite"
+// attribute. Otherwise returns a null WebString.
+WebKit::WebString GetSubResourceLinkFromElement(
+ const WebKit::WebElement& element);
+
+// Puts the meta-elements of |document| that have the specified |name| in
+// |meta_elements|.
+void GetMetaElementsWithName(WebKit::WebDocument* document,
+ const string16& name,
+ std::vector<WebKit::WebElement>* meta_elements);
+
+} // namespace webkit_glue
+
+#endif // WEBKIT_GLUE_DOM_OPERATIONS_H__
diff --git a/webkit/glue/dom_operations_unittest.cc b/webkit/glue/dom_operations_unittest.cc
new file mode 100644
index 0000000..e9f590c
--- /dev/null
+++ b/webkit/glue/dom_operations_unittest.cc
@@ -0,0 +1,184 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/file_util.h"
+#include "base/path_service.h"
+#include "base/string_util.h"
+#include "net/base/net_util.h"
+#include "net/url_request/url_request_context.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebView.h"
+#include "webkit/glue/dom_operations.h"
+#include "webkit/tools/test_shell/simple_resource_loader_bridge.h"
+#include "webkit/tools/test_shell/test_shell_test.h"
+
+namespace {
+
+class DomOperationsTests : public TestShellTest {
+ public:
+ // Test function GetAllSavableResourceLinksForCurrentPage with a web page.
+ // We expect result of GetAllSavableResourceLinksForCurrentPage exactly
+ // matches expected_resources_set.
+ void GetSavableResourceLinksForPage(const FilePath& page_file_path,
+ const std::set<GURL>& expected_resources_set);
+
+ protected:
+ // testing::Test
+ virtual void SetUp() {
+ TestShellTest::SetUp();
+ }
+
+ virtual void TearDown() {
+ TestShellTest::TearDown();
+ }
+};
+
+
+void DomOperationsTests::GetSavableResourceLinksForPage(
+ const FilePath& page_file_path,
+ const std::set<GURL>& expected_resources_set) {
+ // Convert local file path to file URL.
+ GURL file_url = net::FilePathToFileURL(page_file_path);
+ // Load the test file.
+ test_shell_->ResetTestController();
+ test_shell_->LoadURL(file_url);
+ test_shell_->WaitTestFinished();
+ // Get all savable resource links for the page.
+ std::vector<GURL> resources_list;
+ std::vector<GURL> referrers_list;
+ std::vector<GURL> frames_list;
+ webkit_glue::SavableResourcesResult result(&resources_list,
+ &referrers_list,
+ &frames_list);
+
+ const char* savable_schemes[] = {
+ "http",
+ "https",
+ "file",
+ NULL
+ };
+
+ ASSERT_TRUE(webkit_glue::GetAllSavableResourceLinksForCurrentPage(
+ test_shell_->webView(), file_url, &result, savable_schemes));
+ // Check all links of sub-resource
+ for (std::vector<GURL>::const_iterator cit = resources_list.begin();
+ cit != resources_list.end(); ++cit) {
+ ASSERT_TRUE(expected_resources_set.find(*cit) !=
+ expected_resources_set.end());
+ }
+ // Check all links of frame.
+ for (std::vector<GURL>::const_iterator cit = frames_list.begin();
+ cit != frames_list.end(); ++cit) {
+ ASSERT_TRUE(expected_resources_set.find(*cit) !=
+ expected_resources_set.end());
+ }
+}
+
+// Test function GetAllSavableResourceLinksForCurrentPage with a web page
+// which has valid savable resource links.
+TEST_F(DomOperationsTests, GetSavableResourceLinksWithPageHasValidLinks) {
+ std::set<GURL> expected_resources_set;
+ // Set directory of test data.
+ FilePath page_file_path = data_dir_.AppendASCII("dom_serializer");
+
+ const char* expected_sub_resource_links[] = {
+ "file:///c:/yt/css/base_all-vfl36460.css",
+ "file:///c:/yt/js/base_all_with_bidi-vfl36451.js",
+ "file:///c:/yt/img/pixel-vfl73.gif"
+ };
+ const char* expected_frame_links[] = {
+ "youtube_1.htm",
+ "youtube_2.htm"
+ };
+ // Add all expected links of sub-resource to expected set.
+ for (size_t i = 0; i < arraysize(expected_sub_resource_links); ++i)
+ expected_resources_set.insert(GURL(expected_sub_resource_links[i]));
+ // Add all expected links of frame to expected set.
+ for (size_t i = 0; i < arraysize(expected_frame_links); ++i) {
+ const FilePath expected_frame_url =
+ page_file_path.AppendASCII(expected_frame_links[i]);
+ expected_resources_set.insert(
+ net::FilePathToFileURL(expected_frame_url));
+ }
+
+ page_file_path = page_file_path.AppendASCII("youtube_1.htm");
+ GetSavableResourceLinksForPage(page_file_path, expected_resources_set);
+}
+
+// Test function GetAllSavableResourceLinksForCurrentPage with a web page
+// which does not have valid savable resource links.
+TEST_F(DomOperationsTests, GetSavableResourceLinksWithPageHasInvalidLinks) {
+ std::set<GURL> expected_resources_set;
+ // Set directory of test data.
+ FilePath page_file_path = data_dir_.AppendASCII("dom_serializer");
+
+ const char* expected_frame_links[] = {
+ "youtube_2.htm"
+ };
+ // Add all expected links of frame to expected set.
+ for (size_t i = 0; i < arraysize(expected_frame_links); ++i) {
+ FilePath expected_frame_url =
+ page_file_path.AppendASCII(expected_frame_links[i]);
+ expected_resources_set.insert(
+ net::FilePathToFileURL(expected_frame_url));
+ }
+
+ page_file_path = page_file_path.AppendASCII("youtube_2.htm");
+ GetSavableResourceLinksForPage(page_file_path, expected_resources_set);
+}
+
+// Tests ParseIconSizes with various input.
+TEST_F(DomOperationsTests, ParseIconSizes) {
+ struct TestData {
+ const char* input;
+ const bool expected_result;
+ const bool is_any;
+ const size_t expected_size_count;
+ const int width1;
+ const int height1;
+ const int width2;
+ const int height2;
+ } data[] = {
+ // Bogus input cases.
+ { "10", false, false, 0, 0, 0, 0, 0 },
+ { "10 10", false, false, 0, 0, 0, 0, 0 },
+ { "010", false, false, 0, 0, 0, 0, 0 },
+ { " 010 ", false, false, 0, 0, 0, 0, 0 },
+ { " 10x ", false, false, 0, 0, 0, 0, 0 },
+ { " x10 ", false, false, 0, 0, 0, 0, 0 },
+ { "any 10x10", false, false, 0, 0, 0, 0, 0 },
+ { "", false, false, 0, 0, 0, 0, 0 },
+ { "10ax11", false, false, 0, 0, 0, 0, 0 },
+
+ // Any.
+ { "any", true, true, 0, 0, 0, 0, 0 },
+ { " any", true, true, 0, 0, 0, 0, 0 },
+ { " any ", true, true, 0, 0, 0, 0, 0 },
+
+ // Sizes.
+ { "10x11", true, false, 1, 10, 11, 0, 0 },
+ { " 10x11 ", true, false, 1, 10, 11, 0, 0 },
+ { " 10x11 1x2", true, false, 2, 10, 11, 1, 2 },
+ };
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(data); ++i) {
+ bool is_any;
+ std::vector<gfx::Size> sizes;
+ bool result = webkit_glue::ParseIconSizes(
+ ASCIIToUTF16(data[i].input), &sizes, &is_any);
+ ASSERT_EQ(result, data[i].expected_result);
+ if (result) {
+ ASSERT_EQ(data[i].is_any, is_any);
+ ASSERT_EQ(data[i].expected_size_count, sizes.size());
+ if (sizes.size() > 0) {
+ ASSERT_EQ(data[i].width1, sizes[0].width());
+ ASSERT_EQ(data[i].height1, sizes[0].height());
+ }
+ if (sizes.size() > 1) {
+ ASSERT_EQ(data[i].width2, sizes[1].width());
+ ASSERT_EQ(data[i].height2, sizes[1].height());
+ }
+ }
+ }
+}
+
+} // namespace
diff --git a/webkit/glue/dom_serializer_unittest.cc b/webkit/glue/dom_serializer_unittest.cc
new file mode 100644
index 0000000..a1846f3
--- /dev/null
+++ b/webkit/glue/dom_serializer_unittest.cc
@@ -0,0 +1,850 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/compiler_specific.h"
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/hash_tables.h"
+#include "base/utf_string_conversions.h"
+#include "net/base/net_util.h"
+#include "net/url_request/url_request_context.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebCString.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebData.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebDocument.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebElement.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebFrame.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebNode.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebNodeCollection.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebNodeList.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebPageSerializer.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebPageSerializerClient.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebString.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURL.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebVector.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebView.h"
+#include "webkit/glue/dom_operations.h"
+#include "webkit/glue/webkit_glue.h"
+#include "webkit/tools/test_shell/simple_resource_loader_bridge.h"
+#include "webkit/tools/test_shell/test_shell_test.h"
+
+using WebKit::WebCString;
+using WebKit::WebData;
+using WebKit::WebDocument;
+using WebKit::WebElement;
+using WebKit::WebFrame;
+using WebKit::WebNode;
+using WebKit::WebNodeCollection;
+using WebKit::WebNodeList;
+using WebKit::WebPageSerializer;
+using WebKit::WebPageSerializerClient;
+using WebKit::WebNode;
+using WebKit::WebString;
+using WebKit::WebURL;
+using WebKit::WebView;
+using WebKit::WebVector;
+
+namespace {
+
+// Iterate recursively over sub-frames to find one with with a given url.
+WebFrame* FindSubFrameByURL(WebView* web_view, const GURL& url) {
+ if (!web_view->mainFrame())
+ return NULL;
+
+ std::vector<WebFrame*> stack;
+ stack.push_back(web_view->mainFrame());
+
+ while (!stack.empty()) {
+ WebFrame* current_frame = stack.back();
+ stack.pop_back();
+ if (GURL(current_frame->url()) == url)
+ return current_frame;
+ WebNodeCollection all = current_frame->document().all();
+ for (WebNode node = all.firstItem();
+ !node.isNull(); node = all.nextItem()) {
+ if (!node.isElementNode())
+ continue;
+ // Check frame tag and iframe tag
+ WebElement element = node.to<WebElement>();
+ if (!element.hasTagName("frame") && !element.hasTagName("iframe"))
+ continue;
+ WebFrame* sub_frame = WebFrame::fromFrameOwnerElement(element);
+ if (sub_frame)
+ stack.push_back(sub_frame);
+ }
+ }
+ return NULL;
+}
+
+class DomSerializerTests : public TestShellTest,
+ public WebPageSerializerClient {
+ public:
+ DomSerializerTests()
+ : local_directory_name_(FILE_PATH_LITERAL("./dummy_files/")) { }
+
+ // DomSerializerDelegate.
+ void didSerializeDataForFrame(const WebURL& frame_web_url,
+ const WebCString& data,
+ PageSerializationStatus status) {
+
+ GURL frame_url(frame_web_url);
+ // If the all frames are finished saving, check all finish status
+ if (status == WebPageSerializerClient::AllFramesAreFinished) {
+ SerializationFinishStatusMap::iterator it =
+ serialization_finish_status_.begin();
+ for (; it != serialization_finish_status_.end(); ++it)
+ ASSERT_TRUE(it->second);
+ serialized_ = true;
+ return;
+ }
+
+ // Check finish status of current frame.
+ SerializationFinishStatusMap::iterator it =
+ serialization_finish_status_.find(frame_url.spec());
+ // New frame, set initial status as false.
+ if (it == serialization_finish_status_.end())
+ serialization_finish_status_[frame_url.spec()] = false;
+
+ it = serialization_finish_status_.find(frame_url.spec());
+ ASSERT_TRUE(it != serialization_finish_status_.end());
+ // In process frame, finish status should be false.
+ ASSERT_FALSE(it->second);
+
+ // Add data to corresponding frame's content.
+ serialized_frame_map_[frame_url.spec()] += data.data();
+
+ // Current frame is completed saving, change the finish status.
+ if (status == WebPageSerializerClient::CurrentFrameIsFinished)
+ it->second = true;
+ }
+
+ bool HasSerializedFrame(const GURL& frame_url) {
+ return serialized_frame_map_.find(frame_url.spec()) !=
+ serialized_frame_map_.end();
+ }
+
+ const std::string& GetSerializedContentForFrame(
+ const GURL& frame_url) {
+ return serialized_frame_map_[frame_url.spec()];
+ }
+
+ // Load web page according to specific URL.
+ void LoadPageFromURL(const GURL& page_url) {
+ // Load the test file.
+ test_shell_->ResetTestController();
+ test_shell_->LoadURL(page_url);
+ test_shell_->WaitTestFinished();
+ }
+
+ // Load web page according to input content and relative URLs within
+ // the document.
+ void LoadContents(const std::string& contents,
+ const GURL& base_url,
+ const WebString encoding_info) {
+ test_shell_->ResetTestController();
+ // If input encoding is empty, use UTF-8 as default encoding.
+ if (encoding_info.isEmpty()) {
+ test_shell_->webView()->mainFrame()->loadHTMLString(contents, base_url);
+ } else {
+ WebData data(contents.data(), contents.length());
+
+ // Do not use WebFrame.LoadHTMLString because it assumes that input
+ // html contents use UTF-8 encoding.
+ // TODO(darin): This should use WebFrame::loadData.
+ WebFrame* web_frame =
+ test_shell_->webView()->mainFrame();
+
+ ASSERT_TRUE(web_frame != NULL);
+
+ web_frame->loadData(data, "text/html", encoding_info, base_url);
+ }
+
+ test_shell_->WaitTestFinished();
+ }
+
+ // Serialize page DOM according to specific page URL. The parameter
+ // recursive_serialization indicates whether we will serialize all
+ // sub-frames.
+ void SerializeDomForURL(const GURL& page_url,
+ bool recursive_serialization) {
+ // Find corresponding WebFrame according to page_url.
+ WebFrame* web_frame = FindSubFrameByURL(test_shell_->webView(),
+ page_url);
+ ASSERT_TRUE(web_frame != NULL);
+ // Add input file URl to links_.
+ links_.assign(&page_url,1);
+ // Add dummy file path to local_path_.
+ WebString file_path = webkit_glue::FilePathStringToWebString(
+ FILE_PATH_LITERAL("c:\\dummy.htm"));
+ local_paths_.assign(&file_path, 1);
+ // Start serializing DOM.
+ bool result = WebPageSerializer::serialize(web_frame,
+ recursive_serialization,
+ static_cast<WebPageSerializerClient*>(this),
+ links_,
+ local_paths_,
+ webkit_glue::FilePathToWebString(local_directory_name_));
+ ASSERT_TRUE(result);
+ ASSERT_TRUE(serialized_);
+ }
+
+ private:
+ // Map frame_url to corresponding serialized_content.
+ typedef base::hash_map<std::string, std::string> SerializedFrameContentMap;
+ SerializedFrameContentMap serialized_frame_map_;
+ // Map frame_url to corresponding status of serialization finish.
+ typedef base::hash_map<std::string, bool> SerializationFinishStatusMap;
+ SerializationFinishStatusMap serialization_finish_status_;
+ // Flag indicates whether the process of serializing DOM is finished or not.
+ bool serialized_;
+ // The links_ contain dummy original URLs of all saved links.
+ WebVector<WebURL> links_;
+ // The local_paths_ contain dummy corresponding local file paths of all saved
+ // links, which matched links_ one by one.
+ WebVector<WebString> local_paths_;
+ // The local_directory_name_ is dummy relative path of directory which
+ // contain all saved auxiliary files included all sub frames and resources.
+ const FilePath local_directory_name_;
+
+ protected:
+ // testing::Test
+ virtual void SetUp() {
+ TestShellTest::SetUp();
+ serialized_ = false;
+ }
+
+ virtual void TearDown() {
+ TestShellTest::TearDown();
+ }
+};
+
+// Helper function that test whether the first node in the doc is a doc type
+// node.
+bool HasDocType(const WebDocument& doc) {
+ WebNode node = doc.firstChild();
+ if (node.isNull())
+ return false;
+ return node.nodeType() == WebNode::DocumentTypeNode;
+}
+
+// Helper function for checking whether input node is META tag. Return true
+// means it is META element, otherwise return false. The parameter charset_info
+// return actual charset info if the META tag has charset declaration.
+bool IsMetaElement(const WebNode& node, std::string& charset_info) {
+ if (!node.isElementNode())
+ return false;
+ const WebElement meta = node.toConst<WebElement>();
+ if (!meta.hasTagName("meta"))
+ return false;
+ charset_info.erase(0, charset_info.length());
+ // Check the META charset declaration.
+ WebString httpEquiv = meta.getAttribute("http-equiv");
+ if (LowerCaseEqualsASCII(httpEquiv, "content-type")) {
+ std::string content = meta.getAttribute("content").utf8();
+ int pos = content.find("charset", 0);
+ if (pos > -1) {
+ // Add a dummy charset declaration to charset_info, which indicates this
+ // META tag has charset declaration although we do not get correct value
+ // yet.
+ charset_info.append("has-charset-declaration");
+ int remaining_length = content.length() - pos - 7;
+ if (!remaining_length)
+ return true;
+ int start_pos = pos + 7;
+ // Find "=" symbol.
+ while (remaining_length--)
+ if (content[start_pos++] == L'=')
+ break;
+ // Skip beginning space.
+ while (remaining_length) {
+ if (content[start_pos] > 0x0020)
+ break;
+ ++start_pos;
+ --remaining_length;
+ }
+ if (!remaining_length)
+ return true;
+ int end_pos = start_pos;
+ // Now we find out the start point of charset info. Search the end point.
+ while (remaining_length--) {
+ if (content[end_pos] <= 0x0020 || content[end_pos] == L';')
+ break;
+ ++end_pos;
+ }
+ // Get actual charset info.
+ charset_info = content.substr(start_pos, end_pos - start_pos);
+ return true;
+ }
+ }
+ return true;
+}
+
+// If original contents have document type, the serialized contents also have
+// document type.
+TEST_F(DomSerializerTests, SerializeHTMLDOMWithDocType) {
+ FilePath page_file_path = data_dir_;
+ page_file_path = page_file_path.AppendASCII("dom_serializer");
+ page_file_path = page_file_path.AppendASCII("youtube_1.htm");
+ GURL file_url = net::FilePathToFileURL(page_file_path);
+ ASSERT_TRUE(file_url.SchemeIsFile());
+ // Load the test file.
+ LoadPageFromURL(file_url);
+ // Make sure original contents have document type.
+ WebFrame* web_frame = FindSubFrameByURL(test_shell_->webView(), file_url);
+ ASSERT_TRUE(web_frame != NULL);
+ WebDocument doc = web_frame->document();
+ ASSERT_TRUE(HasDocType(doc));
+ // Do serialization.
+ SerializeDomForURL(file_url, false);
+ // Load the serialized contents.
+ ASSERT_TRUE(HasSerializedFrame(file_url));
+ const std::string& serialized_contents =
+ GetSerializedContentForFrame(file_url);
+ LoadContents(serialized_contents, file_url,
+ web_frame->encoding());
+ // Make sure serialized contents still have document type.
+ web_frame = test_shell_->webView()->mainFrame();
+ doc = web_frame->document();
+ ASSERT_TRUE(HasDocType(doc));
+}
+
+// If original contents do not have document type, the serialized contents
+// also do not have document type.
+TEST_F(DomSerializerTests, SerializeHTMLDOMWithoutDocType) {
+ FilePath page_file_path = data_dir_;
+ page_file_path = page_file_path.AppendASCII("dom_serializer");
+ page_file_path = page_file_path.AppendASCII("youtube_2.htm");
+ GURL file_url = net::FilePathToFileURL(page_file_path);
+ ASSERT_TRUE(file_url.SchemeIsFile());
+ // Load the test file.
+ LoadPageFromURL(file_url);
+ // Make sure original contents do not have document type.
+ WebFrame* web_frame = FindSubFrameByURL(test_shell_->webView(), file_url);
+ ASSERT_TRUE(web_frame != NULL);
+ WebDocument doc = web_frame->document();
+ ASSERT_TRUE(!HasDocType(doc));
+ // Do serialization.
+ SerializeDomForURL(file_url, false);
+ // Load the serialized contents.
+ ASSERT_TRUE(HasSerializedFrame(file_url));
+ const std::string& serialized_contents =
+ GetSerializedContentForFrame(file_url);
+ LoadContents(serialized_contents, file_url,
+ web_frame->encoding());
+ // Make sure serialized contents do not have document type.
+ web_frame = test_shell_->webView()->mainFrame();
+ doc = web_frame->document();
+ ASSERT_TRUE(!HasDocType(doc));
+}
+
+// Serialize XML document which has all 5 built-in entities. After
+// finishing serialization, the serialized contents should be same
+// with original XML document.
+TEST_F(DomSerializerTests, SerializeXMLDocWithBuiltInEntities) {
+ FilePath page_file_path = data_dir_;
+ page_file_path = page_file_path.AppendASCII("dom_serializer");
+ page_file_path = page_file_path.AppendASCII("note.xml");
+ // Read original contents for later comparison.
+ std::string original_contents;
+ ASSERT_TRUE(file_util::ReadFileToString(page_file_path, &original_contents));
+ // Get file URL.
+ GURL file_url = net::FilePathToFileURL(page_file_path);
+ ASSERT_TRUE(file_url.SchemeIsFile());
+ // Load the test file.
+ LoadPageFromURL(file_url);
+ // Do serialization.
+ SerializeDomForURL(file_url, false);
+ // Compare the serialized contents with original contents.
+ ASSERT_TRUE(HasSerializedFrame(file_url));
+ const std::string& serialized_contents =
+ GetSerializedContentForFrame(file_url);
+ ASSERT_EQ(original_contents, serialized_contents);
+}
+
+// When serializing DOM, we add MOTW declaration before html tag.
+TEST_F(DomSerializerTests, SerializeHTMLDOMWithAddingMOTW) {
+ FilePath page_file_path = data_dir_;
+ page_file_path = page_file_path.AppendASCII("dom_serializer");
+ page_file_path = page_file_path.AppendASCII("youtube_2.htm");
+ // Read original contents for later comparison .
+ std::string original_contents;
+ ASSERT_TRUE(file_util::ReadFileToString(page_file_path, &original_contents));
+ // Get file URL.
+ GURL file_url = net::FilePathToFileURL(page_file_path);
+ ASSERT_TRUE(file_url.SchemeIsFile());
+ // Make sure original contents does not have MOTW;
+ std::string motw_declaration =
+ WebPageSerializer::generateMarkOfTheWebDeclaration(file_url).utf8();
+ ASSERT_FALSE(motw_declaration.empty());
+ // The encoding of original contents is ISO-8859-1, so we convert the MOTW
+ // declaration to ASCII and search whether original contents has it or not.
+ ASSERT_TRUE(std::string::npos ==
+ original_contents.find(motw_declaration));
+ // Load the test file.
+ LoadPageFromURL(file_url);
+ // Do serialization.
+ SerializeDomForURL(file_url, false);
+ // Make sure the serialized contents have MOTW ;
+ ASSERT_TRUE(HasSerializedFrame(file_url));
+ const std::string& serialized_contents =
+ GetSerializedContentForFrame(file_url);
+ ASSERT_FALSE(std::string::npos ==
+ serialized_contents.find(motw_declaration));
+}
+
+// When serializing DOM, we will add the META which have correct charset
+// declaration as first child of HEAD element for resolving WebKit bug:
+// http://bugs.webkit.org/show_bug.cgi?id=16621 even the original document
+// does not have META charset declaration.
+TEST_F(DomSerializerTests, SerializeHTMLDOMWithNoMetaCharsetInOriginalDoc) {
+ FilePath page_file_path = data_dir_;
+ page_file_path = page_file_path.AppendASCII("dom_serializer");
+ page_file_path = page_file_path.AppendASCII("youtube_1.htm");
+ // Get file URL.
+ GURL file_url = net::FilePathToFileURL(page_file_path);
+ ASSERT_TRUE(file_url.SchemeIsFile());
+ // Load the test file.
+ LoadPageFromURL(file_url);
+
+ // Make sure there is no META charset declaration in original document.
+ WebFrame* web_frame = FindSubFrameByURL(test_shell_->webView(), file_url);
+ ASSERT_TRUE(web_frame != NULL);
+ WebDocument doc = web_frame->document();
+ ASSERT_TRUE(doc.isHTMLDocument());
+ WebElement head_element = doc.head();
+ ASSERT_TRUE(!head_element.isNull());
+ // Go through all children of HEAD element.
+ for (WebNode child = head_element.firstChild(); !child.isNull();
+ child = child.nextSibling()) {
+ std::string charset_info;
+ if (IsMetaElement(child, charset_info))
+ ASSERT_TRUE(charset_info.empty());
+ }
+ // Do serialization.
+ SerializeDomForURL(file_url, false);
+
+ // Load the serialized contents.
+ ASSERT_TRUE(HasSerializedFrame(file_url));
+ const std::string& serialized_contents =
+ GetSerializedContentForFrame(file_url);
+ LoadContents(serialized_contents, file_url,
+ web_frame->encoding());
+ // Make sure the first child of HEAD element is META which has charset
+ // declaration in serialized contents.
+ web_frame = test_shell_->webView()->mainFrame();
+ ASSERT_TRUE(web_frame != NULL);
+ doc = web_frame->document();
+ ASSERT_TRUE(doc.isHTMLDocument());
+ head_element = doc.head();
+ ASSERT_TRUE(!head_element.isNull());
+ WebNode meta_node = head_element.firstChild();
+ ASSERT_TRUE(!meta_node.isNull());
+ // Get meta charset info.
+ std::string charset_info2;
+ ASSERT_TRUE(IsMetaElement(meta_node, charset_info2));
+ ASSERT_TRUE(!charset_info2.empty());
+ ASSERT_TRUE(charset_info2 == std::string(web_frame->encoding().utf8()));
+
+ // Make sure no more additional META tags which have charset declaration.
+ for (WebNode child = meta_node.nextSibling(); !child.isNull();
+ child = child.nextSibling()) {
+ std::string charset_info;
+ if (IsMetaElement(child, charset_info))
+ ASSERT_TRUE(charset_info.empty());
+ }
+}
+
+// When serializing DOM, if the original document has multiple META charset
+// declaration, we will add the META which have correct charset declaration
+// as first child of HEAD element and remove all original META charset
+// declarations.
+TEST_F(DomSerializerTests,
+ SerializeHTMLDOMWithMultipleMetaCharsetInOriginalDoc) {
+ FilePath page_file_path = data_dir_;
+ page_file_path = page_file_path.AppendASCII("dom_serializer");
+ page_file_path = page_file_path.AppendASCII("youtube_2.htm");
+ // Get file URL.
+ GURL file_url = net::FilePathToFileURL(page_file_path);
+ ASSERT_TRUE(file_url.SchemeIsFile());
+ // Load the test file.
+ LoadPageFromURL(file_url);
+
+ // Make sure there are multiple META charset declarations in original
+ // document.
+ WebFrame* web_frame = FindSubFrameByURL(test_shell_->webView(), file_url);
+ ASSERT_TRUE(web_frame != NULL);
+ WebDocument doc = web_frame->document();
+ ASSERT_TRUE(doc.isHTMLDocument());
+ WebElement head_ele = doc.head();
+ ASSERT_TRUE(!head_ele.isNull());
+ // Go through all children of HEAD element.
+ int charset_declaration_count = 0;
+ for (WebNode child = head_ele.firstChild(); !child.isNull();
+ child = child.nextSibling()) {
+ std::string charset_info;
+ if (IsMetaElement(child, charset_info) && !charset_info.empty())
+ charset_declaration_count++;
+ }
+ // The original doc has more than META tags which have charset declaration.
+ ASSERT_TRUE(charset_declaration_count > 1);
+
+ // Do serialization.
+ SerializeDomForURL(file_url, false);
+
+ // Load the serialized contents.
+ ASSERT_TRUE(HasSerializedFrame(file_url));
+ const std::string& serialized_contents =
+ GetSerializedContentForFrame(file_url);
+ LoadContents(serialized_contents, file_url,
+ web_frame->encoding());
+ // Make sure only first child of HEAD element is META which has charset
+ // declaration in serialized contents.
+ web_frame = test_shell_->webView()->mainFrame();
+ ASSERT_TRUE(web_frame != NULL);
+ doc = web_frame->document();
+ ASSERT_TRUE(doc.isHTMLDocument());
+ head_ele = doc.head();
+ ASSERT_TRUE(!head_ele.isNull());
+ WebNode meta_node = head_ele.firstChild();
+ ASSERT_TRUE(!meta_node.isNull());
+ // Get meta charset info.
+ std::string charset_info2;
+ ASSERT_TRUE(IsMetaElement(meta_node, charset_info2));
+ ASSERT_TRUE(!charset_info2.empty());
+ ASSERT_TRUE(charset_info2 == std::string(web_frame->encoding().utf8()));
+
+ // Make sure no more additional META tags which have charset declaration.
+ for (WebNode child = meta_node.nextSibling(); !child.isNull();
+ child = child.nextSibling()) {
+ std::string charset_info;
+ if (IsMetaElement(child, charset_info))
+ ASSERT_TRUE(charset_info.empty());
+ }
+}
+
+// Test situation of html entities in text when serializing HTML DOM.
+TEST_F(DomSerializerTests, SerializeHTMLDOMWithEntitiesInText) {
+ FilePath page_file_path = data_dir_;
+ page_file_path = page_file_path.AppendASCII(
+ "dom_serializer/htmlentities_in_text.htm");
+ // Get file URL. The URL is dummy URL to identify the following loading
+ // actions. The test content is in constant:original_contents.
+ GURL file_url = net::FilePathToFileURL(page_file_path);
+ ASSERT_TRUE(file_url.SchemeIsFile());
+ // Test contents.
+ static const char* const original_contents =
+ "<html><body>&amp;&lt;&gt;\"\'</body></html>";
+ // Load the test contents.
+ LoadContents(original_contents, file_url, WebString());
+
+ // Get BODY's text content in DOM.
+ WebFrame* web_frame = FindSubFrameByURL(test_shell_->webView(), file_url);
+ ASSERT_TRUE(web_frame != NULL);
+ WebDocument doc = web_frame->document();
+ ASSERT_TRUE(doc.isHTMLDocument());
+ WebElement body_ele = doc.body();
+ ASSERT_TRUE(!body_ele.isNull());
+ WebNode text_node = body_ele.firstChild();
+ ASSERT_TRUE(text_node.isTextNode());
+ ASSERT_TRUE(std::string(text_node.createMarkup().utf8()) ==
+ "&amp;&lt;&gt;\"\'");
+ // Do serialization.
+ SerializeDomForURL(file_url, false);
+ // Compare the serialized contents with original contents.
+ ASSERT_TRUE(HasSerializedFrame(file_url));
+ const std::string& serialized_contents =
+ GetSerializedContentForFrame(file_url);
+ // Compare the serialized contents with original contents to make sure
+ // they are same.
+ // Because we add MOTW when serializing DOM, so before comparison, we also
+ // need to add MOTW to original_contents.
+ std::string original_str =
+ WebPageSerializer::generateMarkOfTheWebDeclaration(file_url).utf8();
+ original_str += original_contents;
+ // Since WebCore now inserts a new HEAD element if there is no HEAD element
+ // when creating BODY element. (Please see HTMLParser::bodyCreateErrorCheck.)
+ // We need to append the HEAD content and corresponding META content if we
+ // find WebCore-generated HEAD element.
+ if (!doc.head().isNull()) {
+ WebString encoding = web_frame->encoding();
+ std::string htmlTag("<html>");
+ std::string::size_type pos = original_str.find(htmlTag);
+ ASSERT_NE(std::string::npos, pos);
+ pos += htmlTag.length();
+ std::string head_part("<head>");
+ head_part +=
+ WebPageSerializer::generateMetaCharsetDeclaration(encoding).utf8();
+ head_part += "</head>";
+ original_str.insert(pos, head_part);
+ }
+ ASSERT_EQ(original_str, serialized_contents);
+}
+
+// Test situation of html entities in attribute value when serializing
+// HTML DOM.
+TEST_F(DomSerializerTests, SerializeHTMLDOMWithEntitiesInAttributeValue) {
+ FilePath page_file_path = data_dir_;
+ page_file_path = page_file_path.AppendASCII(
+ "dom_serializer/htmlentities_in_attribute_value.htm");
+ // Get file URL. The URL is dummy URL to identify the following loading
+ // actions. The test content is in constant:original_contents.
+ GURL file_url = net::FilePathToFileURL(page_file_path);
+ ASSERT_TRUE(file_url.SchemeIsFile());
+ // Test contents.
+ static const char* const original_contents =
+ "<html><body title=\"&amp;&lt;&gt;&quot;&#39;\"></body></html>";
+ // Load the test contents.
+ LoadContents(original_contents, file_url, WebString());
+ // Get value of BODY's title attribute in DOM.
+ WebFrame* web_frame = FindSubFrameByURL(test_shell_->webView(), file_url);
+ ASSERT_TRUE(web_frame != NULL);
+ WebDocument doc = web_frame->document();
+ ASSERT_TRUE(doc.isHTMLDocument());
+ WebElement body_ele = doc.body();
+ ASSERT_TRUE(!body_ele.isNull());
+ WebString value = body_ele.getAttribute("title");
+ ASSERT_TRUE(std::string(value.utf8()) == "&<>\"\'");
+ // Do serialization.
+ SerializeDomForURL(file_url, false);
+ // Compare the serialized contents with original contents.
+ ASSERT_TRUE(HasSerializedFrame(file_url));
+ const std::string& serialized_contents =
+ GetSerializedContentForFrame(file_url);
+ // Compare the serialized contents with original contents to make sure
+ // they are same.
+ std::string original_str =
+ WebPageSerializer::generateMarkOfTheWebDeclaration(file_url).utf8();
+ original_str += original_contents;
+ if (!doc.isNull()) {
+ WebString encoding = web_frame->encoding();
+ std::string htmlTag("<html>");
+ std::string::size_type pos = original_str.find(htmlTag);
+ ASSERT_NE(std::string::npos, pos);
+ pos += htmlTag.length();
+ std::string head_part("<head>");
+ head_part +=
+ WebPageSerializer::generateMetaCharsetDeclaration(encoding).utf8();
+ head_part += "</head>";
+ original_str.insert(pos, head_part);
+ }
+ ASSERT_EQ(original_str, serialized_contents);
+}
+
+// Test situation of non-standard HTML entities when serializing HTML DOM.
+TEST_F(DomSerializerTests, SerializeHTMLDOMWithNonStandardEntities) {
+ // Make a test file URL and load it.
+ FilePath page_file_path = data_dir_;
+ page_file_path = page_file_path.AppendASCII("dom_serializer");
+ page_file_path = page_file_path.AppendASCII("nonstandard_htmlentities.htm");
+ GURL file_url = net::FilePathToFileURL(page_file_path);
+ LoadPageFromURL(file_url);
+
+ // Get value of BODY's title attribute in DOM.
+ WebFrame* web_frame = FindSubFrameByURL(test_shell_->webView(), file_url);
+ WebDocument doc = web_frame->document();
+ ASSERT_TRUE(doc.isHTMLDocument());
+ WebElement body_element = doc.body();
+ // Unescaped string for "&percnt;&nsup;&supl;&apos;".
+ static const wchar_t parsed_value[] = {
+ '%', 0x2285, 0x00b9, '\'', 0
+ };
+ WebString value = body_element.getAttribute("title");
+ ASSERT_TRUE(UTF16ToWide(value) == parsed_value);
+ ASSERT_TRUE(UTF16ToWide(body_element.innerText()) == parsed_value);
+
+ // Do serialization.
+ SerializeDomForURL(file_url, false);
+ // Check the serialized string.
+ ASSERT_TRUE(HasSerializedFrame(file_url));
+ const std::string& serialized_contents =
+ GetSerializedContentForFrame(file_url);
+ // Confirm that the serialized string has no non-standard HTML entities.
+ ASSERT_EQ(std::string::npos, serialized_contents.find("&percnt;"));
+ ASSERT_EQ(std::string::npos, serialized_contents.find("&nsup;"));
+ ASSERT_EQ(std::string::npos, serialized_contents.find("&supl;"));
+ ASSERT_EQ(std::string::npos, serialized_contents.find("&apos;"));
+}
+
+// Test situation of BASE tag in original document when serializing HTML DOM.
+// When serializing, we should comment the BASE tag, append a new BASE tag.
+// rewrite all the savable URLs to relative local path, and change other URLs
+// to absolute URLs.
+TEST_F(DomSerializerTests, SerializeHTMLDOMWithBaseTag) {
+ // There are total 2 available base tags in this test file.
+ const int kTotalBaseTagCountInTestFile = 2;
+
+ FilePath page_file_path = data_dir_.AppendASCII("dom_serializer");
+ file_util::EnsureEndsWithSeparator(&page_file_path);
+
+ // Get page dir URL which is base URL of this file.
+ GURL path_dir_url = net::FilePathToFileURL(page_file_path);
+ // Get file path.
+ page_file_path =
+ page_file_path.AppendASCII("html_doc_has_base_tag.htm");
+ // Get file URL.
+ GURL file_url = net::FilePathToFileURL(page_file_path);
+ ASSERT_TRUE(file_url.SchemeIsFile());
+ // Load the test file.
+ LoadPageFromURL(file_url);
+ // Since for this test, we assume there is no savable sub-resource links for
+ // this test file, also all links are relative URLs in this test file, so we
+ // need to check those relative URLs and make sure document has BASE tag.
+ WebFrame* web_frame = FindSubFrameByURL(test_shell_->webView(), file_url);
+ ASSERT_TRUE(web_frame != NULL);
+ WebDocument doc = web_frame->document();
+ ASSERT_TRUE(doc.isHTMLDocument());
+ // Go through all descent nodes.
+ WebNodeCollection all = doc.all();
+ int original_base_tag_count = 0;
+ for (WebNode node = all.firstItem(); !node.isNull();
+ node = all.nextItem()) {
+ if (!node.isElementNode())
+ continue;
+ WebElement element = node.to<WebElement>();
+ if (element.hasTagName("base")) {
+ original_base_tag_count++;
+ } else {
+ // Get link.
+ WebString value =
+ webkit_glue::GetSubResourceLinkFromElement(element);
+ if (value.isNull() && element.hasTagName("a")) {
+ value = element.getAttribute("href");
+ if (value.isEmpty())
+ value = WebString();
+ }
+ // Each link is relative link.
+ if (!value.isNull()) {
+ GURL link(value.utf8());
+ ASSERT_TRUE(link.scheme().empty());
+ }
+ }
+ }
+ ASSERT_EQ(original_base_tag_count, kTotalBaseTagCountInTestFile);
+ // Make sure in original document, the base URL is not equal with the
+ // |path_dir_url|.
+ GURL original_base_url(doc.baseURL());
+ ASSERT_NE(original_base_url, path_dir_url);
+
+ // Do serialization.
+ SerializeDomForURL(file_url, false);
+
+ // Load the serialized contents.
+ ASSERT_TRUE(HasSerializedFrame(file_url));
+ const std::string& serialized_contents =
+ GetSerializedContentForFrame(file_url);
+ LoadContents(serialized_contents, file_url,
+ web_frame->encoding());
+
+ // Make sure all links are absolute URLs and doc there are some number of
+ // BASE tags in serialized HTML data. Each of those BASE tags have same base
+ // URL which is as same as URL of current test file.
+ web_frame = test_shell_->webView()->mainFrame();
+ ASSERT_TRUE(web_frame != NULL);
+ doc = web_frame->document();
+ ASSERT_TRUE(doc.isHTMLDocument());
+ // Go through all descent nodes.
+ all = doc.all();
+ int new_base_tag_count = 0;
+ for (WebNode node = all.firstItem(); !node.isNull();
+ node = all.nextItem()) {
+ if (!node.isElementNode())
+ continue;
+ WebElement element = node.to<WebElement>();
+ if (element.hasTagName("base")) {
+ new_base_tag_count++;
+ } else {
+ // Get link.
+ WebString value =
+ webkit_glue::GetSubResourceLinkFromElement(element);
+ if (value.isNull() && element.hasTagName("a")) {
+ value = element.getAttribute("href");
+ if (value.isEmpty())
+ value = WebString();
+ }
+ // Each link is absolute link.
+ if (!value.isNull()) {
+ GURL link(std::string(value.utf8()));
+ ASSERT_FALSE(link.scheme().empty());
+ }
+ }
+ }
+ // We have one more added BASE tag which is generated by JavaScript.
+ ASSERT_EQ(new_base_tag_count, original_base_tag_count + 1);
+ // Make sure in new document, the base URL is equal with the |path_dir_url|.
+ GURL new_base_url(doc.baseURL());
+ ASSERT_EQ(new_base_url, path_dir_url);
+}
+
+// Serializing page which has an empty HEAD tag.
+TEST_F(DomSerializerTests, SerializeHTMLDOMWithEmptyHead) {
+ FilePath page_file_path = data_dir_;
+ page_file_path = page_file_path.AppendASCII("dom_serializer");
+ page_file_path = page_file_path.AppendASCII("empty_head.htm");
+ GURL file_url = net::FilePathToFileURL(page_file_path);
+ ASSERT_TRUE(file_url.SchemeIsFile());
+
+ // Load the test html content.
+ static const char* const empty_head_contents =
+ "<html><head></head><body>hello world</body></html>";
+ LoadContents(empty_head_contents, file_url, WebString());
+
+ // Make sure the head tag is empty.
+ WebFrame* web_frame = test_shell_->webView()->mainFrame();
+ ASSERT_TRUE(web_frame != NULL);
+ WebDocument doc = web_frame->document();
+ ASSERT_TRUE(doc.isHTMLDocument());
+ WebElement head_element = doc.head();
+ ASSERT_TRUE(!head_element.isNull());
+ ASSERT_TRUE(!head_element.hasChildNodes());
+ ASSERT_TRUE(head_element.childNodes().length() == 0);
+
+ // Do serialization.
+ SerializeDomForURL(file_url, false);
+ // Make sure the serialized contents have META ;
+ ASSERT_TRUE(HasSerializedFrame(file_url));
+ const std::string& serialized_contents =
+ GetSerializedContentForFrame(file_url);
+
+ // Reload serialized contents and make sure there is only one META tag.
+ LoadContents(serialized_contents, file_url, web_frame->encoding());
+ web_frame = test_shell_->webView()->mainFrame();
+ ASSERT_TRUE(web_frame != NULL);
+ doc = web_frame->document();
+ ASSERT_TRUE(doc.isHTMLDocument());
+ head_element = doc.head();
+ ASSERT_TRUE(!head_element.isNull());
+ ASSERT_TRUE(head_element.hasChildNodes());
+ ASSERT_TRUE(head_element.childNodes().length() == 1);
+ WebNode meta_node = head_element.firstChild();
+ ASSERT_TRUE(!meta_node.isNull());
+ // Get meta charset info.
+ std::string charset_info;
+ ASSERT_TRUE(IsMetaElement(meta_node, charset_info));
+ ASSERT_TRUE(!charset_info.empty());
+ ASSERT_TRUE(charset_info == std::string(web_frame->encoding().utf8()));
+
+ // Check the body's first node is text node and its contents are
+ // "hello world"
+ WebElement body_element = doc.body();
+ ASSERT_TRUE(!body_element.isNull());
+ WebNode text_node = body_element.firstChild();
+ ASSERT_TRUE(text_node.isTextNode());
+ WebString text_node_contents = text_node.nodeValue();
+ ASSERT_TRUE(std::string(text_node_contents.utf8()) == "hello world");
+}
+
+// Test that we don't crash when the page contains an iframe that
+// was handled as a download (http://crbug.com/42212).
+TEST_F(DomSerializerTests, SerializeDocumentWithDownloadedIFrame) {
+ FilePath page_file_path = data_dir_;
+ page_file_path = page_file_path.AppendASCII("dom_serializer");
+ page_file_path = page_file_path.AppendASCII("iframe-src-is-exe.htm");
+ GURL file_url = net::FilePathToFileURL(page_file_path);
+ ASSERT_TRUE(file_url.SchemeIsFile());
+ // Load the test file.
+ LoadPageFromURL(file_url);
+ // Do a recursive serialization. We pass if we don't crash.
+ SerializeDomForURL(file_url, true);
+}
+
+} // namespace
diff --git a/webkit/glue/form_data.h b/webkit/glue/form_data.h
new file mode 100644
index 0000000..033cac3
--- /dev/null
+++ b/webkit/glue/form_data.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_FORM_DATA_H__
+#define WEBKIT_GLUE_FORM_DATA_H__
+
+#include <vector>
+
+#include "base/string_util.h"
+#include "googleurl/src/gurl.h"
+#include "webkit/glue/form_field.h"
+
+namespace webkit_glue {
+
+// Holds information about a form to be filled and/or submitted.
+struct FormData {
+ // The name of the form.
+ string16 name;
+ // GET or POST.
+ string16 method;
+ // The URL (minus query parameters) containing the form.
+ GURL origin;
+ // The action target of the form.
+ GURL action;
+ // A vector of all the input fields in the form.
+ std::vector<FormField> fields;
+
+ // Used by FormStructureTest.
+ inline bool operator==(const FormData& form) const {
+ return (name == form.name &&
+ StringToLowerASCII(method) == StringToLowerASCII(form.method) &&
+ origin == form.origin &&
+ action == form.action &&
+ fields == form.fields);
+ }
+};
+
+} // namespace webkit_glue
+
+#endif // WEBKIT_GLUE_FORM_DATA_H__
diff --git a/webkit/glue/form_field.cc b/webkit/glue/form_field.cc
new file mode 100644
index 0000000..a0fbdef
--- /dev/null
+++ b/webkit/glue/form_field.cc
@@ -0,0 +1,103 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/form_field.h"
+
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebInputElement.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebOptionElement.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebSelectElement.h"
+
+using WebKit::WebFormControlElement;
+using WebKit::WebElement;
+using WebKit::WebInputElement;
+using WebKit::WebOptionElement;
+using WebKit::WebSelectElement;
+using WebKit::WebVector;
+
+namespace webkit_glue {
+
+FormField::FormField()
+ : size_(0) {
+}
+
+// TODO(jhawkins): This constructor should probably be deprecated and the
+// functionality moved to FormManager.
+FormField::FormField(WebFormControlElement element)
+ : size_(0) {
+ name_ = element.nameForAutofill();
+
+ // TODO(jhawkins): Extract the field label. For now we just use the field
+ // name.
+ label_ = name_;
+
+ form_control_type_ = element.formControlType();
+ if (form_control_type_ == ASCIIToUTF16("text")) {
+ const WebInputElement& input_element = element.toConst<WebInputElement>();
+ value_ = input_element.value();
+ size_ = input_element.size();
+ } else if (form_control_type_ == ASCIIToUTF16("select-one")) {
+ WebSelectElement select_element = element.to<WebSelectElement>();
+ value_ = select_element.value();
+
+ // For select-one elements copy option strings.
+ WebVector<WebElement> list_items = select_element.listItems();
+ option_strings_.reserve(list_items.size());
+ for (size_t i = 0; i < list_items.size(); ++i) {
+ if (list_items[i].hasTagName("option"))
+ option_strings_.push_back(list_items[i].to<WebOptionElement>().value());
+ }
+ }
+
+ TrimWhitespace(value_, TRIM_LEADING, &value_);
+}
+
+FormField::FormField(const string16& label,
+ const string16& name,
+ const string16& value,
+ const string16& form_control_type,
+ int size)
+ : label_(label),
+ name_(name),
+ value_(value),
+ form_control_type_(form_control_type),
+ size_(size) {
+}
+
+bool FormField::operator==(const FormField& field) const {
+ // A FormField stores a value, but the value is not part of the identity of
+ // the field, so we don't want to compare the values.
+ return (label_ == field.label_ &&
+ name_ == field.name_ &&
+ form_control_type_ == field.form_control_type_ &&
+ size_ == field.size_);
+}
+
+bool FormField::operator!=(const FormField& field) const {
+ return !operator==(field);
+}
+
+bool FormField::StrictlyEqualsHack(const FormField& field) const {
+ return (label_ == field.label_ &&
+ name_ == field.name_ &&
+ value_ == field.value_ &&
+ form_control_type_ == field.form_control_type_ &&
+ size_ == field.size_);
+}
+
+std::ostream& operator<<(std::ostream& os, const FormField& field) {
+ return os
+ << UTF16ToUTF8(field.label())
+ << " "
+ << UTF16ToUTF8(field.name())
+ << " "
+ << UTF16ToUTF8(field.value())
+ << " "
+ << UTF16ToUTF8(field.form_control_type())
+ << " "
+ << field.size();
+}
+
+} // namespace webkit_glue
diff --git a/webkit/glue/form_field.h b/webkit/glue/form_field.h
new file mode 100644
index 0000000..2a1ffcf
--- /dev/null
+++ b/webkit/glue/form_field.h
@@ -0,0 +1,73 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_FORM_FIELD_H_
+#define WEBKIT_GLUE_FORM_FIELD_H_
+
+#include <vector>
+
+#include "base/string16.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebFormControlElement.h"
+
+namespace webkit_glue {
+
+// Stores information about a field in a form.
+class FormField {
+ public:
+ FormField();
+ explicit FormField(WebKit::WebFormControlElement element);
+ FormField(const string16& label,
+ const string16& name,
+ const string16& value,
+ const string16& form_control_type,
+ int size);
+
+ const string16& label() const { return label_; }
+ const string16& name() const { return name_; }
+ const string16& value() const { return value_; }
+ const string16& form_control_type() const { return form_control_type_; }
+ int size() const { return size_; }
+ // Returns option string for elements for which they make sense (select-one,
+ // for example) for the rest of elements return an empty array.
+ const std::vector<string16>& option_strings() const {
+ return option_strings_;
+ }
+
+ void set_label(const string16& label) { label_ = label; }
+ void set_name(const string16& name) { name_ = name; }
+ void set_value(const string16& value) { value_ = value; }
+ void set_form_control_type(const string16& form_control_type) {
+ form_control_type_ = form_control_type;
+ }
+ void set_size(int size) { size_ = size; }
+ void set_option_strings(const std::vector<string16>& strings) {
+ option_strings_ = strings;
+ }
+
+ // Equality tests for identity which does not include |value_| or |size_|.
+ // Use |StrictlyEqualsHack| method to test all members.
+ // TODO(dhollowa): These operators need to be revised when we implement field
+ // ids.
+ bool operator==(const FormField& field) const;
+ bool operator!=(const FormField& field) const;
+
+ // Test equality of all data members.
+ // TODO(dhollowa): This will be removed when we implement field ids.
+ bool StrictlyEqualsHack(const FormField& field) const;
+
+ private:
+ string16 label_;
+ string16 name_;
+ string16 value_;
+ string16 form_control_type_;
+ int size_;
+ std::vector<string16> option_strings_;
+};
+
+// So we can compare FormFields with EXPECT_EQ().
+std::ostream& operator<<(std::ostream& os, const FormField& profile);
+
+} // namespace webkit_glue
+
+#endif // WEBKIT_GLUE_FORM_FIELD_H_
diff --git a/webkit/glue/ftp_directory_listing_response_delegate.cc b/webkit/glue/ftp_directory_listing_response_delegate.cc
new file mode 100644
index 0000000..80737db
--- /dev/null
+++ b/webkit/glue/ftp_directory_listing_response_delegate.cc
@@ -0,0 +1,159 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/ftp_directory_listing_response_delegate.h"
+
+#include <vector>
+
+#include "base/i18n/icu_encoding_detection.h"
+#include "base/i18n/icu_string_conversions.h"
+#include "base/logging.h"
+#include "base/string_util.h"
+#include "base/sys_string_conversions.h"
+#include "base/utf_string_conversions.h"
+#include "base/time.h"
+#include "net/base/escape.h"
+#include "net/base/net_errors.h"
+#include "net/base/net_util.h"
+#include "net/ftp/ftp_directory_listing_parser.h"
+#include "net/ftp/ftp_server_type_histograms.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURL.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURLLoaderClient.h"
+
+using net::FtpDirectoryListingEntry;
+
+using WebKit::WebURLLoader;
+using WebKit::WebURLLoaderClient;
+using WebKit::WebURLResponse;
+
+namespace {
+
+string16 ConvertPathToUTF16(const std::string& path) {
+ // Per RFC 2640, FTP servers should use UTF-8 or its proper subset ASCII,
+ // but many old FTP servers use legacy encodings. Try UTF-8 first.
+ if (IsStringUTF8(path))
+ return UTF8ToUTF16(path);
+
+ // Try detecting the encoding. The sample is rather small though, so it may
+ // fail.
+ std::string encoding;
+ if (base::DetectEncoding(path, &encoding) && !encoding.empty()) {
+ string16 path_utf16;
+ if (base::CodepageToUTF16(path, encoding.c_str(),
+ base::OnStringConversionError::SUBSTITUTE,
+ &path_utf16)) {
+ return path_utf16;
+ }
+ }
+
+ // Use system native encoding as the last resort.
+ return WideToUTF16Hack(base::SysNativeMBToWide(path));
+}
+
+} // namespace
+
+namespace webkit_glue {
+
+FtpDirectoryListingResponseDelegate::FtpDirectoryListingResponseDelegate(
+ WebURLLoaderClient* client,
+ WebURLLoader* loader,
+ const WebURLResponse& response)
+ : client_(client),
+ loader_(loader),
+ original_response_(response),
+ buffer_(base::Time::Now()),
+ updated_histograms_(false),
+ had_parsing_error_(false) {
+ Init();
+}
+
+void FtpDirectoryListingResponseDelegate::OnReceivedData(const char* data,
+ int data_len) {
+ if (had_parsing_error_)
+ return;
+
+ if (buffer_.ConsumeData(data, data_len) == net::OK)
+ ProcessReceivedEntries();
+ else
+ had_parsing_error_ = true;
+}
+
+void FtpDirectoryListingResponseDelegate::OnCompletedRequest() {
+ if (!had_parsing_error_ && buffer_.ProcessRemainingData() == net::OK)
+ ProcessReceivedEntries();
+ else
+ had_parsing_error_ = true;
+
+ if (had_parsing_error_)
+ SendDataToClient("<script>onListingParsingError();</script>\n");
+}
+
+void FtpDirectoryListingResponseDelegate::Init() {
+ GURL response_url(original_response_.url());
+ UnescapeRule::Type unescape_rules = UnescapeRule::SPACES |
+ UnescapeRule::URL_SPECIAL_CHARS;
+ std::string unescaped_path = UnescapeURLComponent(response_url.path(),
+ unescape_rules);
+ SendDataToClient(net::GetDirectoryListingHeader(
+ ConvertPathToUTF16(unescaped_path)));
+
+ // If this isn't top level directory (i.e. the path isn't "/",)
+ // add a link to the parent directory.
+ if (response_url.path().length() > 1) {
+ SendDataToClient(net::GetDirectoryListingEntry(
+ ASCIIToUTF16(".."), std::string(), false, 0, base::Time()));
+ }
+}
+
+bool FtpDirectoryListingResponseDelegate::ConvertToServerEncoding(
+ const string16& filename, std::string* raw_bytes) const {
+ if (buffer_.encoding().empty()) {
+ *raw_bytes = std::string();
+ return true;
+ }
+
+ return base::UTF16ToCodepage(filename, buffer_.encoding().c_str(),
+ base::OnStringConversionError::FAIL,
+ raw_bytes);
+}
+
+void FtpDirectoryListingResponseDelegate::ProcessReceivedEntries() {
+ if (!updated_histograms_ && buffer_.EntryAvailable()) {
+ // Only log the server type if we got enough data to reliably detect it.
+ net::UpdateFtpServerTypeHistograms(buffer_.GetServerType());
+ updated_histograms_ = true;
+ }
+
+ while (buffer_.EntryAvailable()) {
+ FtpDirectoryListingEntry entry = buffer_.PopEntry();
+
+ // Skip the current and parent directory entries in the listing. Our header
+ // always includes them.
+ if (EqualsASCII(entry.name, ".") || EqualsASCII(entry.name, ".."))
+ continue;
+
+ bool is_directory = (entry.type == FtpDirectoryListingEntry::DIRECTORY);
+ int64 size = entry.size;
+ if (entry.type != FtpDirectoryListingEntry::FILE)
+ size = 0;
+ std::string raw_bytes;
+ if (ConvertToServerEncoding(entry.name, &raw_bytes)) {
+ SendDataToClient(net::GetDirectoryListingEntry(
+ entry.name, raw_bytes, is_directory, size, entry.last_modified));
+ } else {
+ // Consider an encoding problem a non-fatal error. The server's support
+ // for non-ASCII characters might be buggy. Display an error message,
+ // but keep trying to display the rest of the listing (most file names
+ // are ASCII anyway, we could be just unlucky with this one).
+ had_parsing_error_ = true;
+ }
+ }
+}
+
+void FtpDirectoryListingResponseDelegate::SendDataToClient(
+ const std::string& data) {
+ client_->didReceiveData(loader_, data.data(), data.length());
+}
+
+} // namespace webkit_glue
diff --git a/webkit/glue/ftp_directory_listing_response_delegate.h b/webkit/glue/ftp_directory_listing_response_delegate.h
new file mode 100644
index 0000000..1218da9
--- /dev/null
+++ b/webkit/glue/ftp_directory_listing_response_delegate.h
@@ -0,0 +1,68 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// A delegate class of WebURLLoaderImpl that handles text/vnd.chromium.ftp-dir
+// data.
+
+#ifndef WEBKIT_GLUE_FTP_DIRECTORY_LISTING_RESPONSE_DELEGATE_H_
+#define WEBKIT_GLUE_FTP_DIRECTORY_LISTING_RESPONSE_DELEGATE_H_
+
+#include <string>
+
+#include "net/ftp/ftp_directory_listing_buffer.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURLResponse.h"
+
+namespace WebKit {
+class WebURLLoader;
+class WebURLLoaderClient;
+}
+
+namespace webkit_glue {
+
+class FtpDirectoryListingResponseDelegate {
+ public:
+ FtpDirectoryListingResponseDelegate(WebKit::WebURLLoaderClient* client,
+ WebKit::WebURLLoader* loader,
+ const WebKit::WebURLResponse& response);
+
+ // Passed through from ResourceHandleInternal
+ void OnReceivedData(const char* data, int data_len);
+ void OnCompletedRequest();
+
+ private:
+ void Init();
+
+ // Converts |filename| to detected server encoding and puts the result
+ // in |raw_bytes| (if no conversion is necessary, an empty string is used).
+ // Returns true on success.
+ bool ConvertToServerEncoding(const string16& filename,
+ std::string* raw_bytes) const;
+
+ // Fetches the listing entries from the buffer and sends them to the client.
+ void ProcessReceivedEntries();
+
+ void SendDataToClient(const std::string& data);
+
+ // Pointers to the client and associated loader so we can make callbacks as
+ // we parse pieces of data.
+ WebKit::WebURLLoaderClient* client_;
+ WebKit::WebURLLoader* loader_;
+
+ // The original resource response for this request. We use this as a
+ // starting point for each parts response.
+ WebKit::WebURLResponse original_response_;
+
+ // Data buffer also responsible for parsing the listing data.
+ net::FtpDirectoryListingBuffer buffer_;
+
+ // True if we updated histogram data (we only want to do it once).
+ bool updated_histograms_;
+
+ // True if we got an error when parsing the response.
+ bool had_parsing_error_;
+};
+
+} // namespace webkit_glue
+
+#endif // WEBKIT_GLUE_FTP_DIRECTORY_LISTING_RESPONSE_DELEGATE_H_
diff --git a/webkit/glue/glue_serialize.cc b/webkit/glue/glue_serialize.cc
new file mode 100644
index 0000000..caf37b4
--- /dev/null
+++ b/webkit/glue/glue_serialize.cc
@@ -0,0 +1,473 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/glue_serialize.h"
+
+#include <string>
+
+#include "base/pickle.h"
+#include "base/utf_string_conversions.h"
+#include "googleurl/src/gurl.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebData.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebHistoryItem.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebHTTPBody.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebPoint.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebSerializedScriptValue.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebString.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebVector.h"
+#include "webkit/glue/webkit_glue.h"
+
+using WebKit::WebData;
+using WebKit::WebFileInfo;
+using WebKit::WebHistoryItem;
+using WebKit::WebHTTPBody;
+using WebKit::WebPoint;
+using WebKit::WebSerializedScriptValue;
+using WebKit::WebString;
+using WebKit::WebUChar;
+using WebKit::WebVector;
+
+namespace webkit_glue {
+
+struct SerializeObject {
+ SerializeObject() : iter(NULL) {}
+ SerializeObject(const char* data, int len) : pickle(data, len), iter(NULL) {}
+
+ std::string GetAsString() {
+ return std::string(static_cast<const char*>(pickle.data()), pickle.size());
+ }
+
+ Pickle pickle;
+ mutable void* iter;
+ mutable int version;
+};
+
+// TODO(mpcomplete): obsolete versions 1 and 2 after 1/1/2008.
+// Version ID used in reading/writing history items.
+// 1: Initial revision.
+// 2: Added case for NULL string versus "". Version 2 code can read Version 1
+// data, but not vice versa.
+// 3: Version 2 was broken, it stored number of WebUChars, not number of bytes.
+// This version checks and reads v1 and v2 correctly.
+// 4: Adds support for storing FormData::identifier().
+// 5: Adds support for empty FormData
+// 6: Adds support for documentSequenceNumbers
+// 7: Adds support for stateObject
+// 8: Adds support for file range and modification time
+// 9: Adds support for itemSequenceNumbers
+// Should be const, but unit tests may modify it.
+//
+// NOTE: If the version is -1, then the pickle contains only a URL string.
+// See CreateHistoryStateForURL.
+//
+int kVersion = 9;
+
+// A bunch of convenience functions to read/write to SerializeObjects.
+// The serializers assume the input data is in the correct format and so does
+// no error checking.
+inline void WriteData(const void* data, int length, SerializeObject* obj) {
+ obj->pickle.WriteData(static_cast<const char*>(data), length);
+}
+
+inline void ReadData(const SerializeObject* obj, const void** data,
+ int* length) {
+ const char* tmp = NULL;
+ obj->pickle.ReadData(&obj->iter, &tmp, length);
+ *data = tmp;
+}
+
+inline bool ReadBytes(const SerializeObject* obj, const void** data,
+ int length) {
+ const char *tmp;
+ if (!obj->pickle.ReadBytes(&obj->iter, &tmp, length))
+ return false;
+ *data = tmp;
+ return true;
+}
+
+inline void WriteInteger(int data, SerializeObject* obj) {
+ obj->pickle.WriteInt(data);
+}
+
+inline int ReadInteger(const SerializeObject* obj) {
+ int tmp = 0;
+ obj->pickle.ReadInt(&obj->iter, &tmp);
+ return tmp;
+}
+
+inline void WriteInteger64(int64 data, SerializeObject* obj) {
+ obj->pickle.WriteInt64(data);
+}
+
+inline int64 ReadInteger64(const SerializeObject* obj) {
+ int64 tmp = 0;
+ obj->pickle.ReadInt64(&obj->iter, &tmp);
+ return tmp;
+}
+
+inline void WriteReal(double data, SerializeObject* obj) {
+ WriteData(&data, sizeof(double), obj);
+}
+
+inline double ReadReal(const SerializeObject* obj) {
+ const void* tmp;
+ int length = 0;
+ ReadData(obj, &tmp, &length);
+ if (length > 0 && length >= static_cast<int>(sizeof(0.0)))
+ return *static_cast<const double*>(tmp);
+ else
+ return 0.0;
+}
+
+inline void WriteBoolean(bool data, SerializeObject* obj) {
+ obj->pickle.WriteInt(data ? 1 : 0);
+}
+
+inline bool ReadBoolean(const SerializeObject* obj) {
+ bool tmp = false;
+ obj->pickle.ReadBool(&obj->iter, &tmp);
+ return tmp;
+}
+
+inline void WriteGURL(const GURL& url, SerializeObject* obj) {
+ obj->pickle.WriteString(url.possibly_invalid_spec());
+}
+
+inline GURL ReadGURL(const SerializeObject* obj) {
+ std::string spec;
+ obj->pickle.ReadString(&obj->iter, &spec);
+ return GURL(spec);
+}
+
+// Read/WriteString pickle the WebString as <int length><WebUChar* data>.
+// If length == -1, then the WebString itself is NULL (WebString()).
+// Otherwise the length is the number of WebUChars (not bytes) in the WebString.
+inline void WriteString(const WebString& str, SerializeObject* obj) {
+ switch (kVersion) {
+ case 1:
+ // Version 1 writes <length in bytes><string data>.
+ // It saves WebString() and "" as "".
+ obj->pickle.WriteInt(str.length() * sizeof(WebUChar));
+ obj->pickle.WriteBytes(str.data(), str.length() * sizeof(WebUChar));
+ break;
+ case 2:
+ // Version 2 writes <length in WebUChar><string data>.
+ // It uses -1 in the length field to mean WebString().
+ if (str.isNull()) {
+ obj->pickle.WriteInt(-1);
+ } else {
+ obj->pickle.WriteInt(str.length());
+ obj->pickle.WriteBytes(str.data(),
+ str.length() * sizeof(WebUChar));
+ }
+ break;
+ default:
+ // Version 3+ writes <length in bytes><string data>.
+ // It uses -1 in the length field to mean WebString().
+ if (str.isNull()) {
+ obj->pickle.WriteInt(-1);
+ } else {
+ obj->pickle.WriteInt(str.length() * sizeof(WebUChar));
+ obj->pickle.WriteBytes(str.data(),
+ str.length() * sizeof(WebUChar));
+ }
+ break;
+ }
+}
+
+// This reads a serialized WebString from obj. If a string can't be read,
+// WebString() is returned.
+inline WebString ReadString(const SerializeObject* obj) {
+ int length;
+
+ // Versions 1, 2, and 3 all start with an integer.
+ if (!obj->pickle.ReadInt(&obj->iter, &length))
+ return WebString();
+
+ // Starting with version 2, -1 means WebString().
+ if (length == -1)
+ return WebString();
+
+ // In version 2, the length field was the length in WebUChars.
+ // In version 1 and 3 it is the length in bytes.
+ int bytes = ((obj->version == 2) ? length * sizeof(WebUChar) : length);
+
+ const void* data;
+ if (!ReadBytes(obj, &data, bytes))
+ return WebString();
+ return WebString(static_cast<const WebUChar*>(data),
+ bytes / sizeof(WebUChar));
+}
+
+// Writes a Vector of Strings into a SerializeObject for serialization.
+static void WriteStringVector(
+ const WebVector<WebString>& data, SerializeObject* obj) {
+ WriteInteger(static_cast<int>(data.size()), obj);
+ for (size_t i = 0, c = data.size(); i < c; ++i) {
+ unsigned ui = static_cast<unsigned>(i); // sigh
+ WriteString(data[ui], obj);
+ }
+}
+
+static WebVector<WebString> ReadStringVector(const SerializeObject* obj) {
+ int num_elements = ReadInteger(obj);
+ WebVector<WebString> result(static_cast<size_t>(num_elements));
+ for (int i = 0; i < num_elements; ++i)
+ result[i] = ReadString(obj);
+ return result;
+}
+
+// Writes a FormData object into a SerializeObject for serialization.
+static void WriteFormData(const WebHTTPBody& http_body, SerializeObject* obj) {
+ WriteBoolean(!http_body.isNull(), obj);
+
+ if (http_body.isNull())
+ return;
+
+ WriteInteger(static_cast<int>(http_body.elementCount()), obj);
+ WebHTTPBody::Element element;
+ for (size_t i = 0; http_body.elementAt(i, element); ++i) {
+ WriteInteger(element.type, obj);
+ if (element.type == WebHTTPBody::Element::TypeData) {
+ WriteData(element.data.data(), static_cast<int>(element.data.size()),
+ obj);
+ } else {
+ WriteString(element.filePath, obj);
+ WriteInteger64(element.fileStart, obj);
+ WriteInteger64(element.fileLength, obj);
+ WriteReal(element.fileInfo.modificationTime, obj);
+ }
+ }
+ WriteInteger64(http_body.identifier(), obj);
+}
+
+static WebHTTPBody ReadFormData(const SerializeObject* obj) {
+ // In newer versions, an initial boolean indicates if we have form data.
+ if (obj->version >= 5 && !ReadBoolean(obj))
+ return WebHTTPBody();
+
+ // In older versions, 0 elements implied no form data.
+ int num_elements = ReadInteger(obj);
+ if (num_elements == 0 && obj->version < 5)
+ return WebHTTPBody();
+
+ WebHTTPBody http_body;
+ http_body.initialize();
+
+ for (int i = 0; i < num_elements; ++i) {
+ int type = ReadInteger(obj);
+ if (type == WebHTTPBody::Element::TypeData) {
+ const void* data;
+ int length = -1;
+ ReadData(obj, &data, &length);
+ if (length >= 0)
+ http_body.appendData(WebData(static_cast<const char*>(data), length));
+ } else {
+ WebString file_path = ReadString(obj);
+ long long file_start = 0;
+ long long file_length = -1;
+ WebFileInfo file_info;
+ if (obj->version >= 8) {
+ file_start = ReadInteger64(obj);
+ file_length = ReadInteger64(obj);
+ file_info.modificationTime = ReadReal(obj);
+ }
+ http_body.appendFileRange(file_path, file_start, file_length, file_info);
+ }
+ }
+ if (obj->version >= 4)
+ http_body.setIdentifier(ReadInteger64(obj));
+
+ return http_body;
+}
+
+// Writes the HistoryItem data into the SerializeObject object for
+// serialization.
+static void WriteHistoryItem(
+ const WebHistoryItem& item, SerializeObject* obj) {
+ // WARNING: This data may be persisted for later use. As such, care must be
+ // taken when changing the serialized format. If a new field needs to be
+ // written, only adding at the end will make it easier to deal with loading
+ // older versions. Similarly, this should NOT save fields with sensitive
+ // data, such as password fields.
+ WriteInteger(kVersion, obj);
+ WriteString(item.urlString(), obj);
+ WriteString(item.originalURLString(), obj);
+ WriteString(item.target(), obj);
+ WriteString(item.parent(), obj);
+ WriteString(item.title(), obj);
+ WriteString(item.alternateTitle(), obj);
+ WriteReal(item.lastVisitedTime(), obj);
+ WriteInteger(item.scrollOffset().x, obj);
+ WriteInteger(item.scrollOffset().y, obj);
+ WriteBoolean(item.isTargetItem(), obj);
+ WriteInteger(item.visitCount(), obj);
+ WriteString(item.referrer(), obj);
+
+ WriteStringVector(item.documentState(), obj);
+
+ if (kVersion >= 9)
+ WriteInteger64(item.itemSequenceNumber(), obj);
+ if (kVersion >= 6)
+ WriteInteger64(item.documentSequenceNumber(), obj);
+ if (kVersion >= 7) {
+ bool has_state_object = !item.stateObject().isNull();
+ WriteBoolean(has_state_object, obj);
+ if (has_state_object)
+ WriteString(item.stateObject().toString(), obj);
+ }
+
+ // Yes, the referrer is written twice. This is for backwards
+ // compatibility with the format.
+ WriteFormData(item.httpBody(), obj);
+ WriteString(item.httpContentType(), obj);
+ WriteString(item.referrer(), obj);
+
+ // Subitems
+ const WebVector<WebHistoryItem>& children = item.children();
+ WriteInteger(static_cast<int>(children.size()), obj);
+ for (size_t i = 0, c = children.size(); i < c; ++i)
+ WriteHistoryItem(children[i], obj);
+}
+
+// Creates a new HistoryItem tree based on the serialized string.
+// Assumes the data is in the format returned by WriteHistoryItem.
+static WebHistoryItem ReadHistoryItem(
+ const SerializeObject* obj, bool include_form_data) {
+ // See note in WriteHistoryItem. on this.
+ obj->version = ReadInteger(obj);
+
+ if (obj->version == -1) {
+ GURL url = ReadGURL(obj);
+ WebHistoryItem item;
+ item.initialize();
+ item.setURLString(WebString::fromUTF8(url.possibly_invalid_spec()));
+ return item;
+ }
+
+ if (obj->version > kVersion || obj->version < 1)
+ return WebHistoryItem();
+
+ WebHistoryItem item;
+ item.initialize();
+
+ item.setURLString(ReadString(obj));
+ item.setOriginalURLString(ReadString(obj));
+ item.setTarget(ReadString(obj));
+ item.setParent(ReadString(obj));
+ item.setTitle(ReadString(obj));
+ item.setAlternateTitle(ReadString(obj));
+ item.setLastVisitedTime(ReadReal(obj));
+ int x = ReadInteger(obj);
+ int y = ReadInteger(obj);
+ item.setScrollOffset(WebPoint(x, y));
+ item.setIsTargetItem(ReadBoolean(obj));
+ item.setVisitCount(ReadInteger(obj));
+ item.setReferrer(ReadString(obj));
+
+ item.setDocumentState(ReadStringVector(obj));
+
+ if (obj->version >= 9)
+ item.setItemSequenceNumber(ReadInteger64(obj));
+ if (obj->version >= 6)
+ item.setDocumentSequenceNumber(ReadInteger64(obj));
+ if (obj->version >= 7) {
+ bool has_state_object = ReadBoolean(obj);
+ if (has_state_object) {
+ item.setStateObject(
+ WebSerializedScriptValue::fromString(ReadString(obj)));
+ }
+ }
+
+ // The extra referrer string is read for backwards compat.
+ const WebHTTPBody& http_body = ReadFormData(obj);
+ const WebString& http_content_type = ReadString(obj);
+ ALLOW_UNUSED const WebString& unused_referrer = ReadString(obj);
+ if (include_form_data) {
+ item.setHTTPBody(http_body);
+ item.setHTTPContentType(http_content_type);
+ }
+
+ // Subitems
+ int num_children = ReadInteger(obj);
+ for (int i = 0; i < num_children; ++i)
+ item.appendToChildren(ReadHistoryItem(obj, include_form_data));
+
+ return item;
+}
+
+// Serialize a HistoryItem to a string, using our JSON Value serializer.
+std::string HistoryItemToString(const WebHistoryItem& item) {
+ if (item.isNull())
+ return std::string();
+
+ SerializeObject obj;
+ WriteHistoryItem(item, &obj);
+ return obj.GetAsString();
+}
+
+// Reconstruct a HistoryItem from a string, using our JSON Value deserializer.
+// This assumes that the given serialized string has all the required key,value
+// pairs, and does minimal error checking. If |include_form_data| is true,
+// the form data from a post is restored, otherwise the form data is empty.
+static WebHistoryItem HistoryItemFromString(
+ const std::string& serialized_item,
+ bool include_form_data) {
+ if (serialized_item.empty())
+ return WebHistoryItem();
+
+ SerializeObject obj(serialized_item.data(),
+ static_cast<int>(serialized_item.length()));
+ return ReadHistoryItem(&obj, include_form_data);
+}
+
+WebHistoryItem HistoryItemFromString(
+ const std::string& serialized_item) {
+ return HistoryItemFromString(serialized_item, true);
+}
+
+// For testing purposes only.
+void HistoryItemToVersionedString(const WebHistoryItem& item, int version,
+ std::string* serialized_item) {
+ if (item.isNull()) {
+ serialized_item->clear();
+ return;
+ }
+
+ // Temporarily change the version.
+ int real_version = kVersion;
+ kVersion = version;
+
+ SerializeObject obj;
+ WriteHistoryItem(item, &obj);
+ *serialized_item = obj.GetAsString();
+
+ kVersion = real_version;
+}
+
+std::string CreateHistoryStateForURL(const GURL& url) {
+ // We avoid using the WebKit API here, so that we do not need to have WebKit
+ // initialized before calling this method. Instead, we write a simple
+ // serialization of the given URL with a dummy version number of -1. This
+ // will be interpreted by ReadHistoryItem as a request to create a default
+ // WebHistoryItem.
+ SerializeObject obj;
+ WriteInteger(-1, &obj);
+ WriteGURL(url, &obj);
+ return obj.GetAsString();
+}
+
+std::string RemoveFormDataFromHistoryState(const std::string& content_state) {
+ // TODO(darin): We should avoid using the WebKit API here, so that we do not
+ // need to have WebKit initialized before calling this method.
+ const WebHistoryItem& item = HistoryItemFromString(content_state, false);
+ if (item.isNull()) {
+ // Couldn't parse the string, return an empty string.
+ return std::string();
+ }
+
+ return HistoryItemToString(item);
+}
+
+} // namespace webkit_glue
diff --git a/webkit/glue/glue_serialize.h b/webkit/glue/glue_serialize.h
new file mode 100644
index 0000000..303fe3a
--- /dev/null
+++ b/webkit/glue/glue_serialize.h
@@ -0,0 +1,31 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// This file contains (de)serialization (or if you like python, pickling)
+// methods for various objects that we want to persist.
+// In serialization, we write an object's state to a string in some opaque
+// format. Deserialization reconstructs the object's state from such a string.
+
+#ifndef WEBKIT_GLUE_GLUE_SERIALIZE_H_
+#define WEBKIT_GLUE_GLUE_SERIALIZE_H_
+
+#include <string>
+#include "third_party/WebKit/WebKit/chromium/public/WebHistoryItem.h"
+
+namespace webkit_glue {
+
+// HistoryItem serialization.
+std::string HistoryItemToString(
+ const WebKit::WebHistoryItem& item);
+WebKit::WebHistoryItem HistoryItemFromString(
+ const std::string& serialized_item);
+
+// For testing purposes only.
+void HistoryItemToVersionedString(
+ const WebKit::WebHistoryItem& item, int version,
+ std::string* serialized_item);
+
+} // namespace webkit_glue
+
+#endif // #ifndef WEBKIT_GLUE_GLUE_SERIALIZE_H_
diff --git a/webkit/glue/glue_serialize_unittest.cc b/webkit/glue/glue_serialize_unittest.cc
new file mode 100644
index 0000000..34ce09f
--- /dev/null
+++ b/webkit/glue/glue_serialize_unittest.cc
@@ -0,0 +1,200 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <string>
+
+#include "base/pickle.h"
+#include "base/string_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebHTTPBody.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebPoint.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebVector.h"
+#include "webkit/glue/glue_serialize.h"
+
+using WebKit::WebData;
+using WebKit::WebFileInfo;
+using WebKit::WebHistoryItem;
+using WebKit::WebHTTPBody;
+using WebKit::WebPoint;
+using WebKit::WebString;
+using WebKit::WebUChar;
+using WebKit::WebVector;
+
+namespace {
+class GlueSerializeTest : public testing::Test {
+ public:
+ // Makes a FormData with some random data.
+ WebHTTPBody MakeFormData() {
+ WebHTTPBody http_body;
+ http_body.initialize();
+
+ const char d1[] = "first data block";
+ http_body.appendData(WebData(d1, sizeof(d1)-1));
+
+ http_body.appendFile(WebString::fromUTF8("file.txt"));
+
+ const char d2[] = "data the second";
+ http_body.appendData(WebData(d2, sizeof(d2)-1));
+
+ return http_body;
+ }
+
+ // Constructs a HistoryItem with some random data and an optional child.
+ WebHistoryItem MakeHistoryItem(bool with_form_data, bool pregnant) {
+ WebHistoryItem item;
+ item.initialize();
+
+ item.setURLString(WebString::fromUTF8("urlString"));
+ item.setOriginalURLString(WebString::fromUTF8("originalURLString"));
+ item.setTarget(WebString::fromUTF8("target"));
+ item.setParent(WebString::fromUTF8("parent"));
+ item.setTitle(WebString::fromUTF8("title"));
+ item.setAlternateTitle(WebString::fromUTF8("alternateTitle"));
+ item.setLastVisitedTime(13.37);
+ item.setScrollOffset(WebPoint(42, -42));
+ item.setIsTargetItem(true);
+ item.setVisitCount(42*42);
+
+ WebVector<WebString> document_state(size_t(3));
+ document_state[0] = WebString::fromUTF8("state1");
+ document_state[1] = WebString::fromUTF8("state2");
+ document_state[2] = WebString::fromUTF8("state AWESOME");
+ item.setDocumentState(document_state);
+
+ // Form Data
+ if (with_form_data) {
+ item.setHTTPBody(MakeFormData());
+ item.setHTTPContentType(WebString::fromUTF8("formContentType"));
+ }
+
+ // Setting the FormInfo causes the referrer to be set, so we set the
+ // referrer after setting the form info.
+ item.setReferrer(WebString::fromUTF8("referrer"));
+
+ // Children
+ if (pregnant)
+ item.appendToChildren(MakeHistoryItem(false, false));
+
+ return item;
+ }
+
+ // Checks that a == b.
+ void HistoryItemExpectEqual(const WebHistoryItem& a,
+ const WebHistoryItem& b) {
+ EXPECT_EQ(string16(a.urlString()), string16(b.urlString()));
+ EXPECT_EQ(string16(a.originalURLString()), string16(b.originalURLString()));
+ EXPECT_EQ(string16(a.target()), string16(b.target()));
+ EXPECT_EQ(string16(a.parent()), string16(b.parent()));
+ EXPECT_EQ(string16(a.title()), string16(b.title()));
+ EXPECT_EQ(string16(a.alternateTitle()), string16(b.alternateTitle()));
+ EXPECT_EQ(a.lastVisitedTime(), b.lastVisitedTime());
+ EXPECT_EQ(a.scrollOffset(), b.scrollOffset());
+ EXPECT_EQ(a.isTargetItem(), b.isTargetItem());
+ EXPECT_EQ(a.visitCount(), b.visitCount());
+ EXPECT_EQ(string16(a.referrer()), string16(b.referrer()));
+
+ const WebVector<WebString>& a_docstate = a.documentState();
+ const WebVector<WebString>& b_docstate = b.documentState();
+ EXPECT_EQ(a_docstate.size(), b_docstate.size());
+ for (size_t i = 0, c = a_docstate.size(); i < c; ++i)
+ EXPECT_EQ(string16(a_docstate[i]), string16(b_docstate[i]));
+
+ // Form Data
+ const WebHTTPBody& a_body = a.httpBody();
+ const WebHTTPBody& b_body = b.httpBody();
+ EXPECT_EQ(!a_body.isNull(), !b_body.isNull());
+ if (!a_body.isNull() && !b_body.isNull()) {
+ EXPECT_EQ(a_body.elementCount(), b_body.elementCount());
+ WebHTTPBody::Element a_elem, b_elem;
+ for (size_t i = 0; a_body.elementAt(i, a_elem) &&
+ b_body.elementAt(i, b_elem); ++i) {
+ EXPECT_EQ(a_elem.type, b_elem.type);
+ if (a_elem.type == WebHTTPBody::Element::TypeData) {
+ EXPECT_EQ(std::string(a_elem.data.data(), a_elem.data.size()),
+ std::string(b_elem.data.data(), b_elem.data.size()));
+ } else {
+ EXPECT_EQ(string16(a_elem.filePath), string16(b_elem.filePath));
+ }
+ }
+ }
+ EXPECT_EQ(string16(a.httpContentType()), string16(b.httpContentType()));
+
+ // Children
+ const WebVector<WebHistoryItem>& a_children = a.children();
+ const WebVector<WebHistoryItem>& b_children = b.children();
+ EXPECT_EQ(a_children.size(), b_children.size());
+ for (size_t i = 0, c = a_children.size(); i < c; ++i)
+ HistoryItemExpectEqual(a_children[i], b_children[i]);
+ }
+};
+
+// Test old versions of serialized data to ensure that newer versions of code
+// can still read history items written by previous versions.
+TEST_F(GlueSerializeTest, BackwardsCompatibleTest) {
+ const WebHistoryItem& item = MakeHistoryItem(false, false);
+
+ // Make sure version 3 (current version) can read versions 1 and 2.
+ for (int i = 1; i <= 2; i++) {
+ std::string serialized_item;
+ webkit_glue::HistoryItemToVersionedString(item, i, &serialized_item);
+ const WebHistoryItem& deserialized_item =
+ webkit_glue::HistoryItemFromString(serialized_item);
+ ASSERT_FALSE(item.isNull());
+ ASSERT_FALSE(deserialized_item.isNull());
+ HistoryItemExpectEqual(item, deserialized_item);
+ }
+}
+
+// Makes sure that a HistoryItem remains intact after being serialized and
+// deserialized.
+TEST_F(GlueSerializeTest, HistoryItemSerializeTest) {
+ const WebHistoryItem& item = MakeHistoryItem(true, true);
+ const std::string& serialized_item = webkit_glue::HistoryItemToString(item);
+ const WebHistoryItem& deserialized_item =
+ webkit_glue::HistoryItemFromString(serialized_item);
+
+ ASSERT_FALSE(item.isNull());
+ ASSERT_FALSE(deserialized_item.isNull());
+ HistoryItemExpectEqual(item, deserialized_item);
+}
+
+// Checks that broken messages don't take out our process.
+TEST_F(GlueSerializeTest, BadMessagesTest) {
+ {
+ Pickle p;
+ // Version 1
+ p.WriteInt(1);
+ // Empty strings.
+ for (int i = 0; i < 6; ++i)
+ p.WriteInt(-1);
+ // Bad real number.
+ p.WriteInt(-1);
+ std::string s(static_cast<const char*>(p.data()), p.size());
+ webkit_glue::HistoryItemFromString(s);
+ }
+ {
+ double d = 0;
+ Pickle p;
+ // Version 1
+ p.WriteInt(1);
+ // Empty strings.
+ for (int i = 0; i < 6; ++i)
+ p.WriteInt(-1);
+ // More misc fields.
+ p.WriteData(reinterpret_cast<const char*>(&d), sizeof(d));
+ p.WriteInt(1);
+ p.WriteInt(1);
+ p.WriteInt(0);
+ p.WriteInt(0);
+ p.WriteInt(-1);
+ p.WriteInt(0);
+ // WebForm
+ p.WriteInt(1);
+ p.WriteInt(WebHTTPBody::Element::TypeData);
+ std::string s(static_cast<const char*>(p.data()), p.size());
+ webkit_glue::HistoryItemFromString(s);
+ }
+}
+
+} // namespace
diff --git a/webkit/glue/iframe_redirect_unittest.cc b/webkit/glue/iframe_redirect_unittest.cc
new file mode 100644
index 0000000..9170d19
--- /dev/null
+++ b/webkit/glue/iframe_redirect_unittest.cc
@@ -0,0 +1,48 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <string>
+
+#include "base/file_util.h"
+#include "base/string_util.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebDataSource.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebFrame.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebString.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURL.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebVector.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebView.h"
+#include "webkit/glue/webkit_glue.h"
+#include "webkit/tools/test_shell/test_shell_test.h"
+
+using WebKit::WebDataSource;
+using WebKit::WebFrame;
+using WebKit::WebString;
+using WebKit::WebURL;
+using WebKit::WebVector;
+
+typedef TestShellTest IFrameRedirectTest;
+
+// Tests that loading a page in an iframe from javascript results in
+// a redirect from about:blank.
+TEST_F(IFrameRedirectTest, Test) {
+ FilePath iframes_data_dir_ = data_dir_;
+ iframes_data_dir_ = iframes_data_dir_.AppendASCII("test_shell");
+ iframes_data_dir_ = iframes_data_dir_.AppendASCII("iframe_redirect");
+ ASSERT_TRUE(file_util::PathExists(iframes_data_dir_));
+
+ GURL test_url = GetTestURL(iframes_data_dir_, "main.html");
+
+ test_shell_->LoadURL(test_url);
+ test_shell_->WaitTestFinished();
+
+ WebFrame* iframe =
+ test_shell_->webView()->findFrameByName(WebString::fromUTF8("ifr"));
+ ASSERT_TRUE(iframe != NULL);
+ WebDataSource* iframe_ds = iframe->dataSource();
+ ASSERT_TRUE(iframe_ds != NULL);
+ WebVector<WebURL> redirects;
+ iframe_ds->redirectChain(redirects);
+ ASSERT_FALSE(redirects.isEmpty());
+ ASSERT_TRUE(GURL(redirects[0]) == GURL("about:blank"));
+}
diff --git a/webkit/glue/image_decoder.cc b/webkit/glue/image_decoder.cc
new file mode 100644
index 0000000..6850909
--- /dev/null
+++ b/webkit/glue/image_decoder.cc
@@ -0,0 +1,41 @@
+// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/image_decoder.h"
+
+#include "third_party/WebKit/WebKit/chromium/public/WebData.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebImage.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebSize.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+
+#if WEBKIT_USING_CG
+#include "skia/ext/skia_utils_mac.h"
+#endif
+
+using WebKit::WebData;
+using WebKit::WebImage;
+
+namespace webkit_glue {
+
+ImageDecoder::ImageDecoder() : desired_icon_size_(0, 0) {
+}
+
+ImageDecoder::ImageDecoder(const gfx::Size& desired_icon_size)
+ : desired_icon_size_(desired_icon_size) {
+}
+
+ImageDecoder::~ImageDecoder() {
+}
+
+SkBitmap ImageDecoder::Decode(const unsigned char* data, size_t size) const {
+ const WebImage& image = WebImage::fromData(
+ WebData(reinterpret_cast<const char*>(data), size), desired_icon_size_);
+#if WEBKIT_USING_SKIA
+ return image.getSkBitmap();
+#elif WEBKIT_USING_CG
+ return gfx::CGImageToSkBitmap(image.getCGImageRef());
+#endif
+}
+
+} // namespace webkit_glue
diff --git a/webkit/glue/image_decoder.h b/webkit/glue/image_decoder.h
new file mode 100644
index 0000000..62d9253
--- /dev/null
+++ b/webkit/glue/image_decoder.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/basictypes.h"
+#include "gfx/size.h"
+
+class SkBitmap;
+
+namespace webkit_glue {
+
+// Provides an interface to WebKit's image decoders.
+//
+// Note to future: This class should be deleted. We should have our own nice
+// image decoders in base/gfx, and our port should use those. Currently, it's
+// the other way around.
+class ImageDecoder {
+ public:
+ // Use the constructor with desired_size when you think you may have an .ico
+ // format and care about which size you get back. Otherwise, use the 0-arg
+ // constructor.
+ ImageDecoder();
+ ImageDecoder(const gfx::Size& desired_icon_size);
+ ~ImageDecoder();
+
+ // Call this function to decode the image. If successful, the decoded image
+ // will be returned. Otherwise, an empty bitmap will be returned.
+ SkBitmap Decode(const unsigned char* data, size_t size) const;
+
+ private:
+ // Size will be empty to get the largest possible size.
+ gfx::Size desired_icon_size_;
+
+ DISALLOW_COPY_AND_ASSIGN(ImageDecoder);
+};
+
+} // namespace webkit_glue
diff --git a/webkit/glue/image_resource_fetcher.cc b/webkit/glue/image_resource_fetcher.cc
new file mode 100644
index 0000000..6185834
--- /dev/null
+++ b/webkit/glue/image_resource_fetcher.cc
@@ -0,0 +1,54 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/image_resource_fetcher.h"
+
+#include "base/callback.h"
+#include "gfx/size.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebFrame.h"
+#include "webkit/glue/image_decoder.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+
+using WebKit::WebFrame;
+
+namespace webkit_glue {
+
+ImageResourceFetcher::ImageResourceFetcher(
+ const GURL& image_url,
+ WebFrame* frame,
+ int id,
+ int image_size,
+ Callback* callback)
+ : callback_(callback),
+ id_(id),
+ image_url_(image_url),
+ image_size_(image_size) {
+ fetcher_.reset(new ResourceFetcher(
+ image_url, frame,
+ NewCallback(this, &ImageResourceFetcher::OnURLFetchComplete)));
+}
+
+ImageResourceFetcher::~ImageResourceFetcher() {
+ if (!fetcher_->completed())
+ fetcher_->Cancel();
+}
+
+void ImageResourceFetcher::OnURLFetchComplete(
+ const WebKit::WebURLResponse& response,
+ const std::string& data) {
+ SkBitmap bitmap;
+ if (!response.isNull() && response.httpStatusCode() == 200) {
+ // Request succeeded, try to convert it to an image.
+ ImageDecoder decoder(gfx::Size(image_size_, image_size_));
+ bitmap = decoder.Decode(
+ reinterpret_cast<const unsigned char*>(data.data()), data.size());
+ } // else case:
+ // If we get here, it means no image from server or couldn't decode the
+ // response as an image. The delegate will see a null image, indicating
+ // that an error occurred.
+ callback_->Run(this, bitmap);
+ callback_.reset();
+}
+
+} // namespace webkit_glue
diff --git a/webkit/glue/image_resource_fetcher.h b/webkit/glue/image_resource_fetcher.h
new file mode 100644
index 0000000..8c6f70c
--- /dev/null
+++ b/webkit/glue/image_resource_fetcher.h
@@ -0,0 +1,63 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_IMAGE_RESOURCE_FETCHER_H_
+#define WEBKIT_GLUE_IMAGE_RESOURCE_FETCHER_H_
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "webkit/glue/resource_fetcher.h"
+
+class SkBitmap;
+
+namespace webkit_glue {
+
+// ImageResourceFetcher handles downloading an image for a webview. Once
+// downloading is done the supplied callback is notified. ImageResourceFetcher
+// is used to download the favicon and images for web apps.
+class ImageResourceFetcher {
+ public:
+ typedef Callback2<ImageResourceFetcher*, const SkBitmap&>::Type Callback;
+
+ ImageResourceFetcher(const GURL& image_url,
+ WebKit::WebFrame* frame,
+ int id,
+ int image_size,
+ Callback* callback);
+
+ virtual ~ImageResourceFetcher();
+
+ // URL of the image we're downloading.
+ const GURL& image_url() const { return image_url_; }
+
+ // Unique identifier for the request.
+ int id() const { return id_; }
+
+ private:
+ // ResourceFetcher::Callback. Decodes the image and invokes callback_.
+ void OnURLFetchComplete(const WebKit::WebURLResponse& response,
+ const std::string& data);
+
+ scoped_ptr<Callback> callback_;
+
+ // Unique identifier for the request.
+ const int id_;
+
+ // URL of the image.
+ const GURL image_url_;
+
+ // The size of the image. This is only a hint that is used if the image
+ // contains multiple sizes. A value of 0 results in using the first frame
+ // of the image.
+ const int image_size_;
+
+ // Does the actual download.
+ scoped_ptr<ResourceFetcher> fetcher_;
+
+ DISALLOW_COPY_AND_ASSIGN(ImageResourceFetcher);
+};
+
+} // namespace webkit_glue
+
+#endif // WEBKIT_GLUE_IMAGE_RESOURCE_FETCHER_H_
diff --git a/webkit/glue/inspector_strings.grd b/webkit/glue/inspector_strings.grd
new file mode 100644
index 0000000..9d634b7
--- /dev/null
+++ b/webkit/glue/inspector_strings.grd
@@ -0,0 +1,640 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!-- This file contains definitions of resources that will be translated for
+each locale. Specifically, these are UI strings that are used by WebKit
+Inspector that need to be translated for each locale.-->
+
+<!-- These strings and string descriptions were taken from
+WebKit/WebCore/English.lproj/localizedStrings.js @ revision 46732
+so we include the original license below:
+
+/*
+ * Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+-->
+
+<grit base_dir="." latest_public_release="0" current_release="1"
+ source_lang_id="en" enc_check="möl">
+ <outputs>
+ <output filename="inspectorStrings_am.js" type="js_map_format" lang="am" />
+ <output filename="inspectorStrings_ar.js" type="js_map_format" lang="ar" />
+ <output filename="inspectorStrings_bg.js" type="js_map_format" lang="bg" />
+ <output filename="inspectorStrings_bn.js" type="js_map_format" lang="bn" />
+ <output filename="inspectorStrings_ca.js" type="js_map_format" lang="ca" />
+ <output filename="inspectorStrings_cs.js" type="js_map_format" lang="cs" />
+ <output filename="inspectorStrings_da.js" type="js_map_format" lang="da" />
+ <output filename="inspectorStrings_de.js" type="js_map_format" lang="de" />
+ <output filename="inspectorStrings_el.js" type="js_map_format" lang="el" />
+ <output filename="inspectorStrings_en-GB.js" type="js_map_format" lang="en-GB" />
+ <output filename="inspectorStrings_en-US.js" type="js_map_format" lang="en" />
+ <output filename="inspectorStrings_es.js" type="js_map_format" lang="es" />
+ <output filename="inspectorStrings_es-419.js" type="js_map_format" lang="es-419" />
+ <output filename="inspectorStrings_et.js" type="js_map_format" lang="et" />
+ <output filename="inspectorStrings_fi.js" type="js_map_format" lang="fi" />
+ <output filename="inspectorStrings_fil.js" type="js_map_format" lang="fil" />
+ <output filename="inspectorStrings_fr.js" type="js_map_format" lang="fr" />
+ <output filename="inspectorStrings_gu.js" type="js_map_format" lang="gu" />
+ <output filename="inspectorStrings_he.js" type="js_map_format" lang="he" />
+ <output filename="inspectorStrings_hi.js" type="js_map_format" lang="hi" />
+ <output filename="inspectorStrings_hr.js" type="js_map_format" lang="hr" />
+ <output filename="inspectorStrings_hu.js" type="js_map_format" lang="hu" />
+ <output filename="inspectorStrings_id.js" type="js_map_format" lang="id" />
+ <output filename="inspectorStrings_it.js" type="js_map_format" lang="it" />
+ <output filename="inspectorStrings_ja.js" type="js_map_format" lang="ja" />
+ <output filename="inspectorStrings_kn.js" type="js_map_format" lang="kn" />
+ <output filename="inspectorStrings_ko.js" type="js_map_format" lang="ko" />
+ <output filename="inspectorStrings_lt.js" type="js_map_format" lang="lt" />
+ <output filename="inspectorStrings_lv.js" type="js_map_format" lang="lv" />
+ <output filename="inspectorStrings_ml.js" type="js_map_format" lang="ml" />
+ <output filename="inspectorStrings_mr.js" type="js_map_format" lang="mr" />
+ <output filename="inspectorStrings_nl.js" type="js_map_format" lang="nl" />
+ <!-- The translation console uses 'no' for Norwegian Bokmål. It should
+ be 'nb'. -->
+ <output filename="inspectorStrings_nb.js" type="js_map_format" lang="no" />
+ <output filename="inspectorStrings_or.js" type="js_map_format" lang="or" />
+ <output filename="inspectorStrings_pl.js" type="js_map_format" lang="pl" />
+ <output filename="inspectorStrings_pt-BR.js" type="js_map_format" lang="pt-BR" />
+ <output filename="inspectorStrings_pt-PT.js" type="js_map_format" lang="pt-PT" />
+ <output filename="inspectorStrings_ro.js" type="js_map_format" lang="ro" />
+ <output filename="inspectorStrings_ru.js" type="js_map_format" lang="ru" />
+ <output filename="inspectorStrings_sk.js" type="js_map_format" lang="sk" />
+ <output filename="inspectorStrings_sl.js" type="js_map_format" lang="sl" />
+ <output filename="inspectorStrings_sr.js" type="js_map_format" lang="sr" />
+ <output filename="inspectorStrings_sv.js" type="js_map_format" lang="sv" />
+ <output filename="inspectorStrings_sw.js" type="js_map_format" lang="sw" />
+ <output filename="inspectorStrings_ta.js" type="js_map_format" lang="ta" />
+ <output filename="inspectorStrings_te.js" type="js_map_format" lang="te" />
+ <output filename="inspectorStrings_th.js" type="js_map_format" lang="th" />
+ <output filename="inspectorStrings_tr.js" type="js_map_format" lang="tr" />
+ <output filename="inspectorStrings_uk.js" type="js_map_format" lang="uk" />
+ <output filename="inspectorStrings_vi.js" type="js_map_format" lang="vi" />
+ <output filename="inspectorStrings_zh-CN.js" type="js_map_format" lang="zh-CN" />
+ <output filename="inspectorStrings_zh-TW.js" type="js_map_format" lang="zh-TW" />
+ </outputs>
+ <translations>
+ <!-- TODO add references to each of the XTB files (from the Translation
+ Console) that contain translations of messages in your project. Each
+ takes a form like <file path="english.xtb" />. Remember that all file
+ references are relative to this .grd file. -->
+ </translations>
+ <release seq="1">
+ <messages fallback_to_english="true">
+ <message name="IDS_HEADERS_COUNT" desc="Headers count.">
+ ''' (<ph name="COUNT">%d<ex>1</ex></ph>)
+ </message>
+ <message name="IDS_MESSAGE_REPEAT_COUNT" desc="Message repeat count.">
+ ''' (repeated <ph name="COUNT">%d<ex>2</ex></ph> times)
+ </message>
+ <message name="IDS_SIZE_IN_BYTES" desc="Size in bytes.">
+ <ph name="SIZE">%.0f<ex>100</ex></ph>B
+ </message>
+ <message name="IDS_DURATION_IN_MILLISECONDS" desc="Duration in milliseconds.">
+ <ph name="DURATION">%.0f<ex>100</ex></ph>ms
+ </message>
+ <message name="IDS_DURATION_IN_DAYS" desc="Duration in days.">
+ <ph name="DURATION">%.1f<ex>5</ex></ph> days
+ </message>
+ <message name="IDS_DURATION_IN_HOURS" desc="Duration in hours.">
+ <ph name="DURATION">%.1f<ex>5</ex></ph>hrs
+ </message>
+ <message name="IDS_DURATION_IN_MINUTES" desc="Duration in minutes.">
+ <ph name="DURATION">%.1f<ex>5</ex></ph>min
+ </message>
+ <message name="IDS_SHARE_IN_PERCENTS" desc="Share in percents.">
+ <ph name="SHARE">%.2f<ex>5</ex></ph>%%
+ </message>
+ <message name="IDS_SIZE_IN_KILOBYTES" desc="Size in kilobytes.">
+ <ph name="SIZE">%.2f<ex>5</ex></ph>KB
+ </message>
+ <message name="IDS_DURATION_IN_SECONDS" desc="Duration in seconds.">
+ <ph name="DURATION">%.2f<ex>5</ex></ph>s
+ </message>
+ <message name="IDS_SIZE_IN_MEGABYTES" desc="Size in megabytes.">
+ <ph name="DURATION">%.3f<ex>5</ex></ph>MB
+ </message>
+ <message name="IDS_DURATION_IN_MILLISECONDS_HIGH_PRECISION" desc="Duration in milliseconds, high precision.">
+ <ph name="DURATION">%.3f<ex>5</ex></ph>ms
+ </message>
+ <message name="IDS_ERRORS_COUNT_SINGULAR" desc="Errors count, singular.">
+ <ph name="COUNT">%d<ex>1</ex></ph> error
+ </message>
+ <message name="IDS_ERRORS_AND_WARNINGS_COUNTS_SINGULAR_SINGULAR" desc="Errors and warnings counts, singular-singular.">
+ <ph name="ERROR_COUNT">%1$d<ex>1</ex></ph> error, <ph name="WARNING_COUNT">%2$d<ex>1</ex></ph> warning
+ </message>
+ <message name="IDS_ERRORS_AND_WARNINGS_COUNTS_SINGULAR_PLURAL" desc="Errors and warnings counts, singular-plural.">
+ <ph name="ERROR_COUNT">%1$d<ex>1</ex></ph> error, <ph name="WARNINGS_COUNT">%2$d<ex>2</ex></ph> warnings
+ </message>
+ <message name="IDS_ERRORS_COUNT_PLURAL" desc="Errors count, plural.">
+ <ph name="COUNT">%d<ex>2</ex></ph> errors
+ </message>
+ <message name="IDS_ERRORS_AND_WARNINGS_COUNTS_PLURAL_SINGULAR" desc="Errors and warnings counts, plural-singular.">
+ <ph name="ERRORS_COUNT">%1$d<ex>2</ex></ph> errors, <ph name="WARNING_COUNT">%2$d<ex>1</ex></ph> warning
+ </message>
+ <message name="IDS_ERRORS_AND_WARNINGS_COUNTS_PLURAL_PLURAL" desc="Errors and warnings counts, plural-plural.">
+ <ph name="ERRORS_COUNT">%1$d<ex>2</ex></ph> errors, <ph name="WARNINGS_COUNT">%2$d<ex>1</ex></ph> warnings
+ </message>
+ <message name="IDS_MATCHES_COUNT_PLURAL" desc="Matches count, plural.">
+ <ph name="COUNT">%d<ex>2</ex></ph> matches
+ </message>
+ <message name="IDS_STYLE_CHANGE_SINGULAR" desc="Style changes count, singular.">
+ <ph name="COUNT">%d<ex>1</ex></ph> style change
+ </message>
+ <message name="IDS_STYLE_CHANGE_PLURAL" desc="Style changes count, plural.">
+ <ph name="COUNT">%d<ex>2</ex></ph> style changes
+ </message>
+ <message name="IDS_WARNINGS_COUNT_SINGULAR" desc="Warnings count, singular.">
+ <ph name="COUNT">%d<ex>1</ex></ph> warning
+ </message>
+ <message name="IDS_WARNINGS_COUNT_PLURAL" desc="Warnings count, plural.">
+ <ph name="COUNT">%d<ex>2</ex></ph> warnings
+ </message>
+ <message name="IDS_IMAGE_DIMENSIONS" desc="Image dimensions (uses mutiplication symbol, not x.)">
+ <ph name="WIDTH">%1$d<ex>100</ex></ph> × <ph name="HEIGHT">%2$d<ex>100</ex></ph>
+ </message>
+ <message name="IDS_INDICATES_THAT_RESOURCE_IS_RETRIEVED_FROM_CACHE" desc="Indicates that resource is retrieved from cache.">
+ <ph name="RESOURCE_NAME">%s<ex>picture.gif</ex></ph> (from cache)
+ </message>
+ <message name="IDS_INDICATES_DURATION_OF_RESOURCE_DOWNLOAD" desc="Indicates duration of resource download.">
+ <ph name="DURATION">%s<ex>5ms</ex></ph> download
+ </message>
+ <message name="IDS_INDICATES_LATENCY_OF_RESOURCE_DOWNLOAD" desc="Indicates latency of resource download.">
+ <ph name="LATENCY">%s<ex>5ms</ex></ph> latency
+ </message>
+ <message name="IDS_INDICATES_DURATION_AND_LATENCY_OF_RESOURCE_DOWNLOAD" desc="Indicates duration and latency of resource download.">
+ <ph name="LATENCY">%1$s<ex>5ms</ex></ph> latency, <ph name="DURATION">%2$s<ex>5ms</ex></ph> download (<ph name="TOTAL">%3$s<ex>10ms</ex></ph> total)
+ </message>
+ <message name="IDS_LABELS_AN_ANONYMOUS_JAVASCRIPT_FUNCTION" desc="Labels an anonymous JavaScript function.">
+ (anonymous function)
+ </message>
+ <message name="IDS_LABELS_PROGRAM_AS_A_WHOLE" desc="Labels program as a whole.">
+ (program)
+ </message>
+ <message name="IDS_LABELS_A_SCRIPT_WITH_URL" desc="Labels a script with URL.">
+ (program): <ph name="URL">%s<ex>http://site/script.js</ex></ph>
+ </message>
+ <message name="IDS_LABELS_A_TEXT_NODE_IN_HTML_TREE" desc="Labels a text node in HTML tree.">
+ (text)
+ </message>
+ <message name="IDS_LABELS_A_WHITESPACE_NODE_IN_HTML_TREE" desc="Labels a whitespace node in HTML tree.">
+ (whitespace)
+ </message>
+ <message name="IDS_MATCHES_COUNT_SINGULAR" desc="Matches count, singular.">
+ 1 match
+ </message>
+ <message name="IDS_SPECIFIES_THAT_SOME_FEATURE_IS_ALWAYS_ENABLED" desc="Specifies that some feature is always enabled.">
+ Always enable
+ </message>
+ <message name="IDS_ERROR_MESSAGE_DISPLAYED_ON_FAILURE_TO_READ_A_DATABASE_TABLE" desc="Error message displayed on failure to read a database table.">
+ An error occurred trying to\nread the “<ph name="TABLE_NAME">%s<ex>CLIENTS</ex></ph>” table.
+ </message>
+ <message name="IDS_ERROR_MESSAGE_DISPLAYED_WHEN_AN_UNEXPECTED_ERROR_OCCURS_DURING_DATABASE_QUERY" desc="Error message displayed when an unexpected error occurs during database query.">
+ An unexpected error <ph name="ERROR">%s<ex>No memory</ex></ph> occurred.
+ </message>
+ <message name="IDS_LABEL_OF_A_GRID_COLUMN_SHOWING_AVERAGE_FUNCTION_EXECUTION_TIME" desc="Label of a grid column showing average function execution time.">
+ Average
+ </message>
+ <message name="IDS_LABEL_OF_A_SIDE_PANEL_SHOWING_BREAKPOINTS" desc="Label of a side panel showing breakpoints.">
+ Breakpoints
+ </message>
+ <message name="IDS_LABEL_OF_A_SIDE_PANEL_SHOWING_CALL_STACK" desc="Label of a side panel showing call stack.">
+ Call Stack
+ </message>
+ <message name="IDS_LABEL_OF_A_GRID_COLUMN_SHOWING_FUNCTION_CALL_COUNT" desc="Label of a grid column showing function call count.">
+ Calls
+ </message>
+ <message name="IDS_HINT_FOR_A_BUTTON_CLEARING_CHANGES_LOG" desc="Hint for a button clearing changes log.">
+ Clear changes log.
+ </message>
+ <message name="IDS_HINT_FOR_A_BUTTON_CLEARING_CONSOLE_LOG" desc="Hint for a button clearing console log.">
+ Clear console log.
+ </message>
+ <message name="IDS_LABEL_FOR_A_SECTION_IN_THE_SCOPE_CHAIN_SIDEBAR_THAT_SHOWS_CLOSURE_VARIABLES" desc="Label for a section in the scope chain sidebar that shows closure's variables.">
+ Closure
+ </message>
+ <message name="IDS_LABEL_FOR_A_SECTION_SHOWING_COOKIES_CAPS" desc="Label for a section showing cookies.">
+ COOKIES
+ </message>
+ <message name="IDS_LABEL_FOR_A_SECTION_SHOWING_COMPUTED_STYLE" desc="Label for a section showing computed style.">
+ Computed Style
+ </message>
+ <message name="IDS_LABEL_FOR_A_SECTION_SHOWING_COOKIES" desc="Label for a section showing cookies.">
+ Cookies
+ </message>
+ <message name="IDS_LABEL_FOR_A_SECTION_SHOWING_DATABASES" desc="Label for a section showing databases.">
+ DATABASES
+ </message>
+ <message name="IDS_LABEL_FOR_A_SECTION_SHOWING_DOM" desc="Label for a section showing DOM.">
+ DOM
+ </message>
+ <message name="IDS_ERROR_MESSAGE_INDICATING_DATABASE_VERSION_MISMATCH" desc="Error message indicating database version mismatch.">
+ Database no longer has expected version.
+ </message>
+ <message name="IDS_LABEL_FOR_THE_TAB_SHOWING_DATABASES" desc="Label for the tab showing databases.">
+ Databases
+ </message>
+ <message name="IDS_MESSAGE_INDICATING_THAT_DEBUGGING_IS_DISABLED" desc="Message indicating that debugging is disabled.">
+ Debugging disabled. Click to enable.
+ </message>
+ <message name="IDS_MESSAGE_INDICATING_THAT_DEBUGGING_IS_ENABLED" desc="Message indicating that debugging is enabled.">
+ Debugging enabled. Click to disable.
+ </message>
+ <message name="IDS_A_HINT_THAT_REMINDS_TO_TURN_ON_DEBUGGER" desc="A hint that reminds to turn on debugger.">
+ Debugging scripts requires you to start the debugger.
+ </message>
+ <message name="IDS_A_BUTTON_FOR_DELETING_DOM_STORAGE_ITEMS" desc="A button for deleting DOM storage items.">
+ Delete
+ </message>
+ <message name="IDS_LABEL_OF_IMAGE_DIMENSIONS_GRID_COLUMN" desc="Label of image dimensions grid column.">
+ Dimensions
+ </message>
+ <message name="IDS_HINT_FOR_A_BUTTON_THAT_DOCKS_INSPECTOR_TO_MAIN_WINDOW" desc="Hint for a button that docks Inspector to main window.">
+ Dock to main window.
+ </message>
+ <message name="IDS_LABEL_FOR_DOCUMENTS_RESOURCE_SECTION" desc="Label for documents resource section.">
+ Documents
+ </message>
+ <message name="IDS_HINT_FOR_A_BUTTON_THAT_DISABLES_PAUSE_EXECUTION_ON_EXCEPTIONS" desc="Hint for a button that disables pause execution on exceptions.">
+ Don't pause on exceptions.
+ </message>
+ <message name="IDS_DOUBLE_CLICK_TO_ADD" desc="Instruction on adding an element.">
+ Double-Click to Add
+ </message>
+ <message name="IDS_LABEL_FOR_HTML_ELEMENTS_PANEL" desc="Label for HTML elements panel.">
+ Elements
+ </message>
+ <message name="IDS_HINT_FOR_A_BUTTON_THAT_ENABLES_DEBUGGING" desc="Hint for a button that enables debugging.">
+ Enable Debugging
+ </message>
+ <message name="IDS_HINT_FOR_A_BUTTON_THAT_ENABLES_PROFILING" desc="Hint for a button that enables profiling.">
+ Enable Profiling
+ </message>
+ <message name="IDS_HINT_FOR_A_BUTTON_THAT_ENABLES_RESOURCE_TRACKING" desc="Hint for a button that enables resource tracking.">
+ Enable resource tracking
+ </message>
+ <message name="IDS_A_WARNING_MESSAGE_ABOUT_DEBUGGING" desc="A warning message about debugging.">
+ Enabling debugging will make scripts run slower.
+ </message>
+ <message name="IDS_A_WARNING_MESSAGE_ABOUT_PROFILING" desc="A warning message about profiling.">
+ Enabling profiling will make scripts run slower.
+ </message>
+ <message name="IDS_A_WARNING_MESSAGE_ABOUT_RESOURCE_TRACKING" desc="A warning message about resource tracking.">
+ Enabling resource tracking will reload the page and make page loading slower.
+ </message>
+ <message name="IDS_LABEL_FOR_A_SECTION_IN_THE_SCOPE_CHAIN_SIDEBAR_THAT_SHOWS_EVENT_DOCUMENT_VARIABLES" desc="Label for a section in the scope chain sidebar that shows event document's variables.">
+ Event Document
+ </message>
+ <message name="IDS_LABEL_FOR_A_SECTION_IN_THE_SCOPE_CHAIN_SIDEBAR_THAT_SHOWS_EVENT_TARGET_VARIABLES" desc="Label for a section in the scope chain sidebar that shows event target's variables.">
+ Event Target
+ </message>
+ <message name="IDS_LABEL_FOR_A_BUTTON_THAT_EXCLUDES_A_FUNCTION_FROM_EXECUTION_PROFILE" desc="Label for a button that excludes a function from execution profile.">
+ Exclude selected function.
+ </message>
+ <message name="IDS_LABEL_OF_IMAGE_FILE_SIZE_GRID_COLUMN" desc="Label of image file size grid column.">
+ File size
+ </message>
+ <message name="IDS_LABEL_FOR_A_BUTTON_THAT_FOCUSES_ON_A_FUNCTION_FROM_EXECUTION_PROFILE" desc="Label for a button that focuses on a function from execution profile.">
+ Focus selected function.
+ </message>
+ <message name="IDS_LABEL_FOR_FONTS_RESOURCE_SECTION" desc="Label for fonts resource section.">
+ Fonts
+ </message>
+ <message name="IDS_LABEL_OF_A_GRID_COLUMN_SHOWING_FUNCTION_NAME" desc="Label of a grid column showing function name.">
+ Function
+ </message>
+ <message name="IDS_LABEL_FOR_GRAPHS_SIDEBAR" desc="Label for graphs sidebar.">
+ GRAPHS
+ </message>
+ <message name="IDS_LABEL_FOR_A_SECTION_IN_THE_SCOPE_CHAIN_SIDEBAR_THAT_SHOWS_GLOBAL_VARIABLES" desc="Label for a section in the scope chain sidebar that shows global variables.">
+ Global
+ </message>
+ <message name="IDS_LABEL_INDICATING_THAT_BOTTOM-UP_HEAVY_PROFILE_IS_SHOWN" desc="Label indicating that bottom-up (heavy) profile is shown.">
+ Heavy (Bottom Up)
+ </message>
+ <message name="IDS_LABEL_FOR_A_BUTTON_THAT_HIDES_CHANGES" desc="Label for a button that hides changes view.">
+ Hide changes view.
+ </message>
+ <message name="IDS_LABEL_FOR_A_BUTTON_THAT_HIDES_CONSOLE" desc="Label for a button that hides console.">
+ Hide console.
+ </message>
+ <message name="IDS_LABEL_FOR_IMAGES_RESOURCE_SECTION" desc="Label for images resource section.">
+ Images
+ </message>
+ <message name="IDS_LABEL_FOR_INLINE_STYLE_ATTRIBUTE" desc="Label for inline style attribute.">
+ Inline Style Attribute
+ </message>
+ <message name="IDS_LABEL_FOR_A_GRID_COLUMN_SHOWING_PROPERTY_KEY" desc="Label for a grid column showing property key.">
+ Key
+ </message>
+ <message name="IDS_LABEL_FOR_A_SECTION_SHOWING_LOCAL_STORAGE" desc="Label for a section showing local storage.">
+ LOCAL STORAGE
+ </message>
+ <message name="IDS_LABEL_FOR_A_SECTION_IN_THE_SCOPE_CHAIN_SIDEBAR_THAT_SHOWS_LOCAL_VARIABLES" desc="Label for a section in the scope chain sidebar that shows local variables.">
+ Local
+ </message>
+ <message name="IDS_LABEL_OF_IMAGE_MIME_TYPE_GRID_COLUMN" desc="Label of image MIME type grid column.">
+ MIME type
+ </message>
+ <message name="IDS_LABEL_FOR_HTML_ELEMENT_METRICS_SIDE_PANEL" desc="Label for HTML element metrics side panel.">
+ Metrics
+ </message>
+ <message name="IDS_MESSAGE_IN_BREAKPOINTS_SIDEBAR_PANEL_INDICATING_THAT_THERE_ARE_NO_BREAKPOINTS" desc="Message in breakpoints sidebar panel indicating that there are no breakpoints.">
+ No Breakpoints
+ </message>
+ <message name="IDS_MESSAGE_IN_OBJECT_PROPERTIES_SIDEBAR_PANEL_INDICATING_THAT_THERE_ARE_NO_PROPERTIES" desc="Message in object properties sidebar panel indicating that there are no properties.">
+ No Properties
+ </message>
+ <message name="IDS_MESSAGE_FOR_A_SECTION_IN_THE_SCOPE_CHAIN_SIDEBAR_SHOWING_VARIABLES_ABSENCE" desc="Message for a section in the scope chain sidebar showing variables absence.">
+ No Variables
+ </message>
+ <message name="IDS_MESSAGE_INDICATING_THAT_SEARCHED_STRING_DOESN'T_FOUND" desc="Message indicating that searched string doesn't found.">
+ Not Found
+ </message>
+ <message name="IDS_MESSAGE_INDICATING_THAT_PROGRAM_ISN'T_PAUSED" desc="Message indicating that program isn't paused.">
+ Not Paused
+ </message>
+ <message name="IDS_SPECIFIES_THAT_SOME_FEATURE_IS_ONLY_ENABLED_FOR_CURRENT_SESSION" desc="Specifies that some feature is only enabled for current session.">
+ Only enable for this session
+ </message>
+ <message name="IDS_LABEL_FOR_OTHER_RESOURCES_SECTION" desc="Label for other resources section.">
+ Other
+ </message>
+ <message name="IDS_HINT_FOR_A_BUTTON_THAT_ENABLES_PAUSE_EXECUTION_ON_EXCEPTIONS" desc="Hint for a button that enables pause execution on exceptions.">
+ Pause on exceptions.
+ </message>
+ <message name="IDS_HINT_FOR_A_BUTTON_THAT_PAUSES_SCRIPT_EXECUTION" desc="Hint for a button that pauses script execution.">
+ Pause script execution.
+ </message>
+ <message name="IDS_HINT_FOR_A_PAUSE_BUTTON_IN_DEBUGGER_INDICATING_THAT_EXECUTION_IS_PAUSED" desc="Hint for a pause button in debugger, indicating that execution is paused.">
+ Paused
+ </message>
+ <message name="IDS_HINT_FOR_A_PAUSE_BUTTON_IN_DEBUGGER_INDICATING_THAT_EXECUTION_IS_BEING_PAUSED" desc="Hint for a pause button in debugger, indicating that execution is being paused.">
+ Pausing
+ </message>
+ <message name="IDS_LABEL_FOR_AN_EXECUTION_PROFILE" desc="Label for an execution profile.">
+ Profile <ph name="ID">%d<ex>1</ex></ph>
+ </message>
+ <message name="IDS_LABEL_FOR_PROFILES_TAB" desc="Label for profiles tab.">
+ Profiles
+ </message>
+ <message name="IDS_MESSAGE_INDICATING_THAT_PROFILING_IS_DISABLED" desc="Message indicating that profiling is disabled.">
+ Profiling disabled. Click to enable.
+ </message>
+ <message name="IDS_MESSAGE_INDICATING_THAT_PROFILING_IS_ENABLED" desc="Message indicating that profiling is enabled.">
+ Profiling enabled. Click to disable.
+ </message>
+ <message name="IDS_LABEL_FOR_OBJECT_PROPERTIES_SIDEBAR" desc="Label for object properties sidebar.">
+ Properties
+ </message>
+ <message name="IDS_LABEL_FOR_THE_PROTOTYPE_PROPERTY" desc="Label for the prototype property.">
+ Prototype
+ </message>
+ <message name="IDS_LABEL_FOR_DATABASE_QUERY" desc="Label for database query.">
+ Query
+ </message>
+ <message name="IDS_LABEL_FOR_A_SECTION_SHOWING_RESOURCES" desc="Label for a section showing resources.">
+ RESOURCES
+ </message>
+ <message name="IDS_LABEL_FOR_A_BUTTON_THAT_REFRESHES_CURRENT_VIEW" desc="Label for a button that refreshes current view.">
+ Refresh
+ </message>
+ <message name="IDS_LABEL_FOR_HTTP_REQUEST_HEADERS" desc="Label for HTTP request headers.">
+ Request Headers
+ </message>
+ <message name="IDS_A_WARNING_ABOUT_RESOURCE_TYPE_MISMATCH" desc="A warning about resource type mismatch.">
+ Resource interpreted as <ph name="REAL_TYPE">%1$s<ex>text/javascript</ex></ph> but transferred with MIME type <ph name="SPECIFIED_TYPE">%2$s<ex>text/javascript</ex></ph>.
+ </message>
+ <message name="IDS_MESSAGE_INDICATING_THAT_RESOURCE_TRACKING_IS_DISABLED" desc="Message indicating that resource tracking is disabled.">
+ Resource tracking disabled. Click to enable.
+ </message>
+ <message name="IDS_MESSAGE_INDICATING_THAT_RESOURCE_TRACKING_IS_ENABLED" desc="Message indicating that resource tracking is enabled.">
+ Resource tracking enabled. Click to disable.
+ </message>
+ <message name="IDS_LABEL_FOR_RESOURCES_TAB" desc="Label for resources tab.">
+ Resources
+ </message>
+ <message name="IDS_LABEL_FOR_HTTP_REQUEST_RESPONSE_HEADERS" desc="Label for HTTP request response headers.">
+ Response Headers
+ </message>
+ <message name="IDS_LABEL_FOR_A_BUTTON_THAT_SHOWS_ALL_FUNCTIONS_IN_PROFILER" desc="Label for a button that shows all functions in profiler.">
+ Restore all functions.
+ </message>
+ <message name="IDS_LABEL_FOR_NTH_PROFILER_RUN" desc="Label for Nth profiler run.">
+ Run <ph name="ID">%d<ex>1</ex></ph>
+ </message>
+ <message name="IDS_LABEL_FOR_A_SECTION_SHOWING_SESSION_STORAGE" desc="Label for a section showing session storage.">
+ SESSION STORAGE
+ </message>
+ <message name="IDS_LABEL_FOR_A_SECTION_IN_THE_SCOPE_CHAIN_SIDEBAR_THAT_SHOWS_SCOPE_VARIABLES" desc="Label for a section in the scope chain sidebar that shows scope variables.">
+ Scope Variables
+ </message>
+ <message name="IDS_LABEL_FOR_SCRIPTS_RESOURCE_SECTION" desc="Label for scripts resource section.">
+ Scripts
+ </message>
+ <message name="IDS_LABEL_INDICATING_CURRENT_SEARCH" desc="Label indicating current search.">
+ Search <ph name="STRING">%s<ex>foo</ex></ph>
+ </message>
+ <message name="IDS_HINT_THAT_ELEMENT_SHOULD_BE_SELECTED" desc="Hint that element should be selected.">
+ Select an element in the page to inspect it.
+ </message>
+ <message name="IDS_LABEL_OF_A_GRID_COLUMN_SHOWING_FUNCTION_SELF_EXECUTION_TIME" desc="Label of a grid column showing function self execution time.">
+ Self
+ </message>
+ <message name="IDS_LABEL_FOR_A_BUTTON_TOGGLING_SHOWING_OF_ABSOLUTE_TIMES" desc="Label for a button toggling showing of absolute times.">
+ Show absolute total and self times.
+ </message>
+ <message name="IDS_LABEL_FOR_A_BUTTON_THAT_SHOWS_CHANGES" desc="Label for a button that shows changes view.">
+ Show changes view.
+ </message>
+ <message name="IDS_LABEL_FOR_A_BUTTON_THAT_SHOWS_CONSOLE" desc="Label for a button that shows console.">
+ Show console.
+ </message>
+ <message name="IDS_LABEL_FOR_A_BUTTON_ENABLING_SHOWING_OF_INHERITED_STYLES" desc="Label for a button enabling showing of inherited styles.">
+ Show inherited
+ </message>
+ <message name="IDS_LABEL_FOR_A_BUTTON_THAT_SHOWS_THE_NEXT_SCRIPT_RESOURCE" desc="Label for a button that shows the next script resource.">
+ Show the next script resource.
+ </message>
+ <message name="IDS_LABEL_FOR_A_BUTTON_THAT_SHOWS_THE_PREVIOUS_SCRIPT_RESOURCE" desc="Label for a button that shows the previous script resource.">
+ Show the previous script resource.
+ </message>
+ <message name="IDS_LABEL_FOR_A_BUTTON_TOGGLING_SHOWING_TIMES_AS_PERCENTAGES" desc="Label for a button toggling showing times as percentages.">
+ Show total and self times as percentages.
+ </message>
+ <message name="IDS_LABEL_FOR_A_RESOURCE_SIZE_GRAPH_1" desc="Label for a resource size graph.">
+ Size
+ </message>
+ <message name="IDS_SORT_OPTION_FOR_RESOURCES_GRAPH_2" desc="Sort option for resources graph.">
+ Sort by Duration
+ </message>
+ <message name="IDS_SORT_OPTION_FOR_RESOURCES_GRAPH_3" desc="Sort option for resources graph.">
+ Sort by End Time
+ </message>
+ <message name="IDS_SORT_OPTION_FOR_RESOURCES_GRAPH_4" desc="Sort option for resources graph.">
+ Sort by Latency
+ </message>
+ <message name="IDS_SORT_OPTION_FOR_RESOURCES_GRAPH_5" desc="Sort option for resources graph.">
+ Sort by Response Time
+ </message>
+ <message name="IDS_SORT_OPTION_FOR_RESOURCES_GRAPH_6" desc="Sort option for resources graph.">
+ Sort by Size
+ </message>
+ <message name="IDS_SORT_OPTION_FOR_RESOURCES_GRAPH_7" desc="Sort option for resources graph.">
+ Sort by Start Time
+ </message>
+ <message name="IDS_LABEL_FOR_SCRIPT_SOURCE" desc="Label for script source.">
+ Source
+ </message>
+ <message name="IDS_LABEL_FOR_A_BUTTON_THAT_STARTS_PROFILING" desc="Label for a button that starts profiling.">
+ Start profiling.
+ </message>
+ <message name="IDS_LABEL_FOR_A_BUTTON_THAT_STEPS_INTO_NEXT_FUNCTION_CALL" desc="Label for a button that steps into next function call.">
+ Step into next function call.
+ </message>
+ <message name="IDS_LABEL_FOR_A_BUTTON_THAT_STEPS_OUT_OF_CURRENT_FUNCTION" desc="Label for a button that steps out of current function.">
+ Step out of current function.
+ </message>
+ <message name="IDS_LABEL_FOR_A_BUTTON_THAT_STEPS_OVER_NEXT_FUNCTION_CALL" desc="Label for a button that steps over next function call.">
+ Step over next function call.
+ </message>
+ <message name="IDS_HINT_FOR_A_PAUSE_BUTTON_IN_DEBUGGER,_INDICATING_THAT_DEBUGGER_IS_STEPPING_INTO_NEXT_STATEMENT" desc="Hint for a pause button in debugger, indicating that debugger is stepping into next statement.">
+ Stepping
+ </message>
+ <message name="IDS_LABEL_FOR_A_BUTTON_THAT_STOPS_PROFILING" desc="Label for a button that stops profiling.">
+ Stop profiling.
+ </message>
+ <message name="IDS_LABEL_FOR_STYLE_ATTRIBUTE" desc="Label for style attribute.">
+ Style Attribute
+ </message>
+ <message name="IDS_LABEL_FOR_CSS_STYLES_SIDE_PANEL" desc="Label for CSS styles side panel.">
+ Styles
+ </message>
+ <message name="IDS_LABEL_FOR_STYLESHEETS_RESOURCE_SECTION" desc="Label for stylesheets resource section.">
+ Stylesheets
+ </message>
+ <message name="IDS_MESSAGE_INDICATING_THAT_DATABASE_TABLE_IS_EMPTY" desc="Message indicating that database table is empty.">
+ The “<ph name="TABLE_NAME">%s<ex>foo</ex></ph>”\ntable is empty.
+ </message>
+ <message name="IDS_MESSAGE_INDICATING_THAT_CURRENT_SITE_DOESN'T_HAVE_COOKIES" desc="Message indicating that current site doesn't have cookies.">
+ This site has no cookies.
+ </message>
+ <message name="IDS_MESSAGE_INDICATING_THAT_STORAGE_IS_EMPTY" desc="Message indicating that storage is empty.">
+ This storage is empty.
+ </message>
+ <message name="IDS_LABEL_FOR_A_RESOURCE_TIME_GRAPH" desc="Label for a resource time graph.">
+ Time
+ </message>
+ <message name="IDS_LABEL_OF_A_GRID_COLUMN_SHOWING_FUNCTION_TOTAL_EXECUTION_TIME" desc="Label of a grid column showing function total execution time.">
+ Total
+ </message>
+ <message name="IDS_LABEL_INDICATING_THAT_TOP-DOWN_(TREE)_PROFILE_IS_SHOWN" desc="Label indicating that top-down (tree) profile is shown.">
+ Tree (Top Down)
+ </message>
+ <message name="IDS_HINT_FOR_A_BUTTON_THAT_UNDOCKS_INSPECTOR_FROM_MAIN_WINDOW" desc="Hint for a button that undocks Inspector from main window.">
+ Undock into separate window.
+ </message>
+ <message name="IDS_HINT_FOR_A_BUTTON_THAT_TOGGLES_SHOWING_LARGE_RESOURCE_ROWS" desc="Hint for a button that toggles showing large resource rows.">
+ Use large resource rows.
+ </message>
+ <message name="IDS_HINT_FOR_A_BUTTON_THAT_TOGGLES_SHOWING_SMALL_RESOURCE_ROWS" desc="Hint for a button that toggles showing small resource rows.">
+ Use small resource rows.
+ </message>
+ <message name="IDS_LABEL_FOR_A_GRID_COLUMN_SHOWING_PROPERTY_VALUE" desc="Label for a grid column showing property value.">
+ Value
+ </message>
+ <message name="IDS_LABEL_FOR_A_SECTION_IN_THE_SCOPE_CHAIN_SIDEBAR_THAT_SHOWS_WITH_BLOCK_VARIABLES" desc="Label for a section in the scope chain sidebar that shows with block variables.">
+ With Block
+ </message>
+ <message name="IDS_LABEL_FOR_XHR_RESOURCE_SECTION" desc="Label for XHR resource section.">
+ XHR
+ </message>
+ <message name="IDS_HINT_MESSAGE_ABOUT_POTENTIAL_RESOURCES_COMPRESSION" desc="Hint message about potential resources compression.">
+ You could save bandwidth by having your web server compress this transfer with gzip or zlib.
+ </message>
+ <message name="IDS_HINT_MESSAGE_ABOUT_NEED_FOR_ENABLING_DEBUGGING" desc="Hint message about need for enabling debugging.">
+ You need to enable debugging before you can use the Scripts panel.
+ </message>
+ <message name="IDS_HINT_MESSAGE_ABOUT_NEED_FOR_ENABLING_PROFILING" desc="Hint message about need for enabling profiling.">
+ You need to enable profiling before you can use the Profiles panel.
+ </message>
+ <message name="IDS_HINT_MESSAGE_ABOUT_NEED_FOR_ENABLING_RESOURCE_TRACKING" desc="Hint message about need for enabling resource tracking.">
+ You need to enable resource tracking to use this panel.
+ </message>
+ <message name="IDS_LABEL_FOR_A_BOX_SHOWING_HTML_ELEMENT_BORDER_SIZE" desc="Label for a box showing HTML element border size.">
+ border
+ </message>
+ <message name="IDS_LABEL_FOR_A_BOX_SHOWING_HTML_ELEMENT_CONTENT_SIZE" desc="Label for a box showing HTML element content size.">
+ content
+ </message>
+ <message name="IDS_LABEL_FOR_A_RESOURCE_TYPE" desc="Label for a resource type.">
+ document
+ </message>
+ <message name="IDS_LABEL_FOR_HTML_ELEMENT'S_ATTRIBUTE" desc="Label for HTML element's attribute.">
+ element’s “<ph name="NAME">%s<ex>width</ex></ph>” attribute
+ </message>
+ <message name="IDS_LABEL_FOR_A_RESOURCE_TYPE_1" desc="Label for a resource type.">
+ font
+ </message>
+ <message name="IDS_LABEL_FOR_A_RESOURCE_TYPE_2" desc="Label for a resource type.">
+ image
+ </message>
+ <message name="IDS_LABEL_FOR_A_RESOURCE_TYPE_3" desc="Label for a resource type.">
+ inline stylesheet
+ </message>
+ <message name="IDS_LABEL_FOR_LINE_POSITION" desc="Label for line position.">
+ line <ph name="LINE_NUMBER">%d<ex>25</ex></ph>
+ </message>
+ <message name="IDS_LABEL_FOR_A_BOX_SHOWING_HTML_ELEMENT_MARGIN_SIZE" desc="Label for a box showing HTML element margin size.">
+ margin
+ </message>
+ <message name="IDS_LABEL_FOR_A_RESOURCE_TYPE_4" desc="Label for a resource type.">
+ other
+ </message>
+ <message name="IDS_LABEL_FOR_A_BOX_SHOWING_HTML_ELEMENT_PADDING_SIZE" desc="Label for a box showing HTML element padding size.">
+ padding
+ </message>
+ <message name="IDS_LABEL_FOR_A_POSITION" desc="Label for a position.">
+ position
+ </message>
+ <message name="IDS_LABEL_FOR_A_RESOURCE_TYPE_5" desc="Label for a resource type.">
+ script
+ </message>
+ <message name="IDS_LABEL_FOR_A_RESOURCE_TYPE_6" desc="Label for a resource type.">
+ stylesheet
+ </message>
+ <message name="IDS_LABEL_FOR_A_RESOURCE_TYPE_7" desc="Label for a resource type.">
+ user agent stylesheet
+ </message>
+ <message name="IDS_LABEL_FOR_A_RESOURCE_TYPE_8" desc="Label for a resource type.">
+ user stylesheet
+ </message>
+ <message name="IDS_LABEL_VIA_INSPECTOR" desc="Label for a style attribute added via Inspector.">
+ via inspector
+ </message>
+ </messages>
+ </release>
+</grit>
diff --git a/webkit/glue/media/buffered_data_source.cc b/webkit/glue/media/buffered_data_source.cc
new file mode 100644
index 0000000..dfac588
--- /dev/null
+++ b/webkit/glue/media/buffered_data_source.cc
@@ -0,0 +1,1052 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/callback.h"
+#include "base/compiler_specific.h"
+#include "base/message_loop.h"
+#include "base/process_util.h"
+#include "base/stl_util-inl.h"
+#include "base/string_util.h"
+#include "media/base/filter_host.h"
+#include "media/base/media_format.h"
+#include "net/base/load_flags.h"
+#include "net/base/net_errors.h"
+#include "net/http/http_response_headers.h"
+#include "webkit/glue/media/buffered_data_source.h"
+#include "webkit/glue/webkit_glue.h"
+
+namespace {
+
+const char kHttpScheme[] = "http";
+const char kHttpsScheme[] = "https";
+const char kDataScheme[] = "data";
+const int64 kPositionNotSpecified = -1;
+const int kHttpOK = 200;
+const int kHttpPartialContent = 206;
+
+// Define the number of bytes in a megabyte.
+const size_t kMegabyte = 1024 * 1024;
+
+// Backward capacity of the buffer, by default 2MB.
+const size_t kBackwardCapcity = 2 * kMegabyte;
+
+// Forward capacity of the buffer, by default 10MB.
+const size_t kForwardCapacity = 10 * kMegabyte;
+
+// The threshold of bytes that we should wait until the data arrives in the
+// future instead of restarting a new connection. This number is defined in the
+// number of bytes, we should determine this value from typical connection speed
+// and amount of time for a suitable wait. Now I just make a guess for this
+// number to be 2MB.
+// TODO(hclam): determine a better value for this.
+const int kForwardWaitThreshold = 2 * kMegabyte;
+
+// Defines how long we should wait for more data before we declare a connection
+// timeout and start a new request.
+// TODO(hclam): Set it to 5s, calibrate this value later.
+const int kTimeoutMilliseconds = 5000;
+
+// Defines how many times we should try to read from a buffered resource loader
+// before we declare a read error. After each failure of read from a buffered
+// resource loader, a new one is created to be read.
+const int kReadTrials = 3;
+
+// BufferedDataSource has an intermediate buffer, this value governs the initial
+// size of that buffer. It is set to 32KB because this is a typical read size
+// of FFmpeg.
+const int kInitialReadBufferSize = 32768;
+
+// Returns true if |url| operates on HTTP protocol.
+bool IsHttpProtocol(const GURL& url) {
+ return url.SchemeIs(kHttpScheme) || url.SchemeIs(kHttpsScheme);
+}
+
+bool IsDataProtocol(const GURL& url) {
+ return url.SchemeIs(kDataScheme);
+}
+
+} // namespace
+
+namespace webkit_glue {
+/////////////////////////////////////////////////////////////////////////////
+// BufferedResourceLoader
+BufferedResourceLoader::BufferedResourceLoader(
+ webkit_glue::MediaResourceLoaderBridgeFactory* bridge_factory,
+ const GURL& url,
+ int64 first_byte_position,
+ int64 last_byte_position)
+ : buffer_(new media::SeekableBuffer(kBackwardCapcity, kForwardCapacity)),
+ deferred_(false),
+ defer_allowed_(true),
+ completed_(false),
+ range_requested_(false),
+ partial_response_(false),
+ bridge_factory_(bridge_factory),
+ url_(url),
+ first_byte_position_(first_byte_position),
+ last_byte_position_(last_byte_position),
+ start_callback_(NULL),
+ bridge_(NULL),
+ offset_(0),
+ content_length_(kPositionNotSpecified),
+ instance_size_(kPositionNotSpecified),
+ read_callback_(NULL),
+ read_position_(0),
+ read_size_(0),
+ read_buffer_(NULL),
+ first_offset_(0),
+ last_offset_(0) {
+}
+
+BufferedResourceLoader::~BufferedResourceLoader() {
+}
+
+void BufferedResourceLoader::Start(net::CompletionCallback* start_callback,
+ NetworkEventCallback* event_callback) {
+ // Make sure we have not started.
+ DCHECK(!bridge_.get());
+ DCHECK(!start_callback_.get());
+ DCHECK(!event_callback_.get());
+ DCHECK(start_callback);
+ DCHECK(event_callback);
+
+ start_callback_.reset(start_callback);
+ event_callback_.reset(event_callback);
+
+ if (first_byte_position_ != kPositionNotSpecified) {
+ range_requested_ = true;
+ // TODO(hclam): server may not support range request so |offset_| may not
+ // equal to |first_byte_position_|.
+ offset_ = first_byte_position_;
+ }
+
+ // Creates the bridge on render thread since we can only access
+ // ResourceDispatcher on this thread.
+ bridge_.reset(
+ bridge_factory_->CreateBridge(
+ url_,
+ IsMediaCacheEnabled() ? net::LOAD_NORMAL : net::LOAD_BYPASS_CACHE,
+ first_byte_position_,
+ last_byte_position_));
+
+ // Increment the reference count right before we start the request. This
+ // reference will be release when this request has ended.
+ AddRef();
+
+ // And start the resource loading.
+ bridge_->Start(this);
+}
+
+void BufferedResourceLoader::Stop() {
+ // Reset callbacks.
+ start_callback_.reset();
+ event_callback_.reset();
+ read_callback_.reset();
+
+ // Use the internal buffer to signal that we have been stopped.
+ // TODO(hclam): Not so pretty to do this.
+ if (!buffer_.get())
+ return;
+
+ // Destroy internal buffer.
+ buffer_.reset();
+
+ if (bridge_.get()) {
+ // Cancel the request. This method call will cancel the request
+ // asynchronously. We may still get data or messages until we receive
+ // a response completed message.
+ if (deferred_)
+ bridge_->SetDefersLoading(false);
+ deferred_ = false;
+ bridge_->Cancel();
+ }
+}
+
+void BufferedResourceLoader::Read(int64 position,
+ int read_size,
+ uint8* buffer,
+ net::CompletionCallback* read_callback) {
+ DCHECK(!read_callback_.get());
+ DCHECK(buffer_.get());
+ DCHECK(read_callback);
+ DCHECK(buffer);
+
+ // Save the parameter of reading.
+ read_callback_.reset(read_callback);
+ read_position_ = position;
+ read_size_ = read_size;
+ read_buffer_ = buffer;
+
+ // If read position is beyond the instance size, we cannot read there.
+ if (instance_size_ != kPositionNotSpecified &&
+ instance_size_ <= read_position_) {
+ DoneRead(0);
+ return;
+ }
+
+ // Make sure |offset_| and |read_position_| does not differ by a large
+ // amount.
+ if (read_position_ > offset_ + kint32max ||
+ read_position_ < offset_ + kint32min) {
+ DoneRead(net::ERR_CACHE_MISS);
+ return;
+ }
+
+ // Prepare the parameters.
+ first_offset_ = static_cast<int>(read_position_ - offset_);
+ last_offset_ = first_offset_ + read_size_;
+
+ // If we can serve the request now, do the actual read.
+ if (CanFulfillRead()) {
+ ReadInternal();
+ DisableDeferIfNeeded();
+ return;
+ }
+
+ // If we expected the read request to be fulfilled later, returns
+ // immediately and let more data to flow in.
+ if (WillFulfillRead())
+ return;
+
+ // Make a callback to report failure.
+ DoneRead(net::ERR_CACHE_MISS);
+}
+
+int64 BufferedResourceLoader::GetBufferedFirstBytePosition() {
+ if (buffer_.get())
+ return offset_ - static_cast<int>(buffer_->backward_bytes());
+ return kPositionNotSpecified;
+}
+
+int64 BufferedResourceLoader::GetBufferedLastBytePosition() {
+ if (buffer_.get())
+ return offset_ + static_cast<int>(buffer_->forward_bytes()) - 1;
+ return kPositionNotSpecified;
+}
+
+void BufferedResourceLoader::SetAllowDefer(bool is_allowed) {
+ defer_allowed_ = is_allowed;
+ DisableDeferIfNeeded();
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// BufferedResourceLoader,
+// webkit_glue::ResourceLoaderBridge::Peer implementations
+bool BufferedResourceLoader::OnReceivedRedirect(
+ const GURL& new_url,
+ const webkit_glue::ResourceLoaderBridge::ResponseInfo& info,
+ bool* has_new_first_party_for_cookies,
+ GURL* new_first_party_for_cookies) {
+ DCHECK(bridge_.get());
+
+ // Save the new URL.
+ url_ = new_url;
+ // TODO(wtc): should we return a new first party for cookies URL?
+ *has_new_first_party_for_cookies = false;
+
+ // The load may have been stopped and |start_callback| is destroyed.
+ // In this case we shouldn't do anything.
+ if (!start_callback_.get())
+ return true;
+
+ if (!IsProtocolSupportedForMedia(new_url)) {
+ DoneStart(net::ERR_ADDRESS_INVALID);
+ Stop();
+ return false;
+ }
+ return true;
+}
+
+void BufferedResourceLoader::OnReceivedResponse(
+ const webkit_glue::ResourceLoaderBridge::ResponseInfo& info,
+ bool content_filtered) {
+ DCHECK(bridge_.get());
+
+ // The loader may have been stopped and |start_callback| is destroyed.
+ // In this case we shouldn't do anything.
+ if (!start_callback_.get())
+ return;
+
+ // We make a strong assumption that when we reach here we have either
+ // received a response from HTTP/HTTPS protocol or the request was
+ // successful (in particular range request). So we only verify the partial
+ // response for HTTP and HTTPS protocol.
+ if (IsHttpProtocol(url_)) {
+ int error = net::OK;
+ if (!info.headers) {
+ // We expect to receive headers because this is a HTTP or HTTPS protocol,
+ // if not report failure.
+ error = net::ERR_INVALID_RESPONSE;
+ } else {
+ if (info.headers->response_code() == kHttpPartialContent)
+ partial_response_ = true;
+
+ if (range_requested_ && partial_response_) {
+ // If we have verified the partial response and it is correct, we will
+ // return net::OK.
+ if (!VerifyPartialResponse(info))
+ error = net::ERR_INVALID_RESPONSE;
+ } else if (info.headers->response_code() != kHttpOK) {
+ // We didn't request a range but server didn't reply with "200 OK".
+ error = net::ERR_FAILED;
+ }
+ }
+
+ if (error != net::OK) {
+ DoneStart(error);
+ Stop();
+ return;
+ }
+ } else {
+ // For any protocol other than HTTP and HTTPS, assume range request is
+ // always fulfilled.
+ partial_response_ = range_requested_;
+ }
+
+ // |info.content_length| can be -1, in that case |content_length_| is
+ // not specified and this is a streaming response.
+ content_length_ = info.content_length;
+
+ // If we have not requested a range, then the size of the instance is equal
+ // to the content length.
+ if (!partial_response_)
+ instance_size_ = content_length_;
+
+ // Calls with a successful response.
+ DoneStart(net::OK);
+}
+
+void BufferedResourceLoader::OnReceivedData(const char* data, int len) {
+ DCHECK(bridge_.get());
+
+ // If this loader has been stopped, |buffer_| would be destroyed.
+ // In this case we shouldn't do anything.
+ if (!buffer_.get())
+ return;
+
+ // Writes more data to |buffer_|.
+ buffer_->Append(reinterpret_cast<const uint8*>(data), len);
+
+ // If there is an active read request, try to fulfill the request.
+ if (HasPendingRead() && CanFulfillRead()) {
+ ReadInternal();
+ } else if (!defer_allowed_) {
+ // If we're not allowed to defer, slide the buffer window forward instead
+ // of deferring.
+ if (buffer_->forward_bytes() > buffer_->forward_capacity()) {
+ size_t excess = buffer_->forward_bytes() - buffer_->forward_capacity();
+ bool success = buffer_->Seek(excess);
+ DCHECK(success);
+ offset_ += first_offset_ + excess;
+ }
+ }
+
+ // At last see if the buffer is full and we need to defer the downloading.
+ EnableDeferIfNeeded();
+
+ // Notify that we have received some data.
+ NotifyNetworkEvent();
+}
+
+void BufferedResourceLoader::OnCompletedRequest(
+ const URLRequestStatus& status, const std::string& security_info) {
+ DCHECK(bridge_.get());
+
+ // Saves the information that the request has completed.
+ completed_ = true;
+
+ // If there is a start callback, calls it.
+ if (start_callback_.get()) {
+ DoneStart(status.os_error());
+ }
+
+ // If there is a pending read but the request has ended, returns with what
+ // we have.
+ if (HasPendingRead()) {
+ // Make sure we have a valid buffer before we satisfy a read request.
+ DCHECK(buffer_.get());
+
+ if (status.is_success()) {
+ // Try to fulfill with what is in the buffer.
+ if (CanFulfillRead())
+ ReadInternal();
+ else
+ DoneRead(net::ERR_CACHE_MISS);
+ } else {
+ // If the request has failed, then fail the read.
+ DoneRead(net::ERR_FAILED);
+ }
+ }
+
+ // There must not be any outstanding read request.
+ DCHECK(!HasPendingRead());
+
+ // Notify that network response is completed.
+ NotifyNetworkEvent();
+
+ // We incremented the reference count when the loader was started. We balance
+ // that reference here so that we get destroyed. This is also the only safe
+ // place to destroy the ResourceLoaderBridge.
+ bridge_.reset();
+ Release();
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// BufferedResourceLoader, private
+void BufferedResourceLoader::EnableDeferIfNeeded() {
+ if (!defer_allowed_)
+ return;
+
+ if (!deferred_ &&
+ buffer_->forward_bytes() >= buffer_->forward_capacity()) {
+ deferred_ = true;
+
+ if (bridge_.get())
+ bridge_->SetDefersLoading(true);
+
+ NotifyNetworkEvent();
+ }
+}
+
+void BufferedResourceLoader::DisableDeferIfNeeded() {
+ if (deferred_ &&
+ (!defer_allowed_ ||
+ buffer_->forward_bytes() < buffer_->forward_capacity() / 2)) {
+ deferred_ = false;
+
+ if (bridge_.get())
+ bridge_->SetDefersLoading(false);
+
+ NotifyNetworkEvent();
+ }
+}
+
+bool BufferedResourceLoader::CanFulfillRead() {
+ // If we are reading too far in the backward direction.
+ if (first_offset_ < 0 &&
+ first_offset_ + static_cast<int>(buffer_->backward_bytes()) < 0)
+ return false;
+
+ // If the start offset is too far ahead.
+ if (first_offset_ >= static_cast<int>(buffer_->forward_bytes()))
+ return false;
+
+ // At the point, we verified that first byte requested is within the buffer.
+ // If the request has completed, then just returns with what we have now.
+ if (completed_)
+ return true;
+
+ // If the resource request is still active, make sure the whole requested
+ // range is covered.
+ if (last_offset_ > static_cast<int>(buffer_->forward_bytes()))
+ return false;
+
+ return true;
+}
+
+bool BufferedResourceLoader::WillFulfillRead() {
+ // Reading too far in the backward direction.
+ if (first_offset_ < 0 &&
+ first_offset_ + static_cast<int>(buffer_->backward_bytes()) < 0)
+ return false;
+
+ // Try to read too far ahead.
+ if (last_offset_ > kForwardWaitThreshold)
+ return false;
+
+ // The resource request has completed, there's no way we can fulfill the
+ // read request.
+ if (completed_)
+ return false;
+
+ return true;
+}
+
+void BufferedResourceLoader::ReadInternal() {
+ // Seek to the first byte requested.
+ bool ret = buffer_->Seek(first_offset_);
+ DCHECK(ret);
+
+ // Then do the read.
+ int read = static_cast<int>(buffer_->Read(read_buffer_, read_size_));
+ offset_ += first_offset_ + read;
+
+ // And report with what we have read.
+ DoneRead(read);
+}
+
+bool BufferedResourceLoader::VerifyPartialResponse(
+ const ResourceLoaderBridge::ResponseInfo& info) {
+ int64 first_byte_position, last_byte_position, instance_size;
+ if (!info.headers->GetContentRange(&first_byte_position,
+ &last_byte_position,
+ &instance_size)) {
+ return false;
+ }
+
+ if (instance_size != kPositionNotSpecified)
+ instance_size_ = instance_size;
+
+ if (first_byte_position_ != -1 &&
+ first_byte_position_ != first_byte_position) {
+ return false;
+ }
+
+ // TODO(hclam): I should also check |last_byte_position|, but since
+ // we will never make such a request that it is ok to leave it unimplemented.
+ return true;
+}
+
+void BufferedResourceLoader::DoneRead(int error) {
+ read_callback_->RunWithParams(Tuple1<int>(error));
+ read_callback_.reset();
+ read_position_ = 0;
+ read_size_ = 0;
+ read_buffer_ = NULL;
+ first_offset_ = 0;
+ last_offset_ = 0;
+}
+
+void BufferedResourceLoader::DoneStart(int error) {
+ start_callback_->RunWithParams(Tuple1<int>(error));
+ start_callback_.reset();
+}
+
+void BufferedResourceLoader::NotifyNetworkEvent() {
+ if (event_callback_.get())
+ event_callback_->Run();
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// BufferedDataSource, static methods
+bool BufferedDataSource::IsMediaFormatSupported(
+ const media::MediaFormat& media_format) {
+ std::string mime_type;
+ std::string url;
+ if (media_format.GetAsString(media::MediaFormat::kMimeType, &mime_type) &&
+ media_format.GetAsString(media::MediaFormat::kURL, &url)) {
+ GURL gurl(url);
+
+ // This data source doesn't support data:// protocol, so reject it
+ // explicitly.
+ if (IsProtocolSupportedForMedia(gurl) && !IsDataProtocol(gurl))
+ return true;
+ }
+ return false;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// BufferedDataSource, protected
+BufferedDataSource::BufferedDataSource(
+ MessageLoop* render_loop,
+ webkit_glue::MediaResourceLoaderBridgeFactory* bridge_factory)
+ : total_bytes_(kPositionNotSpecified),
+ loaded_(false),
+ streaming_(false),
+ bridge_factory_(bridge_factory),
+ loader_(NULL),
+ network_activity_(false),
+ initialize_callback_(NULL),
+ read_callback_(NULL),
+ read_position_(0),
+ read_size_(0),
+ read_buffer_(NULL),
+ read_attempts_(0),
+ intermediate_read_buffer_(new uint8[kInitialReadBufferSize]),
+ intermediate_read_buffer_size_(kInitialReadBufferSize),
+ render_loop_(render_loop),
+ stop_signal_received_(false),
+ stopped_on_render_loop_(false),
+ media_is_paused_(true) {
+}
+
+BufferedDataSource::~BufferedDataSource() {
+}
+
+// A factory method to create BufferedResourceLoader using the read parameters.
+// This method can be overrided to inject mock BufferedResourceLoader object
+// for testing purpose.
+BufferedResourceLoader* BufferedDataSource::CreateResourceLoader(
+ int64 first_byte_position, int64 last_byte_position) {
+ DCHECK(MessageLoop::current() == render_loop_);
+
+ return new BufferedResourceLoader(bridge_factory_.get(), url_,
+ first_byte_position,
+ last_byte_position);
+}
+
+// This method simply returns kTimeoutMilliseconds. The purpose of this
+// method is to be overidded so as to provide a different timeout value
+// for testing purpose.
+base::TimeDelta BufferedDataSource::GetTimeoutMilliseconds() {
+ return base::TimeDelta::FromMilliseconds(kTimeoutMilliseconds);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// BufferedDataSource, media::MediaFilter implementation
+void BufferedDataSource::Initialize(const std::string& url,
+ media::FilterCallback* callback) {
+ // Saves the url.
+ url_ = GURL(url);
+
+ if (!IsProtocolSupportedForMedia(url_)) {
+ // This method is called on the thread where host() lives so it is safe
+ // to make this call.
+ host()->SetError(media::PIPELINE_ERROR_NETWORK);
+ callback->Run();
+ delete callback;
+ return;
+ }
+
+ DCHECK(callback);
+ initialize_callback_.reset(callback);
+
+ media_format_.SetAsString(media::MediaFormat::kMimeType,
+ media::mime_type::kApplicationOctetStream);
+ media_format_.SetAsString(media::MediaFormat::kURL, url);
+
+ // Post a task to complete the initialization task.
+ render_loop_->PostTask(FROM_HERE,
+ NewRunnableMethod(this, &BufferedDataSource::InitializeTask));
+}
+
+void BufferedDataSource::Stop(media::FilterCallback* callback) {
+ {
+ AutoLock auto_lock(lock_);
+ stop_signal_received_ = true;
+ }
+ if (callback) {
+ callback->Run();
+ delete callback;
+ }
+ render_loop_->PostTask(FROM_HERE,
+ NewRunnableMethod(this, &BufferedDataSource::CleanupTask));
+}
+
+void BufferedDataSource::SetPlaybackRate(float playback_rate) {
+ render_loop_->PostTask(FROM_HERE,
+ NewRunnableMethod(this, &BufferedDataSource::SetPlaybackRateTask,
+ playback_rate));
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// BufferedDataSource, media::DataSource implementation
+void BufferedDataSource::Read(int64 position, size_t size, uint8* data,
+ media::DataSource::ReadCallback* read_callback) {
+ render_loop_->PostTask(FROM_HERE,
+ NewRunnableMethod(this, &BufferedDataSource::ReadTask,
+ position, static_cast<int>(size), data, read_callback));
+}
+
+bool BufferedDataSource::GetSize(int64* size_out) {
+ if (total_bytes_ != kPositionNotSpecified) {
+ *size_out = total_bytes_;
+ return true;
+ }
+ *size_out = 0;
+ return false;
+}
+
+bool BufferedDataSource::IsStreaming() {
+ return streaming_;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// BufferedDataSource, render thread tasks
+void BufferedDataSource::InitializeTask() {
+ DCHECK(MessageLoop::current() == render_loop_);
+ DCHECK(!loader_.get());
+ DCHECK(!stopped_on_render_loop_);
+
+ // Kick starts the watch dog task that will handle connection timeout.
+ // We run the watch dog 2 times faster the actual timeout so as to catch
+ // the timeout more accurately.
+ watch_dog_timer_.Start(
+ GetTimeoutMilliseconds() / 2,
+ this,
+ &BufferedDataSource::WatchDogTask);
+
+ if (IsHttpProtocol(url_)) {
+ // Fetch only first 1024 bytes as this usually covers the header portion
+ // of a media file that gives enough information about the codecs, etc.
+ // This also serve as a probe to determine server capability to serve
+ // range request.
+ // TODO(hclam): Do some experiments for the best approach.
+ loader_ = CreateResourceLoader(0, 1024);
+ loader_->Start(
+ NewCallback(this, &BufferedDataSource::HttpInitialStartCallback),
+ NewCallback(this, &BufferedDataSource::NetworkEventCallback));
+ } else {
+ // For all other protocols, assume they support range request. We fetch
+ // the full range of the resource to obtain the instance size because
+ // we won't be served HTTP headers.
+ loader_ = CreateResourceLoader(-1, -1);
+ loader_->Start(
+ NewCallback(this, &BufferedDataSource::NonHttpInitialStartCallback),
+ NewCallback(this, &BufferedDataSource::NetworkEventCallback));
+ }
+}
+
+void BufferedDataSource::ReadTask(
+ int64 position, int read_size, uint8* buffer,
+ media::DataSource::ReadCallback* read_callback) {
+ DCHECK(MessageLoop::current() == render_loop_);
+
+ // If CleanupTask() was executed we should return immediately. We check this
+ // variable to prevent doing any actual work after clean up was done. We do
+ // not check |stop_signal_received_| because anything use of it has to be
+ // within |lock_| which is not desirable.
+ if (stopped_on_render_loop_)
+ return;
+
+ DCHECK(!read_callback_.get());
+ DCHECK(read_callback);
+
+ // Saves the read parameters.
+ read_position_ = position;
+ read_size_ = read_size;
+ read_callback_.reset(read_callback);
+ read_buffer_ = buffer;
+ read_submitted_time_ = base::Time::Now();
+ read_attempts_ = 0;
+
+ // Call to read internal to perform the actual read.
+ ReadInternal();
+}
+
+void BufferedDataSource::CleanupTask() {
+ DCHECK(MessageLoop::current() == render_loop_);
+ DCHECK(!stopped_on_render_loop_);
+
+ // Stop the watch dog.
+ watch_dog_timer_.Stop();
+
+ // We just need to stop the loader, so it stops activity.
+ if (loader_.get())
+ loader_->Stop();
+
+ // Reset the parameters of the current read request.
+ read_callback_.reset();
+ read_position_ = 0;
+ read_size_ = 0;
+ read_buffer_ = 0;
+ read_submitted_time_ = base::Time();
+ read_attempts_ = 0;
+
+ // Signal that stop task has finished execution.
+ stopped_on_render_loop_ = true;
+}
+
+void BufferedDataSource::RestartLoadingTask() {
+ DCHECK(MessageLoop::current() == render_loop_);
+
+ // This variable is set in CleanupTask(). We check this and do an early
+ // return. The sequence of actions which enable this conditions is:
+ // 1. Stop() is called from the pipeline.
+ // 2. ReadCallback() is called from the resource loader.
+ // 3. CleanupTask() is executed.
+ // 4. RestartLoadingTask() is executed.
+ if (stopped_on_render_loop_)
+ return;
+
+ // If there's no outstanding read then return early.
+ if (!read_callback_.get())
+ return;
+
+ loader_ = CreateResourceLoader(read_position_, -1);
+ loader_->SetAllowDefer(!media_is_paused_);
+ loader_->Start(
+ NewCallback(this, &BufferedDataSource::PartialReadStartCallback),
+ NewCallback(this, &BufferedDataSource::NetworkEventCallback));
+}
+
+void BufferedDataSource::WatchDogTask() {
+ DCHECK(MessageLoop::current() == render_loop_);
+ DCHECK(!stopped_on_render_loop_);
+
+ // We only care if there is an active read request.
+ if (!read_callback_.get())
+ return;
+
+ DCHECK(loader_.get());
+ base::TimeDelta delta = base::Time::Now() - read_submitted_time_;
+ if (delta < GetTimeoutMilliseconds())
+ return;
+
+ // TODO(hclam): Maybe raise an error here. But if an error is reported
+ // the whole pipeline may get destroyed...
+ if (read_attempts_ >= kReadTrials)
+ return;
+
+ ++read_attempts_;
+ read_submitted_time_ = base::Time::Now();
+
+ // Stops the current loader and creates a new resource loader and
+ // retry the request.
+ loader_->Stop();
+ loader_ = CreateResourceLoader(read_position_, -1);
+ loader_->SetAllowDefer(!media_is_paused_);
+ loader_->Start(
+ NewCallback(this, &BufferedDataSource::PartialReadStartCallback),
+ NewCallback(this, &BufferedDataSource::NetworkEventCallback));
+}
+
+void BufferedDataSource::SetPlaybackRateTask(float playback_rate) {
+ DCHECK(MessageLoop::current() == render_loop_);
+ DCHECK(loader_.get());
+
+ bool previously_paused = media_is_paused_;
+ media_is_paused_ = (playback_rate == 0.0);
+
+ // Disallow deferring data when we are pausing, allow deferring data
+ // when we resume playing.
+ if (previously_paused && !media_is_paused_) {
+ loader_->SetAllowDefer(true);
+ } else if (!previously_paused && media_is_paused_) {
+ loader_->SetAllowDefer(false);
+ }
+}
+
+// This method is the place where actual read happens, |loader_| must be valid
+// prior to make this method call.
+void BufferedDataSource::ReadInternal() {
+ DCHECK(MessageLoop::current() == render_loop_);
+ DCHECK(loader_.get());
+
+ // First we prepare the intermediate read buffer for BufferedResourceLoader
+ // to write to.
+ if (read_size_ > intermediate_read_buffer_size_) {
+ intermediate_read_buffer_.reset(new uint8[read_size_]);
+ }
+
+ // Perform the actual read with BufferedResourceLoader.
+ loader_->Read(read_position_, read_size_, intermediate_read_buffer_.get(),
+ NewCallback(this, &BufferedDataSource::ReadCallback));
+}
+
+// Method to report the results of the current read request. Also reset all
+// the read parameters.
+void BufferedDataSource::DoneRead_Locked(int error) {
+ DCHECK(MessageLoop::current() == render_loop_);
+ DCHECK(read_callback_.get());
+ lock_.AssertAcquired();
+
+ if (error >= 0) {
+ read_callback_->RunWithParams(Tuple1<size_t>(error));
+ } else {
+ read_callback_->RunWithParams(
+ Tuple1<size_t>(static_cast<size_t>(media::DataSource::kReadError)));
+ }
+
+ read_callback_.reset();
+ read_position_ = 0;
+ read_size_ = 0;
+ read_buffer_ = 0;
+}
+
+void BufferedDataSource::DoneInitialization_Locked() {
+ DCHECK(MessageLoop::current() == render_loop_);
+ DCHECK(initialize_callback_.get());
+ lock_.AssertAcquired();
+
+ initialize_callback_->Run();
+ initialize_callback_.reset();
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// BufferedDataSource, callback methods.
+// These methods are called on the render thread for the events reported by
+// BufferedResourceLoader.
+void BufferedDataSource::HttpInitialStartCallback(int error) {
+ DCHECK(MessageLoop::current() == render_loop_);
+ DCHECK(loader_.get());
+
+ int64 instance_size = loader_->instance_size();
+ bool partial_response = loader_->partial_response();
+ bool success = error == net::OK;
+
+ if (success) {
+ // TODO(hclam): Needs more thinking about supporting servers without range
+ // request or their partial response is not complete.
+ total_bytes_ = instance_size;
+ loaded_ = false;
+ streaming_ = (instance_size == kPositionNotSpecified) || !partial_response;
+ } else {
+ // TODO(hclam): In case of failure, we can retry several times.
+ loader_->Stop();
+ }
+
+ // We need to prevent calling to filter host and running the callback if
+ // we have received the stop signal. We need to lock down the whole callback
+ // method to prevent bad things from happening. The reason behind this is
+ // that we cannot guarantee tasks on render thread have completely stopped
+ // when we receive the Stop() method call. The only way to solve this is to
+ // let tasks on render thread to run but make sure they don't call outside
+ // this object when Stop() method is ever called. Locking this method is safe
+ // because |lock_| is only acquired in tasks on render thread.
+ AutoLock auto_lock(lock_);
+ if (stop_signal_received_)
+ return;
+
+ if (!success) {
+ host()->SetError(media::PIPELINE_ERROR_NETWORK);
+ DoneInitialization_Locked();
+ return;
+ }
+
+ if (streaming_) {
+ // If the server didn't reply with an instance size, it is likely this
+ // is a streaming response.
+ host()->SetStreaming(true);
+ } else {
+ // This value governs the range that we can seek to.
+ // TODO(hclam): Report the correct value of buffered bytes.
+ host()->SetTotalBytes(total_bytes_);
+ host()->SetBufferedBytes(0);
+ }
+
+ // Currently, only files can be used reliably w/o a network.
+ host()->SetLoaded(false);
+ DoneInitialization_Locked();
+}
+
+void BufferedDataSource::NonHttpInitialStartCallback(int error) {
+ DCHECK(MessageLoop::current() == render_loop_);
+ DCHECK(loader_.get());
+
+ int64 instance_size = loader_->instance_size();
+ bool success = error == net::OK && instance_size != kPositionNotSpecified;
+
+ if (success) {
+ total_bytes_ = instance_size;
+ loaded_ = true;
+ } else {
+ loader_->Stop();
+ }
+
+ // We need to prevent calling to filter host and running the callback if
+ // we have received the stop signal. We need to lock down the whole callback
+ // method to prevent bad things from happening. The reason behind this is
+ // that we cannot guarantee tasks on render thread have completely stopped
+ // when we receive the Stop() method call. The only way to solve this is to
+ // let tasks on render thread to run but make sure they don't call outside
+ // this object when Stop() method is ever called. Locking this method is safe
+ // because |lock_| is only acquired in tasks on render thread.
+ AutoLock auto_lock(lock_);
+ if (stop_signal_received_)
+ return;
+
+ if (success) {
+ host()->SetTotalBytes(total_bytes_);
+ host()->SetBufferedBytes(total_bytes_);
+ host()->SetLoaded(loaded_);
+ } else {
+ host()->SetError(media::PIPELINE_ERROR_NETWORK);
+ }
+ DoneInitialization_Locked();
+}
+
+void BufferedDataSource::PartialReadStartCallback(int error) {
+ DCHECK(MessageLoop::current() == render_loop_);
+ DCHECK(loader_.get());
+
+ // This callback method is invoked after we have verified the server has
+ // range request capability, so as a safety guard verify again the response
+ // is partial.
+ if (error == net::OK && loader_->partial_response()) {
+ // Once the range request has started successfully, we can proceed with
+ // reading from it.
+ ReadInternal();
+ return;
+ }
+
+ // Stop the resource loader since we have received an error.
+ loader_->Stop();
+
+ // We need to prevent calling to filter host and running the callback if
+ // we have received the stop signal. We need to lock down the whole callback
+ // method to prevent bad things from happening. The reason behind this is
+ // that we cannot guarantee tasks on render thread have completely stopped
+ // when we receive the Stop() method call. So only way to solve this is to
+ // let tasks on render thread to run but make sure they don't call outside
+ // this object when Stop() method is ever called. Locking this method is
+ // safe because |lock_| is only acquired in tasks on render thread.
+ AutoLock auto_lock(lock_);
+ if (stop_signal_received_)
+ return;
+ DoneRead_Locked(net::ERR_INVALID_RESPONSE);
+}
+
+void BufferedDataSource::ReadCallback(int error) {
+ DCHECK(MessageLoop::current() == render_loop_);
+
+ if (error < 0) {
+ DCHECK(loader_.get());
+
+ // Stop the resource load if it failed.
+ loader_->Stop();
+
+ if (error == net::ERR_CACHE_MISS) {
+ render_loop_->PostTask(FROM_HERE,
+ NewRunnableMethod(this, &BufferedDataSource::RestartLoadingTask));
+ return;
+ }
+ }
+
+ // We need to prevent calling to filter host and running the callback if
+ // we have received the stop signal. We need to lock down the whole callback
+ // method to prevent bad things from happening. The reason behind this is
+ // that we cannot guarantee tasks on render thread have completely stopped
+ // when we receive the Stop() method call. So only way to solve this is to
+ // let tasks on render thread to run but make sure they don't call outside
+ // this object when Stop() method is ever called. Locking this method is safe
+ // because |lock_| is only acquired in tasks on render thread.
+ AutoLock auto_lock(lock_);
+ if (stop_signal_received_)
+ return;
+
+ if (error > 0) {
+ // If a position error code is received, read was successful. So copy
+ // from intermediate read buffer to the target read buffer.
+ memcpy(read_buffer_, intermediate_read_buffer_.get(), error);
+ }
+ DoneRead_Locked(error);
+}
+
+void BufferedDataSource::NetworkEventCallback() {
+ DCHECK(MessageLoop::current() == render_loop_);
+ DCHECK(loader_.get());
+
+ // In case of non-HTTP request we don't need to report network events,
+ // so return immediately.
+ if (loaded_)
+ return;
+
+ bool network_activity = loader_->network_activity();
+ int64 buffered_last_byte_position = loader_->GetBufferedLastBytePosition();
+
+ // If we get an unspecified value, return immediately.
+ if (buffered_last_byte_position == kPositionNotSpecified)
+ return;
+
+ // We need to prevent calling to filter host and running the callback if
+ // we have received the stop signal. We need to lock down the whole callback
+ // method to prevent bad things from happening. The reason behind this is
+ // that we cannot guarantee tasks on render thread have completely stopped
+ // when we receive the Stop() method call. So only way to solve this is to
+ // let tasks on render thread to run but make sure they don't call outside
+ // this object when Stop() method is ever called. Locking this method is safe
+ // because |lock_| is only acquired in tasks on render thread.
+ AutoLock auto_lock(lock_);
+ if (stop_signal_received_)
+ return;
+
+ if (network_activity != network_activity_) {
+ network_activity_ = network_activity;
+ host()->SetNetworkActivity(network_activity);
+ }
+ host()->SetBufferedBytes(buffered_last_byte_position + 1);
+}
+
+} // namespace webkit_glue
diff --git a/webkit/glue/media/buffered_data_source.h b/webkit/glue/media/buffered_data_source.h
new file mode 100644
index 0000000..0dc2115
--- /dev/null
+++ b/webkit/glue/media/buffered_data_source.h
@@ -0,0 +1,413 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_MEDIA_BUFFERED_DATA_SOURCE_H_
+#define WEBKIT_GLUE_MEDIA_BUFFERED_DATA_SOURCE_H_
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/lock.h"
+#include "base/scoped_ptr.h"
+#include "base/timer.h"
+#include "base/condition_variable.h"
+#include "googleurl/src/gurl.h"
+#include "media/base/factory.h"
+#include "media/base/filters.h"
+#include "media/base/media_format.h"
+#include "media/base/pipeline.h"
+#include "media/base/seekable_buffer.h"
+#include "net/base/completion_callback.h"
+#include "net/base/file_stream.h"
+#include "webkit/glue/media/media_resource_loader_bridge_factory.h"
+
+namespace webkit_glue {
+/////////////////////////////////////////////////////////////////////////////
+// BufferedResourceLoader
+// This class works inside demuxer thread and render thread. It contains a
+// resource loader bridge and does the actual resource loading. This object
+// does buffering internally, it defers the resource loading if buffer is
+// full and un-defers the resource loading if it is under buffered.
+class BufferedResourceLoader :
+ public base::RefCountedThreadSafe<BufferedResourceLoader>,
+ public webkit_glue::ResourceLoaderBridge::Peer {
+ public:
+ typedef Callback0::Type NetworkEventCallback;
+
+ // |bridge_factory| - Factory to create a ResourceLoaderBridge.
+ // |url| - URL for the resource to be loaded.
+ // |first_byte_position| - First byte to start loading from, -1 for not
+ // specified.
+ // |last_byte_position| - Last byte to be loaded, -1 for not specified.
+ BufferedResourceLoader(
+ webkit_glue::MediaResourceLoaderBridgeFactory* bridge_factory,
+ const GURL& url,
+ int64 first_byte_position,
+ int64 last_byte_position);
+
+ // Start the resource loading with the specified URL and range.
+ // This method operates in asynchronous mode. Once there's a response from the
+ // server, success or fail |callback| is called with the result.
+ // |callback| is called with the following values:
+ // - net::OK
+ // The request has started successfully.
+ // - net::ERR_FAILED
+ // The request has failed because of an error with the network.
+ // - net::ERR_INVALID_RESPONSE
+ // An invalid response is received from the server.
+ // - (Anything else)
+ // An error code that indicates the request has failed.
+ // |event_callback| is called when the response is completed, data is
+ // received, the request is suspended or resumed.
+ virtual void Start(net::CompletionCallback* callback,
+ NetworkEventCallback* event_callback);
+
+ // Stop this loader, cancels and request and release internal buffer.
+ virtual void Stop();
+
+ // Reads the specified |read_size| from |position| into |buffer| and when
+ // the operation is done invoke |callback| with number of bytes read or an
+ // error code.
+ // |callback| is called with the following values:
+ // - (Anything greater than or equal 0)
+ // Read was successful with the indicated number of bytes read.
+ // - net::ERR_FAILED
+ // The read has failed because of an error with the network.
+ // - net::ERR_CACHE_MISS
+ // The read was made too far away from the current buffered position.
+ virtual void Read(int64 position, int read_size,
+ uint8* buffer, net::CompletionCallback* callback);
+
+ // Returns the position of the first byte buffered. Returns -1 if such value
+ // is not available.
+ virtual int64 GetBufferedFirstBytePosition();
+
+ // Returns the position of the last byte buffered. Returns -1 if such value
+ // is not available.
+ virtual int64 GetBufferedLastBytePosition();
+
+ // Sets whether deferring data is allowed or disallowed.
+ virtual void SetAllowDefer(bool is_allowed);
+
+ // Gets the content length in bytes of the instance after this loader has been
+ // started. If this value is -1, then content length is unknown.
+ virtual int64 content_length() { return content_length_; }
+
+ // Gets the original size of the file requested. If this value is -1, then
+ // the size is unknown.
+ virtual int64 instance_size() { return instance_size_; }
+
+ // Returns true if the response for this loader is a partial response.
+ // It means a 206 response in HTTP/HTTPS protocol.
+ virtual bool partial_response() { return partial_response_; }
+
+ // Returns true if network is currently active.
+ virtual bool network_activity() { return !completed_ && !deferred_; }
+
+ /////////////////////////////////////////////////////////////////////////////
+ // webkit_glue::ResourceLoaderBridge::Peer implementations.
+ virtual void OnUploadProgress(uint64 position, uint64 size) {}
+ virtual bool OnReceivedRedirect(
+ const GURL& new_url,
+ const webkit_glue::ResourceLoaderBridge::ResponseInfo& info,
+ bool* has_new_first_party_for_cookies,
+ GURL* new_first_party_for_cookies);
+ virtual void OnReceivedResponse(
+ const webkit_glue::ResourceLoaderBridge::ResponseInfo& info,
+ bool content_filtered);
+ virtual void OnReceivedData(const char* data, int len);
+ virtual void OnCompletedRequest(const URLRequestStatus& status,
+ const std::string& security_info);
+ GURL GetURLForDebugging() const { return url_; }
+
+ protected:
+ friend class base::RefCountedThreadSafe<BufferedResourceLoader>;
+
+ virtual ~BufferedResourceLoader();
+
+ private:
+ friend class BufferedResourceLoaderTest;
+
+ // Defer the resource loading if the buffer is full.
+ void EnableDeferIfNeeded();
+
+ // Disable defer loading if we are under-buffered.
+ void DisableDeferIfNeeded();
+
+ // Returns true if the current read request can be fulfilled by what is in
+ // the buffer.
+ bool CanFulfillRead();
+
+ // Returns true if the current read request will be fulfilled in the future.
+ bool WillFulfillRead();
+
+ // Method that does the actual read and calls the |read_callbac_|, assuming
+ // the request range is in |buffer_|.
+ void ReadInternal();
+
+ // If we have made a range request, verify the response from the server.
+ bool VerifyPartialResponse(const ResourceLoaderBridge::ResponseInfo& info);
+
+ // Done with read. Invokes the read callback and reset parameters for the
+ // read request.
+ void DoneRead(int error);
+
+ // Done with start. Invokes the start callback and reset it.
+ void DoneStart(int error);
+
+ // Calls |event_callback_| in terms of a network event.
+ void NotifyNetworkEvent();
+
+ bool HasPendingRead() { return read_callback_.get() != NULL; }
+
+ // A sliding window of buffer.
+ scoped_ptr<media::SeekableBuffer> buffer_;
+
+ // True if resource loading was deferred.
+ bool deferred_;
+
+ // True if resource loader is allowed to defer, false otherwise.
+ bool defer_allowed_;
+
+ // True if resource loading has completed.
+ bool completed_;
+
+ // True if a range request was made.
+ bool range_requested_;
+
+ // True if response data received is a partial range.
+ bool partial_response_;
+
+ webkit_glue::MediaResourceLoaderBridgeFactory* bridge_factory_;
+ GURL url_;
+ int64 first_byte_position_;
+ int64 last_byte_position_;
+
+ // Callback method that listens to network events.
+ scoped_ptr<NetworkEventCallback> event_callback_;
+
+ // Members used during request start.
+ scoped_ptr<net::CompletionCallback> start_callback_;
+ scoped_ptr<webkit_glue::ResourceLoaderBridge> bridge_;
+ int64 offset_;
+ int64 content_length_;
+ int64 instance_size_;
+
+ // Members used during a read operation. They should be reset after each
+ // read has completed or failed.
+ scoped_ptr<net::CompletionCallback> read_callback_;
+ int64 read_position_;
+ int read_size_;
+ uint8* read_buffer_;
+
+ // Offsets of the requested first byte and last byte in |buffer_|. They are
+ // written by VerifyRead().
+ int first_offset_;
+ int last_offset_;
+
+ DISALLOW_COPY_AND_ASSIGN(BufferedResourceLoader);
+};
+
+class BufferedDataSource : public media::DataSource {
+ public:
+ // Methods called from pipeline thread
+ // Static methods for creating this class.
+ static media::FilterFactory* CreateFactory(
+ MessageLoop* message_loop,
+ webkit_glue::MediaResourceLoaderBridgeFactory* bridge_factory) {
+ return new media::FilterFactoryImpl2<
+ BufferedDataSource,
+ MessageLoop*,
+ webkit_glue::MediaResourceLoaderBridgeFactory*>(
+ message_loop, bridge_factory);
+ }
+
+ // media::FilterFactoryImpl2 implementation.
+ static bool IsMediaFormatSupported(
+ const media::MediaFormat& media_format);
+
+ // media::MediaFilter implementation.
+ virtual void Initialize(const std::string& url,
+ media::FilterCallback* callback);
+ virtual void Stop(media::FilterCallback* callback);
+ virtual void SetPlaybackRate(float playback_rate);
+
+ // media::DataSource implementation.
+ // Called from demuxer thread.
+ virtual void Read(int64 position, size_t size,
+ uint8* data,
+ media::DataSource::ReadCallback* read_callback);
+ virtual bool GetSize(int64* size_out);
+ virtual bool IsStreaming();
+
+ const media::MediaFormat& media_format() {
+ return media_format_;
+ }
+
+ protected:
+ BufferedDataSource(
+ MessageLoop* render_loop,
+ webkit_glue::MediaResourceLoaderBridgeFactory* bridge_factory);
+ virtual ~BufferedDataSource();
+
+ // A factory method to create a BufferedResourceLoader based on the read
+ // parameters. We can override this file to object a mock
+ // BufferedResourceLoader for testing.
+ virtual BufferedResourceLoader* CreateResourceLoader(
+ int64 first_byte_position, int64 last_byte_position);
+
+ // Gets the number of milliseconds to declare a request timeout since
+ // the request was made. This method is made virtual so as to inject a
+ // different number for testing purpose.
+ virtual base::TimeDelta GetTimeoutMilliseconds();
+
+ private:
+ friend class media::FilterFactoryImpl2<
+ BufferedDataSource,
+ MessageLoop*,
+ webkit_glue::MediaResourceLoaderBridgeFactory*>;
+
+ // Posted to perform initialization on render thread and start resource
+ // loading.
+ void InitializeTask();
+
+ // Task posted to perform actual reading on the render thread.
+ void ReadTask(int64 position, int read_size, uint8* read_buffer,
+ media::DataSource::ReadCallback* read_callback);
+
+ // Task posted when Stop() is called. Stops |watch_dog_timer_| and
+ // |loader_|, reset Read() variables, and set |stopped_on_render_loop_|
+ // to signal any remaining tasks to stop.
+ void CleanupTask();
+
+ // Restart resource loading on render thread.
+ void RestartLoadingTask();
+
+ // This task monitors the current active read request. If the current read
+ // request has timed out, this task will destroy the current loader and
+ // creates a new one to accommodate the read request.
+ void WatchDogTask();
+
+ // This task uses the current playback rate with the previous playback rate
+ // to determine whether we are going from pause to play and play to pause,
+ // and signals the buffered resource loader accordingly.
+ void SetPlaybackRateTask(float playback_rate);
+
+ // The method that performs actual read. This method can only be executed on
+ // the render thread.
+ void ReadInternal();
+
+ // Calls |read_callback_| and reset all read parameters.
+ void DoneRead_Locked(int error);
+
+ // Calls |initialize_callback_| and reset it.
+ void DoneInitialization_Locked();
+
+ // Callback method for |loader_| if URL for the resource requested is using
+ // HTTP protocol. This method is called when response for initial request is
+ // received.
+ void HttpInitialStartCallback(int error);
+
+ // Callback method for |loader_| if URL for the resource requested is using
+ // a non-HTTP protocol, e.g. local files. This method is called when response
+ // for initial request is received.
+ void NonHttpInitialStartCallback(int error);
+
+ // Callback method to be passed to BufferedResourceLoader during range
+ // request. Once a resource request has started, this method will be called
+ // with the error code. This method will be executed on the thread
+ // BufferedResourceLoader lives, i.e. render thread.
+ void PartialReadStartCallback(int error);
+
+ // Callback method for making a read request to BufferedResourceLoader.
+ // If data arrives or the request has failed, this method is called with
+ // the error code or the number of bytes read.
+ void ReadCallback(int error);
+
+ // Callback method when a network event is received.
+ void NetworkEventCallback();
+
+ media::MediaFormat media_format_;
+
+ // URL of the resource requested.
+ GURL url_;
+
+ // Members for total bytes of the requested object. It is written once on
+ // render thread but may be read from any thread. However reading of this
+ // member is guaranteed to happen after it is first written, so we don't
+ // need to protect it.
+ int64 total_bytes_;
+
+ // True if this data source is considered loaded.
+ bool loaded_;
+
+ // This value will be true if this data source can only support streaming.
+ // i.e. range request is not supported.
+ bool streaming_;
+
+ // A factory object to produce ResourceLoaderBridge.
+ scoped_ptr<webkit_glue::MediaResourceLoaderBridgeFactory> bridge_factory_;
+
+ // A resource loader for the media resource.
+ scoped_refptr<BufferedResourceLoader> loader_;
+
+ // True if network is active.
+ bool network_activity_;
+
+ // Callback method from the pipeline for initialization.
+ scoped_ptr<media::FilterCallback> initialize_callback_;
+
+ // Read parameters received from the Read() method call.
+ scoped_ptr<media::DataSource::ReadCallback> read_callback_;
+ int64 read_position_;
+ int read_size_;
+ uint8* read_buffer_;
+ base::Time read_submitted_time_;
+ int read_attempts_;
+
+ // This buffer is intermediate, we use it for BufferedResourceLoader to write
+ // to. And when read in BufferedResourceLoader is done, we copy data from
+ // this buffer to |read_buffer_|. The reason for an additional copy is that
+ // we don't own |read_buffer_|. But since the read operation is asynchronous,
+ // |read_buffer| can be destroyed at any time, so we only copy into
+ // |read_buffer| in the final step when it is safe.
+ // Memory is allocated for this member during initialization of this object
+ // because we want buffer to be passed into BufferedResourceLoader to be
+ // always non-null. And by initializing this member with a default size we can
+ // avoid creating zero-sized buffered if the first read has zero size.
+ scoped_array<uint8> intermediate_read_buffer_;
+ int intermediate_read_buffer_size_;
+
+ // The message loop of the render thread.
+ MessageLoop* render_loop_;
+
+ // Protects |stopped_|.
+ Lock lock_;
+
+ // Stop signal to suppressing activities. This variable is set on the pipeline
+ // thread and read from the render thread.
+ bool stop_signal_received_;
+
+ // This variable is set by CleanupTask() that indicates this object is stopped
+ // on the render thread.
+ bool stopped_on_render_loop_;
+
+ // This variable is true when we are in a paused state and false when we
+ // are in a playing state.
+ bool media_is_paused_;
+
+ // This timer is to run the WatchDogTask repeatedly. We use a timer instead
+ // of doing PostDelayedTask() reduce the extra reference held by the message
+ // loop. The RepeatingTimer does PostDelayedTask() internally, by using it
+ // the message loop doesn't hold a reference for the watch dog task.
+ base::RepeatingTimer<BufferedDataSource> watch_dog_timer_;
+
+ DISALLOW_COPY_AND_ASSIGN(BufferedDataSource);
+};
+
+} // namespace webkit_glue
+
+#endif // WEBKIT_GLUE_MEDIA_BUFFERED_DATA_SOURCE_H_
diff --git a/webkit/glue/media/buffered_data_source_unittest.cc b/webkit/glue/media/buffered_data_source_unittest.cc
new file mode 100644
index 0000000..ce42437
--- /dev/null
+++ b/webkit/glue/media/buffered_data_source_unittest.cc
@@ -0,0 +1,934 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <algorithm>
+
+#include "base/callback.h"
+#include "base/format_macros.h"
+#include "base/string_util.h"
+#include "media/base/filters.h"
+#include "media/base/mock_filter_host.h"
+#include "media/base/mock_filters.h"
+#include "net/base/net_errors.h"
+#include "net/http/http_response_headers.h"
+#include "webkit/glue/media/buffered_data_source.h"
+#include "webkit/glue/media/mock_media_resource_loader_bridge_factory.h"
+#include "webkit/glue/mock_resource_loader_bridge.h"
+
+using ::testing::_;
+using ::testing::Assign;
+using ::testing::DeleteArg;
+using ::testing::DoAll;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::InvokeWithoutArgs;
+using ::testing::NotNull;
+using ::testing::Return;
+using ::testing::SetArgumentPointee;
+using ::testing::StrictMock;
+using ::testing::WithArgs;
+
+namespace {
+
+const char* kHttpUrl = "http://test";
+const char* kFileUrl = "file://test";
+const int kDataSize = 1024;
+
+enum NetworkState {
+ NONE,
+ LOADED,
+ LOADING
+};
+
+} // namespace
+
+namespace webkit_glue {
+
+// Submit a request completed event to the resource loader due to request
+// being canceled. Pretending the event is from external.
+ACTION_P(RequestCanceled, loader) {
+ URLRequestStatus status;
+ status.set_status(URLRequestStatus::CANCELED);
+ status.set_os_error(net::ERR_ABORTED);
+ loader->OnCompletedRequest(status, "");
+}
+
+class BufferedResourceLoaderTest : public testing::Test {
+ public:
+ BufferedResourceLoaderTest() {
+ bridge_.reset(new StrictMock<MockResourceLoaderBridge>());
+
+ for (int i = 0; i < kDataSize; ++i)
+ data_[i] = i;
+ }
+
+ ~BufferedResourceLoaderTest() {
+ if (bridge_.get())
+ EXPECT_CALL(*bridge_, OnDestroy());
+ EXPECT_CALL(bridge_factory_, OnDestroy());
+ }
+
+ void Initialize(const char* url, int first_position, int last_position) {
+ gurl_ = GURL(url);
+ first_position_ = first_position;
+ last_position_ = last_position;
+
+ loader_ = new BufferedResourceLoader(&bridge_factory_, gurl_,
+ first_position_, last_position_);
+ EXPECT_EQ(gurl_.spec(), loader_->GetURLForDebugging().spec());
+ }
+
+ void SetLoaderBuffer(size_t forward_capacity, size_t backward_capacity) {
+ loader_->buffer_.reset(
+ new media::SeekableBuffer(backward_capacity, forward_capacity));
+ }
+
+ void Start() {
+ InSequence s;
+ EXPECT_CALL(bridge_factory_,
+ CreateBridge(gurl_, _, first_position_, last_position_))
+ .WillOnce(Return(bridge_.get()));
+ EXPECT_CALL(*bridge_, Start(loader_.get()));
+ loader_->Start(
+ NewCallback(this, &BufferedResourceLoaderTest::StartCallback),
+ NewCallback(this, &BufferedResourceLoaderTest::NetworkCallback));
+ }
+
+ void FullResponse(int64 instance_size) {
+ EXPECT_CALL(*this, StartCallback(net::OK));
+ ResourceLoaderBridge::ResponseInfo info;
+ std::string header = StringPrintf("HTTP/1.1 200 OK\n"
+ "Content-Length: %" PRId64,
+ instance_size);
+ replace(header.begin(), header.end(), '\n', '\0');
+ info.headers = new net::HttpResponseHeaders(header);
+ info.content_length = instance_size;
+ loader_->OnReceivedResponse(info, false);
+ EXPECT_EQ(instance_size, loader_->content_length());
+ EXPECT_EQ(instance_size, loader_->instance_size());
+ EXPECT_FALSE(loader_->partial_response());
+ }
+
+ void PartialResponse(int64 first_position, int64 last_position,
+ int64 instance_size) {
+ EXPECT_CALL(*this, StartCallback(net::OK));
+ int64 content_length = last_position - first_position + 1;
+ ResourceLoaderBridge::ResponseInfo info;
+ std::string header = StringPrintf("HTTP/1.1 206 Partial Content\n"
+ "Content-Range: bytes "
+ "%" PRId64 "-%" PRId64 "/%" PRId64,
+ first_position,
+ last_position,
+ instance_size);
+ replace(header.begin(), header.end(), '\n', '\0');
+ info.headers = new net::HttpResponseHeaders(header);
+ info.content_length = content_length;
+ loader_->OnReceivedResponse(info, false);
+ EXPECT_EQ(content_length, loader_->content_length());
+ EXPECT_EQ(instance_size, loader_->instance_size());
+ EXPECT_TRUE(loader_->partial_response());
+ }
+
+ void StopWhenLoad() {
+ InSequence s;
+ EXPECT_CALL(*bridge_, Cancel())
+ .WillOnce(RequestCanceled(loader_));
+ EXPECT_CALL(*bridge_, OnDestroy())
+ .WillOnce(Invoke(this, &BufferedResourceLoaderTest::ReleaseBridge));
+ loader_->Stop();
+ }
+
+ void ReleaseBridge() {
+ ignore_result(bridge_.release());
+ }
+
+ // Helper method to write to |loader_| from |data_|.
+ void WriteLoader(int position, int size) {
+ EXPECT_CALL(*this, NetworkCallback())
+ .RetiresOnSaturation();
+ loader_->OnReceivedData(reinterpret_cast<char*>(data_ + position), size);
+ }
+
+ // Helper method to read from |loader_|.
+ void ReadLoader(int64 position, int size, uint8* buffer) {
+ loader_->Read(position, size, buffer,
+ NewCallback(this, &BufferedResourceLoaderTest::ReadCallback));
+ }
+
+ // Verifis that data in buffer[0...size] is equal to data_[pos...pos+size].
+ void VerifyBuffer(uint8* buffer, int pos, int size) {
+ EXPECT_EQ(0, memcmp(buffer, data_ + pos, size));
+ }
+
+ // Helper method to disallow deferring in |loader_|.
+ void DisallowLoaderDefer() {
+ if (loader_->deferred_) {
+ EXPECT_CALL(*bridge_, SetDefersLoading(false));
+ EXPECT_CALL(*this, NetworkCallback());
+ }
+ loader_->SetAllowDefer(false);
+ }
+
+ // Helper method to allow deferring in |loader_|.
+ void AllowLoaderDefer() {
+ loader_->SetAllowDefer(true);
+ }
+
+ MOCK_METHOD1(StartCallback, void(int error));
+ MOCK_METHOD1(ReadCallback, void(int error));
+ MOCK_METHOD0(NetworkCallback, void());
+
+ protected:
+ GURL gurl_;
+ int64 first_position_;
+ int64 last_position_;
+
+ scoped_refptr<BufferedResourceLoader> loader_;
+ StrictMock<MockMediaResourceLoaderBridgeFactory> bridge_factory_;
+ scoped_ptr<StrictMock<MockResourceLoaderBridge> > bridge_;
+
+ uint8 data_[kDataSize];
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BufferedResourceLoaderTest);
+};
+
+TEST_F(BufferedResourceLoaderTest, StartStop) {
+ Initialize(kHttpUrl, -1, -1);
+ Start();
+ StopWhenLoad();
+}
+
+// Tests that HTTP header is missing in the response.
+TEST_F(BufferedResourceLoaderTest, MissingHttpHeader) {
+ Initialize(kHttpUrl, -1, -1);
+ Start();
+
+ EXPECT_CALL(*this, StartCallback(net::ERR_INVALID_RESPONSE));
+ EXPECT_CALL(*bridge_, Cancel())
+ .WillOnce(RequestCanceled(loader_));
+ EXPECT_CALL(*bridge_, OnDestroy())
+ .WillOnce(Invoke(this, &BufferedResourceLoaderTest::ReleaseBridge));
+
+ ResourceLoaderBridge::ResponseInfo info;
+ loader_->OnReceivedResponse(info, false);
+}
+
+// Tests that a bad HTTP response is recived, e.g. file not found.
+TEST_F(BufferedResourceLoaderTest, BadHttpResponse) {
+ Initialize(kHttpUrl, -1, -1);
+ Start();
+
+ EXPECT_CALL(*this, StartCallback(net::ERR_FAILED));
+ EXPECT_CALL(*bridge_, Cancel())
+ .WillOnce(RequestCanceled(loader_));
+ EXPECT_CALL(*bridge_, OnDestroy())
+ .WillOnce(Invoke(this, &BufferedResourceLoaderTest::ReleaseBridge));
+
+ ResourceLoaderBridge::ResponseInfo info;
+ info.headers = new net::HttpResponseHeaders("HTTP/1.1 404 Not Found\n");
+ loader_->OnReceivedResponse(info, false);
+}
+
+// Tests that partial content is requested but not fulfilled.
+TEST_F(BufferedResourceLoaderTest, NotPartialResponse) {
+ Initialize(kHttpUrl, 100, -1);
+ Start();
+ FullResponse(1024);
+ StopWhenLoad();
+}
+
+// Tests that a 200 response is received.
+TEST_F(BufferedResourceLoaderTest, FullResponse) {
+ Initialize(kHttpUrl, -1, -1);
+ Start();
+ FullResponse(1024);
+ StopWhenLoad();
+}
+
+// Tests that a partial content response is received.
+TEST_F(BufferedResourceLoaderTest, PartialResponse) {
+ Initialize(kHttpUrl, 100, 200);
+ Start();
+ PartialResponse(100, 200, 1024);
+ StopWhenLoad();
+}
+
+// Tests that an invalid partial response is received.
+TEST_F(BufferedResourceLoaderTest, InvalidPartialResponse) {
+ Initialize(kHttpUrl, 0, 10);
+ Start();
+
+ EXPECT_CALL(*this, StartCallback(net::ERR_INVALID_RESPONSE));
+ EXPECT_CALL(*bridge_, Cancel())
+ .WillOnce(RequestCanceled(loader_));
+ EXPECT_CALL(*bridge_, OnDestroy())
+ .WillOnce(Invoke(this, &BufferedResourceLoaderTest::ReleaseBridge));
+
+ ResourceLoaderBridge::ResponseInfo info;
+ std::string header = StringPrintf("HTTP/1.1 206 Partial Content\n"
+ "Content-Range: bytes %d-%d/%d",
+ 1, 10, 1024);
+ replace(header.begin(), header.end(), '\n', '\0');
+ info.headers = new net::HttpResponseHeaders(header);
+ info.content_length = 10;
+ loader_->OnReceivedResponse(info, false);
+}
+
+// Tests the logic of sliding window for data buffering and reading.
+TEST_F(BufferedResourceLoaderTest, BufferAndRead) {
+ Initialize(kHttpUrl, 10, 29);
+ Start();
+ PartialResponse(10, 29, 30);
+
+ uint8 buffer[10];
+ InSequence s;
+
+ // Writes 10 bytes and read them back.
+ WriteLoader(10, 10);
+ EXPECT_CALL(*this, ReadCallback(10));
+ ReadLoader(10, 10, buffer);
+ VerifyBuffer(buffer, 10, 10);
+
+ // Writes 10 bytes and read 2 times.
+ WriteLoader(20, 10);
+ EXPECT_CALL(*this, ReadCallback(5));
+ ReadLoader(20, 5, buffer);
+ VerifyBuffer(buffer, 20, 5);
+ EXPECT_CALL(*this, ReadCallback(5));
+ ReadLoader(25, 5, buffer);
+ VerifyBuffer(buffer, 25, 5);
+
+ // Read backward within buffer.
+ EXPECT_CALL(*this, ReadCallback(10));
+ ReadLoader(10, 10, buffer);
+ VerifyBuffer(buffer, 10, 10);
+
+ // Read backward outside buffer.
+ EXPECT_CALL(*this, ReadCallback(net::ERR_CACHE_MISS));
+ ReadLoader(9, 10, buffer);
+
+ // Response has completed.
+ EXPECT_CALL(*this, NetworkCallback());
+ EXPECT_CALL(*bridge_, OnDestroy())
+ .WillOnce(Invoke(this, &BufferedResourceLoaderTest::ReleaseBridge));
+ URLRequestStatus status;
+ status.set_status(URLRequestStatus::SUCCESS);
+ loader_->OnCompletedRequest(status, "");
+
+ // Try to read 10 from position 25 will just return with 5 bytes.
+ EXPECT_CALL(*this, ReadCallback(5));
+ ReadLoader(25, 10, buffer);
+ VerifyBuffer(buffer, 25, 5);
+
+ // Try to read outside buffered range after request has completed.
+ EXPECT_CALL(*this, ReadCallback(net::ERR_CACHE_MISS));
+ ReadLoader(5, 10, buffer);
+
+ // Try to read beyond the instance size.
+ EXPECT_CALL(*this, ReadCallback(0));
+ ReadLoader(30, 10, buffer);
+}
+
+TEST_F(BufferedResourceLoaderTest, ReadOutsideBuffer) {
+ Initialize(kHttpUrl, 10, 0x00FFFFFF);
+ Start();
+ PartialResponse(10, 0x00FFFFFF, 0x01000000);
+
+ uint8 buffer[10];
+ InSequence s;
+
+ // Read very far aheard will get a cache miss.
+ EXPECT_CALL(*this, ReadCallback(net::ERR_CACHE_MISS));
+ ReadLoader(0x00FFFFFF, 1, buffer);
+
+ // The following call will not call ReadCallback() because it is waiting for
+ // data to arrive.
+ ReadLoader(10, 10, buffer);
+
+ // Writing to loader will fulfill the read request.
+ EXPECT_CALL(*this, ReadCallback(10));
+ WriteLoader(10, 20);
+ VerifyBuffer(buffer, 10, 10);
+
+ // The following call cannot be fulfilled now.
+ ReadLoader(25, 10, buffer);
+
+ EXPECT_CALL(*this, ReadCallback(5));
+ EXPECT_CALL(*this, NetworkCallback());
+ EXPECT_CALL(*bridge_, OnDestroy())
+ .WillOnce(Invoke(this, &BufferedResourceLoaderTest::ReleaseBridge));
+ URLRequestStatus status;
+ status.set_status(URLRequestStatus::SUCCESS);
+ loader_->OnCompletedRequest(status, "");
+}
+
+TEST_F(BufferedResourceLoaderTest, RequestFailedWhenRead) {
+ Initialize(kHttpUrl, 10, 29);
+ Start();
+ PartialResponse(10, 29, 30);
+
+ uint8 buffer[10];
+ InSequence s;
+
+ ReadLoader(10, 10, buffer);
+ EXPECT_CALL(*this, ReadCallback(net::ERR_FAILED));
+ EXPECT_CALL(*this, NetworkCallback());
+ EXPECT_CALL(*bridge_, OnDestroy())
+ .WillOnce(Invoke(this, &BufferedResourceLoaderTest::ReleaseBridge));
+ URLRequestStatus status;
+ status.set_status(URLRequestStatus::FAILED);
+ loader_->OnCompletedRequest(status, "");
+}
+
+// Tests the logic of caching data to disk when media is paused.
+TEST_F(BufferedResourceLoaderTest, AllowDefer_NoDataReceived) {
+ Initialize(kHttpUrl, 10, 99);
+ SetLoaderBuffer(10, 20);
+ Start();
+ PartialResponse(10, 99, 100);
+
+ // Start in undeferred state, then disallow defer, then allow defer
+ // without receiving data in between.
+ DisallowLoaderDefer();
+ AllowLoaderDefer();
+ StopWhenLoad();
+}
+
+TEST_F(BufferedResourceLoaderTest, AllowDefer_ReadSameWindow) {
+ Initialize(kHttpUrl, 10, 99);
+ SetLoaderBuffer(10, 20);
+ Start();
+ PartialResponse(10, 99, 100);
+
+ uint8 buffer[10];
+
+ // Start in undeferred state, disallow defer, receive data but don't shift
+ // buffer window, then allow defer and read.
+ DisallowLoaderDefer();
+ WriteLoader(10, 10);
+ AllowLoaderDefer();
+
+ EXPECT_CALL(*this, ReadCallback(10));
+ ReadLoader(10, 10, buffer);
+ VerifyBuffer(buffer, 10, 10);
+ StopWhenLoad();
+}
+
+TEST_F(BufferedResourceLoaderTest, AllowDefer_ReadPastWindow) {
+ Initialize(kHttpUrl, 10, 99);
+ SetLoaderBuffer(10, 20);
+ Start();
+ PartialResponse(10, 99, 100);
+
+ uint8 buffer[10];
+
+ // Not deferred, disallow defer, received data and shift buffer window,
+ // allow defer, then read in area outside of buffer window.
+ DisallowLoaderDefer();
+ WriteLoader(10, 10);
+ WriteLoader(20, 50);
+ AllowLoaderDefer();
+
+ EXPECT_CALL(*this, ReadCallback(net::ERR_CACHE_MISS));
+ ReadLoader(10, 10, buffer);
+ StopWhenLoad();
+}
+
+TEST_F(BufferedResourceLoaderTest, AllowDefer_DeferredNoDataReceived) {
+ Initialize(kHttpUrl, 10, 99);
+ SetLoaderBuffer(10, 20);
+ Start();
+ PartialResponse(10, 99, 100);
+
+ uint8 buffer[10];
+
+ // Start in deferred state, then disallow defer, receive no data, and
+ // allow defer and read.
+ EXPECT_CALL(*bridge_, SetDefersLoading(true));
+ EXPECT_CALL(*this, NetworkCallback());
+ WriteLoader(10, 40);
+
+ DisallowLoaderDefer();
+ AllowLoaderDefer();
+
+ EXPECT_CALL(*this, ReadCallback(10));
+ ReadLoader(20, 10, buffer);
+ VerifyBuffer(buffer, 20, 10);
+ StopWhenLoad();
+}
+
+TEST_F(BufferedResourceLoaderTest, AllowDefer_DeferredReadSameWindow) {
+ Initialize(kHttpUrl, 10, 99);
+ SetLoaderBuffer(10, 20);
+ Start();
+ PartialResponse(10, 99, 100);
+
+ uint8 buffer[10];
+
+ // Start in deferred state, disallow defer, receive data and shift buffer
+ // window, allow defer, and read in a place that's still in the window.
+ EXPECT_CALL(*bridge_, SetDefersLoading(true));
+ EXPECT_CALL(*this, NetworkCallback());
+ WriteLoader(10, 30);
+
+ DisallowLoaderDefer();
+ WriteLoader(40, 5);
+ AllowLoaderDefer();
+
+ EXPECT_CALL(*this, ReadCallback(10));
+ ReadLoader(20, 10, buffer);
+ VerifyBuffer(buffer, 20, 10);
+ StopWhenLoad();
+}
+
+TEST_F(BufferedResourceLoaderTest, AllowDefer_DeferredReadPastWindow) {
+ Initialize(kHttpUrl, 10, 99);
+ SetLoaderBuffer(10, 20);
+ Start();
+ PartialResponse(10, 99, 100);
+
+ uint8 buffer[10];
+
+ // Start in deferred state, disallow defer, receive data and shift buffer
+ // window, allow defer, and read outside of the buffer window.
+ EXPECT_CALL(*bridge_, SetDefersLoading(true));
+ EXPECT_CALL(*this, NetworkCallback());
+ WriteLoader(10, 40);
+
+ DisallowLoaderDefer();
+ WriteLoader(50, 20);
+ WriteLoader(70, 40);
+ AllowLoaderDefer();
+
+ EXPECT_CALL(*this, ReadCallback(net::ERR_CACHE_MISS));
+ ReadLoader(20, 5, buffer);
+ StopWhenLoad();
+}
+
+// TODO(hclam): add unit test for defer loading.
+
+class MockBufferedResourceLoader : public BufferedResourceLoader {
+ public:
+ MockBufferedResourceLoader() : BufferedResourceLoader(NULL, GURL(), 0, 0) {
+ }
+
+ MOCK_METHOD2(Start, void(net::CompletionCallback* read_callback,
+ NetworkEventCallback* network_callback));
+ MOCK_METHOD0(Stop, void());
+ MOCK_METHOD4(Read, void(int64 position, int read_size, uint8* buffer,
+ net::CompletionCallback* callback));
+ MOCK_METHOD0(content_length, int64());
+ MOCK_METHOD0(instance_size, int64());
+ MOCK_METHOD0(partial_response, bool());
+ MOCK_METHOD0(network_activity, bool());
+ MOCK_METHOD0(GetBufferedFirstBytePosition, int64());
+ MOCK_METHOD0(GetBufferedLastBytePosition, int64());
+
+ protected:
+ ~MockBufferedResourceLoader() {}
+
+ DISALLOW_COPY_AND_ASSIGN(MockBufferedResourceLoader);
+};
+
+// A mock BufferedDataSource to inject mock BufferedResourceLoader through
+// CreateResourceLoader() method.
+class MockBufferedDataSource : public BufferedDataSource {
+ public:
+ // Static methods for creating this class.
+ static media::FilterFactory* CreateFactory(
+ MessageLoop* message_loop,
+ MediaResourceLoaderBridgeFactory* bridge_factory) {
+ return new media::FilterFactoryImpl2<
+ MockBufferedDataSource,
+ MessageLoop*,
+ MediaResourceLoaderBridgeFactory*>(message_loop,
+ bridge_factory);
+ }
+
+ virtual base::TimeDelta GetTimeoutMilliseconds() {
+ // It is 100 ms because we don't want the test to run too long.
+ return base::TimeDelta::FromMilliseconds(100);
+ }
+
+ MOCK_METHOD2(CreateResourceLoader, BufferedResourceLoader*(
+ int64 first_position, int64 last_position));
+
+ protected:
+ MockBufferedDataSource(
+ MessageLoop* message_loop, MediaResourceLoaderBridgeFactory* factory)
+ : BufferedDataSource(message_loop, factory) {
+ }
+
+ private:
+ friend class media::FilterFactoryImpl2<
+ MockBufferedDataSource,
+ MessageLoop*,
+ MediaResourceLoaderBridgeFactory*>;
+
+ DISALLOW_COPY_AND_ASSIGN(MockBufferedDataSource);
+};
+
+class BufferedDataSourceTest : public testing::Test {
+ public:
+ BufferedDataSourceTest() {
+ message_loop_ = MessageLoop::current();
+ bridge_factory_.reset(
+ new StrictMock<MockMediaResourceLoaderBridgeFactory>());
+ factory_ = MockBufferedDataSource::CreateFactory(message_loop_,
+ bridge_factory_.get());
+
+ // Prepare test data.
+ for (size_t i = 0; i < sizeof(data_); ++i) {
+ data_[i] = i;
+ }
+ }
+
+ virtual ~BufferedDataSourceTest() {
+ if (data_source_) {
+ // Release the bridge factory because we don't own it.
+ // Expects bridge factory to be destroyed along with data source.
+ EXPECT_CALL(*bridge_factory_, OnDestroy())
+ .WillOnce(Invoke(this,
+ &BufferedDataSourceTest::ReleaseBridgeFactory));
+ }
+ }
+
+ void InitializeDataSource(const char* url, int error,
+ bool partial_response, int64 instance_size,
+ NetworkState networkState) {
+ // Saves the url first.
+ gurl_ = GURL(url);
+
+ media::MediaFormat url_format;
+ url_format.SetAsString(media::MediaFormat::kMimeType,
+ media::mime_type::kURL);
+ url_format.SetAsString(media::MediaFormat::kURL, url);
+ data_source_ = factory_->Create<MockBufferedDataSource>(url_format);
+ CHECK(data_source_);
+
+ // There is no need to provide a message loop to data source.
+ data_source_->set_host(&host_);
+
+ // Creates the mock loader to be injected.
+ loader_ = new StrictMock<MockBufferedResourceLoader>();
+
+ bool loaded = networkState == LOADED;
+ {
+ InSequence s;
+ EXPECT_CALL(*data_source_, CreateResourceLoader(_, _))
+ .WillOnce(Return(loader_.get()));
+
+ // The initial response loader will be started.
+ EXPECT_CALL(*loader_, Start(NotNull(), NotNull()))
+ .WillOnce(
+ DoAll(Assign(&error_, error),
+ Invoke(this,
+ &BufferedDataSourceTest::InvokeStartCallback)));
+ }
+
+ StrictMock<media::MockFilterCallback> callback;
+ EXPECT_CALL(*loader_, instance_size())
+ .WillRepeatedly(Return(instance_size));
+ EXPECT_CALL(*loader_, partial_response())
+ .WillRepeatedly(Return(partial_response));
+ if (error == net::OK) {
+ // Expected loaded or not.
+ EXPECT_CALL(host_, SetLoaded(loaded));
+
+ // TODO(hclam): The condition for streaming needs to be adjusted.
+ if (instance_size != -1 && (loaded || partial_response)) {
+ EXPECT_CALL(host_, SetTotalBytes(instance_size));
+ if (loaded)
+ EXPECT_CALL(host_, SetBufferedBytes(instance_size));
+ else
+ EXPECT_CALL(host_, SetBufferedBytes(0));
+ } else {
+ EXPECT_CALL(host_, SetStreaming(true));
+ }
+
+ EXPECT_CALL(callback, OnFilterCallback());
+ EXPECT_CALL(callback, OnCallbackDestroyed());
+ } else {
+ EXPECT_CALL(host_, SetError(media::PIPELINE_ERROR_NETWORK));
+ EXPECT_CALL(*loader_, Stop());
+ EXPECT_CALL(callback, OnFilterCallback());
+ EXPECT_CALL(callback, OnCallbackDestroyed());
+ }
+
+ // Actual initialization of the data source.
+ data_source_->Initialize(url, callback.NewCallback());
+ message_loop_->RunAllPending();
+
+ if (error == net::OK) {
+ // Verify the size of the data source.
+ int64 size;
+ if (instance_size != -1 && (loaded || partial_response)) {
+ EXPECT_TRUE(data_source_->GetSize(&size));
+ EXPECT_EQ(instance_size, size);
+ } else {
+ EXPECT_TRUE(data_source_->IsStreaming());
+ }
+ }
+ }
+
+ void StopDataSource() {
+ if (loader_) {
+ InSequence s;
+ EXPECT_CALL(*loader_, Stop());
+ }
+
+ StrictMock<media::MockFilterCallback> callback;
+ EXPECT_CALL(callback, OnFilterCallback());
+ EXPECT_CALL(callback, OnCallbackDestroyed());
+ data_source_->Stop(callback.NewCallback());
+ message_loop_->RunAllPending();
+ }
+
+ void ReleaseBridgeFactory() {
+ ignore_result(bridge_factory_.release());
+ }
+
+ void InvokeStartCallback(
+ net::CompletionCallback* callback,
+ BufferedResourceLoader::NetworkEventCallback* network_callback) {
+ callback->RunWithParams(Tuple1<int>(error_));
+ delete callback;
+ // TODO(hclam): Save this callback.
+ delete network_callback;
+ }
+
+ void InvokeReadCallback(int64 position, int size, uint8* buffer,
+ net::CompletionCallback* callback) {
+ if (error_ > 0)
+ memcpy(buffer, data_ + static_cast<int>(position), error_);
+ callback->RunWithParams(Tuple1<int>(error_));
+ delete callback;
+ }
+
+ void ReadDataSourceHit(int64 position, int size, int read_size) {
+ EXPECT_TRUE(loader_);
+
+ InSequence s;
+ // Expect the read is delegated to the resource loader.
+ EXPECT_CALL(*loader_, Read(position, size, NotNull(), NotNull()))
+ .WillOnce(DoAll(Assign(&error_, read_size),
+ Invoke(this,
+ &BufferedDataSourceTest::InvokeReadCallback)));
+
+ // The read has succeeded, so read callback will be called.
+ EXPECT_CALL(*this, ReadCallback(read_size));
+
+ data_source_->Read(
+ position, size, buffer_,
+ NewCallback(this, &BufferedDataSourceTest::ReadCallback));
+ message_loop_->RunAllPending();
+
+ // Make sure data is correct.
+ EXPECT_EQ(0,
+ memcmp(buffer_, data_ + static_cast<int>(position), read_size));
+ }
+
+ void ReadDataSourceMiss(int64 position, int size) {
+ EXPECT_TRUE(loader_);
+
+ // 1. Reply with a cache miss for the read.
+ {
+ InSequence s;
+ EXPECT_CALL(*loader_, Read(position, size, NotNull(), NotNull()))
+ .WillOnce(DoAll(Assign(&error_, net::ERR_CACHE_MISS),
+ Invoke(this,
+ &BufferedDataSourceTest::InvokeReadCallback)));
+ EXPECT_CALL(*loader_, Stop());
+ }
+
+ // 2. Then the current loader will be stop and destroyed.
+ StrictMock<MockBufferedResourceLoader> *new_loader =
+ new StrictMock<MockBufferedResourceLoader>();
+ EXPECT_CALL(*data_source_, CreateResourceLoader(position, -1))
+ .WillOnce(Return(new_loader));
+
+ // 3. Then the new loader will be started.
+ EXPECT_CALL(*new_loader, Start(NotNull(), NotNull()))
+ .WillOnce(DoAll(Assign(&error_, net::OK),
+ Invoke(this,
+ &BufferedDataSourceTest::InvokeStartCallback)));
+ EXPECT_CALL(*new_loader, partial_response())
+ .WillRepeatedly(Return(loader_->partial_response()));
+
+ // 4. Then again a read request is made to the new loader.
+ EXPECT_CALL(*new_loader, Read(position, size, NotNull(), NotNull()))
+ .WillOnce(DoAll(Assign(&error_, size),
+ Invoke(this,
+ &BufferedDataSourceTest::InvokeReadCallback)));
+
+ EXPECT_CALL(*this, ReadCallback(size));
+
+ data_source_->Read(
+ position, size, buffer_,
+ NewCallback(this, &BufferedDataSourceTest::ReadCallback));
+ message_loop_->RunAllPending();
+
+ // Make sure data is correct.
+ EXPECT_EQ(0, memcmp(buffer_, data_ + static_cast<int>(position), size));
+
+ loader_ = new_loader;
+ }
+
+ void ReadDataSourceFailed(int64 position, int size, int error) {
+ EXPECT_TRUE(loader_);
+
+ // 1. Expect the read is delegated to the resource loader.
+ EXPECT_CALL(*loader_, Read(position, size, NotNull(), NotNull()))
+ .WillOnce(DoAll(Assign(&error_, error),
+ Invoke(this,
+ &BufferedDataSourceTest::InvokeReadCallback)));
+
+ // 2. Host will then receive an error.
+ EXPECT_CALL(*loader_, Stop());
+
+ // 3. The read has failed, so read callback will be called.
+ EXPECT_CALL(*this, ReadCallback(media::DataSource::kReadError));
+
+ data_source_->Read(
+ position, size, buffer_,
+ NewCallback(this, &BufferedDataSourceTest::ReadCallback));
+
+ message_loop_->RunAllPending();
+ }
+
+ void ReadDataSourceTimesOut(int64 position, int size) {
+ // 1. Drop the request and let it times out.
+ {
+ InSequence s;
+ EXPECT_CALL(*loader_, Read(position, size, NotNull(), NotNull()))
+ .WillOnce(DeleteArg<3>());
+ EXPECT_CALL(*loader_, Stop());
+ }
+
+ // 2. Then the current loader will be stop and destroyed.
+ StrictMock<MockBufferedResourceLoader> *new_loader =
+ new StrictMock<MockBufferedResourceLoader>();
+ EXPECT_CALL(*data_source_, CreateResourceLoader(position, -1))
+ .WillOnce(Return(new_loader));
+
+ // 3. Then the new loader will be started and respond to queries about
+ // whether this is a partial response using the value of the previous
+ // loader.
+ EXPECT_CALL(*new_loader, Start(NotNull(), NotNull()))
+ .WillOnce(DoAll(Assign(&error_, net::OK),
+ Invoke(this,
+ &BufferedDataSourceTest::InvokeStartCallback)));
+ EXPECT_CALL(*new_loader, partial_response())
+ .WillRepeatedly(Return(loader_->partial_response()));
+
+ // 4. Then again a read request is made to the new loader.
+ EXPECT_CALL(*new_loader, Read(position, size, NotNull(), NotNull()))
+ .WillOnce(DoAll(Assign(&error_, size),
+ Invoke(this,
+ &BufferedDataSourceTest::InvokeReadCallback),
+ InvokeWithoutArgs(message_loop_,
+ &MessageLoop::Quit)));
+
+ EXPECT_CALL(*this, ReadCallback(size));
+
+ data_source_->Read(
+ position, size, buffer_,
+ NewCallback(this, &BufferedDataSourceTest::ReadCallback));
+
+ // This blocks the current thread until the watch task is executed and
+ // triggers a read callback to quit this message loop.
+ message_loop_->Run();
+
+ // Make sure data is correct.
+ EXPECT_EQ(0, memcmp(buffer_, data_ + static_cast<int>(position), size));
+
+ loader_ = new_loader;
+ }
+
+ MOCK_METHOD1(ReadCallback, void(size_t size));
+
+ scoped_ptr<StrictMock<MockMediaResourceLoaderBridgeFactory> >
+ bridge_factory_;
+ scoped_refptr<StrictMock<MockBufferedResourceLoader> > loader_;
+ scoped_refptr<MockBufferedDataSource> data_source_;
+ scoped_refptr<media::FilterFactory> factory_;
+
+ StrictMock<media::MockFilterHost> host_;
+ GURL gurl_;
+ MessageLoop* message_loop_;
+
+ int error_;
+ uint8 buffer_[1024];
+ uint8 data_[1024];
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BufferedDataSourceTest);
+};
+
+TEST_F(BufferedDataSourceTest, InitializationSuccess) {
+ InitializeDataSource(kHttpUrl, net::OK, true, 1024, LOADING);
+ StopDataSource();
+}
+
+TEST_F(BufferedDataSourceTest, InitiailizationFailed) {
+ InitializeDataSource(kHttpUrl, net::ERR_FILE_NOT_FOUND, false, 0, NONE);
+ StopDataSource();
+}
+
+TEST_F(BufferedDataSourceTest, MissingContentLength) {
+ InitializeDataSource(kHttpUrl, net::OK, true, -1, LOADING);
+ StopDataSource();
+}
+
+TEST_F(BufferedDataSourceTest, RangeRequestNotSupported) {
+ InitializeDataSource(kHttpUrl, net::OK, false, 1024, LOADING);
+ StopDataSource();
+}
+
+TEST_F(BufferedDataSourceTest,
+ MissingContentLengthAndRangeRequestNotSupported) {
+ InitializeDataSource(kHttpUrl, net::OK, false, -1, LOADING);
+ StopDataSource();
+}
+
+TEST_F(BufferedDataSourceTest, ReadCacheHit) {
+ InitializeDataSource(kHttpUrl, net::OK, true, 25, LOADING);
+
+ // Performs read with cache hit.
+ ReadDataSourceHit(10, 10, 10);
+
+ // Performs read with cache hit but partially filled.
+ ReadDataSourceHit(20, 10, 5);
+
+ StopDataSource();
+}
+
+TEST_F(BufferedDataSourceTest, ReadCacheMiss) {
+ InitializeDataSource(kHttpUrl, net::OK, true, 1024, LOADING);
+ ReadDataSourceMiss(1000, 10);
+ ReadDataSourceMiss(20, 10);
+ StopDataSource();
+}
+
+TEST_F(BufferedDataSourceTest, ReadFailed) {
+ InitializeDataSource(kHttpUrl, net::OK, true, 1024, LOADING);
+ ReadDataSourceHit(10, 10, 10);
+ ReadDataSourceFailed(10, 10, net::ERR_CONNECTION_RESET);
+ StopDataSource();
+}
+
+TEST_F(BufferedDataSourceTest, ReadTimesOut) {
+ InitializeDataSource(kHttpUrl, net::OK, true, 1024, LOADING);
+ ReadDataSourceTimesOut(20, 10);
+ StopDataSource();
+}
+
+TEST_F(BufferedDataSourceTest, FileHasLoadedState) {
+ InitializeDataSource(kFileUrl, net::OK, true, 1024, LOADED);
+ ReadDataSourceTimesOut(20, 10);
+ StopDataSource();
+}
+
+} // namespace webkit_glue
diff --git a/webkit/glue/media/media_resource_loader_bridge_factory.cc b/webkit/glue/media/media_resource_loader_bridge_factory.cc
new file mode 100644
index 0000000..1961bcc
--- /dev/null
+++ b/webkit/glue/media/media_resource_loader_bridge_factory.cc
@@ -0,0 +1,76 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/media/media_resource_loader_bridge_factory.h"
+
+#include "base/format_macros.h"
+#include "base/string_util.h"
+
+namespace {
+
+// A constant for an unknown position.
+const int64 kPositionNotSpecified = -1;
+
+} // namespace
+
+namespace webkit_glue {
+
+MediaResourceLoaderBridgeFactory::MediaResourceLoaderBridgeFactory(
+ const GURL& referrer,
+ const std::string& frame_origin,
+ const std::string& main_frame_origin,
+ int origin_pid,
+ int appcache_host_id,
+ int32 routing_id)
+ : referrer_(referrer),
+ frame_origin_(frame_origin),
+ main_frame_origin_(main_frame_origin),
+ origin_pid_(origin_pid),
+ appcache_host_id_(appcache_host_id),
+ routing_id_(routing_id) {
+}
+
+ResourceLoaderBridge* MediaResourceLoaderBridgeFactory::CreateBridge(
+ const GURL& url,
+ int load_flags,
+ int64 first_byte_position,
+ int64 last_byte_position) {
+ webkit_glue::ResourceLoaderBridge::RequestInfo request_info;
+ request_info.method = "GET";
+ request_info.url = url;
+ request_info.first_party_for_cookies = url;
+ request_info.referrer = referrer_;
+ request_info.frame_origin = frame_origin_;
+ request_info.main_frame_origin = main_frame_origin_;
+ request_info.headers = GenerateHeaders(first_byte_position,
+ last_byte_position);
+ request_info.load_flags = load_flags;
+ request_info.requestor_pid = origin_pid_;
+ request_info.request_type = ResourceType::MEDIA;
+ request_info.appcache_host_id = appcache_host_id_;
+ request_info.routing_id = routing_id_;
+ return webkit_glue::ResourceLoaderBridge::Create(request_info);
+}
+
+// static
+const std::string MediaResourceLoaderBridgeFactory::GenerateHeaders (
+ int64 first_byte_position, int64 last_byte_position) {
+ // Construct the range header.
+ std::string header;
+ if (first_byte_position > kPositionNotSpecified &&
+ last_byte_position > kPositionNotSpecified) {
+ if (first_byte_position <= last_byte_position) {
+ header = StringPrintf("Range: bytes=%" PRId64 "-%" PRId64,
+ first_byte_position,
+ last_byte_position);
+ }
+ } else if (first_byte_position > kPositionNotSpecified) {
+ header = StringPrintf("Range: bytes=%" PRId64 "-", first_byte_position);
+ } else if (last_byte_position > kPositionNotSpecified) {
+ NOTIMPLEMENTED() << "Suffix range not implemented";
+ }
+ return header;
+}
+
+} // namespace webkit_glue
diff --git a/webkit/glue/media/media_resource_loader_bridge_factory.h b/webkit/glue/media/media_resource_loader_bridge_factory.h
new file mode 100644
index 0000000..6408949
--- /dev/null
+++ b/webkit/glue/media/media_resource_loader_bridge_factory.h
@@ -0,0 +1,76 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_MEDIA_MEDIA_RESOURCE_LOADER_BRIDGE_FACTORY_H_
+#define WEBKIT_GLUE_MEDIA_MEDIA_RESOURCE_LOADER_BRIDGE_FACTORY_H_
+
+#include "testing/gtest/include/gtest/gtest_prod.h"
+#include "webkit/glue/resource_loader_bridge.h"
+
+namespace webkit_glue {
+
+// A factory used to create a ResourceLoaderBridge for the media player.
+// This factory is used also for testing. Testing code can use this class and
+// override CreateBridge() to inject a mock ResourceLoaderBridge for code that
+// interacts with it, e.g. BufferedDataSource.
+class MediaResourceLoaderBridgeFactory {
+ public:
+ MediaResourceLoaderBridgeFactory(
+ const GURL& referrer,
+ const std::string& frame_origin,
+ const std::string& main_frame_origin,
+ int origin_pid,
+ int appcache_host_id,
+ int32 routing_id);
+
+ virtual ~MediaResourceLoaderBridgeFactory() {}
+
+ // Factory method to create a ResourceLoaderBridge with the following
+ // parameters:
+ // |url| - URL of the resource to be loaded.
+ // |load_flags| - Load flags for this loading.
+ // |first_byte_position| - First byte position for a range request, -1 if not.
+ // |last_byte_position| - Last byte position for a range request, -1 if not.
+ virtual ResourceLoaderBridge* CreateBridge(
+ const GURL& url,
+ int load_flags,
+ int64 first_byte_position,
+ int64 last_byte_position);
+
+ protected:
+ // An empty constructor only used by inherited classes.
+ MediaResourceLoaderBridgeFactory() {
+ }
+
+ private:
+ FRIEND_TEST(MediaResourceLoaderBridgeFactoryTest, GenerateHeaders);
+
+ // Returns a range request header using parameters |first_byte_position| and
+ // |last_byte_position|.
+ // Negative numbers other than -1 are not allowed for |first_byte_position|
+ // and |last_byte_position|. |first_byte_position| should always be less than
+ // or equal to |last_byte_position| if they are both not -1.
+ // Here's a list of valid parameters:
+ // |first_byte_position| |last_byte_position|
+ // 0 1000
+ // 4096 4096
+ // 0 -1
+ // -1 -1
+ // Empty string is returned on invalid parameters.
+ static const std::string GenerateHeaders(int64 first_byte_position,
+ int64 last_byte_position);
+
+ GURL first_party_for_cookies_;
+ GURL referrer_;
+ std::string frame_origin_;
+ std::string main_frame_origin_;
+ std::string headers_;
+ int origin_pid_;
+ int appcache_host_id_;
+ int32 routing_id_;
+};
+
+} // namespace webkit_glue
+
+#endif // WEBKIT_GLUE_MEDIA_MEDIA_RESOURCE_LOADER_BRIDGE_FACTORY_H_
diff --git a/webkit/glue/media/media_resource_loader_bridge_factory_unittest.cc b/webkit/glue/media/media_resource_loader_bridge_factory_unittest.cc
new file mode 100644
index 0000000..4c0126b
--- /dev/null
+++ b/webkit/glue/media/media_resource_loader_bridge_factory_unittest.cc
@@ -0,0 +1,44 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http/http_util.h"
+#include "webkit/glue/media/media_resource_loader_bridge_factory.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace webkit_glue {
+
+TEST(MediaResourceLoaderBridgeFactoryTest, GenerateHeaders) {
+ static const struct {
+ const bool success;
+ const int64 first_byte_position;
+ const int64 last_byte_position;
+ } data[] = {
+ { false, -1, -1 },
+ { false, -5, 0 },
+ { false, 100, 0 },
+ { true, 0, -1 },
+ { true, 0, 0 },
+ { true, 100, 100 },
+ { true, 50, -1 },
+ { true, 10000, -1 },
+ { true, 50, 100 },
+ };
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(data); ++i) {
+ std::string headers = MediaResourceLoaderBridgeFactory::GenerateHeaders(
+ data[i].first_byte_position, data[i].last_byte_position);
+ std::vector<net::HttpByteRange> ranges;
+ bool ret = net::HttpUtil::ParseRanges(headers, &ranges);
+ EXPECT_EQ(data[i].success, ret);
+ if (ret) {
+ EXPECT_EQ(1u, ranges.size());
+ EXPECT_EQ(data[i].first_byte_position,
+ ranges[0].first_byte_position());
+ EXPECT_EQ(data[i].last_byte_position,
+ ranges[0].last_byte_position());
+ }
+ }
+}
+
+} // namespace webkit_glue
diff --git a/webkit/glue/media/mock_media_resource_loader_bridge_factory.h b/webkit/glue/media/mock_media_resource_loader_bridge_factory.h
new file mode 100644
index 0000000..7bb27fe
--- /dev/null
+++ b/webkit/glue/media/mock_media_resource_loader_bridge_factory.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_MOCK_RESOURCE_LOADER_BRIDGE_H_
+#define WEBKIT_GLUE_MOCK_RESOURCE_LOADER_BRIDGE_H_
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "webkit/glue/media/media_resource_loader_bridge_factory.h"
+
+namespace webkit_glue {
+
+class MockMediaResourceLoaderBridgeFactory
+ : public webkit_glue::MediaResourceLoaderBridgeFactory {
+ public:
+ MockMediaResourceLoaderBridgeFactory() {
+ }
+
+ virtual ~MockMediaResourceLoaderBridgeFactory() {
+ OnDestroy();
+ }
+
+ MOCK_METHOD4(CreateBridge,
+ webkit_glue::ResourceLoaderBridge*(const GURL& url,
+ int load_flags,
+ int64 first_byte_position,
+ int64 last_byte_position));
+ MOCK_METHOD0(OnDestroy, void());
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockMediaResourceLoaderBridgeFactory);
+};
+
+} // namespace webkit_glue
+
+#endif // WEBKIT_GLUE_MEDIA_MOCK_RESOURCE_LOADER_BRIDGE_H_
diff --git a/webkit/glue/media/simple_data_source.cc b/webkit/glue/media/simple_data_source.cc
new file mode 100644
index 0000000..20bf0af
--- /dev/null
+++ b/webkit/glue/media/simple_data_source.cc
@@ -0,0 +1,236 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/message_loop.h"
+#include "base/process_util.h"
+#include "media/base/filter_host.h"
+#include "net/base/load_flags.h"
+#include "net/base/data_url.h"
+#include "net/http/http_response_headers.h"
+#include "net/url_request/url_request_status.h"
+#include "webkit/glue/media/simple_data_source.h"
+#include "webkit/glue/resource_loader_bridge.h"
+#include "webkit/glue/webkit_glue.h"
+
+namespace {
+
+const char kHttpScheme[] = "http";
+const char kHttpsScheme[] = "https";
+const char kDataScheme[] = "data";
+
+// A helper method that accepts only HTTP, HTTPS and FILE protocol.
+bool IsDataProtocol(const GURL& url) {
+ return url.SchemeIs(kDataScheme);
+}
+
+} // namespace
+
+namespace webkit_glue {
+
+bool SimpleDataSource::IsMediaFormatSupported(
+ const media::MediaFormat& media_format) {
+ std::string mime_type;
+ std::string url;
+ if (media_format.GetAsString(media::MediaFormat::kMimeType, &mime_type) &&
+ media_format.GetAsString(media::MediaFormat::kURL, &url)) {
+ GURL gurl(url);
+ if (IsProtocolSupportedForMedia(gurl))
+ return true;
+ }
+ return false;
+}
+
+SimpleDataSource::SimpleDataSource(
+ MessageLoop* render_loop,
+ webkit_glue::MediaResourceLoaderBridgeFactory* bridge_factory)
+ : render_loop_(render_loop),
+ bridge_factory_(bridge_factory),
+ size_(-1),
+ state_(UNINITIALIZED) {
+ DCHECK(render_loop);
+}
+
+SimpleDataSource::~SimpleDataSource() {
+ AutoLock auto_lock(lock_);
+ DCHECK(state_ == UNINITIALIZED || state_ == STOPPED);
+}
+
+void SimpleDataSource::Stop(media::FilterCallback* callback) {
+ AutoLock auto_lock(lock_);
+ state_ = STOPPED;
+ if (callback) {
+ callback->Run();
+ delete callback;
+ }
+
+ // Post a task to the render thread to cancel loading the resource.
+ render_loop_->PostTask(FROM_HERE,
+ NewRunnableMethod(this, &SimpleDataSource::CancelTask));
+}
+
+void SimpleDataSource::Initialize(const std::string& url,
+ media::FilterCallback* callback) {
+ AutoLock auto_lock(lock_);
+ DCHECK_EQ(state_, UNINITIALIZED);
+ DCHECK(callback);
+ state_ = INITIALIZING;
+ initialize_callback_.reset(callback);
+
+ // Validate the URL.
+ SetURL(GURL(url));
+ if (!url_.is_valid() || !IsProtocolSupportedForMedia(url_)) {
+ host()->SetError(media::PIPELINE_ERROR_NETWORK);
+ initialize_callback_->Run();
+ initialize_callback_.reset();
+ return;
+ }
+
+ // Post a task to the render thread to start loading the resource.
+ render_loop_->PostTask(FROM_HERE,
+ NewRunnableMethod(this, &SimpleDataSource::StartTask));
+}
+
+const media::MediaFormat& SimpleDataSource::media_format() {
+ return media_format_;
+}
+
+void SimpleDataSource::Read(int64 position,
+ size_t size,
+ uint8* data,
+ ReadCallback* read_callback) {
+ DCHECK_GE(size_, 0);
+ if (position >= size_) {
+ read_callback->RunWithParams(Tuple1<size_t>(0));
+ delete read_callback;
+ } else {
+ size_t copied = std::min(size, static_cast<size_t>(size_ - position));
+ memcpy(data, data_.c_str() + position, copied);
+ read_callback->RunWithParams(Tuple1<size_t>(copied));
+ delete read_callback;
+ }
+}
+
+bool SimpleDataSource::GetSize(int64* size_out) {
+ *size_out = size_;
+ return true;
+}
+
+bool SimpleDataSource::IsStreaming() {
+ return false;
+}
+
+void SimpleDataSource::OnDownloadProgress(uint64 position, uint64 size) {}
+
+void SimpleDataSource::OnUploadProgress(uint64 position, uint64 size) {}
+
+bool SimpleDataSource::OnReceivedRedirect(
+ const GURL& new_url,
+ const webkit_glue::ResourceLoaderBridge::ResponseInfo& info,
+ bool* has_new_first_party_for_cookies,
+ GURL* new_first_party_for_cookies) {
+ SetURL(new_url);
+ // TODO(wtc): should we return a new first party for cookies URL?
+ *has_new_first_party_for_cookies = false;
+ return true;
+}
+
+void SimpleDataSource::OnReceivedResponse(
+ const webkit_glue::ResourceLoaderBridge::ResponseInfo& info,
+ bool content_filtered) {
+ size_ = info.content_length;
+}
+
+void SimpleDataSource::OnReceivedData(const char* data, int len) {
+ data_.append(data, len);
+}
+
+void SimpleDataSource::OnCompletedRequest(const URLRequestStatus& status,
+ const std::string& security_info) {
+ AutoLock auto_lock(lock_);
+ // It's possible this gets called after Stop(), in which case |host_| is no
+ // longer valid.
+ if (state_ == STOPPED) {
+ return;
+ }
+
+ // Otherwise we should be initializing and have created a bridge.
+ DCHECK_EQ(state_, INITIALIZING);
+ DCHECK(bridge_.get());
+ bridge_.reset();
+
+ // If we don't get a content length or the request has failed, report it
+ // as a network error.
+ DCHECK(size_ == -1 || static_cast<size_t>(size_) == data_.length());
+ if (size_ == -1) {
+ size_ = data_.length();
+ }
+
+ DoneInitialization_Locked(status.is_success());
+}
+
+GURL SimpleDataSource::GetURLForDebugging() const {
+ return url_;
+}
+
+void SimpleDataSource::SetURL(const GURL& url) {
+ url_ = url;
+ media_format_.Clear();
+ media_format_.SetAsString(media::MediaFormat::kMimeType,
+ media::mime_type::kApplicationOctetStream);
+ media_format_.SetAsString(media::MediaFormat::kURL, url.spec());
+}
+
+void SimpleDataSource::StartTask() {
+ AutoLock auto_lock(lock_);
+ DCHECK(MessageLoop::current() == render_loop_);
+
+ // We may have stopped.
+ if (state_ == STOPPED)
+ return;
+
+ DCHECK_EQ(state_, INITIALIZING);
+
+ if (IsDataProtocol(url_)) {
+ // If this using data protocol, we just need to decode it.
+ std::string mime_type, charset;
+ bool success = net::DataURL::Parse(url_, &mime_type, &charset, &data_);
+
+ // Don't care about the mime-type just proceed if decoding was successful.
+ size_ = data_.length();
+ DoneInitialization_Locked(success);
+ } else {
+ // Create our bridge and start loading the resource.
+ bridge_.reset(bridge_factory_->CreateBridge(
+ url_, net::LOAD_BYPASS_CACHE, -1, -1));
+ bridge_->Start(this);
+ }
+}
+
+void SimpleDataSource::CancelTask() {
+ AutoLock auto_lock(lock_);
+ DCHECK_EQ(state_, STOPPED);
+
+ // Cancel any pending requests.
+ if (bridge_.get()) {
+ bridge_->Cancel();
+ bridge_.reset();
+ }
+}
+
+void SimpleDataSource::DoneInitialization_Locked(bool success) {
+ lock_.AssertAcquired();
+ if (success) {
+ state_ = INITIALIZED;
+ host()->SetTotalBytes(size_);
+ host()->SetBufferedBytes(size_);
+ // If scheme is file or data, say we are loaded.
+ host()->SetLoaded(url_.SchemeIsFile() || IsDataProtocol(url_));
+ } else {
+ host()->SetError(media::PIPELINE_ERROR_NETWORK);
+ }
+ initialize_callback_->Run();
+ initialize_callback_.reset();
+}
+
+} // namespace webkit_glue
diff --git a/webkit/glue/media/simple_data_source.h b/webkit/glue/media/simple_data_source.h
new file mode 100644
index 0000000..577d973
--- /dev/null
+++ b/webkit/glue/media/simple_data_source.h
@@ -0,0 +1,125 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// An extremely simple implementation of DataSource that downloads the entire
+// media resource into memory before signaling that initialization has finished.
+// Primarily used to test <audio> and <video> with buffering/caching removed
+// from the equation.
+
+#ifndef WEBKIT_GLUE_MEDIA_SIMPLE_DATA_SOURCE_H_
+#define WEBKIT_GLUE_MEDIA_SIMPLE_DATA_SOURCE_H_
+
+#include "base/message_loop.h"
+#include "base/scoped_ptr.h"
+#include "media/base/factory.h"
+#include "media/base/filters.h"
+#include "webkit/glue/media/media_resource_loader_bridge_factory.h"
+
+class MessageLoop;
+class WebMediaPlayerDelegateImpl;
+
+namespace webkit_glue {
+
+class SimpleDataSource : public media::DataSource,
+ public webkit_glue::ResourceLoaderBridge::Peer {
+ public:
+ static media::FilterFactory* CreateFactory(
+ MessageLoop* message_loop,
+ webkit_glue::MediaResourceLoaderBridgeFactory* bridge_factory) {
+ return new media::FilterFactoryImpl2<
+ SimpleDataSource,
+ MessageLoop*,
+ webkit_glue::MediaResourceLoaderBridgeFactory*>(message_loop,
+ bridge_factory);
+ }
+
+ // media::FilterFactoryImpl2 implementation.
+ static bool IsMediaFormatSupported(
+ const media::MediaFormat& media_format);
+
+ // MediaFilter implementation.
+ virtual void Stop(media::FilterCallback* callback);
+
+ // DataSource implementation.
+ virtual void Initialize(const std::string& url,
+ media::FilterCallback* callback);
+ virtual const media::MediaFormat& media_format();
+ virtual void Read(int64 position, size_t size,
+ uint8* data, ReadCallback* read_callback);
+ virtual bool GetSize(int64* size_out);
+ virtual bool IsStreaming();
+
+ // webkit_glue::ResourceLoaderBridge::Peer implementation.
+ virtual void OnDownloadProgress(uint64 position, uint64 size);
+ virtual void OnUploadProgress(uint64 position, uint64 size);
+ virtual bool OnReceivedRedirect(
+ const GURL& new_url,
+ const webkit_glue::ResourceLoaderBridge::ResponseInfo& info,
+ bool* has_new_first_party_for_cookies,
+ GURL* new_first_party_for_cookies);
+ virtual void OnReceivedResponse(
+ const webkit_glue::ResourceLoaderBridge::ResponseInfo& info,
+ bool content_filtered);
+ virtual void OnReceivedData(const char* data, int len);
+ virtual void OnCompletedRequest(const URLRequestStatus& status,
+ const std::string& security_info);
+ virtual GURL GetURLForDebugging() const;
+
+ private:
+ friend class media::FilterFactoryImpl2<
+ SimpleDataSource,
+ MessageLoop*,
+ webkit_glue::MediaResourceLoaderBridgeFactory*>;
+ SimpleDataSource(
+ MessageLoop* render_loop,
+ webkit_glue::MediaResourceLoaderBridgeFactory* bridge_factory);
+ virtual ~SimpleDataSource();
+
+ // Updates |url_| and |media_format_| with the given URL.
+ void SetURL(const GURL& url);
+
+ // Creates and starts the resource loading on the render thread.
+ void StartTask();
+
+ // Cancels and deletes the resource loading on the render thread.
+ void CancelTask();
+
+ // Perform initialization completion tasks under a lock.
+ void DoneInitialization_Locked(bool success);
+
+ // Primarily used for asserting the bridge is loading on the render thread.
+ MessageLoop* render_loop_;
+
+ // Factory to create a bridge.
+ scoped_ptr<webkit_glue::MediaResourceLoaderBridgeFactory> bridge_factory_;
+
+ // Bridge used to load the media resource.
+ scoped_ptr<webkit_glue::ResourceLoaderBridge> bridge_;
+
+ media::MediaFormat media_format_;
+ GURL url_;
+ std::string data_;
+ int64 size_;
+
+ // Simple state tracking variable.
+ enum State {
+ UNINITIALIZED,
+ INITIALIZING,
+ INITIALIZED,
+ STOPPED,
+ };
+ State state_;
+
+ // Used for accessing |state_|.
+ Lock lock_;
+
+ // Filter callbacks.
+ scoped_ptr<media::FilterCallback> initialize_callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(SimpleDataSource);
+};
+
+} // namespace webkit_glue
+
+#endif // WEBKIT_GLUE_MEDIA_SIMPLE_DATA_SOURCE_H_
diff --git a/webkit/glue/media/simple_data_source_unittest.cc b/webkit/glue/media/simple_data_source_unittest.cc
new file mode 100644
index 0000000..e6acba9
--- /dev/null
+++ b/webkit/glue/media/simple_data_source_unittest.cc
@@ -0,0 +1,252 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/callback.h"
+#include "media/base/filters.h"
+#include "media/base/mock_filter_host.h"
+#include "media/base/mock_filters.h"
+#include "webkit/glue/media/mock_media_resource_loader_bridge_factory.h"
+#include "webkit/glue/media/simple_data_source.h"
+#include "webkit/glue/mock_resource_loader_bridge.h"
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::NiceMock;
+using ::testing::NotNull;
+using ::testing::Return;
+using ::testing::SetArgumentPointee;
+using ::testing::StrictMock;
+using ::testing::WithArgs;
+
+namespace {
+
+const int kDataSize = 1024;
+const char kHttpUrl[] = "http://test";
+const char kHttpsUrl[] = "https://test";
+const char kFileUrl[] = "file://test";
+const char kDataUrl[] =
+ "data:text/plain;base64,YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoK";
+const char kDataUrlDecoded[] = "abcdefghijklmnopqrstuvwxyz";
+const char kInvalidUrl[] = "whatever://test";
+
+} // namespace
+
+namespace webkit_glue {
+
+class SimpleDataSourceTest : public testing::Test {
+ public:
+ SimpleDataSourceTest() {
+ bridge_factory_.reset(
+ new NiceMock<MockMediaResourceLoaderBridgeFactory>());
+ bridge_.reset(new NiceMock<MockResourceLoaderBridge>());
+ factory_ = SimpleDataSource::CreateFactory(MessageLoop::current(),
+ bridge_factory_.get());
+
+ for (int i = 0; i < kDataSize; ++i) {
+ data_[i] = i;
+ }
+ }
+
+ virtual ~SimpleDataSourceTest() {
+ if (bridge_.get())
+ EXPECT_CALL(*bridge_, OnDestroy());
+ if (bridge_factory_.get())
+ EXPECT_CALL(*bridge_factory_, OnDestroy());
+ }
+
+ void InitializeDataSource(const char* url) {
+ media::MediaFormat url_format;
+ url_format.SetAsString(media::MediaFormat::kMimeType,
+ media::mime_type::kURL);
+ url_format.SetAsString(media::MediaFormat::kURL, url);
+ data_source_ = factory_->Create<SimpleDataSource>(url_format);
+ CHECK(data_source_);
+
+ // There is no need to provide a message loop to data source.
+ data_source_->set_host(&host_);
+
+ // First a bridge is created.
+ InSequence s;
+ EXPECT_CALL(*bridge_factory_, CreateBridge(GURL(url), _, -1, -1))
+ .WillOnce(Return(bridge_.get()));
+ EXPECT_CALL(*bridge_, Start(data_source_.get()))
+ .WillOnce(Return(true));
+
+ data_source_->Initialize(url, callback_.NewCallback());
+
+ MessageLoop::current()->RunAllPending();
+ }
+
+ void RequestSucceeded(bool is_loaded) {
+ ResourceLoaderBridge::ResponseInfo info;
+ info.content_length = kDataSize;
+
+ data_source_->OnReceivedResponse(info, false);
+ int64 size;
+ EXPECT_TRUE(data_source_->GetSize(&size));
+ EXPECT_EQ(kDataSize, size);
+
+ for (int i = 0; i < kDataSize; ++i)
+ data_source_->OnReceivedData(data_ + i, 1);
+
+ EXPECT_CALL(host_, SetLoaded(is_loaded));
+
+ InSequence s;
+ EXPECT_CALL(*bridge_, OnDestroy())
+ .WillOnce(Invoke(this, &SimpleDataSourceTest::ReleaseBridge));
+ EXPECT_CALL(host_, SetTotalBytes(kDataSize));
+ EXPECT_CALL(host_, SetBufferedBytes(kDataSize));
+ EXPECT_CALL(callback_, OnFilterCallback());
+ EXPECT_CALL(callback_, OnCallbackDestroyed());
+
+ URLRequestStatus status;
+ status.set_status(URLRequestStatus::SUCCESS);
+ status.set_os_error(0);
+ data_source_->OnCompletedRequest(status, "");
+
+ // Let the tasks to be executed.
+ MessageLoop::current()->RunAllPending();
+ }
+
+ void RequestFailed() {
+ InSequence s;
+ EXPECT_CALL(*bridge_, OnDestroy())
+ .WillOnce(Invoke(this, &SimpleDataSourceTest::ReleaseBridge));
+ EXPECT_CALL(host_, SetError(media::PIPELINE_ERROR_NETWORK));
+ EXPECT_CALL(callback_, OnFilterCallback());
+ EXPECT_CALL(callback_, OnCallbackDestroyed());
+
+ URLRequestStatus status;
+ status.set_status(URLRequestStatus::FAILED);
+ status.set_os_error(100);
+ data_source_->OnCompletedRequest(status, "");
+
+ // Let the tasks to be executed.
+ MessageLoop::current()->RunAllPending();
+ }
+
+ void DestroyDataSource() {
+ EXPECT_CALL(*bridge_factory_, OnDestroy())
+ .WillOnce(Invoke(this, &SimpleDataSourceTest::ReleaseBridgeFactory));
+
+ StrictMock<media::MockFilterCallback> callback;
+ EXPECT_CALL(callback, OnFilterCallback());
+ EXPECT_CALL(callback, OnCallbackDestroyed());
+ data_source_->Stop(callback.NewCallback());
+ MessageLoop::current()->RunAllPending();
+
+ data_source_ = NULL;
+ }
+
+ void AsyncRead() {
+ for (int i = 0; i < kDataSize; ++i) {
+ uint8 buffer[1];
+
+ EXPECT_CALL(*this, ReadCallback(1));
+ data_source_->Read(
+ i, 1, buffer,
+ NewCallback(this, &SimpleDataSourceTest::ReadCallback));
+ EXPECT_EQ(static_cast<uint8>(data_[i]), buffer[0]);
+ }
+ }
+
+ void ReleaseBridge() {
+ ignore_result(bridge_.release());
+ }
+
+ void ReleaseBridgeFactory() {
+ ignore_result(bridge_factory_.release());
+ }
+
+ MOCK_METHOD1(ReadCallback, void(size_t size));
+
+ protected:
+ scoped_ptr<MessageLoop> message_loop_;
+ scoped_ptr<NiceMock<MockMediaResourceLoaderBridgeFactory> > bridge_factory_;
+ scoped_ptr<NiceMock<MockResourceLoaderBridge> > bridge_;
+ scoped_refptr<media::FilterFactory> factory_;
+ scoped_refptr<SimpleDataSource> data_source_;
+ StrictMock<media::MockFilterHost> host_;
+ StrictMock<media::MockFilterCallback> callback_;
+ char data_[kDataSize];
+
+ DISALLOW_COPY_AND_ASSIGN(SimpleDataSourceTest);
+};
+
+TEST_F(SimpleDataSourceTest, InitializeHTTP) {
+ InitializeDataSource(kHttpUrl);
+ RequestSucceeded(false);
+ DestroyDataSource();
+}
+
+TEST_F(SimpleDataSourceTest, InitializeHTTPS) {
+ InitializeDataSource(kHttpsUrl);
+ RequestSucceeded(false);
+ DestroyDataSource();
+}
+
+TEST_F(SimpleDataSourceTest, InitializeFile) {
+ InitializeDataSource(kFileUrl);
+ RequestSucceeded(true);
+ DestroyDataSource();
+}
+
+TEST_F(SimpleDataSourceTest, InitializeData) {
+ media::MediaFormat url_format;
+ url_format.SetAsString(media::MediaFormat::kMimeType,
+ media::mime_type::kURL);
+ url_format.SetAsString(media::MediaFormat::kURL, kDataUrl);
+ data_source_ = factory_->Create<SimpleDataSource>(url_format);
+ CHECK(data_source_);
+
+ // There is no need to provide a message loop to data source.
+ data_source_->set_host(&host_);
+
+ EXPECT_CALL(host_, SetLoaded(true));
+ EXPECT_CALL(host_, SetTotalBytes(sizeof(kDataUrlDecoded)));
+ EXPECT_CALL(host_, SetBufferedBytes(sizeof(kDataUrlDecoded)));
+ EXPECT_CALL(callback_, OnFilterCallback());
+ EXPECT_CALL(callback_, OnCallbackDestroyed());
+
+ data_source_->Initialize(kDataUrl, callback_.NewCallback());
+ MessageLoop::current()->RunAllPending();
+
+ DestroyDataSource();
+}
+
+TEST_F(SimpleDataSourceTest, InitializeInvalid) {
+ media::MediaFormat url_format;
+ url_format.SetAsString(media::MediaFormat::kMimeType,
+ media::mime_type::kURL);
+ url_format.SetAsString(media::MediaFormat::kURL, kInvalidUrl);
+ data_source_ = factory_->Create<SimpleDataSource>(url_format);
+ EXPECT_FALSE(data_source_);
+}
+
+TEST_F(SimpleDataSourceTest, RequestFailed) {
+ InitializeDataSource(kHttpUrl);
+ RequestFailed();
+ DestroyDataSource();
+}
+
+TEST_F(SimpleDataSourceTest, StopWhenDownloading) {
+ InitializeDataSource(kHttpUrl);
+
+ EXPECT_CALL(*bridge_, Cancel());
+ EXPECT_CALL(*bridge_, OnDestroy())
+ .WillOnce(Invoke(this, &SimpleDataSourceTest::ReleaseBridge));
+ EXPECT_CALL(callback_, OnCallbackDestroyed());
+ DestroyDataSource();
+}
+
+TEST_F(SimpleDataSourceTest, AsyncRead) {
+ InitializeDataSource(kFileUrl);
+ RequestSucceeded(true);
+ AsyncRead();
+ DestroyDataSource();
+}
+
+} // namespace webkit_glue
diff --git a/webkit/glue/media/video_renderer_impl.cc b/webkit/glue/media/video_renderer_impl.cc
new file mode 100644
index 0000000..796d07f
--- /dev/null
+++ b/webkit/glue/media/video_renderer_impl.cc
@@ -0,0 +1,321 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/media/video_renderer_impl.h"
+
+#include "media/base/video_frame.h"
+#include "media/base/yuv_convert.h"
+#include "webkit/glue/webmediaplayer_impl.h"
+
+namespace webkit_glue {
+
+VideoRendererImpl::VideoRendererImpl(WebMediaPlayerImpl::Proxy* proxy,
+ bool pts_logging)
+ : proxy_(proxy),
+ last_converted_frame_(NULL),
+ pts_logging_(pts_logging) {
+ // TODO(hclam): decide whether to do the following line in this thread or
+ // in the render thread.
+ proxy_->SetVideoRenderer(this);
+}
+
+// static
+media::FilterFactory* VideoRendererImpl::CreateFactory(
+ WebMediaPlayerImpl::Proxy* proxy,
+ bool pts_logging) {
+ return new media::FilterFactoryImpl2<VideoRendererImpl,
+ WebMediaPlayerImpl::Proxy*,
+ bool>(proxy, pts_logging);
+}
+
+// static
+bool VideoRendererImpl::IsMediaFormatSupported(
+ const media::MediaFormat& media_format) {
+ return ParseMediaFormat(media_format, NULL, NULL, NULL, NULL);
+}
+
+bool VideoRendererImpl::OnInitialize(media::VideoDecoder* decoder) {
+ video_size_.SetSize(width(), height());
+ bitmap_.setConfig(SkBitmap::kARGB_8888_Config, width(), height());
+ if (bitmap_.allocPixels(NULL, NULL)) {
+ bitmap_.eraseRGB(0x00, 0x00, 0x00);
+ return true;
+ }
+
+ NOTREACHED();
+ return false;
+}
+
+void VideoRendererImpl::OnStop(media::FilterCallback* callback) {
+ if (callback) {
+ callback->Run();
+ delete callback;
+ }
+}
+
+void VideoRendererImpl::OnFrameAvailable() {
+ proxy_->Repaint();
+}
+
+void VideoRendererImpl::SetRect(const gfx::Rect& rect) {
+}
+
+// This method is always called on the renderer's thread.
+void VideoRendererImpl::Paint(skia::PlatformCanvas* canvas,
+ const gfx::Rect& dest_rect) {
+ scoped_refptr<media::VideoFrame> video_frame;
+ GetCurrentFrame(&video_frame);
+ if (!video_frame) {
+ SkPaint paint;
+ paint.setColor(SK_ColorBLACK);
+ canvas->drawRectCoords(
+ static_cast<float>(dest_rect.x()),
+ static_cast<float>(dest_rect.y()),
+ static_cast<float>(dest_rect.right()),
+ static_cast<float>(dest_rect.bottom()),
+ paint);
+ } else {
+ if (CanFastPaint(canvas, dest_rect)) {
+ FastPaint(video_frame, canvas, dest_rect);
+ } else {
+ SlowPaint(video_frame, canvas, dest_rect);
+ }
+
+ // Presentation timestamp logging is primarily used to measure performance
+ // on low-end devices. When profiled on an Intel Atom N280 @ 1.66GHz this
+ // code had a ~63 microsecond perf hit when logging to a file (not stdout),
+ // which is neglible enough for measuring playback performance.
+ if (pts_logging_) {
+ LOG(INFO) << "pts="
+ << video_frame->GetTimestamp().InMicroseconds();
+ }
+ }
+
+ PutCurrentFrame(video_frame);
+}
+
+// CanFastPaint is a helper method to determine the conditions for fast
+// painting. The conditions are:
+// 1. No skew in canvas matrix.
+// 2. No flipping nor mirroring.
+// 3. Canvas has pixel format ARGB8888.
+// 4. Canvas is opaque.
+// TODO(hclam): The fast paint method should support flipping and mirroring.
+// Disable the flipping and mirroring checks once we have it.
+bool VideoRendererImpl::CanFastPaint(skia::PlatformCanvas* canvas,
+ const gfx::Rect& dest_rect) {
+ // Fast paint does not handle opacity value other than 1.0. Hence use slow
+ // paint if opacity is not 1.0. Since alpha = opacity * 0xFF, we check that
+ // alpha != 0xFF.
+ //
+ // Additonal notes: If opacity = 0.0, the chrome display engine does not try
+ // to render the video. So, this method is never called. However, if the
+ // opacity = 0.0001, alpha is again 0, but the display engine tries to render
+ // the video. If we use Fast paint, the video shows up with opacity = 1.0.
+ // Hence we use slow paint also in the case where alpha = 0. It would be ideal
+ // if rendering was never called even for cases where alpha is 0. Created
+ // bug 48090 for this.
+ SkCanvas::LayerIter layer_iter(canvas, false);
+ SkColor sk_color = layer_iter.paint().getColor();
+ SkAlpha sk_alpha = SkColorGetA(sk_color);
+ if (sk_alpha != 0xFF) {
+ return false;
+ }
+
+ const SkMatrix& total_matrix = canvas->getTotalMatrix();
+ // Perform the following checks here:
+ // 1. Check for skewing factors of the transformation matrix. They should be
+ // zero.
+ // 2. Check for mirroring and flipping. Make sure they are greater than zero.
+ if (SkScalarNearlyZero(total_matrix.getSkewX()) &&
+ SkScalarNearlyZero(total_matrix.getSkewY()) &&
+ total_matrix.getScaleX() > 0 &&
+ total_matrix.getScaleY() > 0) {
+ // Get the properties of the SkDevice and the clip rect.
+ SkDevice* device = canvas->getDevice();
+
+ // Get the boundary of the device.
+ SkIRect device_rect;
+ device->getBounds(&device_rect);
+
+ // Get the pixel config of the device.
+ const SkBitmap::Config config = device->config();
+ // Get the total clip rect associated with the canvas.
+ const SkRegion& total_clip = canvas->getTotalClip();
+
+ SkIRect dest_irect;
+ TransformToSkIRect(canvas->getTotalMatrix(), dest_rect, &dest_irect);
+
+ if (config == SkBitmap::kARGB_8888_Config && device->isOpaque() &&
+ device_rect.contains(total_clip.getBounds())) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void VideoRendererImpl::SlowPaint(media::VideoFrame* video_frame,
+ skia::PlatformCanvas* canvas,
+ const gfx::Rect& dest_rect) {
+ // 1. Convert YUV frame to RGB.
+ base::TimeDelta timestamp = video_frame->GetTimestamp();
+ if (video_frame != last_converted_frame_ ||
+ timestamp != last_converted_timestamp_) {
+ last_converted_frame_ = video_frame;
+ last_converted_timestamp_ = timestamp;
+ DCHECK(video_frame->format() == media::VideoFrame::YV12 ||
+ video_frame->format() == media::VideoFrame::YV16);
+ DCHECK(video_frame->stride(media::VideoFrame::kUPlane) ==
+ video_frame->stride(media::VideoFrame::kVPlane));
+ DCHECK(video_frame->planes() == media::VideoFrame::kNumYUVPlanes);
+ bitmap_.lockPixels();
+ media::YUVType yuv_type =
+ (video_frame->format() == media::VideoFrame::YV12) ?
+ media::YV12 : media::YV16;
+ media::ConvertYUVToRGB32(video_frame->data(media::VideoFrame::kYPlane),
+ video_frame->data(media::VideoFrame::kUPlane),
+ video_frame->data(media::VideoFrame::kVPlane),
+ static_cast<uint8*>(bitmap_.getPixels()),
+ video_frame->width(),
+ video_frame->height(),
+ video_frame->stride(media::VideoFrame::kYPlane),
+ video_frame->stride(media::VideoFrame::kUPlane),
+ bitmap_.rowBytes(),
+ yuv_type);
+ bitmap_.unlockPixels();
+ }
+
+ // 2. Paint the bitmap to canvas.
+ SkMatrix matrix;
+ matrix.setTranslate(static_cast<SkScalar>(dest_rect.x()),
+ static_cast<SkScalar>(dest_rect.y()));
+ if (dest_rect.width() != video_size_.width() ||
+ dest_rect.height() != video_size_.height()) {
+ matrix.preScale(SkIntToScalar(dest_rect.width()) /
+ SkIntToScalar(video_size_.width()),
+ SkIntToScalar(dest_rect.height()) /
+ SkIntToScalar(video_size_.height()));
+ }
+ SkPaint paint;
+ paint.setFlags(SkPaint::kFilterBitmap_Flag);
+ canvas->drawBitmapMatrix(bitmap_, matrix, &paint);
+}
+
+void VideoRendererImpl::FastPaint(media::VideoFrame* video_frame,
+ skia::PlatformCanvas* canvas,
+ const gfx::Rect& dest_rect) {
+ DCHECK(video_frame->format() == media::VideoFrame::YV12 ||
+ video_frame->format() == media::VideoFrame::YV16);
+ DCHECK(video_frame->stride(media::VideoFrame::kUPlane) ==
+ video_frame->stride(media::VideoFrame::kVPlane));
+ DCHECK(video_frame->planes() == media::VideoFrame::kNumYUVPlanes);
+ const SkBitmap& bitmap = canvas->getDevice()->accessBitmap(true);
+ media::YUVType yuv_type = (video_frame->format() == media::VideoFrame::YV12) ?
+ media::YV12 : media::YV16;
+ int y_shift = yuv_type; // 1 for YV12, 0 for YV16.
+
+ // Create a rectangle backed by SkScalar.
+ SkRect scalar_dest_rect;
+ scalar_dest_rect.iset(dest_rect.x(), dest_rect.y(),
+ dest_rect.right(), dest_rect.bottom());
+
+ // Transform the destination rectangle to local coordinates.
+ const SkMatrix& local_matrix = canvas->getTotalMatrix();
+ SkRect local_dest_rect;
+ local_matrix.mapRect(&local_dest_rect, scalar_dest_rect);
+
+ // After projecting the destination rectangle to local coordinates, round
+ // the projected rectangle to integer values, this will give us pixel values
+ // of the rectangle.
+ SkIRect local_dest_irect, local_dest_irect_saved;
+ local_dest_rect.round(&local_dest_irect);
+ local_dest_rect.round(&local_dest_irect_saved);
+
+ // Only does the paint if the destination rect intersects with the clip
+ // rect.
+ if (local_dest_irect.intersect(canvas->getTotalClip().getBounds())) {
+ // At this point |local_dest_irect| contains the rect that we should draw
+ // to within the clipping rect.
+
+ // Calculate the address for the top left corner of destination rect in
+ // the canvas that we will draw to. The address is obtained by the base
+ // address of the canvas shifted by "left" and "top" of the rect.
+ uint8* dest_rect_pointer = static_cast<uint8*>(bitmap.getPixels()) +
+ local_dest_irect.fTop * bitmap.rowBytes() +
+ local_dest_irect.fLeft * 4;
+
+ // Project the clip rect to the original video frame, obtains the
+ // dimensions of the projected clip rect, "left" and "top" of the rect.
+ // The math here are all integer math so we won't have rounding error and
+ // write outside of the canvas.
+ // We have the assumptions of dest_rect.width() and dest_rect.height()
+ // being non-zero, these are valid assumptions since finding intersection
+ // above rejects empty rectangle so we just do a DCHECK here.
+ DCHECK_NE(0, dest_rect.width());
+ DCHECK_NE(0, dest_rect.height());
+ size_t frame_clip_width = local_dest_irect.width() *
+ video_frame->width() / local_dest_irect_saved.width();
+ size_t frame_clip_height = local_dest_irect.height() *
+ video_frame->height() / local_dest_irect_saved.height();
+
+ // Project the "left" and "top" of the final destination rect to local
+ // coordinates of the video frame, use these values to find the offsets
+ // in the video frame to start reading.
+ size_t frame_clip_left =
+ (local_dest_irect.fLeft - local_dest_irect_saved.fLeft) *
+ video_frame->width() / local_dest_irect_saved.width();
+ size_t frame_clip_top =
+ (local_dest_irect.fTop - local_dest_irect_saved.fTop) *
+ video_frame->height() / local_dest_irect_saved.height();
+
+ // Use the "left" and "top" of the destination rect to locate the offset
+ // in Y, U and V planes.
+ size_t y_offset = video_frame->stride(media::VideoFrame::kYPlane) *
+ frame_clip_top + frame_clip_left;
+ // For format YV12, there is one U, V value per 2x2 block.
+ // For format YV16, there is one u, V value per 2x1 block.
+ size_t uv_offset = (video_frame->stride(media::VideoFrame::kUPlane) *
+ (frame_clip_top >> y_shift)) + (frame_clip_left >> 1);
+ uint8* frame_clip_y =
+ video_frame->data(media::VideoFrame::kYPlane) + y_offset;
+ uint8* frame_clip_u =
+ video_frame->data(media::VideoFrame::kUPlane) + uv_offset;
+ uint8* frame_clip_v =
+ video_frame->data(media::VideoFrame::kVPlane) + uv_offset;
+ bitmap.lockPixels();
+
+ // TODO(hclam): do rotation and mirroring here.
+ // TODO(fbarchard): switch filtering based on performance.
+ media::ScaleYUVToRGB32(frame_clip_y,
+ frame_clip_u,
+ frame_clip_v,
+ dest_rect_pointer,
+ frame_clip_width,
+ frame_clip_height,
+ local_dest_irect.width(),
+ local_dest_irect.height(),
+ video_frame->stride(media::VideoFrame::kYPlane),
+ video_frame->stride(media::VideoFrame::kUPlane),
+ bitmap.rowBytes(),
+ yuv_type,
+ media::ROTATE_0,
+ media::FILTER_BILINEAR);
+ bitmap.unlockPixels();
+ }
+}
+
+void VideoRendererImpl::TransformToSkIRect(const SkMatrix& matrix,
+ const gfx::Rect& src_rect,
+ SkIRect* dest_rect) {
+ // Transform destination rect to local coordinates.
+ SkRect transformed_rect;
+ SkRect skia_dest_rect;
+ skia_dest_rect.iset(src_rect.x(), src_rect.y(),
+ src_rect.right(), src_rect.bottom());
+ matrix.mapRect(&transformed_rect, skia_dest_rect);
+ transformed_rect.round(dest_rect);
+}
+
+} // namespace webkit_glue
diff --git a/webkit/glue/media/video_renderer_impl.h b/webkit/glue/media/video_renderer_impl.h
new file mode 100644
index 0000000..30f2e38
--- /dev/null
+++ b/webkit/glue/media/video_renderer_impl.h
@@ -0,0 +1,122 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// The video renderer implementation to be use by the media pipeline. It lives
+// inside video renderer thread and also WebKit's main thread. We need to be
+// extra careful about members shared by two different threads, especially
+// video frame buffers.
+
+#ifndef WEBKIT_GLUE_MEDIA_VIDEO_RENDERER_IMPL_H_
+#define WEBKIT_GLUE_MEDIA_VIDEO_RENDERER_IMPL_H_
+
+#include "gfx/rect.h"
+#include "gfx/size.h"
+#include "media/base/buffers.h"
+#include "media/base/filters.h"
+#include "media/filters/video_renderer_base.h"
+#include "skia/ext/platform_canvas.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebMediaPlayer.h"
+#include "webkit/glue/media/web_video_renderer.h"
+#include "webkit/glue/webmediaplayer_impl.h"
+
+namespace webkit_glue {
+
+class VideoRendererImpl : public WebVideoRenderer {
+ public:
+ // WebVideoRenderer implementation.
+ virtual void SetRect(const gfx::Rect& rect);
+ virtual void Paint(skia::PlatformCanvas* canvas, const gfx::Rect& dest_rect);
+
+ // Static method for creating factory for this object.
+ static media::FilterFactory* CreateFactory(WebMediaPlayerImpl::Proxy* proxy,
+ bool pts_logging);
+
+ // FilterFactoryImpl2 implementation.
+ static bool IsMediaFormatSupported(const media::MediaFormat& media_format);
+
+ // TODO(scherkus): remove this mega-hack, see http://crbug.com/28207
+ class FactoryFactory : public webkit_glue::WebVideoRendererFactoryFactory {
+ public:
+ FactoryFactory(bool pts_logging)
+ : webkit_glue::WebVideoRendererFactoryFactory(),
+ pts_logging_(pts_logging) {
+ }
+
+ virtual media::FilterFactory* CreateFactory(
+ webkit_glue::WebMediaPlayerImpl::Proxy* proxy) {
+ return VideoRendererImpl::CreateFactory(proxy, pts_logging_);
+ }
+
+ private:
+ // Whether we're logging video presentation timestamps (PTS).
+ bool pts_logging_;
+
+ DISALLOW_COPY_AND_ASSIGN(FactoryFactory);
+ };
+
+ protected:
+ // Method called by VideoRendererBase during initialization.
+ virtual bool OnInitialize(media::VideoDecoder* decoder);
+
+ // Method called by the VideoRendererBase when stopping.
+ virtual void OnStop(media::FilterCallback* callback);
+
+ // Method called by the VideoRendererBase when a frame is available.
+ virtual void OnFrameAvailable();
+
+ private:
+ // Only the filter factories can create instances.
+ friend class media::FilterFactoryImpl2<VideoRendererImpl,
+ WebMediaPlayerImpl::Proxy*,
+ bool>;
+ VideoRendererImpl(WebMediaPlayerImpl::Proxy* proxy, bool pts_logging);
+ virtual ~VideoRendererImpl() {}
+
+ // Determine the conditions to perform fast paint. Returns true if we can do
+ // fast paint otherwise false.
+ bool CanFastPaint(skia::PlatformCanvas* canvas, const gfx::Rect& dest_rect);
+
+ // Slow paint does a YUV => RGB, and scaled blit in two separate operations.
+ void SlowPaint(media::VideoFrame* video_frame,
+ skia::PlatformCanvas* canvas,
+ const gfx::Rect& dest_rect);
+
+ // Fast paint does YUV => RGB, scaling, blitting all in one step into the
+ // canvas. It's not always safe and appropriate to perform fast paint.
+ // CanFastPaint() is used to determine the conditions.
+ void FastPaint(media::VideoFrame* video_frame,
+ skia::PlatformCanvas* canvas,
+ const gfx::Rect& dest_rect);
+
+ void TransformToSkIRect(const SkMatrix& matrix, const gfx::Rect& src_rect,
+ SkIRect* dest_rect);
+
+ // Pointer to our parent object that is called to request repaints.
+ scoped_refptr<WebMediaPlayerImpl::Proxy> proxy_;
+
+ // An RGB bitmap used to convert the video frames.
+ SkBitmap bitmap_;
+
+ // These two members are used to determine if the |bitmap_| contains
+ // an already converted image of the current frame. IMPORTANT NOTE: The
+ // value of |last_converted_frame_| must only be used for comparison purposes,
+ // and it should be assumed that the value of the pointer is INVALID unless
+ // it matches the pointer returned from GetCurrentFrame(). Even then, just
+ // to make sure, we compare the timestamp to be sure the bits in the
+ // |current_frame_bitmap_| are valid.
+ media::VideoFrame* last_converted_frame_;
+ base::TimeDelta last_converted_timestamp_;
+
+ // The size of the video.
+ gfx::Size video_size_;
+
+ // Whether we're logging video presentation timestamps (PTS).
+ bool pts_logging_;
+
+ DISALLOW_COPY_AND_ASSIGN(VideoRendererImpl);
+};
+
+} // namespace webkit_glue
+
+#endif // WEBKIT_GLUE_MEDIA_VIDEO_RENDERER_IMPL_H_
diff --git a/webkit/glue/media/web_video_renderer.h b/webkit/glue/media/web_video_renderer.h
new file mode 100644
index 0000000..8bafb1a
--- /dev/null
+++ b/webkit/glue/media/web_video_renderer.h
@@ -0,0 +1,39 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved. Use of this
+// source code is governed by a BSD-style license that can be found in the
+// LICENSE file.
+
+#ifndef WEBKIT_GLUE_MEDIA_WEB_VIDEO_RENDERER_H_
+#define WEBKIT_GLUE_MEDIA_WEB_VIDEO_RENDERER_H_
+
+#include "media/filters/video_renderer_base.h"
+
+namespace webkit_glue {
+
+// A specialized version of a VideoRenderer designed to be used inside WebKit.
+class WebVideoRenderer : public media::VideoRendererBase {
+ public:
+ WebVideoRenderer() : media::VideoRendererBase() {}
+ virtual ~WebVideoRenderer() {}
+
+ // This method is called with the same rect as the Paint() method and could
+ // be used by future implementations to implement an improved color space +
+ // scale code on a separate thread. Since we always do the stretch on the
+ // same thread as the Paint method, we just ignore the call for now.
+ //
+ // Method called on the render thread.
+ virtual void SetRect(const gfx::Rect& rect) = 0;
+
+ // Paint the current front frame on the |canvas| stretching it to fit the
+ // |dest_rect|.
+ //
+ // Method called on the render thread.
+ virtual void Paint(skia::PlatformCanvas* canvas,
+ const gfx::Rect& dest_rect) = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(WebVideoRenderer);
+};
+
+} // namespace webkit_glue
+
+#endif // WEBKIT_GLUE_MEDIA_WEB_VIDEO_RENDERER_H_
diff --git a/webkit/glue/mimetype_unittest.cc b/webkit/glue/mimetype_unittest.cc
new file mode 100644
index 0000000..152d20d
--- /dev/null
+++ b/webkit/glue/mimetype_unittest.cc
@@ -0,0 +1,91 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <string>
+
+#include "base/string_util.h"
+#include "net/url_request/url_request_unittest.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebFrame.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebView.h"
+#include "webkit/glue/unittest_test_server.h"
+#include "webkit/glue/webkit_glue.h"
+#include "webkit/tools/test_shell/test_shell_test.h"
+
+using WebKit::WebFrame;
+
+namespace {
+
+class MimeTypeTests : public TestShellTest {
+ public:
+ void LoadURL(const GURL& url) {
+ test_shell_->LoadURL(url);
+ test_shell_->WaitTestFinished();
+ }
+
+ void CheckMimeType(const char* mimetype, const std::wstring& expected) {
+ std::string path("contenttype?");
+ GURL url = server_->TestServerPage(path + mimetype);
+ LoadURL(url);
+ WebFrame* frame = test_shell_->webView()->mainFrame();
+ EXPECT_EQ(expected, webkit_glue::DumpDocumentText(frame));
+ }
+
+ scoped_refptr<UnittestTestServer> server_;
+};
+
+TEST_F(MimeTypeTests, MimeTypeTests) {
+ server_ = UnittestTestServer::CreateServer();
+ ASSERT_TRUE(NULL != server_.get());
+
+ std::wstring expected_src(L"<html>\n<body>\n"
+ L"<p>HTML text</p>\n</body>\n</html>\n");
+
+ // These files should all be displayed as plain text.
+ const char* plain_text[] = {
+ // It is unclear whether to display text/css or download it.
+ // Firefox 3: Display
+ // Internet Explorer 7: Download
+ // Safari 3.2: Download
+ // We choose to match Firefox due to the lot of complains
+ // from the users if css files are downloaded:
+ // http://code.google.com/p/chromium/issues/detail?id=7192
+ "text/css",
+ "text/javascript",
+ "text/plain",
+ "application/x-javascript",
+ };
+ for (size_t i = 0; i < arraysize(plain_text); ++i) {
+ CheckMimeType(plain_text[i], expected_src);
+ }
+
+ // These should all be displayed as html content.
+ const char* html_src[] = {
+ "text/html",
+ "text/xml",
+ "text/xsl",
+ "application/xhtml+xml",
+ };
+ for (size_t i = 0; i < arraysize(html_src); ++i) {
+ CheckMimeType(html_src[i], L"HTML text");
+ }
+
+ // These shouldn't be rendered as text or HTML, but shouldn't download
+ // either.
+ const char* not_text[] = {
+ "image/png",
+ "image/gif",
+ "image/jpeg",
+ "image/bmp",
+ };
+ for (size_t i = 0; i < arraysize(not_text); ++i) {
+ CheckMimeType(not_text[i], L"");
+ test_shell_->webView()->mainFrame()->stopLoading();
+ }
+
+ // TODO(tc): make sure other mime types properly go to download (e.g.,
+ // image/foo).
+
+}
+
+} // namespace
diff --git a/webkit/glue/mock_resource_loader_bridge.h b/webkit/glue/mock_resource_loader_bridge.h
new file mode 100644
index 0000000..7176e04
--- /dev/null
+++ b/webkit/glue/mock_resource_loader_bridge.h
@@ -0,0 +1,42 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_MEDIA_MOCK_MEDIA_RESOURCE_LOADER_BRIDGE_FACTORY_H_
+#define WEBKIT_GLUE_MEDIA_MOCK_MEDIA_RESOURCE_LOADER_BRIDGE_FACTORY_H_
+
+#include "base/file_path.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "webkit/glue/resource_loader_bridge.h"
+
+namespace webkit_glue {
+
+class MockResourceLoaderBridge : public webkit_glue::ResourceLoaderBridge {
+ public:
+ MockResourceLoaderBridge() {
+ }
+
+ virtual ~MockResourceLoaderBridge() {
+ OnDestroy();
+ }
+
+ MOCK_METHOD2(AppendDataToUpload, void(const char* data, int data_len));
+ MOCK_METHOD4(AppendFileRangeToUpload,
+ void(const FilePath& file_path,
+ uint64 offset,
+ uint64 length,
+ const base::Time& expected_modification_time));
+ MOCK_METHOD1(SetUploadIdentifier, void(int64 identifier));
+ MOCK_METHOD1(Start, bool(ResourceLoaderBridge::Peer* peer));
+ MOCK_METHOD0(Cancel, void());
+ MOCK_METHOD1(SetDefersLoading, void(bool value));
+ MOCK_METHOD1(SyncLoad, void(SyncLoadResponse* response));
+ MOCK_METHOD0(OnDestroy, void());
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockResourceLoaderBridge);
+};
+
+} // namespace webkit_glue
+
+#endif // WEBKIT_GLUE_MEDIA_MOCK_MEDIA_RESOURCE_LOADER_BRIDGE_FACTORY_H_
diff --git a/webkit/glue/multipart_response_delegate.cc b/webkit/glue/multipart_response_delegate.cc
new file mode 100644
index 0000000..0b37050
--- /dev/null
+++ b/webkit/glue/multipart_response_delegate.cc
@@ -0,0 +1,373 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/multipart_response_delegate.h"
+
+#include "base/logging.h"
+#include "base/string_util.h"
+#include "net/base/net_util.h"
+#include "net/http/http_util.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebHTTPHeaderVisitor.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebString.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURL.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURLLoaderClient.h"
+
+using WebKit::WebHTTPHeaderVisitor;
+using WebKit::WebString;
+using WebKit::WebURLLoader;
+using WebKit::WebURLLoaderClient;
+using WebKit::WebURLResponse;
+
+namespace webkit_glue {
+
+namespace {
+
+// The list of response headers that we do not copy from the original
+// response when generating a WebURLResponse for a MIME payload.
+const char* kReplaceHeaders[] = {
+ "content-type",
+ "content-length",
+ "content-disposition",
+ "content-range",
+ "range",
+ "set-cookie"
+};
+
+class HeaderCopier : public WebHTTPHeaderVisitor {
+ public:
+ HeaderCopier(WebURLResponse* response)
+ : response_(response) {
+ }
+ virtual void visitHeader(const WebString& name, const WebString& value) {
+ const std::string& name_utf8 = name.utf8();
+ for (size_t i = 0; i < arraysize(kReplaceHeaders); ++i) {
+ if (LowerCaseEqualsASCII(name_utf8, kReplaceHeaders[i]))
+ return;
+ }
+ response_->setHTTPHeaderField(name, value);
+ }
+ private:
+ WebURLResponse* response_;
+};
+
+} // namespace
+
+MultipartResponseDelegate::MultipartResponseDelegate(
+ WebURLLoaderClient* client,
+ WebURLLoader* loader,
+ const WebURLResponse& response,
+ const std::string& boundary)
+ : client_(client),
+ loader_(loader),
+ original_response_(response),
+ boundary_("--"),
+ first_received_data_(true),
+ processing_headers_(false),
+ stop_sending_(false),
+ has_sent_first_response_(false) {
+ // Some servers report a boundary prefixed with "--". See bug 5786.
+ if (StartsWithASCII(boundary, "--", true)) {
+ boundary_.assign(boundary);
+ } else {
+ boundary_.append(boundary);
+ }
+}
+
+void MultipartResponseDelegate::OnReceivedData(const char* data,
+ int data_len) {
+ // stop_sending_ means that we've already received the final boundary token.
+ // The server should stop sending us data at this point, but if it does, we
+ // just throw it away.
+ if (stop_sending_)
+ return;
+
+ data_.append(data, data_len);
+ if (first_received_data_) {
+ // Some servers don't send a boundary token before the first chunk of
+ // data. We handle this case anyway (Gecko does too).
+ first_received_data_ = false;
+
+ // Eat leading \r\n
+ int pos = PushOverLine(data_, 0);
+ if (pos)
+ data_ = data_.substr(pos);
+
+ if (data_.length() < boundary_.length() + 2) {
+ // We don't have enough data yet to make a boundary token. Just wait
+ // until the next chunk of data arrives.
+ first_received_data_ = true;
+ return;
+ }
+
+ if (0 != data_.compare(0, boundary_.length(), boundary_)) {
+ data_ = boundary_ + "\n" + data_;
+ }
+ }
+ DCHECK(!first_received_data_);
+
+ // Headers
+ if (processing_headers_) {
+ // Eat leading \r\n
+ int pos = PushOverLine(data_, 0);
+ if (pos)
+ data_ = data_.substr(pos);
+
+ if (ParseHeaders()) {
+ // Successfully parsed headers.
+ processing_headers_ = false;
+ } else {
+ // Get more data before trying again.
+ return;
+ }
+ }
+ DCHECK(!processing_headers_);
+
+ size_t boundary_pos;
+ while ((boundary_pos = FindBoundary()) != std::string::npos) {
+ if (boundary_pos > 0 && client_) {
+ // Strip out trailing \n\r characters in the buffer preceding the
+ // boundary on the same lines as Firefox.
+ size_t data_length = boundary_pos;
+ if (data_[boundary_pos - 1] == '\n') {
+ data_length--;
+ if (data_[boundary_pos - 2] == '\r') {
+ data_length--;
+ }
+ }
+ if (data_length > 0) {
+ // Send the last data chunk.
+ client_->didReceiveData(loader_,
+ data_.data(),
+ static_cast<int>(data_length));
+ }
+ }
+ size_t boundary_end_pos = boundary_pos + boundary_.length();
+ if (boundary_end_pos < data_.length() && '-' == data_[boundary_end_pos]) {
+ // This was the last boundary so we can stop processing.
+ stop_sending_ = true;
+ data_.clear();
+ return;
+ }
+
+ // We can now throw out data up through the boundary
+ int offset = PushOverLine(data_, boundary_end_pos);
+ data_ = data_.substr(boundary_end_pos + offset);
+
+ // Ok, back to parsing headers
+ if (!ParseHeaders()) {
+ processing_headers_ = true;
+ break;
+ }
+ }
+
+ // At this point, we should send over any data we have, but keep enough data
+ // buffered to handle a boundary that may have been truncated.
+ if (!processing_headers_ && data_.length() > boundary_.length()) {
+ // If the last character is a new line character, go ahead and just send
+ // everything we have buffered. This matches an optimization in Gecko.
+ int send_length = data_.length() - boundary_.length();
+ if (data_[data_.length() - 1] == '\n')
+ send_length = data_.length();
+ if (client_)
+ client_->didReceiveData(loader_, data_.data(), send_length);
+ data_ = data_.substr(send_length);
+ }
+}
+
+void MultipartResponseDelegate::OnCompletedRequest() {
+ // If we have any pending data and we're not in a header, go ahead and send
+ // it to WebCore.
+ if (!processing_headers_ && !data_.empty() && !stop_sending_ && client_) {
+ client_->didReceiveData(loader_,
+ data_.data(),
+ static_cast<int>(data_.length()));
+ }
+}
+
+int MultipartResponseDelegate::PushOverLine(const std::string& data,
+ size_t pos) {
+ int offset = 0;
+ if (pos < data.length() && (data[pos] == '\r' || data[pos] == '\n')) {
+ ++offset;
+ if (pos + 1 < data.length() && data[pos + 1] == '\n')
+ ++offset;
+ }
+ return offset;
+}
+
+bool MultipartResponseDelegate::ParseHeaders() {
+ int line_feed_increment = 1;
+
+ // Grab the headers being liberal about line endings.
+ size_t line_start_pos = 0;
+ size_t line_end_pos = data_.find('\n');
+ while (line_end_pos != std::string::npos) {
+ // Handle CRLF
+ if (line_end_pos > line_start_pos && data_[line_end_pos - 1] == '\r') {
+ line_feed_increment = 2;
+ --line_end_pos;
+ } else {
+ line_feed_increment = 1;
+ }
+ if (line_start_pos == line_end_pos) {
+ // A blank line, end of headers
+ line_end_pos += line_feed_increment;
+ break;
+ }
+ // Find the next header line.
+ line_start_pos = line_end_pos + line_feed_increment;
+ line_end_pos = data_.find('\n', line_start_pos);
+ }
+ // Truncated in the middle of a header, stop parsing.
+ if (line_end_pos == std::string::npos)
+ return false;
+
+ // Eat headers
+ std::string headers("\n");
+ headers.append(data_, 0, line_end_pos);
+ data_ = data_.substr(line_end_pos);
+
+ // Create a WebURLResponse based on the original set of headers + the
+ // replacement headers. We only replace the same few headers that gecko
+ // does. See netwerk/streamconv/converters/nsMultiMixedConv.cpp.
+ std::string content_type = net::GetSpecificHeader(headers, "content-type");
+ std::string mime_type;
+ std::string charset;
+ bool has_charset = false;
+ net::HttpUtil::ParseContentType(content_type, &mime_type, &charset,
+ &has_charset);
+ WebURLResponse response(original_response_.url());
+ response.setMIMEType(WebString::fromUTF8(mime_type));
+ response.setTextEncodingName(WebString::fromUTF8(charset));
+
+ HeaderCopier copier(&response);
+ original_response_.visitHTTPHeaderFields(&copier);
+
+ for (size_t i = 0; i < arraysize(kReplaceHeaders); ++i) {
+ std::string name(kReplaceHeaders[i]);
+ std::string value = net::GetSpecificHeader(headers, name);
+ if (!value.empty()) {
+ response.setHTTPHeaderField(WebString::fromUTF8(name),
+ WebString::fromUTF8(value));
+ }
+ }
+ // To avoid recording every multipart load as a separate visit in
+ // the history database, we want to keep track of whether the response
+ // is part of a multipart payload. We do want to record the first visit,
+ // so we only set isMultipartPayload to true after the first visit.
+ response.setIsMultipartPayload(has_sent_first_response_);
+ has_sent_first_response_ = true;
+ // Send the response!
+ if (client_)
+ client_->didReceiveResponse(loader_, response);
+
+ return true;
+}
+
+// Boundaries are supposed to be preceeded with --, but it looks like gecko
+// doesn't require the dashes to exist. See nsMultiMixedConv::FindToken.
+size_t MultipartResponseDelegate::FindBoundary() {
+ size_t boundary_pos = data_.find(boundary_);
+ if (boundary_pos != std::string::npos) {
+ // Back up over -- for backwards compat
+ // TODO(tc): Don't we only want to do this once? Gecko code doesn't seem
+ // to care.
+ if (boundary_pos >= 2) {
+ if ('-' == data_[boundary_pos - 1] && '-' == data_[boundary_pos - 2]) {
+ boundary_pos -= 2;
+ boundary_ = "--" + boundary_;
+ }
+ }
+ }
+ return boundary_pos;
+}
+
+bool MultipartResponseDelegate::ReadMultipartBoundary(
+ const WebURLResponse& response,
+ std::string* multipart_boundary) {
+ std::string content_type =
+ response.httpHeaderField(WebString::fromUTF8("Content-Type")).utf8();
+
+ size_t boundary_start_offset = content_type.find("boundary=");
+ if (boundary_start_offset == std::wstring::npos) {
+ return false;
+ }
+
+ boundary_start_offset += strlen("boundary=");
+
+ size_t boundary_end_offset = content_type.find(';', boundary_start_offset);
+
+ if (boundary_end_offset == std::string::npos)
+ boundary_end_offset = content_type.length();
+
+ size_t boundary_length = boundary_end_offset - boundary_start_offset;
+
+ *multipart_boundary =
+ content_type.substr(boundary_start_offset, boundary_length);
+ // The byte range response can have quoted boundary strings. This is legal
+ // as per MIME specifications. Individual data fragements however don't
+ // contain quoted boundary strings.
+ TrimString(*multipart_boundary, "\"", multipart_boundary);
+ return true;
+}
+
+bool MultipartResponseDelegate::ReadContentRanges(
+ const WebURLResponse& response,
+ int* content_range_lower_bound,
+ int* content_range_upper_bound) {
+
+ std::string content_range = response.httpHeaderField("Content-Range").utf8();
+ if (content_range.empty()) {
+ content_range = response.httpHeaderField("Range").utf8();
+ }
+
+ if (content_range.empty()) {
+ DLOG(WARNING) << "Failed to read content range from response.";
+ return false;
+ }
+
+ size_t byte_range_lower_bound_start_offset = content_range.find(" ");
+ if (byte_range_lower_bound_start_offset == std::string::npos) {
+ return false;
+ }
+
+ // Skip over the initial space.
+ byte_range_lower_bound_start_offset++;
+
+ size_t byte_range_lower_bound_end_offset =
+ content_range.find("-", byte_range_lower_bound_start_offset);
+ if (byte_range_lower_bound_end_offset == std::string::npos) {
+ return false;
+ }
+
+ size_t byte_range_lower_bound_characters =
+ byte_range_lower_bound_end_offset - byte_range_lower_bound_start_offset;
+ std::string byte_range_lower_bound =
+ content_range.substr(byte_range_lower_bound_start_offset,
+ byte_range_lower_bound_characters);
+
+ size_t byte_range_upper_bound_start_offset =
+ byte_range_lower_bound_end_offset + 1;
+
+ size_t byte_range_upper_bound_end_offset =
+ content_range.find("/", byte_range_upper_bound_start_offset);
+ if (byte_range_upper_bound_end_offset == std::string::npos) {
+ return false;
+ }
+
+ size_t byte_range_upper_bound_characters =
+ byte_range_upper_bound_end_offset - byte_range_upper_bound_start_offset;
+
+ std::string byte_range_upper_bound =
+ content_range.substr(byte_range_upper_bound_start_offset,
+ byte_range_upper_bound_characters);
+
+ if (!StringToInt(byte_range_lower_bound, content_range_lower_bound))
+ return false;
+ if (!StringToInt(byte_range_upper_bound, content_range_upper_bound))
+ return false;
+ return true;
+}
+
+} // namespace webkit_glue
diff --git a/webkit/glue/multipart_response_delegate.h b/webkit/glue/multipart_response_delegate.h
new file mode 100644
index 0000000..aded54a
--- /dev/null
+++ b/webkit/glue/multipart_response_delegate.h
@@ -0,0 +1,147 @@
+// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// A delegate class of WebURLLoaderImpl that handles multipart/x-mixed-replace
+// data. We special case multipart/x-mixed-replace because WebCore expects a
+// separate didReceiveResponse for each new message part.
+//
+// Most of the logic and edge case handling are based on the Mozilla's
+// implementation in netwerk/streamconv/converters/nsMultiMixedConv.cpp.
+// This seems like a derivative work, so here's the original license:
+
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef WEBKIT_GLUE_MULTIPART_RESPONSE_DELEGATE_H_
+#define WEBKIT_GLUE_MULTIPART_RESPONSE_DELEGATE_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURLResponse.h"
+
+namespace WebKit {
+class WebURLLoader;
+class WebURLLoaderClient;
+}
+
+namespace webkit_glue {
+
+// Used by unit tests to access private members.
+class MultipartResponseDelegateTester;
+
+class MultipartResponseDelegate {
+ public:
+ MultipartResponseDelegate(WebKit::WebURLLoaderClient* client,
+ WebKit::WebURLLoader* loader,
+ const WebKit::WebURLResponse& response,
+ const std::string& boundary);
+
+ // Passed through from ResourceHandleInternal
+ void OnReceivedData(const char* data, int data_len);
+ void OnCompletedRequest();
+
+ // The request has been canceled, so stop making calls to the client.
+ void Cancel() {
+ client_ = NULL;
+ loader_ = NULL;
+ }
+
+ // Returns the multi part boundary string from the Content-type header
+ // in the response.
+ // Returns true on success.
+ static bool ReadMultipartBoundary(const WebKit::WebURLResponse& response,
+ std::string* multipart_boundary);
+
+ // Returns the lower and higher content ranges from an individual multipart
+ // in a multipart response.
+ // Returns true on success.
+ static bool ReadContentRanges(const WebKit::WebURLResponse& response,
+ int* content_range_lower_bound,
+ int* content_range_upper_bound);
+
+ private:
+ friend class MultipartResponseDelegateTester; // For unittests.
+
+ // Pointers to the client and associated loader so we can make callbacks as
+ // we parse pieces of data.
+ WebKit::WebURLLoaderClient* client_;
+ WebKit::WebURLLoader* loader_;
+
+ // The original resource response for this request. We use this as a
+ // starting point for each parts response.
+ WebKit::WebURLResponse original_response_;
+
+ // Checks to see if data[pos] character is a line break; handles crlf, lflf,
+ // lf, or cr. Returns the number of characters to skip over (0, 1 or 2).
+ int PushOverLine(const std::string& data, size_t pos);
+
+ // Tries to parse http headers from the start of data_. Returns true if it
+ // succeeds and sends a didReceiveResponse to m_client. Returns false if
+ // the header is incomplete (in which case we just wait for more data).
+ bool ParseHeaders();
+
+ // Find the next boundary in data_. Returns std::string::npos if there's no
+ // full token.
+ size_t FindBoundary();
+
+ // A temporary buffer to hold data between reads for multipart data that
+ // gets split in the middle of a header.
+ std::string data_;
+
+ // Multipart boundary token
+ std::string boundary_;
+
+ // true until we get our first on received data call
+ bool first_received_data_;
+
+ // true if we're truncated in the middle of a header
+ bool processing_headers_;
+
+ // true when we're done sending information. At that point, we stop
+ // processing AddData requests.
+ bool stop_sending_;
+
+ // true after we've sent our first response to the WebURLLoaderClient.
+ bool has_sent_first_response_;
+
+ DISALLOW_COPY_AND_ASSIGN(MultipartResponseDelegate);
+};
+
+} // namespace webkit_glue
+
+#endif
diff --git a/webkit/glue/multipart_response_delegate_unittest.cc b/webkit/glue/multipart_response_delegate_unittest.cc
new file mode 100644
index 0000000..0433b52
--- /dev/null
+++ b/webkit/glue/multipart_response_delegate_unittest.cc
@@ -0,0 +1,638 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebString.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURL.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURLLoaderClient.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURLResponse.h"
+#include "webkit/glue/multipart_response_delegate.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using std::string;
+using WebKit::WebString;
+using WebKit::WebURL;
+using WebKit::WebURLError;
+using WebKit::WebURLLoader;
+using WebKit::WebURLLoaderClient;
+using WebKit::WebURLRequest;
+using WebKit::WebURLResponse;
+using webkit_glue::MultipartResponseDelegate;
+using webkit_glue::MultipartResponseDelegateTester;
+
+namespace webkit_glue {
+
+class MultipartResponseDelegateTester {
+ public:
+ MultipartResponseDelegateTester(MultipartResponseDelegate* delegate)
+ : delegate_(delegate) {
+ }
+
+ int PushOverLine(const std::string& data, size_t pos) {
+ return delegate_->PushOverLine(data, pos);
+ }
+
+ bool ParseHeaders() { return delegate_->ParseHeaders(); }
+ size_t FindBoundary() { return delegate_->FindBoundary(); }
+ std::string& boundary() { return delegate_->boundary_; }
+ std::string& data() { return delegate_->data_; }
+
+ private:
+ MultipartResponseDelegate* delegate_;
+};
+
+} // namespace webkit_glue
+
+namespace {
+
+class MultipartResponseTest : public testing::Test {
+};
+
+class MockWebURLLoaderClient : public WebURLLoaderClient {
+ public:
+ MockWebURLLoaderClient() { Reset(); }
+
+ virtual void willSendRequest(
+ WebURLLoader*, WebURLRequest&, const WebURLResponse&) {}
+ virtual void didSendData(
+ WebURLLoader*, unsigned long long, unsigned long long) {}
+
+ virtual void didReceiveResponse(WebURLLoader* loader,
+ const WebURLResponse& response) {
+ ++received_response_;
+ response_ = response;
+ data_.clear();
+ }
+ virtual void didReceiveData(WebURLLoader* loader,
+ const char* data, int data_length) {
+ ++received_data_;
+ data_.append(data, data_length);
+ }
+
+ virtual void didFinishLoading(WebURLLoader*) {}
+ virtual void didFail(WebURLLoader*, const WebURLError&) {}
+
+ void Reset() {
+ received_response_ = received_data_ = 0;
+ data_.clear();
+ response_.reset();
+ }
+
+ string GetResponseHeader(const char* name) const {
+ return string(response_.httpHeaderField(WebString::fromUTF8(name)).utf8());
+ }
+
+ int received_response_, received_data_;
+ string data_;
+ WebURLResponse response_;
+};
+
+// We can't put this in an anonymous function because it's a friend class for
+// access to private members.
+TEST(MultipartResponseTest, Functions) {
+ // PushOverLine tests
+
+ WebURLResponse response;
+ response.initialize();
+ response.setMIMEType(WebString::fromUTF8("multipart/x-mixed-replace"));
+ response.setHTTPHeaderField(WebString::fromUTF8("Foo"),
+ WebString::fromUTF8("Bar"));
+ response.setHTTPHeaderField(WebString::fromUTF8("Content-type"),
+ WebString::fromUTF8("text/plain"));
+ MockWebURLLoaderClient client;
+ MultipartResponseDelegate delegate(&client, NULL, response, "bound");
+ MultipartResponseDelegateTester delegate_tester(&delegate);
+
+ struct {
+ const char* input;
+ const int position;
+ const int expected;
+ } line_tests[] = {
+ { "Line", 0, 0 },
+ { "Line", 2, 0 },
+ { "Line", 10, 0 },
+ { "\r\nLine", 0, 2 },
+ { "\nLine", 0, 1 },
+ { "\n\nLine", 0, 2 },
+ { "\rLine", 0, 1 },
+ { "Line\r\nLine", 4, 2 },
+ { "Line\nLine", 4, 1 },
+ { "Line\n\nLine", 4, 2 },
+ { "Line\rLine", 4, 1 },
+ { "Line\r\rLine", 4, 1 },
+ };
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(line_tests); ++i) {
+ EXPECT_EQ(line_tests[i].expected,
+ delegate_tester.PushOverLine(line_tests[i].input,
+ line_tests[i].position));
+ }
+
+ // ParseHeaders tests
+ struct {
+ const char* data;
+ const bool rv;
+ const int received_response_calls;
+ const char* newdata;
+ } header_tests[] = {
+ { "This is junk", false, 0, "This is junk" },
+ { "Foo: bar\nBaz:\n\nAfter:\n", true, 1, "After:\n" },
+ { "Foo: bar\nBaz:\n", false, 0, "Foo: bar\nBaz:\n" },
+ { "Foo: bar\r\nBaz:\r\n\r\nAfter:\r\n", true, 1, "After:\r\n" },
+ { "Foo: bar\r\nBaz:\r\n", false, 0, "Foo: bar\r\nBaz:\r\n" },
+ { "Foo: bar\nBaz:\r\n\r\nAfter:\n\n", true, 1, "After:\n\n" },
+ { "Foo: bar\r\nBaz:\n", false, 0, "Foo: bar\r\nBaz:\n" },
+ { "\r\n", true, 1, "" },
+ };
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(header_tests); ++i) {
+ client.Reset();
+ delegate_tester.data().assign(header_tests[i].data);
+ EXPECT_EQ(header_tests[i].rv,
+ delegate_tester.ParseHeaders());
+ EXPECT_EQ(header_tests[i].received_response_calls,
+ client.received_response_);
+ EXPECT_EQ(string(header_tests[i].newdata),
+ delegate_tester.data());
+ }
+ // Test that the resource response is filled in correctly when parsing
+ // headers.
+ client.Reset();
+ string test_header("content-type: image/png\ncontent-length: 10\n\n");
+ delegate_tester.data().assign(test_header);
+ EXPECT_TRUE(delegate_tester.ParseHeaders());
+ EXPECT_TRUE(delegate_tester.data().length() == 0);
+ EXPECT_EQ(string("image/png"), client.GetResponseHeader("Content-Type"));
+ EXPECT_EQ(string("10"), client.GetResponseHeader("content-length"));
+ // This header is passed from the original request.
+ EXPECT_EQ(string("Bar"), client.GetResponseHeader("foo"));
+
+ // Make sure we parse the right mime-type if a charset is provided.
+ client.Reset();
+ string test_header2("content-type: text/html; charset=utf-8\n\n");
+ delegate_tester.data().assign(test_header2);
+ EXPECT_TRUE(delegate_tester.ParseHeaders());
+ EXPECT_TRUE(delegate_tester.data().length() == 0);
+ EXPECT_EQ(string("text/html; charset=utf-8"),
+ client.GetResponseHeader("Content-Type"));
+ EXPECT_EQ(string("utf-8"),
+ string(client.response_.textEncodingName().utf8()));
+
+ // FindBoundary tests
+ struct {
+ const char* boundary;
+ const char* data;
+ const size_t position;
+ } boundary_tests[] = {
+ { "bound", "bound", 0 },
+ { "bound", "--bound", 0 },
+ { "bound", "junkbound", 4 },
+ { "bound", "junk--bound", 4 },
+ { "foo", "bound", string::npos },
+ { "bound", "--boundbound", 0 },
+ };
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(boundary_tests); ++i) {
+ delegate_tester.boundary().assign(boundary_tests[i].boundary);
+ delegate_tester.data().assign(boundary_tests[i].data);
+ EXPECT_EQ(boundary_tests[i].position,
+ delegate_tester.FindBoundary());
+ }
+}
+
+TEST(MultipartResponseTest, MissingBoundaries) {
+ WebURLResponse response;
+ response.initialize();
+ response.setMIMEType(WebString::fromUTF8("multipart/x-mixed-replace"));
+ response.setHTTPHeaderField(WebString::fromUTF8("Foo"),
+ WebString::fromUTF8("Bar"));
+ response.setHTTPHeaderField(WebString::fromUTF8("Content-type"),
+ WebString::fromUTF8("text/plain"));
+ MockWebURLLoaderClient client;
+ MultipartResponseDelegate delegate(&client, NULL, response, "bound");
+
+ // No start boundary
+ string no_start_boundary(
+ "Content-type: text/plain\n\n"
+ "This is a sample response\n"
+ "--bound--"
+ "ignore junk after end token --bound\n\nTest2\n");
+ delegate.OnReceivedData(no_start_boundary.c_str(),
+ static_cast<int>(no_start_boundary.length()));
+ EXPECT_EQ(1, client.received_response_);
+ EXPECT_EQ(1, client.received_data_);
+ EXPECT_EQ(string("This is a sample response"),
+ client.data_);
+
+ delegate.OnCompletedRequest();
+ EXPECT_EQ(1, client.received_response_);
+ EXPECT_EQ(1, client.received_data_);
+
+ // No end boundary
+ client.Reset();
+ MultipartResponseDelegate delegate2(&client, NULL, response, "bound");
+ string no_end_boundary(
+ "bound\nContent-type: text/plain\n\n"
+ "This is a sample response\n");
+ delegate2.OnReceivedData(no_end_boundary.c_str(),
+ static_cast<int>(no_end_boundary.length()));
+ EXPECT_EQ(1, client.received_response_);
+ EXPECT_EQ(1, client.received_data_);
+ EXPECT_EQ("This is a sample response\n", client.data_);
+
+ delegate2.OnCompletedRequest();
+ EXPECT_EQ(1, client.received_response_);
+ EXPECT_EQ(1, client.received_data_);
+ EXPECT_EQ(string("This is a sample response\n"),
+ client.data_);
+
+ // Neither boundary
+ client.Reset();
+ MultipartResponseDelegate delegate3(&client, NULL, response, "bound");
+ string no_boundaries(
+ "Content-type: text/plain\n\n"
+ "This is a sample response\n");
+ delegate3.OnReceivedData(no_boundaries.c_str(),
+ static_cast<int>(no_boundaries.length()));
+ EXPECT_EQ(1, client.received_response_);
+ EXPECT_EQ(1, client.received_data_);
+ EXPECT_EQ("This is a sample response\n", client.data_);
+
+ delegate3.OnCompletedRequest();
+ EXPECT_EQ(1, client.received_response_);
+ EXPECT_EQ(1, client.received_data_);
+ EXPECT_EQ(string("This is a sample response\n"),
+ client.data_);
+}
+
+TEST(MultipartResponseTest, MalformedBoundary) {
+ // Some servers send a boundary that is prefixed by "--". See bug 5786.
+
+ WebURLResponse response;
+ response.initialize();
+ response.setMIMEType(WebString::fromUTF8("multipart/x-mixed-replace"));
+ response.setHTTPHeaderField(WebString::fromUTF8("Foo"),
+ WebString::fromUTF8("Bar"));
+ response.setHTTPHeaderField(WebString::fromUTF8("Content-type"),
+ WebString::fromUTF8("text/plain"));
+ MockWebURLLoaderClient client;
+ MultipartResponseDelegate delegate(&client, NULL, response, "--bound");
+
+ string data(
+ "--bound\n"
+ "Content-type: text/plain\n\n"
+ "This is a sample response\n"
+ "--bound--"
+ "ignore junk after end token --bound\n\nTest2\n");
+ delegate.OnReceivedData(data.c_str(), static_cast<int>(data.length()));
+ EXPECT_EQ(1, client.received_response_);
+ EXPECT_EQ(1, client.received_data_);
+ EXPECT_EQ(string("This is a sample response"), client.data_);
+
+ delegate.OnCompletedRequest();
+ EXPECT_EQ(1, client.received_response_);
+ EXPECT_EQ(1, client.received_data_);
+}
+
+
+// Used in for tests that break the data in various places.
+struct TestChunk {
+ const int start_pos; // offset in data
+ const int end_pos; // end offset in data
+ const int expected_responses;
+ const int expected_received_data;
+ const char* expected_data;
+};
+
+void VariousChunkSizesTest(const TestChunk chunks[], int chunks_size,
+ int responses, int received_data,
+ const char* completed_data) {
+ const string data(
+ "--bound\n" // 0-7
+ "Content-type: image/png\n\n" // 8-32
+ "datadatadatadatadata" // 33-52
+ "--bound\n" // 53-60
+ "Content-type: image/jpg\n\n" // 61-85
+ "foofoofoofoofoo" // 86-100
+ "--bound--"); // 101-109
+
+ WebURLResponse response;
+ response.initialize();
+ response.setMIMEType(WebString::fromUTF8("multipart/x-mixed-replace"));
+ MockWebURLLoaderClient client;
+ MultipartResponseDelegate delegate(&client, NULL, response, "bound");
+
+ for (int i = 0; i < chunks_size; ++i) {
+ ASSERT_TRUE(chunks[i].start_pos < chunks[i].end_pos);
+ string chunk = data.substr(chunks[i].start_pos,
+ chunks[i].end_pos - chunks[i].start_pos);
+ delegate.OnReceivedData(chunk.c_str(), static_cast<int>(chunk.length()));
+ EXPECT_EQ(chunks[i].expected_responses,
+ client.received_response_);
+ EXPECT_EQ(chunks[i].expected_received_data,
+ client.received_data_);
+ EXPECT_EQ(string(chunks[i].expected_data),
+ client.data_);
+ }
+ // Check final state
+ delegate.OnCompletedRequest();
+ EXPECT_EQ(responses,
+ client.received_response_);
+ EXPECT_EQ(received_data,
+ client.received_data_);
+ EXPECT_EQ(string(completed_data),
+ client.data_);
+}
+
+TEST(MultipartResponseTest, BreakInBoundary) {
+ // Break in the first boundary
+ const TestChunk bound1[] = {
+ { 0, 4, 0, 0, ""},
+ { 4, 110, 2, 2, "foofoofoofoofoo" },
+ };
+ VariousChunkSizesTest(bound1, arraysize(bound1),
+ 2, 2, "foofoofoofoofoo");
+
+ // Break in first and second
+ const TestChunk bound2[] = {
+ { 0, 4, 0, 0, ""},
+ { 4, 55, 1, 1, "datadatadatadat" },
+ { 55, 65, 1, 2, "datadatadatadatadata" },
+ { 65, 110, 2, 3, "foofoofoofoofoo" },
+ };
+ VariousChunkSizesTest(bound2, arraysize(bound2),
+ 2, 3, "foofoofoofoofoo");
+
+ // Break in second only
+ const TestChunk bound3[] = {
+ { 0, 55, 1, 1, "datadatadatadat" },
+ { 55, 110, 2, 3, "foofoofoofoofoo" },
+ };
+ VariousChunkSizesTest(bound3, arraysize(bound3),
+ 2, 3, "foofoofoofoofoo");
+}
+
+TEST(MultipartResponseTest, BreakInHeaders) {
+ // Break in first header
+ const TestChunk header1[] = {
+ { 0, 10, 0, 0, "" },
+ { 10, 35, 1, 0, "" },
+ { 35, 110, 2, 2, "foofoofoofoofoo" },
+ };
+ VariousChunkSizesTest(header1, arraysize(header1),
+ 2, 2, "foofoofoofoofoo");
+
+ // Break in both headers
+ const TestChunk header2[] = {
+ { 0, 10, 0, 0, "" },
+ { 10, 65, 1, 1, "datadatadatadatadata" },
+ { 65, 110, 2, 2, "foofoofoofoofoo" },
+ };
+ VariousChunkSizesTest(header2, arraysize(header2),
+ 2, 2, "foofoofoofoofoo");
+
+ // Break at end of a header
+ const TestChunk header3[] = {
+ { 0, 33, 1, 0, "" },
+ { 33, 65, 1, 1, "datadatadatadatadata" },
+ { 65, 110, 2, 2, "foofoofoofoofoo" },
+ };
+ VariousChunkSizesTest(header3, arraysize(header3),
+ 2, 2, "foofoofoofoofoo");
+}
+
+TEST(MultipartResponseTest, BreakInData) {
+ // All data as one chunk
+ const TestChunk data1[] = {
+ { 0, 110, 2, 2, "foofoofoofoofoo" },
+ };
+ VariousChunkSizesTest(data1, arraysize(data1),
+ 2, 2, "foofoofoofoofoo");
+
+ // breaks in data segment
+ const TestChunk data2[] = {
+ { 0, 35, 1, 0, "" },
+ { 35, 65, 1, 1, "datadatadatadatadata" },
+ { 65, 90, 2, 1, "" },
+ { 90, 110, 2, 2, "foofoofoofoofoo" },
+ };
+ VariousChunkSizesTest(data2, arraysize(data2),
+ 2, 2, "foofoofoofoofoo");
+
+ // Incomplete send
+ const TestChunk data3[] = {
+ { 0, 35, 1, 0, "" },
+ { 35, 90, 2, 1, "" },
+ };
+ VariousChunkSizesTest(data3, arraysize(data3),
+ 2, 2, "foof");
+}
+
+TEST(MultipartResponseTest, MultipleBoundaries) {
+ // Test multiple boundaries back to back
+ WebURLResponse response;
+ response.initialize();
+ response.setMIMEType(WebString::fromUTF8("multipart/x-mixed-replace"));
+ MockWebURLLoaderClient client;
+ MultipartResponseDelegate delegate(&client, NULL, response, "bound");
+
+ string data("--bound\r\n\r\n--bound\r\n\r\nfoofoo--bound--");
+ delegate.OnReceivedData(data.c_str(), static_cast<int>(data.length()));
+ EXPECT_EQ(2,
+ client.received_response_);
+ EXPECT_EQ(1,
+ client.received_data_);
+ EXPECT_EQ(string("foofoo"),
+ client.data_);
+}
+
+TEST(MultipartResponseTest, MultipartByteRangeParsingTest) {
+ // Test multipart/byteranges based boundary parsing.
+ WebURLResponse response1;
+ response1.initialize();
+ response1.setMIMEType(WebString::fromUTF8("multipart/x-mixed-replace"));
+ response1.setHTTPHeaderField(WebString::fromUTF8("Content-Length"),
+ WebString::fromUTF8("200"));
+ response1.setHTTPHeaderField(
+ WebString::fromUTF8("Content-type"),
+ WebString::fromUTF8("multipart/byteranges; boundary=--bound--"));
+
+ std::string multipart_boundary;
+ bool result = MultipartResponseDelegate::ReadMultipartBoundary(
+ response1, &multipart_boundary);
+ EXPECT_EQ(result, true);
+ EXPECT_EQ(string("--bound--"),
+ multipart_boundary);
+
+ WebURLResponse response2;
+ response2.initialize();
+ response2.setMIMEType(WebString::fromUTF8("image/png"));
+
+ response2.setHTTPHeaderField(WebString::fromUTF8("Content-Length"),
+ WebString::fromUTF8("300"));
+ response2.setHTTPHeaderField(
+ WebString::fromUTF8("Last-Modified"),
+ WebString::fromUTF8("Mon, 04 Apr 2005 20:36:01 GMT"));
+ response2.setHTTPHeaderField(
+ WebString::fromUTF8("Date"),
+ WebString::fromUTF8("Thu, 11 Sep 2008 18:21:42 GMT"));
+
+ multipart_boundary.clear();
+ result = MultipartResponseDelegate::ReadMultipartBoundary(
+ response2, &multipart_boundary);
+ EXPECT_EQ(result, false);
+
+ WebURLResponse response3;
+ response3.initialize();
+ response3.setMIMEType(WebString::fromUTF8("multipart/byteranges"));
+
+ response3.setHTTPHeaderField(WebString::fromUTF8("Content-Length"),
+ WebString::fromUTF8("300"));
+ response3.setHTTPHeaderField(
+ WebString::fromUTF8("Last-Modified"),
+ WebString::fromUTF8("Mon, 04 Apr 2005 20:36:01 GMT"));
+ response3.setHTTPHeaderField(
+ WebString::fromUTF8("Date"),
+ WebString::fromUTF8("Thu, 11 Sep 2008 18:21:42 GMT"));
+ response3.setHTTPHeaderField(
+ WebString::fromUTF8("Content-type"),
+ WebString::fromUTF8("multipart/byteranges"));
+
+ multipart_boundary.clear();
+ result = MultipartResponseDelegate::ReadMultipartBoundary(
+ response3, &multipart_boundary);
+ EXPECT_EQ(result, false);
+ EXPECT_EQ(multipart_boundary.length(), 0U);
+
+ WebURLResponse response4;
+ response4.initialize();
+ response4.setMIMEType(WebString::fromUTF8("multipart/byteranges"));
+ response4.setHTTPHeaderField(WebString::fromUTF8("Content-Length"),
+ WebString::fromUTF8("200"));
+ response4.setHTTPHeaderField(
+ WebString::fromUTF8("Content-type"),
+ WebString::fromUTF8(
+ "multipart/byteranges; boundary=--bound--; charSet=utf8"));
+
+ multipart_boundary.clear();
+
+ result = MultipartResponseDelegate::ReadMultipartBoundary(
+ response4, &multipart_boundary);
+ EXPECT_EQ(result, true);
+ EXPECT_EQ(string("--bound--"), multipart_boundary);
+
+ WebURLResponse response5;
+ response5.initialize();
+ response5.setMIMEType(WebString::fromUTF8("multipart/byteranges"));
+ response5.setHTTPHeaderField(WebString::fromUTF8("Content-Length"),
+ WebString::fromUTF8("200"));
+ response5.setHTTPHeaderField(
+ WebString::fromUTF8("Content-type"),
+ WebString::fromUTF8(
+ "multipart/byteranges; boundary=\"--bound--\"; charSet=utf8"));
+
+ multipart_boundary.clear();
+
+ result = MultipartResponseDelegate::ReadMultipartBoundary(
+ response5, &multipart_boundary);
+ EXPECT_EQ(result, true);
+ EXPECT_EQ(string("--bound--"), multipart_boundary);
+}
+
+TEST(MultipartResponseTest, MultipartContentRangesTest) {
+ WebURLResponse response1;
+ response1.initialize();
+ response1.setMIMEType("application/pdf");
+ response1.setHTTPHeaderField("Content-Length", "200");
+ response1.setHTTPHeaderField("Content-Range", "bytes 1000-1050/5000");
+
+ int content_range_lower_bound = 0;
+ int content_range_upper_bound = 0;
+
+ bool result = MultipartResponseDelegate::ReadContentRanges(
+ response1, &content_range_lower_bound,
+ &content_range_upper_bound);
+
+ EXPECT_EQ(result, true);
+ EXPECT_EQ(content_range_lower_bound, 1000);
+ EXPECT_EQ(content_range_upper_bound, 1050);
+
+ WebURLResponse response2;
+ response2.initialize();
+ response2.setMIMEType("application/pdf");
+ response2.setHTTPHeaderField("Content-Length", "200");
+ response2.setHTTPHeaderField("Content-Range", "bytes 1000/1050");
+
+ content_range_lower_bound = 0;
+ content_range_upper_bound = 0;
+
+ result = MultipartResponseDelegate::ReadContentRanges(
+ response2, &content_range_lower_bound,
+ &content_range_upper_bound);
+
+ EXPECT_EQ(result, false);
+
+ WebURLResponse response3;
+ response3.initialize();
+ response3.setMIMEType("application/pdf");
+ response3.setHTTPHeaderField("Content-Length", "200");
+ response3.setHTTPHeaderField("Range", "bytes 1000-1050/5000");
+
+ content_range_lower_bound = 0;
+ content_range_upper_bound = 0;
+
+ result = MultipartResponseDelegate::ReadContentRanges(
+ response3, &content_range_lower_bound,
+ &content_range_upper_bound);
+
+ EXPECT_EQ(result, true);
+ EXPECT_EQ(content_range_lower_bound, 1000);
+ EXPECT_EQ(content_range_upper_bound, 1050);
+
+ WebURLResponse response4;
+ response4.initialize();
+ response4.setMIMEType("application/pdf");
+ response4.setHTTPHeaderField("Content-Length", "200");
+
+ content_range_lower_bound = 0;
+ content_range_upper_bound = 0;
+
+ result = MultipartResponseDelegate::ReadContentRanges(
+ response4, &content_range_lower_bound,
+ &content_range_upper_bound);
+
+ EXPECT_EQ(result, false);
+}
+
+TEST(MultipartResponseTest, MultipartPayloadSet) {
+ WebURLResponse response;
+ response.initialize();
+ response.setMIMEType(WebString::fromUTF8("multipart/x-mixed-replace"));
+ MockWebURLLoaderClient client;
+ MultipartResponseDelegate delegate(&client, NULL, response, "bound");
+
+ string data(
+ "--bound\n"
+ "Content-type: text/plain\n\n"
+ "response data\n"
+ "--bound\n");
+ delegate.OnReceivedData(data.c_str(), static_cast<int>(data.length()));
+ EXPECT_EQ(1,
+ client.received_response_);
+ EXPECT_EQ(string("response data"),
+ client.data_);
+ EXPECT_EQ(false, client.response_.isMultipartPayload());
+
+ string data2(
+ "Content-type: text/plain\n\n"
+ "response data2\n"
+ "--bound\n");
+ delegate.OnReceivedData(data2.c_str(), static_cast<int>(data2.length()));
+ EXPECT_EQ(2,
+ client.received_response_);
+ EXPECT_EQ(string("response data2"),
+ client.data_);
+ EXPECT_EQ(true, client.response_.isMultipartPayload());
+}
+
+} // namespace
diff --git a/webkit/glue/npruntime_util.cc b/webkit/glue/npruntime_util.cc
new file mode 100644
index 0000000..dfc3a2b
--- /dev/null
+++ b/webkit/glue/npruntime_util.cc
@@ -0,0 +1,51 @@
+// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/npruntime_util.h"
+
+#include "base/pickle.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebBindings.h"
+
+using WebKit::WebBindings;
+
+namespace webkit_glue {
+
+bool SerializeNPIdentifier(NPIdentifier identifier, Pickle* pickle) {
+ const NPUTF8* string;
+ int32_t number;
+ bool is_string;
+ WebBindings::extractIdentifierData(identifier, string, number, is_string);
+
+ if (!pickle->WriteBool(is_string))
+ return false;
+ if (is_string) {
+ // Write the null byte for efficiency on the other end.
+ return pickle->WriteData(string, strlen(string) + 1);
+ }
+ return pickle->WriteInt(number);
+}
+
+bool DeserializeNPIdentifier(const Pickle& pickle, void** pickle_iter,
+ NPIdentifier* identifier) {
+ bool is_string;
+ if (!pickle.ReadBool(pickle_iter, &is_string))
+ return false;
+
+ if (is_string) {
+ const char* data;
+ int data_len;
+ if (!pickle.ReadData(pickle_iter, &data, &data_len))
+ return false;
+ DCHECK_EQ((static_cast<size_t>(data_len)), strlen(data) + 1);
+ *identifier = WebBindings::getStringIdentifier(data);
+ } else {
+ int number;
+ if (!pickle.ReadInt(pickle_iter, &number))
+ return false;
+ *identifier = WebBindings::getIntIdentifier(number);
+ }
+ return true;
+}
+
+} // namespace webkit_glue
diff --git a/webkit/glue/npruntime_util.h b/webkit/glue/npruntime_util.h
new file mode 100644
index 0000000..1135246
--- /dev/null
+++ b/webkit/glue/npruntime_util.h
@@ -0,0 +1,21 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_NPRUNTIME_UTIL_H_
+#define WEBKIT_GLUE_NPRUNTIME_UTIL_H_
+
+#include "third_party/npapi/bindings/npruntime.h"
+
+class Pickle;
+
+namespace webkit_glue {
+
+// Efficiently serialize/deserialize a NPIdentifier
+bool SerializeNPIdentifier(NPIdentifier identifier, Pickle* pickle);
+bool DeserializeNPIdentifier(const Pickle& pickle, void** pickle_iter,
+ NPIdentifier* identifier);
+
+} // namespace webkit_glue
+
+#endif // WEBKIT_GLUE_NPRUNTIME_UTIL_H_
diff --git a/webkit/glue/password_form.h b/webkit/glue/password_form.h
new file mode 100644
index 0000000..57a54c1
--- /dev/null
+++ b/webkit/glue/password_form.h
@@ -0,0 +1,168 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PASSWORD_FORM_H__
+#define WEBKIT_GLUE_PASSWORD_FORM_H__
+
+#include <string>
+#include <map>
+
+#include "base/time.h"
+#include "googleurl/src/gurl.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebPasswordFormData.h"
+
+namespace webkit_glue {
+
+// The PasswordForm struct encapsulates information about a login form,
+// which can be an HTML form or a dialog with username/password text fields.
+//
+// The Web Data database stores saved username/passwords and associated form
+// metdata using a PasswordForm struct, typically one that was created from
+// a parsed HTMLFormElement or LoginDialog, but the saved entries could have
+// also been created by imported data from another browser.
+//
+// The PasswordManager implements a fuzzy-matching algorithm to compare saved
+// PasswordForm entries against PasswordForms that were created from a parsed
+// HTML or dialog form. As one might expect, the more data contained in one
+// of the saved PasswordForms, the better the job the PasswordManager can do
+// in matching it against the actual form it was saved on, and autofill
+// accurately. But it is not always possible, especially when importing from
+// other browsers with different data models, to copy over all the information
+// about a particular "saved password entry" to our PasswordForm
+// representation.
+//
+// The field descriptions in the struct specification below are
+// intended to describe which fields are not strictly required when adding a saved
+// password entry to the database and how they can affect the matching process.
+
+struct PasswordForm {
+ // Enum to differentiate between HTML form based authentication, and dialogs
+ // using basic or digest schemes. Default is SCHEME_HTML. Only PasswordForms
+ // of the same Scheme will be matched/autofilled against each other.
+ enum Scheme {
+ SCHEME_HTML,
+ SCHEME_BASIC,
+ SCHEME_DIGEST,
+ SCHEME_OTHER
+ } scheme;
+
+ // The "Realm" for the sign-on (scheme, host, port for SCHEME_HTML, and
+ // contains the HTTP realm for dialog-based forms).
+ // The signon_realm is effectively the primary key used for retrieving
+ // data from the database, so it must not be empty.
+ std::string signon_realm;
+
+ // The URL (minus query parameters) containing the form. This is the primary
+ // data used by the PasswordManager to decide (in longest matching prefix
+ // fashion) whether or not a given PasswordForm result from the database is a
+ // good fit for a particular form on a page, so it must not be empty.
+ GURL origin;
+
+ // The action target of the form. This is the primary data used by the
+ // PasswordManager for form autofill; that is, the action of the saved
+ // credentials must match the action of the form on the page to be autofilled.
+ // If this is empty / not available, it will result in a "restricted"
+ // IE-like autofill policy, where we wait for the user to type in his
+ // username before autofilling the password. In these cases, after successful
+ // login the action URL will automatically be assigned by the
+ // PasswordManager.
+ //
+ // When parsing an HTML form, this must always be set.
+ GURL action;
+
+ // The name of the submit button used. Optional; only used in scoring
+ // of PasswordForm results from the database to make matches as tight as
+ // possible.
+ //
+ // When parsing an HTML form, this must always be set.
+ string16 submit_element;
+
+ // The name of the username input element. Optional (improves scoring).
+ //
+ // When parsing an HTML form, this must always be set.
+ string16 username_element;
+
+ // The username. Optional.
+ //
+ // When parsing an HTML form, this is typically empty unless the site
+ // has implemented some form of autofill.
+ string16 username_value;
+
+ // The name of the password input element, Optional (improves scoring).
+ //
+ // When parsing an HTML form, this must always be set.
+ string16 password_element;
+
+ // The password. Required.
+ //
+ // When parsing an HTML form, this is typically empty.
+ string16 password_value;
+
+ // If the form was a change password form, the name of the
+ // 'old password' input element. Optional.
+ string16 old_password_element;
+
+ // The old password. Optional.
+ string16 old_password_value;
+
+ // Whether or not this login was saved under an HTTPS session with a valid
+ // SSL cert. We will never match or autofill a PasswordForm where
+ // ssl_valid == true with a PasswordForm where ssl_valid == false. This means
+ // passwords saved under HTTPS will never get autofilled onto an HTTP page.
+ // When importing, this should be set to true if the page URL is HTTPS, thus
+ // giving it "the benefit of the doubt" that the SSL cert was valid when it
+ // was saved. Default to false.
+ bool ssl_valid;
+
+ // True if this PasswordForm represents the last username/password login the
+ // user selected to log in to the site. If there is only one saved entry for
+ // the site, this will always be true, but when there are multiple entries
+ // the PasswordManager ensures that only one of them has a preferred bit set
+ // to true. Default to false.
+ //
+ // When parsing an HTML form, this is not used.
+ bool preferred;
+
+ // When the login was saved (by chrome).
+ //
+ // When parsing an HTML form, this is not used.
+ base::Time date_created;
+
+ // Tracks if the user opted to never remember passwords for this form. Default
+ // to false.
+ //
+ // When parsing an HTML form, this is not used.
+ bool blacklisted_by_user;
+
+ PasswordForm()
+ : scheme(SCHEME_HTML),
+ ssl_valid(false),
+ preferred(false),
+ blacklisted_by_user(false) {
+ }
+
+ PasswordForm(const WebKit::WebPasswordFormData& web_password_form)
+ : scheme(SCHEME_HTML),
+ signon_realm(web_password_form.signonRealm.utf8()),
+ origin(web_password_form.origin),
+ action(web_password_form.action),
+ submit_element(web_password_form.submitElement),
+ username_element(web_password_form.userNameElement),
+ username_value(web_password_form.userNameValue),
+ password_element(web_password_form.passwordElement),
+ password_value(web_password_form.passwordValue),
+ old_password_element(web_password_form.oldPasswordElement),
+ old_password_value(web_password_form.oldPasswordValue),
+ ssl_valid(false),
+ preferred(false),
+ blacklisted_by_user(false) {
+ }
+};
+
+// Map username to PasswordForm* for convenience. See password_form_manager.h.
+typedef std::map<string16, PasswordForm*> PasswordFormMap;
+
+} // namespace webkit_glue
+
+#endif // WEBKIT_GLUE_PASSWORD_FORM_H__
diff --git a/webkit/glue/password_form_dom_manager.cc b/webkit/glue/password_form_dom_manager.cc
new file mode 100644
index 0000000..adbb8dd
--- /dev/null
+++ b/webkit/glue/password_form_dom_manager.cc
@@ -0,0 +1,70 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/password_form_dom_manager.h"
+
+#include "base/logging.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebInputElement.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebPasswordFormData.h"
+#include "webkit/glue/form_field.h"
+
+using WebKit::WebFormElement;
+using WebKit::WebInputElement;
+using WebKit::WebPasswordFormData;
+
+namespace webkit_glue {
+
+PasswordFormFillData::PasswordFormFillData() : wait_for_username(false) {
+}
+
+PasswordFormFillData::~PasswordFormFillData() {
+}
+
+PasswordForm* PasswordFormDomManager::CreatePasswordForm(
+ const WebFormElement& webform) {
+ WebPasswordFormData web_password_form(webform);
+ if (web_password_form.isValid())
+ return new PasswordForm(web_password_form);
+ return NULL;
+}
+
+// static
+void PasswordFormDomManager::InitFillData(
+ const PasswordForm& form_on_page,
+ const PasswordFormMap& matches,
+ const PasswordForm* const preferred_match,
+ bool wait_for_username_before_autofill,
+ PasswordFormFillData* result) {
+ DCHECK(preferred_match);
+ // Fill basic form data.
+ result->basic_data.origin = form_on_page.origin;
+ result->basic_data.action = form_on_page.action;
+ // TODO(jhawkins): Is it right to use an empty string for the form control
+ // type? I don't think the password autocomplete really cares, but we should
+ // correct this anyway.
+ // TODO(dhollowa): Similarly, |size| ideally should be set from the form
+ // control itself. But it is currently unused.
+ result->basic_data.fields.push_back(
+ FormField(string16(),
+ form_on_page.username_element,
+ preferred_match->username_value,
+ string16(),
+ 0));
+ result->basic_data.fields.push_back(
+ FormField(string16(),
+ form_on_page.password_element,
+ preferred_match->password_value,
+ string16(),
+ 0));
+ result->wait_for_username = wait_for_username_before_autofill;
+
+ // Copy additional username/value pairs.
+ PasswordFormMap::const_iterator iter;
+ for (iter = matches.begin(); iter != matches.end(); iter++) {
+ if (iter->second != preferred_match)
+ result->additional_logins[iter->first] = iter->second->password_value;
+ }
+}
+
+} // namespace webkit_glue
diff --git a/webkit/glue/password_form_dom_manager.h b/webkit/glue/password_form_dom_manager.h
new file mode 100644
index 0000000..63a6f45
--- /dev/null
+++ b/webkit/glue/password_form_dom_manager.h
@@ -0,0 +1,63 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PASSWORD_FORM_DOM_MANAGER_H_
+#define WEBKIT_GLUE_PASSWORD_FORM_DOM_MANAGER_H_
+
+#include "webkit/glue/form_data.h"
+#include "webkit/glue/password_form.h"
+
+namespace WebKit {
+class WebForm;
+}
+
+class GURL;
+
+namespace webkit_glue {
+
+// Structure used for autofilling password forms.
+// basic_data identifies the HTML form on the page and preferred username/
+// password for login, while
+// additional_logins is a list of other matching user/pass pairs for the form.
+// wait_for_username tells us whether we need to wait for the user to enter
+// a valid username before we autofill the password. By default, this is off
+// unless the PasswordManager determined there is an additional risk
+// associated with this form. This can happen, for example, if action URI's
+// of the observed form and our saved representation don't match up.
+struct PasswordFormFillData {
+ typedef std::map<string16, string16> LoginCollection;
+
+ FormData basic_data;
+ LoginCollection additional_logins;
+ bool wait_for_username;
+ PasswordFormFillData();
+ ~PasswordFormFillData();
+};
+
+class PasswordFormDomManager {
+ public:
+ // Create a PasswordForm from DOM form. Webkit doesn't allow storing
+ // custom metadata to DOM nodes, so we have to do this every time an event
+ // happens with a given form and compare against previously Create'd forms
+ // to identify..which sucks.
+ static PasswordForm* CreatePasswordForm(const WebKit::WebFormElement& form);
+
+ // Create a FillData structure in preparation for autofilling a form,
+ // from basic_data identifying which form to fill, and a collection of
+ // matching stored logins to use as username/password values.
+ // preferred_match should equal (address) one of matches.
+ // wait_for_username_before_autofill is true if we should not autofill
+ // anything until the user typed in a valid username and blurred the field.
+ static void InitFillData(const PasswordForm& form_on_page,
+ const PasswordFormMap& matches,
+ const PasswordForm* const preferred_match,
+ bool wait_for_username_before_autofill,
+ PasswordFormFillData* result);
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(PasswordFormDomManager);
+};
+
+} // namespace webkit_glue
+
+#endif // WEBKIT_GLUE_PASSWORD_FORM_DOM_MANAGER_H__
diff --git a/webkit/glue/plugins/DEPS b/webkit/glue/plugins/DEPS
new file mode 100644
index 0000000..4ac8845
--- /dev/null
+++ b/webkit/glue/plugins/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+ "+printing",
+ "+third_party/ppapi/c",
+]
diff --git a/webkit/glue/plugins/carbon_plugin_window_tracker_mac.cc b/webkit/glue/plugins/carbon_plugin_window_tracker_mac.cc
new file mode 100644
index 0000000..6131b45
--- /dev/null
+++ b/webkit/glue/plugins/carbon_plugin_window_tracker_mac.cc
@@ -0,0 +1,55 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/logging.h"
+#include "webkit/glue/plugins/carbon_plugin_window_tracker_mac.h"
+
+CarbonPluginWindowTracker::CarbonPluginWindowTracker() {
+}
+
+CarbonPluginWindowTracker* CarbonPluginWindowTracker::SharedInstance() {
+ static CarbonPluginWindowTracker* tracker = new CarbonPluginWindowTracker();
+ return tracker;
+}
+
+WindowRef CarbonPluginWindowTracker::CreateDummyWindowForDelegate(
+ WebPluginDelegateImpl* delegate) {
+ // The real size will be set by the plugin instance, once that size is known.
+ Rect window_bounds = { 0, 0, 100, 100 };
+ WindowRef new_ref = NULL;
+ if (CreateNewWindow(kDocumentWindowClass,
+ kWindowNoTitleBarAttribute,
+ &window_bounds,
+ &new_ref) == noErr) {
+ window_to_delegate_map_[new_ref] = delegate;
+ delegate_to_window_map_[delegate] = new_ref;
+ }
+ return new_ref;
+}
+
+WebPluginDelegateImpl* CarbonPluginWindowTracker::GetDelegateForDummyWindow(
+ WindowRef window) const {
+ WindowToDelegateMap::const_iterator i = window_to_delegate_map_.find(window);
+ if (i != window_to_delegate_map_.end())
+ return i->second;
+ return NULL;
+}
+
+WindowRef CarbonPluginWindowTracker::GetDummyWindowForDelegate(
+ WebPluginDelegateImpl* delegate) const {
+ DelegateToWindowMap::const_iterator i =
+ delegate_to_window_map_.find(delegate);
+ if (i != delegate_to_window_map_.end())
+ return i->second;
+ return NULL;
+}
+
+void CarbonPluginWindowTracker::DestroyDummyWindowForDelegate(
+ WebPluginDelegateImpl* delegate, WindowRef window) {
+ DCHECK(GetDelegateForDummyWindow(window) == delegate);
+ window_to_delegate_map_.erase(window);
+ delegate_to_window_map_.erase(delegate);
+ if (window) // Check just in case the initial window creation failed.
+ DisposeWindow(window);
+}
diff --git a/webkit/glue/plugins/carbon_plugin_window_tracker_mac.h b/webkit/glue/plugins/carbon_plugin_window_tracker_mac.h
new file mode 100644
index 0000000..3e6ef2d
--- /dev/null
+++ b/webkit/glue/plugins/carbon_plugin_window_tracker_mac.h
@@ -0,0 +1,51 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_CARBON_PLUGIN_WINDOW_TRACKER_MAC_H_
+#define WEBKIT_GLUE_PLUGINS_CARBON_PLUGIN_WINDOW_TRACKER_MAC_H_
+
+#include <Carbon/Carbon.h>
+#include <map>
+
+#include "base/basictypes.h"
+
+class WebPluginDelegateImpl;
+
+// Creates and tracks the invisible windows that are necessary for
+// Carbon-event-model plugins.
+//
+// Serves as a bridge between plugin delegate instances and the Carbon
+// interposing library. The Carbon functions we interpose work in terms of
+// WindowRefs, and we need to be able to map from those back to the plugin
+// delegates that know what we should claim about the state of the window.
+class __attribute__((visibility("default"))) CarbonPluginWindowTracker {
+ public:
+ CarbonPluginWindowTracker();
+
+ // Returns the shared window tracker instance.
+ static CarbonPluginWindowTracker* SharedInstance();
+
+ // Creates a new carbon window associated with |delegate|.
+ WindowRef CreateDummyWindowForDelegate(WebPluginDelegateImpl* delegate);
+
+ // Returns the WebPluginDelegate associated with the given dummy window.
+ WebPluginDelegateImpl* GetDelegateForDummyWindow(WindowRef window) const;
+
+ // Returns the dummy window associated with |delegate|.
+ WindowRef GetDummyWindowForDelegate(WebPluginDelegateImpl* delegate) const;
+
+ // Destroys the dummy window for |delegate|.
+ void DestroyDummyWindowForDelegate(WebPluginDelegateImpl* delegate,
+ WindowRef window);
+
+ private:
+ typedef std::map<WindowRef, WebPluginDelegateImpl*> WindowToDelegateMap;
+ typedef std::map<WebPluginDelegateImpl*, WindowRef> DelegateToWindowMap;
+ WindowToDelegateMap window_to_delegate_map_;
+ DelegateToWindowMap delegate_to_window_map_;
+
+ DISALLOW_COPY_AND_ASSIGN(CarbonPluginWindowTracker);
+};
+
+#endif // WEBKIT_GLUE_PLUGINS_CARBON_PLUGIN_WINDOW_TRACKER_MAC_H_
diff --git a/webkit/glue/plugins/coregraphics_private_symbols_mac.h b/webkit/glue/plugins/coregraphics_private_symbols_mac.h
new file mode 100644
index 0000000..0342d6f
--- /dev/null
+++ b/webkit/glue/plugins/coregraphics_private_symbols_mac.h
@@ -0,0 +1,27 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_COREGRAPHICS_PRIVATE_SYMBOLS_MAC_H_
+#define WEBKIT_GLUE_PLUGINS_COREGRAPHICS_PRIVATE_SYMBOLS_MAC_H_
+
+// These are CoreGraphics SPI, verified to exist in both 10.5 and 10.6.
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Copies the contents of the window with id |wid| into the given rect in the
+// given context
+OSStatus CGContextCopyWindowCaptureContentsToRect(
+ CGContextRef, CGRect, int cid, int wid, int unknown);
+
+// Returns the connection ID we need for the third argument to
+// CGContextCopyWindowCaptureContentsToRect
+int _CGSDefaultConnection(void);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // WEBKIT_GLUE_PLUGINS_COREGRAPHICS_PRIVATE_SYMBOLS_MAC_H_
diff --git a/webkit/glue/plugins/default_plugin_shared.h b/webkit/glue/plugins/default_plugin_shared.h
new file mode 100644
index 0000000..79d06b3
--- /dev/null
+++ b/webkit/glue/plugins/default_plugin_shared.h
@@ -0,0 +1,31 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Thes file contains stuff that should be shared among projects that do some
+// special handling with default plugin
+
+#ifndef WEBKIT_GLUE_PLUGINS_DEFAULT_PLUGIN_SHARED_H
+#define WEBKIT_GLUE_PLUGINS_DEFAULT_PLUGIN_SHARED_H
+
+namespace default_plugin {
+
+// We use the NPNGetValue host function to send notification message to host.
+// This corresponds to NPNVariable defined in npapi.h, and should be chosen so
+// as to not overlap values if NPAPI is updated.
+
+const int kMissingPluginStatusStart = 5000;
+
+enum MissingPluginStatus {
+ MISSING_PLUGIN_AVAILABLE,
+ MISSING_PLUGIN_USER_STARTED_DOWNLOAD
+};
+
+#if defined(OS_WIN)
+#include <windows.h>
+const int kInstallMissingPluginMessage = WM_APP + 117;
+#endif
+
+} // namespace default_plugin
+
+#endif // WEBKIT_GLUE_PLUGINS_DEFAULT_PLUGIN_SHARED_H
diff --git a/webkit/glue/plugins/gtk_plugin_container.cc b/webkit/glue/plugins/gtk_plugin_container.cc
new file mode 100644
index 0000000..c80bbf1
--- /dev/null
+++ b/webkit/glue/plugins/gtk_plugin_container.cc
@@ -0,0 +1,85 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/gtk_plugin_container.h"
+
+#include <gtk/gtk.h>
+
+#include "base/basictypes.h"
+
+namespace {
+
+// NOTE: This class doesn't have constructors/destructors, it is created
+// through GLib's object management.
+class GtkPluginContainer : public GtkSocket {
+ public:
+ // Sets the requested size of the widget.
+ void set_size(int width, int height) {
+ width_ = width;
+ height_ = height;
+ }
+
+ // Casts a widget into a GtkPluginContainer, after checking the type.
+ template <class T>
+ static GtkPluginContainer *CastChecked(T *instance) {
+ return G_TYPE_CHECK_INSTANCE_CAST(instance, GetType(), GtkPluginContainer);
+ }
+
+ // Create and register our custom container type with GTK.
+ static GType GetType() {
+ static GType type = 0; // We only want to register our type once.
+ if (!type) {
+ static const GTypeInfo info = {
+ sizeof(GtkSocketClass),
+ NULL, NULL,
+ static_cast<GClassInitFunc>(&ClassInit),
+ NULL, NULL,
+ sizeof(GtkPluginContainer),
+ 0, &InstanceInit,
+ };
+ type = g_type_register_static(GTK_TYPE_SOCKET,
+ "GtkPluginContainer",
+ &info,
+ static_cast<GTypeFlags>(0));
+ }
+ return type;
+ }
+
+ // Implementation of the class initializer.
+ static void ClassInit(gpointer klass, gpointer class_data_unusued) {
+ GtkWidgetClass* widget_class = reinterpret_cast<GtkWidgetClass*>(klass);
+ widget_class->size_request = &HandleSizeRequest;
+ }
+
+ // Implementation of the instance initializer (constructor).
+ static void InstanceInit(GTypeInstance *instance, gpointer klass) {
+ GtkPluginContainer *container = CastChecked(instance);
+ container->set_size(0, 0);
+ }
+
+ // Report our allocation size during size requisition.
+ static void HandleSizeRequest(GtkWidget* widget,
+ GtkRequisition* requisition) {
+ GtkPluginContainer *container = CastChecked(widget);
+ requisition->width = container->width_;
+ requisition->height = container->height_;
+ }
+
+ int width_;
+ int height_;
+ DISALLOW_IMPLICIT_CONSTRUCTORS(GtkPluginContainer);
+};
+
+} // anonymous namespace
+
+// Create a new instance of our GTK widget object.
+GtkWidget* gtk_plugin_container_new() {
+ return GTK_WIDGET(g_object_new(GtkPluginContainer::GetType(), NULL));
+}
+
+void gtk_plugin_container_set_size(GtkWidget *widget, int width, int height) {
+ GtkPluginContainer::CastChecked(widget)->set_size(width, height);
+ // Signal the parent that the size request has changed.
+ gtk_widget_queue_resize_no_redraw(widget);
+}
diff --git a/webkit/glue/plugins/gtk_plugin_container.h b/webkit/glue/plugins/gtk_plugin_container.h
new file mode 100644
index 0000000..eed6b94
--- /dev/null
+++ b/webkit/glue/plugins/gtk_plugin_container.h
@@ -0,0 +1,26 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_GTK_PLUGIN_CONTAINER_H_
+#define WEBKIT_GLUE_PLUGINS_GTK_PLUGIN_CONTAINER_H_
+
+// Windowed plugins are embedded via XEmbed, which is implemented by
+// GtkPlug/GtkSocket. But we want to control sizing and positioning
+// directly, so we need a subclass of GtkSocket that sidesteps the
+// size_request handler.
+//
+// The custom size_request handler just reports the size set by
+// gtk_plugin_container_set_size.
+
+typedef struct _GtkWidget GtkWidget;
+
+// Return a new GtkPluginContainer.
+// Intentionally GTK-style here since we're creating a custom GTK widget.
+// This is a GtkSocket subclass; see its documentation for available methods.
+GtkWidget* gtk_plugin_container_new();
+
+// Sets the size of the GtkPluginContainer.
+void gtk_plugin_container_set_size(GtkWidget *widget, int width, int height);
+
+#endif // WEBKIT_GLUE_PLUGINS_GTK_PLUGIN_CONTAINER_H_
diff --git a/webkit/glue/plugins/gtk_plugin_container_manager.cc b/webkit/glue/plugins/gtk_plugin_container_manager.cc
new file mode 100644
index 0000000..9d9ee4b
--- /dev/null
+++ b/webkit/glue/plugins/gtk_plugin_container_manager.cc
@@ -0,0 +1,147 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/gtk_plugin_container_manager.h"
+
+#include <gtk/gtk.h>
+
+#include "base/logging.h"
+#include "gfx/gtk_util.h"
+#include "webkit/glue/plugins/gtk_plugin_container.h"
+#include "webkit/glue/plugins/webplugin.h"
+
+GtkWidget* GtkPluginContainerManager::CreatePluginContainer(
+ gfx::PluginWindowHandle id) {
+ DCHECK(host_widget_);
+ GtkWidget *widget = gtk_plugin_container_new();
+ plugin_window_to_widget_map_.insert(std::make_pair(id, widget));
+
+ // The Realize callback is responsible for adding the plug into the socket.
+ // The reason is 2-fold:
+ // - the plug can't be added until the socket is realized, but this may not
+ // happen until the socket is attached to a top-level window, which isn't the
+ // case for background tabs.
+ // - when dragging tabs, the socket gets unrealized, which breaks the XEMBED
+ // connection. We need to make it again when the tab is reattached, and the
+ // socket gets realized again.
+ //
+ // Note, the RealizeCallback relies on the plugin_window_to_widget_map_ to
+ // have the mapping.
+ g_signal_connect(widget, "realize",
+ G_CALLBACK(RealizeCallback), this);
+
+ // Don't destroy the widget when the plug is removed.
+ g_signal_connect(widget, "plug-removed",
+ G_CALLBACK(gtk_true), NULL);
+
+ gtk_container_add(GTK_CONTAINER(host_widget_), widget);
+ gtk_widget_show(widget);
+
+ return widget;
+}
+
+void GtkPluginContainerManager::DestroyPluginContainer(
+ gfx::PluginWindowHandle id) {
+ DCHECK(host_widget_);
+ GtkWidget* widget = MapIDToWidget(id);
+ if (widget)
+ gtk_widget_destroy(widget);
+
+ plugin_window_to_widget_map_.erase(id);
+}
+
+void GtkPluginContainerManager::MovePluginContainer(
+ const webkit_glue::WebPluginGeometry& move) {
+ DCHECK(host_widget_);
+ GtkWidget *widget = MapIDToWidget(move.window);
+ if (!widget)
+ return;
+
+ DCHECK(!GTK_WIDGET_NO_WINDOW(widget));
+
+ if (!move.visible) {
+ gtk_widget_hide(widget);
+ return;
+ }
+
+ DCHECK(GTK_WIDGET_REALIZED(widget));
+ gtk_widget_show(widget);
+
+ if (!move.rects_valid)
+ return;
+
+ GdkRectangle clip_rect = move.clip_rect.ToGdkRectangle();
+ GdkRegion* clip_region = gdk_region_rectangle(&clip_rect);
+ gfx::SubtractRectanglesFromRegion(clip_region, move.cutout_rects);
+ gdk_window_shape_combine_region(widget->window, clip_region, 0, 0);
+ gdk_region_destroy(clip_region);
+
+ // Update the window position. Resizing is handled by WebPluginDelegate.
+ // TODO(deanm): Verify that we only need to move and not resize.
+ // TODO(evanm): we should cache the last shape and position and skip all
+ // of this business in the common case where nothing has changed.
+ int current_x, current_y;
+
+ // Until the above TODO is resolved, we can grab the last position
+ // off of the GtkFixed with a bit of hackery.
+ GValue value = {0};
+ g_value_init(&value, G_TYPE_INT);
+ gtk_container_child_get_property(GTK_CONTAINER(host_widget_), widget,
+ "x", &value);
+ current_x = g_value_get_int(&value);
+ gtk_container_child_get_property(GTK_CONTAINER(host_widget_), widget,
+ "y", &value);
+ current_y = g_value_get_int(&value);
+ g_value_unset(&value);
+
+ if (move.window_rect.x() != current_x ||
+ move.window_rect.y() != current_y) {
+ // Calling gtk_fixed_move unnecessarily is a no-no, as it causes the
+ // parent window to repaint!
+ gtk_fixed_move(GTK_FIXED(host_widget_),
+ widget,
+ move.window_rect.x(),
+ move.window_rect.y());
+ }
+
+ gtk_plugin_container_set_size(widget,
+ move.window_rect.width(),
+ move.window_rect.height());
+}
+
+GtkWidget* GtkPluginContainerManager::MapIDToWidget(
+ gfx::PluginWindowHandle id) {
+ PluginWindowToWidgetMap::const_iterator i =
+ plugin_window_to_widget_map_.find(id);
+ if (i != plugin_window_to_widget_map_.end())
+ return i->second;
+
+ LOG(ERROR) << "Request for widget host for unknown window id " << id;
+
+ return NULL;
+}
+
+gfx::PluginWindowHandle GtkPluginContainerManager::MapWidgetToID(
+ GtkWidget* widget) {
+ for (PluginWindowToWidgetMap::const_iterator i =
+ plugin_window_to_widget_map_.begin();
+ i != plugin_window_to_widget_map_.end(); ++i) {
+ if (i->second == widget)
+ return i->first;
+ }
+
+ LOG(ERROR) << "Request for id for unknown widget";
+ return 0;
+}
+
+// static
+void GtkPluginContainerManager::RealizeCallback(GtkWidget* widget,
+ void* user_data) {
+ GtkPluginContainerManager* plugin_container_manager =
+ static_cast<GtkPluginContainerManager*>(user_data);
+
+ gfx::PluginWindowHandle id = plugin_container_manager->MapWidgetToID(widget);
+ if (id)
+ gtk_socket_add_id(GTK_SOCKET(widget), id);
+}
diff --git a/webkit/glue/plugins/gtk_plugin_container_manager.h b/webkit/glue/plugins/gtk_plugin_container_manager.h
new file mode 100644
index 0000000..c33099d
--- /dev/null
+++ b/webkit/glue/plugins/gtk_plugin_container_manager.h
@@ -0,0 +1,56 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_GTK_PLUGIN_CONTAINER_MANAGER_H_
+#define WEBKIT_GLUE_PLUGINS_GTK_PLUGIN_CONTAINER_MANAGER_H_
+
+#include <gtk/gtk.h>
+#include <map>
+
+#include "gfx/native_widget_types.h"
+
+typedef struct _GtkWidget GtkWidget;
+
+namespace webkit_glue {
+struct WebPluginGeometry;
+}
+
+// Helper class that creates and manages plugin containers (GtkSocket).
+class GtkPluginContainerManager {
+ public:
+ GtkPluginContainerManager() : host_widget_(NULL) { }
+
+ // Sets the widget that will host the plugin containers. Must be a GtkFixed.
+ void set_host_widget(GtkWidget *widget) { host_widget_ = widget; }
+
+ // Creates a new plugin container, for a given plugin XID.
+ GtkWidget* CreatePluginContainer(gfx::PluginWindowHandle id);
+
+ // Destroys a plugin container, given the plugin XID.
+ void DestroyPluginContainer(gfx::PluginWindowHandle id);
+
+ // Takes an update from WebKit about a plugin's position and side and moves
+ // the plugin accordingly.
+ void MovePluginContainer(const webkit_glue::WebPluginGeometry& move);
+
+ private:
+ // Maps a plugin XID to the corresponding container widget.
+ GtkWidget* MapIDToWidget(gfx::PluginWindowHandle id);
+
+ // Maps a container widget to the corresponding plugin XID.
+ gfx::PluginWindowHandle MapWidgetToID(GtkWidget* widget);
+
+ // Callback for when the plugin container gets realized, at which point it
+ // plugs the plugin XID.
+ static void RealizeCallback(GtkWidget *widget, void *user_data);
+
+ // Parent of the plugin containers.
+ GtkWidget* host_widget_;
+
+ // A map that associates plugin containers to the plugin XID.
+ typedef std::map<gfx::PluginWindowHandle, GtkWidget*> PluginWindowToWidgetMap;
+ PluginWindowToWidgetMap plugin_window_to_widget_map_;
+};
+
+#endif // WEBKIT_GLUE_PLUGINS_GTK_PLUGIN_CONTAINER_MANAGER_H_
diff --git a/webkit/glue/plugins/mac_accelerated_surface_container.cc b/webkit/glue/plugins/mac_accelerated_surface_container.cc
new file mode 100644
index 0000000..8eca9d2
--- /dev/null
+++ b/webkit/glue/plugins/mac_accelerated_surface_container.cc
@@ -0,0 +1,158 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/mac_accelerated_surface_container.h"
+
+#include "app/surface/io_surface_support_mac.h"
+#include "base/logging.h"
+#include "webkit/glue/plugins/mac_accelerated_surface_container_manager.h"
+#include "webkit/glue/plugins/webplugin.h"
+
+MacAcceleratedSurfaceContainer::MacAcceleratedSurfaceContainer()
+ : x_(0),
+ y_(0),
+ surface_(NULL),
+ width_(0),
+ height_(0),
+ texture_(0) {
+}
+
+MacAcceleratedSurfaceContainer::~MacAcceleratedSurfaceContainer() {
+ ReleaseIOSurface();
+}
+
+void MacAcceleratedSurfaceContainer::ReleaseIOSurface() {
+ if (surface_) {
+ CFRelease(surface_);
+ surface_ = NULL;
+ }
+}
+
+void MacAcceleratedSurfaceContainer::SetSizeAndIOSurface(
+ int32 width,
+ int32 height,
+ uint64 io_surface_identifier,
+ MacAcceleratedSurfaceContainerManager* manager) {
+ ReleaseIOSurface();
+ IOSurfaceSupport* io_surface_support = IOSurfaceSupport::Initialize();
+ if (io_surface_support) {
+ surface_ = io_surface_support->IOSurfaceLookup(
+ static_cast<uint32>(io_surface_identifier));
+ EnqueueTextureForDeletion(manager);
+ width_ = width;
+ height_ = height;
+ }
+}
+
+void MacAcceleratedSurfaceContainer::SetSizeAndTransportDIB(
+ int32 width,
+ int32 height,
+ TransportDIB::Handle transport_dib,
+ MacAcceleratedSurfaceContainerManager* manager) {
+ if (TransportDIB::is_valid(transport_dib)) {
+ transport_dib_.reset(TransportDIB::Map(transport_dib));
+ EnqueueTextureForDeletion(manager);
+ width_ = width;
+ height_ = height;
+ }
+}
+
+void MacAcceleratedSurfaceContainer::MoveTo(
+ const webkit_glue::WebPluginGeometry& geom) {
+ x_ = geom.window_rect.x();
+ y_ = geom.window_rect.y();
+ // TODO(kbr): may need to pay attention to cutout rects.
+ clipRect_ = geom.clip_rect;
+}
+
+void MacAcceleratedSurfaceContainer::Draw(CGLContextObj context) {
+ IOSurfaceSupport* io_surface_support = IOSurfaceSupport::Initialize();
+ GLenum target = GL_TEXTURE_RECTANGLE_ARB;
+ if (!texture_) {
+ if ((io_surface_support && !surface_) ||
+ (!io_surface_support && !transport_dib_.get()))
+ return;
+ glGenTextures(1, &texture_);
+ glBindTexture(target, texture_);
+ glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameterf(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ // When using an IOSurface, the texture does not need to be repeatedly
+ // uploaded, so bind the IOSurface once during texture gen in this case.
+ if (io_surface_support) {
+ DCHECK(surface_);
+ // Don't think we need to identify a plane.
+ GLuint plane = 0;
+ io_surface_support->CGLTexImageIOSurface2D(context,
+ target,
+ GL_RGBA,
+ width_,
+ height_,
+ GL_BGRA,
+ GL_UNSIGNED_INT_8_8_8_8_REV,
+ surface_,
+ plane);
+ } else {
+ // Reserve space on the card for the actual texture upload, done with the
+ // glTexSubImage2D() call, below.
+ glTexImage2D(target,
+ 0, // mipmap level 0
+ GL_RGBA, // internal format
+ width_,
+ height_,
+ 0, // no border
+ GL_BGRA, // The GPU plugin read BGRA pixels
+ GL_UNSIGNED_INT_8_8_8_8_REV,
+ NULL); // No data, this call just reserves room.
+ }
+ }
+
+ // If using TransportDIBs, the texture needs to be uploaded every frame.
+ if (transport_dib_.get() != NULL) {
+ void* pixel_memory = transport_dib_->memory();
+ if (pixel_memory) {
+ glBindTexture(target, texture_);
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // Needed for NPOT textures.
+ glTexSubImage2D(target,
+ 0, // mipmap level 0
+ 0, // x-offset
+ 0, // y-offset
+ width_,
+ height_,
+ GL_BGRA, // The GPU plugin gave us BGRA pixels
+ GL_UNSIGNED_INT_8_8_8_8_REV,
+ pixel_memory);
+ }
+ }
+
+ if (texture_) {
+ // TODO(kbr): convert this to use only OpenGL ES 2.0 functionality
+ glBindTexture(target, texture_);
+ glEnable(target);
+ glBegin(GL_TRIANGLE_STRIP);
+ // TODO(kbr): may need to pay attention to cutout rects.
+ int clipX = clipRect_.x();
+ int clipY = clipRect_.y();
+ int clipWidth = clipRect_.width();
+ int clipHeight = clipRect_.height();
+ int x = x_ + clipX;
+ int y = y_ + clipY;
+ glTexCoord2f(clipX, height_ - clipY);
+ glVertex3f(x, y, 0);
+ glTexCoord2f(clipX + clipWidth, height_ - clipY);
+ glVertex3f(x + clipWidth, y, 0);
+ glTexCoord2f(clipX, height_ - clipY - clipHeight);
+ glVertex3f(x, y + clipHeight, 0);
+ glTexCoord2f(clipX + clipWidth, height_ - clipY - clipHeight);
+ glVertex3f(x + clipWidth, y + clipHeight, 0);
+ glEnd();
+ glDisable(target);
+ }
+}
+
+void MacAcceleratedSurfaceContainer::EnqueueTextureForDeletion(
+ MacAcceleratedSurfaceContainerManager* manager) {
+ manager->EnqueueTextureForDeletion(texture_);
+ texture_ = 0;
+}
+
diff --git a/webkit/glue/plugins/mac_accelerated_surface_container.h b/webkit/glue/plugins/mac_accelerated_surface_container.h
new file mode 100644
index 0000000..0fd1793
--- /dev/null
+++ b/webkit/glue/plugins/mac_accelerated_surface_container.h
@@ -0,0 +1,110 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_MAC_ACCELERATED_SURFACE_CONTAINER_H_
+#define WEBKIT_GLUE_PLUGINS_MAC_ACCELERATED_SURFACE_CONTAINER_H_
+
+// The "GPU plugin" is currently implemented as a special kind of
+// NPAPI plugin to provide high-performance on-screen 3D rendering for
+// Pepper 3D.
+//
+// On Windows and X11 platforms the GPU plugin relies on cross-process
+// parenting of windows, which is not supported via any public APIs in
+// the Mac OS X window system.
+//
+// To achieve full hardware acceleration we use the new IOSurface APIs
+// introduced in Mac OS X 10.6. The GPU plugin's process produces an
+// IOSurface and renders into it using OpenGL. It uses the
+// IOSurfaceGetID and IOSurfaceLookup APIs to pass a reference to this
+// surface to the browser process for on-screen rendering. The GPU
+// plugin essentially looks like a windowless plugin; the browser
+// process gets all of the mouse events, because the plugin process
+// does not have an on-screen window.
+//
+// This class encapsulates some of the management of these data
+// structures, in conjunction with the MacAcceleratedSurfaceContainerManager.
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <OpenGL/OpenGL.h>
+
+#include "app/gfx/native_widget_types.h"
+#include "app/surface/transport_dib.h"
+#include "base/basictypes.h"
+#include "base/scoped_ptr.h"
+#include "base/gfx/rect.h"
+
+namespace webkit_glue {
+struct WebPluginGeometry;
+}
+
+class MacAcceleratedSurfaceContainerManager;
+
+class MacAcceleratedSurfaceContainer {
+ public:
+ MacAcceleratedSurfaceContainer();
+ virtual ~MacAcceleratedSurfaceContainer();
+
+ // Sets the backing store and size of this accelerated surface container.
+ // There are two versions: the IOSurface version is used on systems where the
+ // IOSurface API is supported (Mac OS X 10.6 and later); the TransportDIB is
+ // used on Mac OS X 10.5 and earlier.
+ void SetSizeAndIOSurface(int32 width,
+ int32 height,
+ uint64 io_surface_identifier,
+ MacAcceleratedSurfaceContainerManager* manager);
+ void SetSizeAndTransportDIB(int32 width,
+ int32 height,
+ TransportDIB::Handle transport_dib,
+ MacAcceleratedSurfaceContainerManager* manager);
+
+ // Tells the accelerated surface container that it has moved relative to the
+ // origin of the window, for example because of a scroll event.
+ void MoveTo(const webkit_glue::WebPluginGeometry& geom);
+
+ // Draws this accelerated surface's contents, texture mapped onto a quad in
+ // the given OpenGL context. TODO(kbr): figure out and define exactly how the
+ // coordinate system will work out.
+ void Draw(CGLContextObj context);
+
+ // Enqueue our texture for later deletion. Call this before deleting
+ // this object.
+ void EnqueueTextureForDeletion(MacAcceleratedSurfaceContainerManager* manager);
+
+ private:
+ // The x and y coordinates of the plugin window on the web page.
+ int x_;
+ int y_;
+
+ void ReleaseIOSurface();
+
+ // The IOSurfaceRef, if any, that has been handed from the GPU
+ // plugin process back to the browser process for drawing.
+ // This is held as a CFTypeRef because we can't refer to the
+ // IOSurfaceRef type when building on 10.5.
+ CFTypeRef surface_;
+
+ // The TransportDIB which is used in pre-10.6 systems where the IOSurface
+ // API is not supported. This is a weak reference to the actual TransportDIB
+ // whic is owned by the GPU process.
+ scoped_ptr<TransportDIB> transport_dib_;
+
+ // The width and height of the surface.
+ int32 width_;
+ int32 height_;
+
+ // The clip rectangle, relative to the (x_, y_) origin.
+ gfx::Rect clipRect_;
+
+ // The "live" OpenGL texture referring to this IOSurfaceRef. Note
+ // that per the CGLTexImageIOSurface2D API we do not need to
+ // explicitly update this texture's contents once created. All we
+ // need to do is ensure it is re-bound before attempting to draw
+ // with it.
+ GLuint texture_;
+
+ DISALLOW_COPY_AND_ASSIGN(MacAcceleratedSurfaceContainer);
+};
+
+#endif // WEBKIT_GLUE_PLUGINS_MAC_ACCELERATED_SURFACE_CONTAINER_H_
+
diff --git a/webkit/glue/plugins/mac_accelerated_surface_container_manager.cc b/webkit/glue/plugins/mac_accelerated_surface_container_manager.cc
new file mode 100644
index 0000000..635348f
--- /dev/null
+++ b/webkit/glue/plugins/mac_accelerated_surface_container_manager.cc
@@ -0,0 +1,103 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/mac_accelerated_surface_container_manager.h"
+
+#include "base/logging.h"
+#include "webkit/glue/plugins/mac_accelerated_surface_container.h"
+#include "webkit/glue/plugins/webplugin.h"
+
+MacAcceleratedSurfaceContainerManager::MacAcceleratedSurfaceContainerManager()
+ : current_id_(0) {
+}
+
+gfx::PluginWindowHandle
+MacAcceleratedSurfaceContainerManager::AllocateFakePluginWindowHandle() {
+ MacAcceleratedSurfaceContainer* container =
+ new MacAcceleratedSurfaceContainer();
+ gfx::PluginWindowHandle res =
+ static_cast<gfx::PluginWindowHandle>(++current_id_);
+ plugin_window_to_container_map_.insert(std::make_pair(res, container));
+ return res;
+}
+
+void MacAcceleratedSurfaceContainerManager::DestroyFakePluginWindowHandle(
+ gfx::PluginWindowHandle id) {
+ MacAcceleratedSurfaceContainer* container = MapIDToContainer(id);
+ if (container)
+ delete container;
+ plugin_window_to_container_map_.erase(id);
+}
+
+void MacAcceleratedSurfaceContainerManager::SetSizeAndIOSurface(
+ gfx::PluginWindowHandle id,
+ int32 width,
+ int32 height,
+ uint64 io_surface_identifier) {
+ MacAcceleratedSurfaceContainer* container = MapIDToContainer(id);
+ if (container)
+ container->SetSizeAndIOSurface(width, height,
+ io_surface_identifier, this);
+}
+
+void MacAcceleratedSurfaceContainerManager::SetSizeAndTransportDIB(
+ gfx::PluginWindowHandle id,
+ int32 width,
+ int32 height,
+ TransportDIB::Handle transport_dib) {
+ MacAcceleratedSurfaceContainer* container = MapIDToContainer(id);
+ if (container)
+ container->SetSizeAndTransportDIB(width, height,
+ transport_dib, this);
+}
+
+void MacAcceleratedSurfaceContainerManager::MovePluginContainer(
+ const webkit_glue::WebPluginGeometry& move) {
+ MacAcceleratedSurfaceContainer* container = MapIDToContainer(move.window);
+ if (container)
+ container->MoveTo(move);
+}
+
+void MacAcceleratedSurfaceContainerManager::Draw(CGLContextObj context) {
+ glClearColor(0, 0, 0, 0);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ GLenum target = GL_TEXTURE_RECTANGLE_ARB;
+ glTexEnvi(target, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+
+ for (PluginWindowToContainerMap::const_iterator i =
+ plugin_window_to_container_map_.begin();
+ i != plugin_window_to_container_map_.end(); ++i) {
+ MacAcceleratedSurfaceContainer* container = i->second;
+ container->Draw(context);
+ }
+
+ // Unbind any texture from the texture target to ensure that the
+ // next time through we will have to re-bind the texture and thereby
+ // pick up modifications from the other process.
+ glBindTexture(target, 0);
+
+ glFlush();
+}
+
+void MacAcceleratedSurfaceContainerManager::EnqueueTextureForDeletion(
+ GLuint texture) {
+ if (texture) {
+ textures_pending_deletion_.push_back(texture);
+ }
+}
+
+MacAcceleratedSurfaceContainer*
+ MacAcceleratedSurfaceContainerManager::MapIDToContainer(
+ gfx::PluginWindowHandle id) {
+ PluginWindowToContainerMap::const_iterator i =
+ plugin_window_to_container_map_.find(id);
+ if (i != plugin_window_to_container_map_.end())
+ return i->second;
+
+ LOG(ERROR) << "Request for plugin container for unknown window id " << id;
+
+ return NULL;
+}
+
diff --git a/webkit/glue/plugins/mac_accelerated_surface_container_manager.h b/webkit/glue/plugins/mac_accelerated_surface_container_manager.h
new file mode 100644
index 0000000..da86e36
--- /dev/null
+++ b/webkit/glue/plugins/mac_accelerated_surface_container_manager.h
@@ -0,0 +1,78 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_MAC_ACCELERATED_SURFACE_CONTAINER_MANAGER_H_
+#define WEBKIT_GLUE_PLUGINS_MAC_ACCELERATED_SURFACE_CONTAINER_MANAGER_H_
+
+#include <OpenGL/OpenGL.h>
+#include <map>
+#include <vector>
+
+#include "app/gfx/native_widget_types.h"
+#include "app/surface/transport_dib.h"
+#include "base/basictypes.h"
+
+namespace webkit_glue {
+struct WebPluginGeometry;
+}
+
+class MacAcceleratedSurfaceContainer;
+
+// Helper class that manages the backing store and on-screen rendering
+// of instances of the GPU plugin on the Mac.
+class MacAcceleratedSurfaceContainerManager {
+ public:
+ MacAcceleratedSurfaceContainerManager();
+
+ // Allocates a new "fake" PluginWindowHandle, which is used as the
+ // key for the other operations.
+ gfx::PluginWindowHandle AllocateFakePluginWindowHandle();
+
+ // Destroys a fake PluginWindowHandle and associated storage.
+ void DestroyFakePluginWindowHandle(gfx::PluginWindowHandle id);
+
+ // Sets the size and backing store of the plugin instance. There are two
+ // versions: the IOSurface version is used on systems where the IOSurface
+ // API is supported (Mac OS X 10.6 and later); the TransportDIB is used on
+ // Mac OS X 10.5 and earlier.
+ void SetSizeAndIOSurface(gfx::PluginWindowHandle id,
+ int32 width,
+ int32 height,
+ uint64 io_surface_identifier);
+ void SetSizeAndTransportDIB(gfx::PluginWindowHandle id,
+ int32 width,
+ int32 height,
+ TransportDIB::Handle transport_dib);
+
+ // Takes an update from WebKit about a plugin's position and size and moves
+ // the plugin accordingly.
+ void MovePluginContainer(const webkit_glue::WebPluginGeometry& move);
+
+ // Draws all of the managed plugin containers into the given OpenGL
+ // context, which must already be current.
+ void Draw(CGLContextObj context);
+
+ // Called by the container to enqueue its OpenGL texture objects for
+ // deletion.
+ void EnqueueTextureForDeletion(GLuint texture);
+
+ private:
+ uint32 current_id_;
+
+ // Maps a "fake" plugin window handle to the corresponding container.
+ MacAcceleratedSurfaceContainer* MapIDToContainer(gfx::PluginWindowHandle id);
+
+ // A map that associates plugin window handles with their containers.
+ typedef std::map<gfx::PluginWindowHandle, MacAcceleratedSurfaceContainer*>
+ PluginWindowToContainerMap;
+ PluginWindowToContainerMap plugin_window_to_container_map_;
+
+ // A list of OpenGL textures waiting to be deleted
+ std::vector<GLuint> textures_pending_deletion_;
+
+ DISALLOW_COPY_AND_ASSIGN(MacAcceleratedSurfaceContainerManager);
+};
+
+#endif // WEBKIT_GLUE_PLUGINS_MAC_ACCELERATED_SURFACE_CONTAINER_MANAGER_H_
+
diff --git a/webkit/glue/plugins/npapi_extension_thunk.cc b/webkit/glue/plugins/npapi_extension_thunk.cc
new file mode 100644
index 0000000..4779535
--- /dev/null
+++ b/webkit/glue/plugins/npapi_extension_thunk.cc
@@ -0,0 +1,551 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/npapi_extension_thunk.h"
+
+#include "base/logging.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "third_party/npapi/bindings/npapi_extensions.h"
+#include "webkit/glue/plugins/plugin_instance.h"
+#include "webkit/glue/plugins/webplugin.h"
+#include "webkit/glue/plugins/webplugin_delegate.h"
+#include "webkit/glue/webkit_glue.h"
+
+// FindInstance()
+// Finds a PluginInstance from an NPP.
+// The caller must take a reference if needed.
+static NPAPI::PluginInstance* FindInstance(NPP id) {
+ if (id == NULL) {
+ NOTREACHED();
+ return NULL;
+ }
+ return static_cast<NPAPI::PluginInstance*>(id->ndata);
+}
+
+// 2D device API ---------------------------------------------------------------
+
+static NPError Device2DQueryCapability(NPP id, int32_t capability,
+ int32_t* value) {
+ scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
+ if (plugin) {
+ plugin->webplugin()->delegate()->Device2DQueryCapability(capability, value);
+ return NPERR_NO_ERROR;
+ } else {
+ return NPERR_GENERIC_ERROR;
+ }
+}
+
+static NPError Device2DQueryConfig(NPP id,
+ const NPDeviceConfig* request,
+ NPDeviceConfig* obtain) {
+ scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
+ if (plugin) {
+ return plugin->webplugin()->delegate()->Device2DQueryConfig(
+ static_cast<const NPDeviceContext2DConfig*>(request),
+ static_cast<NPDeviceContext2DConfig*>(obtain));
+ }
+ return NPERR_GENERIC_ERROR;
+}
+
+static NPError Device2DInitializeContext(NPP id,
+ const NPDeviceConfig* config,
+ NPDeviceContext* context) {
+ scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
+ if (plugin) {
+ return plugin->webplugin()->delegate()->Device2DInitializeContext(
+ static_cast<const NPDeviceContext2DConfig*>(config),
+ static_cast<NPDeviceContext2D*>(context));
+ }
+ return NPERR_GENERIC_ERROR;
+}
+
+static NPError Device2DSetStateContext(NPP id,
+ NPDeviceContext* context,
+ int32_t state,
+ intptr_t value) {
+ scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
+ if (plugin) {
+ return plugin->webplugin()->delegate()->Device2DSetStateContext(
+ static_cast<NPDeviceContext2D*>(context), state, value);
+ }
+ return NPERR_GENERIC_ERROR;
+}
+
+static NPError Device2DGetStateContext(NPP id,
+ NPDeviceContext* context,
+ int32_t state,
+ intptr_t* value) {
+ scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
+ if (plugin) {
+ return plugin->webplugin()->delegate()->Device2DGetStateContext(
+ static_cast<NPDeviceContext2D*>(context), state, value);
+ }
+ return NPERR_GENERIC_ERROR;
+}
+
+static NPError Device2DFlushContext(NPP id,
+ NPDeviceContext* context,
+ NPDeviceFlushContextCallbackPtr callback,
+ void* user_data) {
+ scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
+ if (plugin) {
+ NPError err = plugin->webplugin()->delegate()->Device2DFlushContext(
+ id, static_cast<NPDeviceContext2D*>(context), callback, user_data);
+
+ // Invoke the callback to inform the caller the work was done.
+ // TODO(brettw) this is probably not how we want this to work, this should
+ // happen when the frame is painted so the plugin knows when it can draw
+ // the next frame.
+ if (callback != NULL)
+ (*callback)(id, context, err, user_data);
+
+ // Return any errors.
+ return err;
+ }
+ return NPERR_GENERIC_ERROR;
+}
+
+static NPError Device2DDestroyContext(NPP id,
+ NPDeviceContext* context) {
+ scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
+ if (plugin) {
+ return plugin->webplugin()->delegate()->Device2DDestroyContext(
+ static_cast<NPDeviceContext2D*>(context));
+ }
+ return NPERR_GENERIC_ERROR;
+}
+
+static NPError Device2DCreateBuffer(NPP id,
+ NPDeviceContext* context,
+ size_t size,
+ int32_t* buffer_id) {
+ return NPERR_GENERIC_ERROR;
+}
+
+static NPError Device2DDestroyBuffer(NPP id,
+ NPDeviceContext* context,
+ int32_t buffer_id) {
+ return NPERR_GENERIC_ERROR;
+}
+
+static NPError Device2DMapBuffer(NPP id,
+ NPDeviceContext* context,
+ int32_t buffer_id,
+ NPDeviceBuffer* buffer) {
+ return NPERR_GENERIC_ERROR;
+}
+
+// 3D device API ---------------------------------------------------------------
+
+static NPError Device3DQueryCapability(NPP id, int32_t capability,
+ int32_t* value) {
+ scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
+ if (plugin) {
+ plugin->webplugin()->delegate()->Device3DQueryCapability(capability, value);
+ return NPERR_NO_ERROR;
+ } else {
+ return NPERR_GENERIC_ERROR;
+ }
+}
+
+static NPError Device3DQueryConfig(NPP id,
+ const NPDeviceConfig* request,
+ NPDeviceConfig* obtain) {
+ scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
+ if (plugin) {
+ return plugin->webplugin()->delegate()->Device3DQueryConfig(
+ static_cast<const NPDeviceContext3DConfig*>(request),
+ static_cast<NPDeviceContext3DConfig*>(obtain));
+ }
+ return NPERR_GENERIC_ERROR;
+}
+
+static NPError Device3DInitializeContext(NPP id,
+ const NPDeviceConfig* config,
+ NPDeviceContext* context) {
+ scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
+ if (plugin) {
+ return plugin->webplugin()->delegate()->Device3DInitializeContext(
+ static_cast<const NPDeviceContext3DConfig*>(config),
+ static_cast<NPDeviceContext3D*>(context));
+ }
+ return NPERR_GENERIC_ERROR;
+}
+
+static NPError Device3DSetStateContext(NPP id,
+ NPDeviceContext* context,
+ int32_t state,
+ intptr_t value) {
+ scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
+ if (plugin) {
+ return plugin->webplugin()->delegate()->Device3DSetStateContext(
+ static_cast<NPDeviceContext3D*>(context), state, value);
+ }
+ return NPERR_GENERIC_ERROR;
+}
+
+static NPError Device3DGetStateContext(NPP id,
+ NPDeviceContext* context,
+ int32_t state,
+ intptr_t* value) {
+ scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
+ if (plugin) {
+ return plugin->webplugin()->delegate()->Device3DGetStateContext(
+ static_cast<NPDeviceContext3D*>(context), state, value);
+ }
+ return NPERR_GENERIC_ERROR;
+}
+
+static NPError Device3DFlushContext(NPP id,
+ NPDeviceContext* context,
+ NPDeviceFlushContextCallbackPtr callback,
+ void* user_data) {
+ scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
+ if (plugin) {
+ return plugin->webplugin()->delegate()->Device3DFlushContext(
+ id, static_cast<NPDeviceContext3D*>(context), callback, user_data);
+ }
+ return NPERR_GENERIC_ERROR;
+}
+
+static NPError Device3DDestroyContext(NPP id,
+ NPDeviceContext* context) {
+ scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
+ if (plugin) {
+ return plugin->webplugin()->delegate()->Device3DDestroyContext(
+ static_cast<NPDeviceContext3D*>(context));
+ }
+ return NPERR_GENERIC_ERROR;
+}
+
+static NPError Device3DCreateBuffer(NPP id,
+ NPDeviceContext* context,
+ size_t size,
+ int32_t* buffer_id) {
+ scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
+ if (plugin) {
+ return plugin->webplugin()->delegate()->Device3DCreateBuffer(
+ static_cast<NPDeviceContext3D*>(context), size, buffer_id);
+ }
+ return NPERR_GENERIC_ERROR;
+}
+
+static NPError Device3DDestroyBuffer(NPP id,
+ NPDeviceContext* context,
+ int32_t buffer_id) {
+ scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
+ if (plugin) {
+ return plugin->webplugin()->delegate()->Device3DDestroyBuffer(
+ static_cast<NPDeviceContext3D*>(context), buffer_id);
+ }
+ return NPERR_GENERIC_ERROR;
+}
+
+static NPError Device3DMapBuffer(NPP id,
+ NPDeviceContext* context,
+ int32_t buffer_id,
+ NPDeviceBuffer* buffer) {
+ scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
+ if (plugin) {
+ return plugin->webplugin()->delegate()->Device3DMapBuffer(
+ static_cast<NPDeviceContext3D*>(context), buffer_id, buffer);
+ }
+ return NPERR_GENERIC_ERROR;
+}
+
+// Experimental 3D device API --------------------------------------------------
+
+static NPError Device3DGetNumConfigs(NPP id, int32_t* num_configs) {
+ scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
+ if (plugin) {
+ return plugin->webplugin()->delegate()->Device3DGetNumConfigs(num_configs);
+ }
+ return NPERR_GENERIC_ERROR;
+}
+
+static NPError Device3DGetConfigAttribs(NPP id,
+ int32_t config,
+ int32_t* attrib_list) {
+ scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
+ if (plugin) {
+ return plugin->webplugin()->delegate()->Device3DGetConfigAttribs(
+ config,
+ attrib_list);
+ }
+ return NPERR_GENERIC_ERROR;
+}
+
+static NPError Device3DCreateContext(NPP id,
+ int32_t config,
+ const int32_t* attrib_list,
+ NPDeviceContext** context) {
+ scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
+ if (plugin) {
+ return plugin->webplugin()->delegate()->Device3DCreateContext(
+ config,
+ attrib_list,
+ reinterpret_cast<NPDeviceContext3D**>(context));
+ }
+ return NPERR_GENERIC_ERROR;
+}
+
+static NPError Device3DSynchronizeContext(
+ NPP id,
+ NPDeviceContext* context,
+ NPDeviceSynchronizationMode mode,
+ const int32_t* input_attrib_list,
+ int32_t* output_attrib_list,
+ NPDeviceSynchronizeContextCallbackPtr callback,
+ void* callback_data) {
+ scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
+ if (plugin) {
+ return plugin->webplugin()->delegate()->Device3DSynchronizeContext(
+ id,
+ static_cast<NPDeviceContext3D*>(context),
+ mode,
+ input_attrib_list,
+ output_attrib_list,
+ callback,
+ callback_data);
+ }
+ return NPERR_GENERIC_ERROR;
+}
+
+static NPError Device3DRegisterCallback(
+ NPP id,
+ NPDeviceContext* context,
+ int32_t callback_type,
+ NPDeviceGenericCallbackPtr callback,
+ void* callback_data) {
+ scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
+ if (plugin) {
+ return plugin->webplugin()->delegate()->Device3DRegisterCallback(
+ id,
+ static_cast<NPDeviceContext3D*>(context),
+ callback_type,
+ callback,
+ callback_data);
+ }
+ return NPERR_GENERIC_ERROR;
+}
+
+// Audio device API ------------------------------------------------------------
+
+static NPError DeviceAudioQueryCapability(NPP id, int32_t capability,
+ int32_t* value) {
+ scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
+ if (plugin) {
+ plugin->webplugin()->delegate()->DeviceAudioQueryCapability(capability,
+ value);
+ return NPERR_NO_ERROR;
+ } else {
+ return NPERR_GENERIC_ERROR;
+ }
+}
+
+static NPError DeviceAudioQueryConfig(NPP id,
+ const NPDeviceConfig* request,
+ NPDeviceConfig* obtain) {
+ scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
+ if (plugin) {
+ return plugin->webplugin()->delegate()->DeviceAudioQueryConfig(
+ static_cast<const NPDeviceContextAudioConfig*>(request),
+ static_cast<NPDeviceContextAudioConfig*>(obtain));
+ }
+ return NPERR_GENERIC_ERROR;
+}
+
+static NPError DeviceAudioInitializeContext(NPP id,
+ const NPDeviceConfig* config,
+ NPDeviceContext* context) {
+ scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
+ if (plugin) {
+ return plugin->webplugin()->delegate()->DeviceAudioInitializeContext(
+ static_cast<const NPDeviceContextAudioConfig*>(config),
+ static_cast<NPDeviceContextAudio*>(context));
+ }
+ return NPERR_GENERIC_ERROR;
+}
+
+static NPError DeviceAudioSetStateContext(NPP id,
+ NPDeviceContext* context,
+ int32_t state,
+ intptr_t value) {
+ scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
+ if (plugin) {
+ return plugin->webplugin()->delegate()->DeviceAudioSetStateContext(
+ static_cast<NPDeviceContextAudio*>(context), state, value);
+ }
+ return NPERR_GENERIC_ERROR;
+}
+
+static NPError DeviceAudioGetStateContext(NPP id,
+ NPDeviceContext* context,
+ int32_t state,
+ intptr_t* value) {
+ scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
+ return plugin->webplugin()->delegate()->DeviceAudioGetStateContext(
+ static_cast<NPDeviceContextAudio*>(context), state, value);
+}
+
+static NPError DeviceAudioFlushContext(NPP id,
+ NPDeviceContext* context,
+ NPDeviceFlushContextCallbackPtr callback,
+ void* user_data) {
+ scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
+ return plugin->webplugin()->delegate()->DeviceAudioFlushContext(
+ id, static_cast<NPDeviceContextAudio*>(context), callback, user_data);
+}
+
+static NPError DeviceAudioDestroyContext(NPP id,
+ NPDeviceContext* context) {
+ scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
+ return plugin->webplugin()->delegate()->DeviceAudioDestroyContext(
+ static_cast<NPDeviceContextAudio*>(context));
+}
+// -----------------------------------------------------------------------------
+
+static NPDevice* AcquireDevice(NPP id, NPDeviceID device_id) {
+ static NPDevice device_2d = {
+ Device2DQueryCapability,
+ Device2DQueryConfig,
+ Device2DInitializeContext,
+ Device2DSetStateContext,
+ Device2DGetStateContext,
+ Device2DFlushContext,
+ Device2DDestroyContext,
+ Device2DCreateBuffer,
+ Device2DDestroyBuffer,
+ Device2DMapBuffer,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ };
+ static NPDevice device_3d = {
+ Device3DQueryCapability,
+ Device3DQueryConfig,
+ Device3DInitializeContext,
+ Device3DSetStateContext,
+ Device3DGetStateContext,
+ Device3DFlushContext,
+ Device3DDestroyContext,
+ Device3DCreateBuffer,
+ Device3DDestroyBuffer,
+ Device3DMapBuffer,
+ Device3DGetNumConfigs,
+ Device3DGetConfigAttribs,
+ Device3DCreateContext,
+ Device3DRegisterCallback,
+ Device3DSynchronizeContext,
+ };
+ static NPDevice device_audio = {
+ DeviceAudioQueryCapability,
+ DeviceAudioQueryConfig,
+ DeviceAudioInitializeContext,
+ DeviceAudioSetStateContext,
+ DeviceAudioGetStateContext,
+ DeviceAudioFlushContext,
+ DeviceAudioDestroyContext,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ };
+
+ switch (device_id) {
+ case NPPepper2DDevice:
+ return const_cast<NPDevice*>(&device_2d);
+ case NPPepper3DDevice:
+ return const_cast<NPDevice*>(&device_3d);
+ case NPPepperAudioDevice:
+ return const_cast<NPDevice*>(&device_audio);
+ default:
+ return NULL;
+ }
+}
+
+static NPError ChooseFile(NPP id,
+ const char* mime_types,
+ NPChooseFileMode mode,
+ NPChooseFileCallback callback,
+ void* user_data) {
+ scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
+ if (!plugin)
+ return NPERR_GENERIC_ERROR;
+
+ if (!plugin->webplugin()->delegate()->ChooseFile(mime_types,
+ static_cast<int>(mode),
+ callback, user_data))
+ return NPERR_GENERIC_ERROR;
+
+ return NPERR_NO_ERROR;
+}
+
+static void NumberOfFindResultsChanged(NPP id, int total, bool final_result) {
+ scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
+ if (plugin) {
+ plugin->webplugin()->delegate()->NumberOfFindResultsChanged(
+ total, final_result);
+ }
+}
+
+static void SelectedFindResultChanged(NPP id, int index) {
+ scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
+ if (plugin)
+ plugin->webplugin()->delegate()->SelectedFindResultChanged(index);
+}
+
+static NPWidgetExtensions* GetWidgetExtensions(NPP id) {
+ scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
+ if (!plugin)
+ return NULL;
+
+ return plugin->webplugin()->delegate()->GetWidgetExtensions();
+}
+
+static NPError NPSetCursor(NPP id, NPCursorType type) {
+ scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
+ if (!plugin)
+ return NPERR_GENERIC_ERROR;
+
+ return plugin->webplugin()->delegate()->SetCursor(type) ?
+ NPERR_NO_ERROR : NPERR_GENERIC_ERROR;
+}
+
+static NPFontExtensions* GetFontExtensions(NPP id) {
+ scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
+ if (!plugin)
+ return NULL;
+
+ return plugin->webplugin()->delegate()->GetFontExtensions();
+}
+
+namespace NPAPI {
+
+NPError GetPepperExtensionsFunctions(void* value) {
+ static const NPNExtensions kExtensions = {
+ &AcquireDevice,
+ &NumberOfFindResultsChanged,
+ &SelectedFindResultChanged,
+ &ChooseFile,
+ &GetWidgetExtensions,
+ &NPSetCursor,
+ &GetFontExtensions,
+ };
+
+ // Return a pointer to the canonical function table.
+ NPNExtensions* extensions = const_cast<NPNExtensions*>(&kExtensions);
+ NPNExtensions** exts = reinterpret_cast<NPNExtensions**>(value);
+ *exts = extensions;
+ return NPERR_NO_ERROR;
+}
+
+} // namespace NPAPI
diff --git a/webkit/glue/plugins/npapi_extension_thunk.h b/webkit/glue/plugins/npapi_extension_thunk.h
new file mode 100644
index 0000000..fada6bc
--- /dev/null
+++ b/webkit/glue/plugins/npapi_extension_thunk.h
@@ -0,0 +1,23 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_NPAPI_EXTENSION_THUNK_H_
+#define WEBKIT_GLUE_PLUGINS_NPAPI_EXTENSION_THUNK_H_
+
+#include "third_party/npapi/bindings/npapi_extensions.h"
+
+// This file implements forwarding for the NPAPI "Pepper" extensions through to
+// the WebPluginDelegate associated with the plugin.
+
+namespace NPAPI {
+
+// Implements NPN_GetValue for the case of NPNVPepperExtensions. The function
+// pointers in the returned structure implement all the extensions.
+NPError GetPepperExtensionsFunctions(void* value);
+
+} // namespace NPAPI
+
+#endif // WEBKIT_GLUE_PLUGINS_NPAPI_EXTENSION_THUNK_H_
+
+
diff --git a/webkit/glue/plugins/pepper_buffer.cc b/webkit/glue/plugins/pepper_buffer.cc
new file mode 100644
index 0000000..1c0bdd8
--- /dev/null
+++ b/webkit/glue/plugins/pepper_buffer.cc
@@ -0,0 +1,116 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/pepper_buffer.h"
+
+#include <algorithm>
+
+#include "base/logging.h"
+#include "base/scoped_ptr.h"
+#include "third_party/ppapi/c/pp_instance.h"
+#include "third_party/ppapi/c/pp_module.h"
+#include "third_party/ppapi/c/pp_resource.h"
+#include "third_party/ppapi/c/ppb_buffer.h"
+#include "webkit/glue/plugins/pepper_plugin_instance.h"
+#include "webkit/glue/plugins/pepper_plugin_module.h"
+
+namespace pepper {
+
+namespace {
+
+PP_Resource Create(PP_Module module_id, int32_t size) {
+ PluginModule* module = PluginModule::FromPPModule(module_id);
+ if (!module)
+ return NULL;
+
+ scoped_refptr<Buffer> buffer(new Buffer(module));
+ if (!buffer->Init(size))
+ return NULL;
+
+ return buffer->GetReference();
+}
+
+bool IsBuffer(PP_Resource resource) {
+ return !!Resource::GetAs<Buffer>(resource);
+}
+
+bool Describe(PP_Resource resource, int32_t* size_in_bytes) {
+ scoped_refptr<Buffer> buffer(Resource::GetAs<Buffer>(resource));
+ if (!buffer)
+ return false;
+ buffer->Describe(size_in_bytes);
+ return true;
+}
+
+void* Map(PP_Resource resource) {
+ scoped_refptr<Buffer> buffer(Resource::GetAs<Buffer>(resource));
+ if (!buffer)
+ return NULL;
+ return buffer->Map();
+}
+
+void Unmap(PP_Resource resource) {
+ scoped_refptr<Buffer> buffer(Resource::GetAs<Buffer>(resource));
+ if (!buffer)
+ return;
+ return buffer->Unmap();
+}
+
+const PPB_Buffer ppb_buffer = {
+ &Create,
+ &IsBuffer,
+ &Describe,
+ &Map,
+ &Unmap,
+};
+
+} // namespace
+
+Buffer::Buffer(PluginModule* module)
+ : Resource(module),
+ size_(0) {
+}
+
+Buffer::~Buffer() {
+}
+
+// static
+const PPB_Buffer* Buffer::GetInterface() {
+ return &ppb_buffer;
+}
+
+bool Buffer::Init(int size) {
+ if (size == 0)
+ return false;
+ Unmap();
+ size_ = size;
+ return true;
+}
+
+void Buffer::Describe(int* size_in_bytes) const {
+ *size_in_bytes = size_;
+}
+
+void* Buffer::Map() {
+ if (size_ == 0)
+ return NULL;
+
+ if (!is_mapped()) {
+ mem_buffer_.reset(new unsigned char[size_]);
+ memset(mem_buffer_.get(), 0, size_);
+ }
+ return mem_buffer_.get();
+}
+
+void Buffer::Unmap() {
+ mem_buffer_.reset();
+}
+
+void Buffer::Swap(Buffer* other) {
+ swap(other->mem_buffer_, mem_buffer_);
+ std::swap(other->size_, size_);
+}
+
+} // namespace pepper
+
diff --git a/webkit/glue/plugins/pepper_buffer.h b/webkit/glue/plugins/pepper_buffer.h
new file mode 100644
index 0000000..5d750ec
--- /dev/null
+++ b/webkit/glue/plugins/pepper_buffer.h
@@ -0,0 +1,55 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_PEPPER_BUFFER_H_
+#define WEBKIT_GLUE_PLUGINS_PEPPER_BUFFER_H_
+
+#include "base/basictypes.h"
+#include "base/scoped_ptr.h"
+#include "third_party/ppapi/c/ppb_buffer.h"
+#include "webkit/glue/plugins/pepper_resource.h"
+
+namespace pepper {
+
+class PluginInstance;
+
+class Buffer : public Resource {
+ public:
+ explicit Buffer(PluginModule* module);
+ virtual ~Buffer();
+
+ int size() const { return size_; }
+ unsigned char* mapped_buffer() { return mem_buffer_.get(); }
+
+ // Returns true if this buffer is mapped. False means that the buffer is
+ // either invalid or not mapped.
+ bool is_mapped() const { return !!mem_buffer_.get(); }
+
+ // Returns a pointer to the interface implementing PPB_Buffer that is
+ // exposed to the plugin.
+ static const PPB_Buffer* GetInterface();
+
+ // Resource overrides.
+ Buffer* AsBuffer() { return this; }
+
+ // PPB_Buffer implementation.
+ bool Init(int size);
+ void Describe(int* size_in_bytes) const;
+ void* Map();
+ void Unmap();
+
+ // Swaps the guts of this buffer with another.
+ void Swap(Buffer* other);
+
+ private:
+ int size_;
+ scoped_array<unsigned char> mem_buffer_;
+
+ DISALLOW_COPY_AND_ASSIGN(Buffer);
+};
+
+} // namespace pepper
+
+#endif // WEBKIT_GLUE_PLUGINS_PEPPER_BUFFER_H_
+
diff --git a/webkit/glue/plugins/pepper_device_context_2d.cc b/webkit/glue/plugins/pepper_device_context_2d.cc
new file mode 100644
index 0000000..45ed9ee
--- /dev/null
+++ b/webkit/glue/plugins/pepper_device_context_2d.cc
@@ -0,0 +1,553 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/pepper_device_context_2d.h"
+
+#include <iterator>
+
+#include "base/logging.h"
+#include "base/message_loop.h"
+#include "base/task.h"
+#include "gfx/blit.h"
+#include "gfx/point.h"
+#include "gfx/rect.h"
+#include "skia/ext/platform_canvas.h"
+#include "third_party/ppapi/c/pp_errors.h"
+#include "third_party/ppapi/c/pp_module.h"
+#include "third_party/ppapi/c/pp_rect.h"
+#include "third_party/ppapi/c/pp_resource.h"
+#include "third_party/ppapi/c/ppb_device_context_2d.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "webkit/glue/plugins/pepper_image_data.h"
+#include "webkit/glue/plugins/pepper_plugin_instance.h"
+#include "webkit/glue/plugins/pepper_plugin_module.h"
+
+#if defined(OS_MACOSX)
+#include "base/mac_util.h"
+#include "base/scoped_cftyperef.h"
+#endif
+
+namespace pepper {
+
+namespace {
+
+// Converts a rect inside an image of the given dimensions. The rect may be
+// NULL to indicate it should be the entire image. If the rect is outside of
+// the image, this will do nothing and return false.
+bool ValidateAndConvertRect(const PP_Rect* rect,
+ int image_width, int image_height,
+ gfx::Rect* dest) {
+ if (!rect) {
+ // Use the entire image area.
+ *dest = gfx::Rect(0, 0, image_width, image_height);
+ } else {
+ // Validate the passed-in area.
+ if (rect->point.x < 0 || rect->point.y < 0 ||
+ rect->size.width <= 0 || rect->size.height <= 0)
+ return false;
+
+ // Check the max bounds, being careful of overflow.
+ if (static_cast<int64>(rect->point.x) +
+ static_cast<int64>(rect->size.width) >
+ static_cast<int64>(image_width))
+ return false;
+ if (static_cast<int64>(rect->point.y) +
+ static_cast<int64>(rect->size.height) >
+ static_cast<int64>(image_height))
+ return false;
+
+ *dest = gfx::Rect(rect->point.x, rect->point.y,
+ rect->size.width, rect->size.height);
+ }
+ return true;
+}
+
+PP_Resource Create(PP_Module module_id,
+ const PP_Size* size,
+ bool is_always_opaque) {
+ PluginModule* module = PluginModule::FromPPModule(module_id);
+ if (!module)
+ return NULL;
+
+ scoped_refptr<DeviceContext2D> context(new DeviceContext2D(module));
+ if (!context->Init(size->width, size->height, is_always_opaque))
+ return NULL;
+ return context->GetReference();
+}
+
+bool IsDeviceContext2D(PP_Resource resource) {
+ return !!Resource::GetAs<DeviceContext2D>(resource);
+}
+
+bool Describe(PP_Resource device_context,
+ PP_Size* size,
+ bool* is_always_opaque) {
+ scoped_refptr<DeviceContext2D> context(
+ Resource::GetAs<DeviceContext2D>(device_context));
+ if (!context)
+ return false;
+ return context->Describe(size, is_always_opaque);
+}
+
+bool PaintImageData(PP_Resource device_context,
+ PP_Resource image,
+ const PP_Point* top_left,
+ const PP_Rect* src_rect) {
+ scoped_refptr<DeviceContext2D> context(
+ Resource::GetAs<DeviceContext2D>(device_context));
+ if (!context)
+ return false;
+ return context->PaintImageData(image, top_left, src_rect);
+}
+
+bool Scroll(PP_Resource device_context,
+ const PP_Rect* clip_rect,
+ const PP_Point* amount) {
+ scoped_refptr<DeviceContext2D> context(
+ Resource::GetAs<DeviceContext2D>(device_context));
+ if (!context)
+ return false;
+ return context->Scroll(clip_rect, amount);
+}
+
+bool ReplaceContents(PP_Resource device_context, PP_Resource image) {
+ scoped_refptr<DeviceContext2D> context(
+ Resource::GetAs<DeviceContext2D>(device_context));
+ if (!context)
+ return false;
+ return context->ReplaceContents(image);
+}
+
+int32_t Flush(PP_Resource device_context,
+ PP_CompletionCallback callback) {
+ scoped_refptr<DeviceContext2D> context(
+ Resource::GetAs<DeviceContext2D>(device_context));
+ if (!context)
+ return PP_ERROR_BADRESOURCE;
+ return context->Flush(callback);
+}
+
+const PPB_DeviceContext2D ppb_devicecontext2d = {
+ &Create,
+ &IsDeviceContext2D,
+ &Describe,
+ &PaintImageData,
+ &Scroll,
+ &ReplaceContents,
+ &Flush
+};
+
+} // namespace
+
+struct DeviceContext2D::QueuedOperation {
+ enum Type {
+ PAINT,
+ SCROLL,
+ REPLACE
+ };
+
+ QueuedOperation(Type t)
+ : type(t),
+ paint_x(0),
+ paint_y(0),
+ scroll_dx(0),
+ scroll_dy(0) {
+ }
+
+ Type type;
+
+ // Valid when type == PAINT.
+ scoped_refptr<ImageData> paint_image;
+ int paint_x, paint_y;
+ gfx::Rect paint_src_rect;
+
+ // Valid when type == SCROLL.
+ gfx::Rect scroll_clip_rect;
+ int scroll_dx, scroll_dy;
+
+ // Valid when type == REPLACE.
+ scoped_refptr<ImageData> replace_image;
+};
+
+DeviceContext2D::DeviceContext2D(PluginModule* module)
+ : Resource(module),
+ bound_instance_(NULL),
+ flushed_any_data_(false),
+ offscreen_flush_pending_(false) {
+}
+
+DeviceContext2D::~DeviceContext2D() {
+}
+
+// static
+const PPB_DeviceContext2D* DeviceContext2D::GetInterface() {
+ return &ppb_devicecontext2d;
+}
+
+bool DeviceContext2D::Init(int width, int height, bool is_always_opaque) {
+ // The underlying ImageData will validate the dimensions.
+ image_data_ = new ImageData(module());
+ if (!image_data_->Init(PP_IMAGEDATAFORMAT_BGRA_PREMUL, width, height, true) ||
+ !image_data_->Map()) {
+ image_data_ = NULL;
+ return false;
+ }
+
+ return true;
+}
+
+bool DeviceContext2D::Describe(PP_Size* size, bool* is_always_opaque) {
+ size->width = image_data_->width();
+ size->height = image_data_->height();
+ *is_always_opaque = false; // TODO(brettw) implement this.
+ return true;
+}
+
+bool DeviceContext2D::PaintImageData(PP_Resource image,
+ const PP_Point* top_left,
+ const PP_Rect* src_rect) {
+ if (!top_left)
+ return false;
+
+ scoped_refptr<ImageData> image_resource(Resource::GetAs<ImageData>(image));
+ if (!image_resource)
+ return false;
+
+ QueuedOperation operation(QueuedOperation::PAINT);
+ operation.paint_image = image_resource;
+ if (!ValidateAndConvertRect(src_rect, image_resource->width(),
+ image_resource->height(),
+ &operation.paint_src_rect))
+ return false;
+
+ // Validate the bitmap position using the previously-validated rect, there
+ // should be no painted area outside of the image.
+ int64 x64 = static_cast<int64>(top_left->x);
+ int64 y64 = static_cast<int64>(top_left->y);
+ if (x64 + static_cast<int64>(operation.paint_src_rect.x()) < 0 ||
+ x64 + static_cast<int64>(operation.paint_src_rect.right()) >
+ image_data_->width())
+ return false;
+ if (y64 + static_cast<int64>(operation.paint_src_rect.y()) < 0 ||
+ y64 + static_cast<int64>(operation.paint_src_rect.bottom()) >
+ image_data_->height())
+ return false;
+ operation.paint_x = top_left->x;
+ operation.paint_y = top_left->y;
+
+ queued_operations_.push_back(operation);
+ return true;
+}
+
+bool DeviceContext2D::Scroll(const PP_Rect* clip_rect,
+ const PP_Point* amount) {
+ QueuedOperation operation(QueuedOperation::SCROLL);
+ if (!ValidateAndConvertRect(clip_rect,
+ image_data_->width(),
+ image_data_->height(),
+ &operation.scroll_clip_rect))
+ return false;
+
+ // If we're being asked to scroll by more than the clip rect size, just
+ // ignore this scroll command and say it worked.
+ int32 dx = amount->x;
+ int32 dy = amount->y;
+ if (dx <= -image_data_->width() || dx >= image_data_->width() ||
+ dx <= -image_data_->height() || dy >= image_data_->height())
+ return true;
+
+ operation.scroll_dx = dx;
+ operation.scroll_dy = dy;
+
+ queued_operations_.push_back(operation);
+ return false;
+}
+
+bool DeviceContext2D::ReplaceContents(PP_Resource image) {
+ scoped_refptr<ImageData> image_resource(Resource::GetAs<ImageData>(image));
+ if (!image_resource)
+ return false;
+ if (image_resource->format() != PP_IMAGEDATAFORMAT_BGRA_PREMUL)
+ return false;
+
+ if (image_resource->width() != image_data_->width() ||
+ image_resource->height() != image_data_->height())
+ return false;
+
+ QueuedOperation operation(QueuedOperation::REPLACE);
+ operation.replace_image = image_resource;
+ queued_operations_.push_back(operation);
+
+ return true;
+}
+
+int32_t DeviceContext2D::Flush(const PP_CompletionCallback& callback) {
+ // Don't allow more than one pending flush at a time.
+ if (HasPendingFlush())
+ return PP_ERROR_INPROGRESS;
+
+ // TODO(brettw) check that the current thread is not the main one and
+ // implement blocking flushes in this case.
+ if (!callback.func)
+ return PP_ERROR_BADARGUMENT;
+
+ gfx::Rect changed_rect;
+ for (size_t i = 0; i < queued_operations_.size(); i++) {
+ QueuedOperation& operation = queued_operations_[i];
+ gfx::Rect op_rect;
+ switch (operation.type) {
+ case QueuedOperation::PAINT:
+ ExecutePaintImageData(operation.paint_image,
+ operation.paint_x, operation.paint_y,
+ operation.paint_src_rect,
+ &op_rect);
+ break;
+ case QueuedOperation::SCROLL:
+ ExecuteScroll(operation.scroll_clip_rect,
+ operation.scroll_dx, operation.scroll_dy,
+ &op_rect);
+ break;
+ case QueuedOperation::REPLACE:
+ ExecuteReplaceContents(operation.replace_image, &op_rect);
+ break;
+ }
+ changed_rect = changed_rect.Union(op_rect);
+ }
+ queued_operations_.clear();
+ flushed_any_data_ = true;
+
+ // We need the rect to be in terms of the current clip rect of the plugin
+ // since that's what will actually be painted. If we issue an invalidate
+ // for a clipped-out region, WebKit will do nothing and we won't get any
+ // ViewInitiatedPaint/ViewFlushedPaint calls, leaving our callback stranded.
+ gfx::Rect visible_changed_rect;
+ if (bound_instance_ && !changed_rect.IsEmpty())
+ visible_changed_rect = bound_instance_->clip().Intersect(changed_rect);
+
+ if (bound_instance_ && !visible_changed_rect.IsEmpty()) {
+ unpainted_flush_callback_.Set(callback);
+ bound_instance_->InvalidateRect(visible_changed_rect);
+ } else {
+ // There's nothing visible to invalidate so just schedule the callback to
+ // execute in the next round of the message loop.
+ ScheduleOffscreenCallback(FlushCallbackData(callback));
+ }
+ return PP_ERROR_WOULDBLOCK;
+}
+
+bool DeviceContext2D::ReadImageData(PP_Resource image,
+ const PP_Point* top_left) {
+ // Get and validate the image object to paint into.
+ scoped_refptr<ImageData> image_resource(Resource::GetAs<ImageData>(image));
+ if (!image_resource)
+ return false;
+ if (image_resource->format() != PP_IMAGEDATAFORMAT_BGRA_PREMUL)
+ return false; // Must be in the right format.
+
+ // Validate the bitmap position.
+ int x = top_left->x;
+ if (x < 0 ||
+ static_cast<int64>(x) + static_cast<int64>(image_resource->width()) >
+ image_data_->width())
+ return false;
+ int y = top_left->y;
+ if (y < 0 ||
+ static_cast<int64>(y) + static_cast<int64>(image_resource->height()) >
+ image_data_->height())
+ return false;
+
+ ImageDataAutoMapper auto_mapper(image_resource);
+ if (!auto_mapper.is_valid())
+ return false;
+ skia::PlatformCanvas* dest_canvas = image_resource->mapped_canvas();
+
+ SkIRect src_irect = { x, y,
+ x + image_resource->width(),
+ y + image_resource->height() };
+ SkRect dest_rect = { SkIntToScalar(0),
+ SkIntToScalar(0),
+ SkIntToScalar(image_resource->width()),
+ SkIntToScalar(image_resource->height()) };
+
+ // We want to replace the contents of the bitmap rather than blend.
+ SkPaint paint;
+ paint.setXfermodeMode(SkXfermode::kSrc_Mode);
+ dest_canvas->drawBitmapRect(*image_data_->GetMappedBitmap(),
+ &src_irect, dest_rect, &paint);
+ return true;
+}
+
+bool DeviceContext2D::BindToInstance(PluginInstance* new_instance) {
+ if (bound_instance_ == new_instance)
+ return true; // Rebinding the same device, nothing to do.
+ if (bound_instance_ && new_instance)
+ return false; // Can't change a bound device.
+
+ if (!new_instance) {
+ // When the device is detached, we'll not get any more paint callbacks so
+ // we need to clear the list, but we still want to issue any pending
+ // callbacks to the plugin.
+ if (!unpainted_flush_callback_.is_null()) {
+ ScheduleOffscreenCallback(unpainted_flush_callback_);
+ unpainted_flush_callback_.Clear();
+ }
+ if (!painted_flush_callback_.is_null()) {
+ ScheduleOffscreenCallback(painted_flush_callback_);
+ painted_flush_callback_.Clear();
+ }
+ } else if (flushed_any_data_) {
+ // Only schedule a paint if this backing store has had any data flushed to
+ // it. This is an optimization. A "normal" plugin will first allocated a
+ // backing store, bind it, and then execute their normal painting and
+ // update loop. If binding a device always invalidated, it would mean we
+ // would get one paint for the bind, and one for the first time the plugin
+ // actually painted something. By not bothering to schedule an invalidate
+ // when an empty device is initially bound, we can save an extra paint for
+ // many plugins during the critical page initialization phase.
+ new_instance->InvalidateRect(gfx::Rect());
+ }
+
+ bound_instance_ = new_instance;
+ return true;
+}
+
+void DeviceContext2D::Paint(WebKit::WebCanvas* canvas,
+ const gfx::Rect& plugin_rect,
+ const gfx::Rect& paint_rect) {
+ // We're guaranteed to have a mapped canvas since we mapped it in Init().
+ const SkBitmap& backing_bitmap = *image_data_->GetMappedBitmap();
+
+#if defined(OS_MACOSX)
+ SkAutoLockPixels lock(backing_bitmap);
+
+ scoped_cftyperef<CGDataProviderRef> data_provider(
+ CGDataProviderCreateWithData(
+ NULL, backing_bitmap.getAddr32(0, 0),
+ backing_bitmap.rowBytes() * backing_bitmap.height(), NULL));
+ scoped_cftyperef<CGImageRef> image(
+ CGImageCreate(
+ backing_bitmap.width(), backing_bitmap.height(),
+ 8, 32, backing_bitmap.rowBytes(),
+ mac_util::GetSystemColorSpace(),
+ kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host,
+ data_provider, NULL, false, kCGRenderingIntentDefault));
+
+ // Flip the transform
+ CGContextSaveGState(canvas);
+ float window_height = static_cast<float>(CGBitmapContextGetHeight(canvas));
+ CGContextTranslateCTM(canvas, 0, window_height);
+ CGContextScaleCTM(canvas, 1.0, -1.0);
+
+ CGRect bounds;
+ bounds.origin.x = plugin_rect.origin().x();
+ bounds.origin.y = window_height - plugin_rect.origin().y() -
+ backing_bitmap.height();
+ bounds.size.width = backing_bitmap.width();
+ bounds.size.height = backing_bitmap.height();
+
+ CGContextDrawImage(canvas, bounds, image);
+ CGContextRestoreGState(canvas);
+#else
+ gfx::Point origin(plugin_rect.origin().x(), plugin_rect.origin().y());
+ canvas->drawBitmap(backing_bitmap,
+ SkIntToScalar(plugin_rect.origin().x()),
+ SkIntToScalar(plugin_rect.origin().y()));
+#endif
+}
+
+void DeviceContext2D::ViewInitiatedPaint() {
+ // Move any "unpainted" callback to the painted state. See
+ // |unpainted_flush_callback_| in the header for more.
+ if (!unpainted_flush_callback_.is_null()) {
+ DCHECK(painted_flush_callback_.is_null());
+ std::swap(painted_flush_callback_, unpainted_flush_callback_);
+ }
+}
+
+void DeviceContext2D::ViewFlushedPaint() {
+ // Notify any "painted" callback. See |unpainted_flush_callback_| in the
+ // header for more.
+ if (!painted_flush_callback_.is_null()) {
+ // We must clear this variable before issuing the callback. It will be
+ // common for the plugin to issue another invalidate in response to a flush
+ // callback, and we don't want to think that a callback is already pending.
+ FlushCallbackData callback;
+ std::swap(callback, painted_flush_callback_);
+ callback.Execute(PP_OK);
+ }
+}
+
+void DeviceContext2D::ExecutePaintImageData(ImageData* image,
+ int x, int y,
+ const gfx::Rect& src_rect,
+ gfx::Rect* invalidated_rect) {
+ // Ensure the source image is mapped to read from it.
+ ImageDataAutoMapper auto_mapper(image);
+ if (!auto_mapper.is_valid())
+ return;
+
+ // Portion within the source image to cut out.
+ SkIRect src_irect = { src_rect.x(), src_rect.y(),
+ src_rect.right(), src_rect.bottom() };
+
+ // Location within the backing store to copy to.
+ *invalidated_rect = src_rect;
+ invalidated_rect->Offset(x, y);
+ SkRect dest_rect = { SkIntToScalar(invalidated_rect->x()),
+ SkIntToScalar(invalidated_rect->y()),
+ SkIntToScalar(invalidated_rect->right()),
+ SkIntToScalar(invalidated_rect->bottom()) };
+
+ // We're guaranteed to have a mapped canvas since we mapped it in Init().
+ skia::PlatformCanvas* backing_canvas = image_data_->mapped_canvas();
+
+ // We want to replace the contents of the bitmap rather than blend.
+ SkPaint paint;
+ paint.setXfermodeMode(SkXfermode::kSrc_Mode);
+ backing_canvas->drawBitmapRect(*image->GetMappedBitmap(),
+ &src_irect, dest_rect, &paint);
+}
+
+void DeviceContext2D::ExecuteScroll(const gfx::Rect& clip, int dx, int dy,
+ gfx::Rect* invalidated_rect) {
+ gfx::ScrollCanvas(image_data_->mapped_canvas(),
+ clip, gfx::Point(dx, dy));
+ *invalidated_rect = clip;
+}
+
+void DeviceContext2D::ExecuteReplaceContents(ImageData* image,
+ gfx::Rect* invalidated_rect) {
+ image_data_->Swap(image);
+ *invalidated_rect = gfx::Rect(0, 0,
+ image_data_->width(), image_data_->height());
+}
+
+void DeviceContext2D::ScheduleOffscreenCallback(
+ const FlushCallbackData& callback) {
+ DCHECK(!HasPendingFlush());
+ offscreen_flush_pending_ = true;
+ MessageLoop::current()->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(this,
+ &DeviceContext2D::ExecuteOffscreenCallback,
+ callback));
+}
+
+void DeviceContext2D::ExecuteOffscreenCallback(FlushCallbackData data) {
+ DCHECK(offscreen_flush_pending_);
+
+ // We must clear this flag before issuing the callback. It will be
+ // common for the plugin to issue another invalidate in response to a flush
+ // callback, and we don't want to think that a callback is already pending.
+ offscreen_flush_pending_ = false;
+ data.Execute(PP_OK);
+}
+
+bool DeviceContext2D::HasPendingFlush() const {
+ return !unpainted_flush_callback_.is_null() ||
+ !painted_flush_callback_.is_null() ||
+ offscreen_flush_pending_;
+}
+
+} // namespace pepper
diff --git a/webkit/glue/plugins/pepper_device_context_2d.h b/webkit/glue/plugins/pepper_device_context_2d.h
new file mode 100644
index 0000000..603bd52
--- /dev/null
+++ b/webkit/glue/plugins/pepper_device_context_2d.h
@@ -0,0 +1,175 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_PEPPER_DEVICE_CONTEXT_2D_H_
+#define WEBKIT_GLUE_PLUGINS_PEPPER_DEVICE_CONTEXT_2D_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "third_party/ppapi/c/pp_completion_callback.h"
+#include "third_party/ppapi/c/ppb_device_context_2d.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebCanvas.h"
+#include "webkit/glue/plugins/pepper_resource.h"
+
+typedef struct _ppb_DeviceContext2D PPB_DeviceContext2D;
+
+namespace gfx {
+class Rect;
+}
+
+namespace pepper {
+
+class ImageData;
+class PluginInstance;
+class PluginModule;
+
+class DeviceContext2D : public Resource {
+ public:
+ DeviceContext2D(PluginModule* module);
+ virtual ~DeviceContext2D();
+
+ // Returns a pointer to the interface implementing PPB_ImageData that is
+ // exposed to the plugin.
+ static const PPB_DeviceContext2D* GetInterface();
+
+ bool Init(int width, int height, bool is_always_opaque);
+
+ // Resource override.
+ virtual DeviceContext2D* AsDeviceContext2D() { return this; }
+
+ // PPB_DeviceContext2D functions.
+ bool Describe(PP_Size* size, bool* is_always_opaque);
+ bool PaintImageData(PP_Resource image,
+ const PP_Point* top_left,
+ const PP_Rect* src_rect);
+ bool Scroll(const PP_Rect* clip_rect, const PP_Point* amount);
+ bool ReplaceContents(PP_Resource image);
+ int32_t Flush(const PP_CompletionCallback& callback);
+
+ bool ReadImageData(PP_Resource image, const PP_Point* top_left);
+
+ // Assciates this device with the given plugin instance. You can pass NULL to
+ // clear the existing device. Returns true on success. In this case, a
+ // repaint of the page will also be scheduled. Failure means that the device
+ // is already bound to a different instance, and nothing will happen.
+ bool BindToInstance(PluginInstance* new_instance);
+
+ // Paints the current backing store to the web page.
+ void Paint(WebKit::WebCanvas* canvas,
+ const gfx::Rect& plugin_rect,
+ const gfx::Rect& paint_rect);
+
+ // Notifications that the view has rendered the page and that it has been
+ // flushed to the screen. These messages are used to send Flush callbacks to
+ // the plugin. See
+ void ViewInitiatedPaint();
+ void ViewFlushedPaint();
+
+ ImageData* image_data() { return image_data_.get(); }
+
+ private:
+ // Tracks a call to flush that requires a callback.
+ class FlushCallbackData {
+ public:
+ FlushCallbackData() {
+ Clear();
+ }
+
+ FlushCallbackData(const PP_CompletionCallback& callback) {
+ Set(callback);
+ }
+
+ bool is_null() const { return !callback_.func; }
+
+ void Set(const PP_CompletionCallback& callback) {
+ callback_ = callback;
+ }
+
+ void Clear() {
+ callback_ = PP_MakeCompletionCallback(NULL, 0);
+ }
+
+ void Execute(int32_t result) {
+ PP_RunCompletionCallback(&callback_, result);
+ }
+
+ private:
+ PP_CompletionCallback callback_;
+ };
+
+ // Called internally to execute the different queued commands. The
+ // parameters to these functions will have already been validated. The last
+ // rect argument will be filled by each function with the area affected by
+ // the update that requires invalidation. If there were no pixels changed,
+ // this rect can be untouched.
+ void ExecutePaintImageData(ImageData* image,
+ int x, int y,
+ const gfx::Rect& src_rect,
+ gfx::Rect* invalidated_rect);
+ void ExecuteScroll(const gfx::Rect& clip, int dx, int dy,
+ gfx::Rect* invalidated_rect);
+ void ExecuteReplaceContents(ImageData* image,
+ gfx::Rect* invalidated_rect);
+
+ // Schedules the offscreen callback to be fired at a future time. This
+ // will add the given item to the offscreen_flush_callbacks_ vector.
+ void ScheduleOffscreenCallback(const FlushCallbackData& callback);
+
+ // Function scheduled to execute by ScheduleOffscreenCallback that actually
+ // issues the offscreen callbacks.
+ void ExecuteOffscreenCallback(FlushCallbackData data);
+
+ // Returns true if there is any type of flush callback pending.
+ bool HasPendingFlush() const;
+
+ scoped_refptr<ImageData> image_data_;
+
+ // Non-owning pointer to the plugin instance this device context is currently
+ // bound to, if any. If the device context is currently unbound, this will
+ // be NULL.
+ PluginInstance* bound_instance_;
+
+ // Keeps track of all drawing commands queued before a Flush call.
+ struct QueuedOperation;
+ typedef std::vector<QueuedOperation> OperationQueue;
+ OperationQueue queued_operations_;
+
+ // Indicates whether any changes have been flushed to the backing store.
+ // This is initially false and is set to true at the first Flush() call.
+ bool flushed_any_data_;
+
+ // The plugin can give us one "Flush" at a time. This flush will either be in
+ // the "unpainted" state (in which case unpainted_flush_callback_ will be
+ // non-NULL) or painted, in which case painted_flush_callback_ will be
+ // non-NULL). There can also be an offscreen callback which is handled
+ // separately (see offscreen_callback_pending_). Only one of these three
+ // things may be set at a time to enforce the "only one pending flush at a
+ // time" constraint.
+ //
+ // "Unpainted" ones are flush requests which have never been painted. These
+ // could have been done while the RenderView was already waiting for an ACK
+ // from a previous paint, so won't generate a new one yet.
+ //
+ // "Painted" ones are those flushes that have been painted by RenderView, but
+ // for which the ACK from the browser has not yet been received.
+ //
+ // When we get updates from a plugin with a callback, it is first added to
+ // the unpainted callbacks. When the renderer has initiated a paint, we'll
+ // move it to the painted callbacks list. When the renderer receives a flush,
+ // we'll execute the callback and remove it from the list.
+ FlushCallbackData unpainted_flush_callback_;
+ FlushCallbackData painted_flush_callback_;
+
+ // When doing offscreen flushes, we issue a task that issues the callback
+ // later. This is set when one of those tasks is pending so that we can
+ // enforce the "only one pending flush at a time" constraint in the API.
+ bool offscreen_flush_pending_;
+
+ DISALLOW_COPY_AND_ASSIGN(DeviceContext2D);
+};
+
+} // namespace pepper
+
+#endif // WEBKIT_GLUE_PLUGINS_PEPPER_DEVICE_CONTEXT_2D_H_
diff --git a/webkit/glue/plugins/pepper_directory_reader.cc b/webkit/glue/plugins/pepper_directory_reader.cc
new file mode 100644
index 0000000..93f19ee
--- /dev/null
+++ b/webkit/glue/plugins/pepper_directory_reader.cc
@@ -0,0 +1,68 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/pepper_directory_reader.h"
+
+#include "base/logging.h"
+#include "third_party/ppapi/c/pp_completion_callback.h"
+#include "third_party/ppapi/c/pp_errors.h"
+#include "webkit/glue/plugins/pepper_file_ref.h"
+#include "webkit/glue/plugins/pepper_resource_tracker.h"
+
+namespace pepper {
+
+namespace {
+
+PP_Resource Create(PP_Resource directory_ref_id) {
+ scoped_refptr<FileRef> directory_ref(
+ Resource::GetAs<FileRef>(directory_ref_id));
+ if (!directory_ref)
+ return 0;
+
+ DirectoryReader* reader = new DirectoryReader(directory_ref);
+ return reader->GetReference();
+}
+
+bool IsDirectoryReader(PP_Resource resource) {
+ return !!Resource::GetAs<DirectoryReader>(resource);
+}
+
+int32_t GetNextEntry(PP_Resource reader_id,
+ PP_DirectoryEntry* entry,
+ PP_CompletionCallback callback) {
+ scoped_refptr<DirectoryReader> reader(
+ Resource::GetAs<DirectoryReader>(reader_id));
+ if (!reader)
+ return PP_ERROR_BADRESOURCE;
+
+ return reader->GetNextEntry(entry, callback);
+}
+
+const PPB_DirectoryReader ppb_directoryreader = {
+ &Create,
+ &IsDirectoryReader,
+ &GetNextEntry
+};
+
+} // namespace
+
+DirectoryReader::DirectoryReader(FileRef* directory_ref)
+ : Resource(directory_ref->module()),
+ directory_ref_(directory_ref) {
+}
+
+DirectoryReader::~DirectoryReader() {
+}
+
+const PPB_DirectoryReader* DirectoryReader::GetInterface() {
+ return &ppb_directoryreader;
+}
+
+int32_t DirectoryReader::GetNextEntry(PP_DirectoryEntry* entry,
+ PP_CompletionCallback callback) {
+ NOTIMPLEMENTED(); // TODO(darin): Implement me!
+ return PP_ERROR_FAILED;
+}
+
+} // namespace pepper
diff --git a/webkit/glue/plugins/pepper_directory_reader.h b/webkit/glue/plugins/pepper_directory_reader.h
new file mode 100644
index 0000000..c477a3e
--- /dev/null
+++ b/webkit/glue/plugins/pepper_directory_reader.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_PEPPER_DIRECTORY_READER_H_
+#define WEBKIT_GLUE_PLUGINS_PEPPER_DIRECTORY_READER_H_
+
+#include "third_party/ppapi/c/ppb_directory_reader.h"
+#include "webkit/glue/plugins/pepper_resource.h"
+
+namespace pepper {
+
+class FileRef;
+
+class DirectoryReader : public Resource {
+ public:
+ explicit DirectoryReader(FileRef* directory_ref);
+ virtual ~DirectoryReader();
+
+ // Returns a pointer to the interface implementing PPB_DirectoryReader that
+ // is exposed to the plugin.
+ static const PPB_DirectoryReader* GetInterface();
+
+ // Resource overrides.
+ DirectoryReader* AsDirectoryReader() { return this; }
+
+ // PPB_DirectoryReader implementation.
+ int32_t GetNextEntry(PP_DirectoryEntry* entry,
+ PP_CompletionCallback callback);
+
+ private:
+ scoped_refptr<FileRef> directory_ref_;
+};
+
+} // namespace pepper
+
+#endif // WEBKIT_GLUE_PLUGINS_PEPPER_DIRECTORY_READER_H_
diff --git a/webkit/glue/plugins/pepper_event_conversion.cc b/webkit/glue/plugins/pepper_event_conversion.cc
new file mode 100644
index 0000000..033ac93
--- /dev/null
+++ b/webkit/glue/plugins/pepper_event_conversion.cc
@@ -0,0 +1,235 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/pepper_event_conversion.h"
+
+#include "base/logging.h"
+#include "base/scoped_ptr.h"
+#include "third_party/ppapi/c/pp_event.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebInputEvent.h"
+
+using WebKit::WebInputEvent;
+using WebKit::WebKeyboardEvent;
+using WebKit::WebMouseEvent;
+using WebKit::WebMouseWheelEvent;
+
+namespace {
+// Anonymous namespace for functions converting WebInputEvent to PP_Event and
+// back.
+PP_Event_Type ConvertEventTypes(WebInputEvent::Type wetype) {
+ switch (wetype) {
+ case WebInputEvent::MouseDown:
+ return PP_EVENT_TYPE_MOUSEDOWN;
+ case WebInputEvent::MouseUp:
+ return PP_EVENT_TYPE_MOUSEUP;
+ case WebInputEvent::MouseMove:
+ return PP_EVENT_TYPE_MOUSEMOVE;
+ case WebInputEvent::MouseEnter:
+ return PP_EVENT_TYPE_MOUSEENTER;
+ case WebInputEvent::MouseLeave:
+ return PP_EVENT_TYPE_MOUSELEAVE;
+ case WebInputEvent::MouseWheel:
+ return PP_EVENT_TYPE_MOUSEWHEEL;
+ case WebInputEvent::RawKeyDown:
+ return PP_EVENT_TYPE_RAWKEYDOWN;
+ case WebInputEvent::KeyDown:
+ return PP_EVENT_TYPE_KEYDOWN;
+ case WebInputEvent::KeyUp:
+ return PP_EVENT_TYPE_KEYUP;
+ case WebInputEvent::Char:
+ return PP_EVENT_TYPE_CHAR;
+ case WebInputEvent::Undefined:
+ default:
+ return PP_EVENT_TYPE_UNDEFINED;
+ }
+}
+
+void BuildKeyEvent(const WebInputEvent* event, PP_Event* pp_event) {
+ const WebKeyboardEvent* key_event =
+ reinterpret_cast<const WebKeyboardEvent*>(event);
+ pp_event->u.key.modifier = key_event->modifiers;
+ pp_event->u.key.normalizedKeyCode = key_event->windowsKeyCode;
+}
+
+void BuildCharEvent(const WebInputEvent* event, PP_Event* pp_event) {
+ const WebKeyboardEvent* key_event =
+ reinterpret_cast<const WebKeyboardEvent*>(event);
+ pp_event->u.character.modifier = key_event->modifiers;
+ // For consistency, check that the sizes of the texts agree.
+ DCHECK(sizeof(pp_event->u.character.text) == sizeof(key_event->text));
+ DCHECK(sizeof(pp_event->u.character.unmodifiedText) ==
+ sizeof(key_event->unmodifiedText));
+ for (size_t i = 0; i < WebKeyboardEvent::textLengthCap; ++i) {
+ pp_event->u.character.text[i] = key_event->text[i];
+ pp_event->u.character.unmodifiedText[i] = key_event->unmodifiedText[i];
+ }
+}
+
+void BuildMouseEvent(const WebInputEvent* event, PP_Event* pp_event) {
+ const WebMouseEvent* mouse_event =
+ reinterpret_cast<const WebMouseEvent*>(event);
+ pp_event->u.mouse.modifier = mouse_event->modifiers;
+ pp_event->u.mouse.button = mouse_event->button;
+ pp_event->u.mouse.x = mouse_event->x;
+ pp_event->u.mouse.y = mouse_event->y;
+ pp_event->u.mouse.clickCount = mouse_event->clickCount;
+}
+
+void BuildMouseWheelEvent(const WebInputEvent* event, PP_Event* pp_event) {
+ const WebMouseWheelEvent* mouse_wheel_event =
+ reinterpret_cast<const WebMouseWheelEvent*>(event);
+ pp_event->u.wheel.modifier = mouse_wheel_event->modifiers;
+ pp_event->u.wheel.deltaX = mouse_wheel_event->deltaX;
+ pp_event->u.wheel.deltaY = mouse_wheel_event->deltaY;
+ pp_event->u.wheel.wheelTicksX = mouse_wheel_event->wheelTicksX;
+ pp_event->u.wheel.wheelTicksY = mouse_wheel_event->wheelTicksY;
+ pp_event->u.wheel.scrollByPage = mouse_wheel_event->scrollByPage;
+}
+
+
+WebKeyboardEvent* BuildKeyEvent(const PP_Event& event) {
+ WebKeyboardEvent* key_event = new WebKeyboardEvent();
+ switch (event.type) {
+ case PP_EVENT_TYPE_RAWKEYDOWN:
+ key_event->type = WebInputEvent::RawKeyDown;
+ break;
+ case PP_EVENT_TYPE_KEYDOWN:
+ key_event->type = WebInputEvent::KeyDown;
+ break;
+ case PP_EVENT_TYPE_KEYUP:
+ key_event->type = WebInputEvent::KeyUp;
+ break;
+ }
+ key_event->timeStampSeconds = event.time_stamp_seconds;
+ key_event->modifiers = event.u.key.modifier;
+ key_event->windowsKeyCode = event.u.key.normalizedKeyCode;
+ return key_event;
+}
+
+WebKeyboardEvent* BuildCharEvent(const PP_Event& event) {
+ WebKeyboardEvent* key_event = new WebKeyboardEvent();
+ key_event->type = WebInputEvent::Char;
+ key_event->timeStampSeconds = event.time_stamp_seconds;
+ key_event->modifiers = event.u.character.modifier;
+ // For consistency, check that the sizes of the texts agree.
+ DCHECK(sizeof(event.u.character.text) == sizeof(key_event->text));
+ DCHECK(sizeof(event.u.character.unmodifiedText) ==
+ sizeof(key_event->unmodifiedText));
+ for (size_t i = 0; i < WebKeyboardEvent::textLengthCap; ++i) {
+ key_event->text[i] = event.u.character.text[i];
+ key_event->unmodifiedText[i] = event.u.character.unmodifiedText[i];
+ }
+ return key_event;
+}
+
+WebMouseEvent* BuildMouseEvent(const PP_Event& event) {
+ WebMouseEvent* mouse_event = new WebMouseEvent();
+ switch (event.type) {
+ case PP_EVENT_TYPE_MOUSEDOWN:
+ mouse_event->type = WebInputEvent::MouseDown;
+ break;
+ case PP_EVENT_TYPE_MOUSEUP:
+ mouse_event->type = WebInputEvent::MouseUp;
+ break;
+ case PP_EVENT_TYPE_MOUSEMOVE:
+ mouse_event->type = WebInputEvent::MouseMove;
+ break;
+ case PP_EVENT_TYPE_MOUSEENTER:
+ mouse_event->type = WebInputEvent::MouseEnter;
+ break;
+ case PP_EVENT_TYPE_MOUSELEAVE:
+ mouse_event->type = WebInputEvent::MouseLeave;
+ break;
+ }
+ mouse_event->timeStampSeconds = event.time_stamp_seconds;
+ mouse_event->modifiers = event.u.mouse.modifier;
+ mouse_event->button =
+ static_cast<WebMouseEvent::Button>(event.u.mouse.button);
+ mouse_event->x = event.u.mouse.x;
+ mouse_event->y = event.u.mouse.y;
+ mouse_event->clickCount = event.u.mouse.clickCount;
+ return mouse_event;
+}
+
+WebMouseWheelEvent* BuildMouseWheelEvent(const PP_Event& event) {
+ WebMouseWheelEvent* mouse_wheel_event = new WebMouseWheelEvent();
+ mouse_wheel_event->type = WebInputEvent::MouseWheel;
+ mouse_wheel_event->timeStampSeconds = event.time_stamp_seconds;
+ mouse_wheel_event->modifiers = event.u.wheel.modifier;
+ mouse_wheel_event->deltaX = event.u.wheel.deltaX;
+ mouse_wheel_event->deltaY = event.u.wheel.deltaY;
+ mouse_wheel_event->wheelTicksX = event.u.wheel.wheelTicksX;
+ mouse_wheel_event->wheelTicksY = event.u.wheel.wheelTicksY;
+ mouse_wheel_event->scrollByPage = event.u.wheel.scrollByPage;
+ return mouse_wheel_event;
+}
+
+} // namespace
+
+namespace pepper {
+
+PP_Event* CreatePP_Event(const WebInputEvent& event) {
+ scoped_ptr<PP_Event> pp_event(new PP_Event);
+
+ pp_event->type = ConvertEventTypes(event.type);
+ pp_event->size = sizeof(pp_event);
+ pp_event->time_stamp_seconds = event.timeStampSeconds;
+ switch (pp_event->type) {
+ case PP_EVENT_TYPE_UNDEFINED:
+ return NULL;
+ case PP_EVENT_TYPE_MOUSEDOWN:
+ case PP_EVENT_TYPE_MOUSEUP:
+ case PP_EVENT_TYPE_MOUSEMOVE:
+ case PP_EVENT_TYPE_MOUSEENTER:
+ case PP_EVENT_TYPE_MOUSELEAVE:
+ BuildMouseEvent(&event, pp_event.get());
+ break;
+ case PP_EVENT_TYPE_MOUSEWHEEL:
+ BuildMouseWheelEvent(&event, pp_event.get());
+ break;
+ case PP_EVENT_TYPE_RAWKEYDOWN:
+ case PP_EVENT_TYPE_KEYDOWN:
+ case PP_EVENT_TYPE_KEYUP:
+ BuildKeyEvent(&event, pp_event.get());
+ break;
+ case PP_EVENT_TYPE_CHAR:
+ BuildCharEvent(&event, pp_event.get());
+ break;
+ }
+
+ return pp_event.release();
+}
+
+WebInputEvent* CreateWebInputEvent(const PP_Event& event) {
+ scoped_ptr<WebInputEvent> web_input_event;
+ switch (event.type) {
+ case PP_EVENT_TYPE_UNDEFINED:
+ return NULL;
+ case PP_EVENT_TYPE_MOUSEDOWN:
+ case PP_EVENT_TYPE_MOUSEUP:
+ case PP_EVENT_TYPE_MOUSEMOVE:
+ case PP_EVENT_TYPE_MOUSEENTER:
+ case PP_EVENT_TYPE_MOUSELEAVE:
+ web_input_event.reset(BuildMouseEvent(event));
+ break;
+ case PP_EVENT_TYPE_MOUSEWHEEL:
+ web_input_event.reset(BuildMouseWheelEvent(event));
+ break;
+ case PP_EVENT_TYPE_RAWKEYDOWN:
+ case PP_EVENT_TYPE_KEYDOWN:
+ case PP_EVENT_TYPE_KEYUP:
+ web_input_event.reset(BuildKeyEvent(event));
+ break;
+ case PP_EVENT_TYPE_CHAR:
+ web_input_event.reset(BuildCharEvent(event));
+ break;
+ case PP_EVENT_TYPE_FOCUS:
+ // NOTIMPLEMENTED();
+ return NULL;
+ }
+
+ return web_input_event.release();
+}
+
+} // namespace pepper
diff --git a/webkit/glue/plugins/pepper_event_conversion.h b/webkit/glue/plugins/pepper_event_conversion.h
new file mode 100644
index 0000000..2d699cd
--- /dev/null
+++ b/webkit/glue/plugins/pepper_event_conversion.h
@@ -0,0 +1,26 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_PEPPER_EVENT_H_
+#define WEBKIT_GLUE_PLUGINS_PEPPER_EVENT_H_
+
+typedef struct _pp_Event PP_Event;
+
+namespace WebKit {
+class WebInputEvent;
+}
+
+namespace pepper {
+
+// Creates a PP_Event from the given WebInputEvent. If it fails, returns NULL.
+// The caller owns the created object on success.
+PP_Event* CreatePP_Event(const WebKit::WebInputEvent& event);
+
+// Creates a WebInputEvent from the given PP_Event. If it fails, returns NULL.
+// The caller owns the created object on success.
+WebKit::WebInputEvent* CreateWebInputEvent(const PP_Event& event);
+
+} // namespace pepper
+
+#endif // WEBKIT_GLUE_PLUGINS_PEPPER_EVENT_H_
diff --git a/webkit/glue/plugins/pepper_file_chooser.cc b/webkit/glue/plugins/pepper_file_chooser.cc
new file mode 100644
index 0000000..5e45600
--- /dev/null
+++ b/webkit/glue/plugins/pepper_file_chooser.cc
@@ -0,0 +1,88 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/pepper_file_chooser.h"
+
+#include "base/logging.h"
+#include "third_party/ppapi/c/pp_completion_callback.h"
+#include "third_party/ppapi/c/pp_errors.h"
+#include "webkit/glue/plugins/pepper_file_ref.h"
+#include "webkit/glue/plugins/pepper_plugin_instance.h"
+#include "webkit/glue/plugins/pepper_resource_tracker.h"
+
+namespace pepper {
+
+namespace {
+
+PP_Resource Create(PP_Instance instance_id,
+ const PP_FileChooserOptions* options) {
+ PluginInstance* instance = PluginInstance::FromPPInstance(instance_id);
+ if (!instance)
+ return 0;
+
+ FileChooser* chooser = new FileChooser(instance, options);
+ return chooser->GetReference();
+}
+
+bool IsFileChooser(PP_Resource resource) {
+ return !!Resource::GetAs<FileChooser>(resource);
+}
+
+int32_t Show(PP_Resource chooser_id, PP_CompletionCallback callback) {
+ scoped_refptr<FileChooser> chooser(
+ Resource::GetAs<FileChooser>(chooser_id));
+ if (!chooser)
+ return PP_ERROR_BADRESOURCE;
+
+ return chooser->Show(callback);
+}
+
+PP_Resource GetNextChosenFile(PP_Resource chooser_id) {
+ scoped_refptr<FileChooser> chooser(
+ Resource::GetAs<FileChooser>(chooser_id));
+ if (!chooser)
+ return 0;
+
+ scoped_refptr<FileRef> file_ref(chooser->GetNextChosenFile());
+ if (!file_ref)
+ return 0;
+
+ return file_ref->GetReference();
+}
+
+const PPB_FileChooser ppb_filechooser = {
+ &Create,
+ &IsFileChooser,
+ &Show,
+ &GetNextChosenFile
+};
+
+} // namespace
+
+FileChooser::FileChooser(PluginInstance* instance,
+ const PP_FileChooserOptions* options)
+ : Resource(instance->module()),
+ mode_(options->mode),
+ accept_mime_types_(options->accept_mime_types) {
+}
+
+FileChooser::~FileChooser() {
+}
+
+// static
+const PPB_FileChooser* FileChooser::GetInterface() {
+ return &ppb_filechooser;
+}
+
+int32_t FileChooser::Show(PP_CompletionCallback callback) {
+ NOTIMPLEMENTED(); // TODO(darin): Implement me!
+ return PP_ERROR_FAILED;
+}
+
+scoped_refptr<FileRef> FileChooser::GetNextChosenFile() {
+ NOTIMPLEMENTED(); // TODO(darin): Implement me!
+ return NULL;
+}
+
+} // namespace pepper
diff --git a/webkit/glue/plugins/pepper_file_chooser.h b/webkit/glue/plugins/pepper_file_chooser.h
new file mode 100644
index 0000000..8474188
--- /dev/null
+++ b/webkit/glue/plugins/pepper_file_chooser.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_PEPPER_FILE_CHOOSER_H_
+#define WEBKIT_GLUE_PLUGINS_PEPPER_FILE_CHOOSER_H_
+
+#include <string>
+
+#include "third_party/ppapi/c/ppb_file_chooser.h"
+#include "webkit/glue/plugins/pepper_resource.h"
+
+namespace pepper {
+
+class PluginInstance;
+
+class FileChooser : public Resource {
+ public:
+ FileChooser(PluginInstance* instance, const PP_FileChooserOptions* options);
+ virtual ~FileChooser();
+
+ // Returns a pointer to the interface implementing PPB_FileChooser that is
+ // exposed to the plugin.
+ static const PPB_FileChooser* GetInterface();
+
+ // Resource overrides.
+ FileChooser* AsFileChooser() { return this; }
+
+ // PPB_FileChooser implementation.
+ int32_t Show(PP_CompletionCallback callback);
+ scoped_refptr<FileRef> GetNextChosenFile();
+
+ private:
+ PP_FileChooserMode mode_;
+ std::string accept_mime_types_;
+};
+
+} // namespace pepper
+
+#endif // WEBKIT_GLUE_PLUGINS_PEPPER_FILE_CHOOSER_H_
diff --git a/webkit/glue/plugins/pepper_file_io.cc b/webkit/glue/plugins/pepper_file_io.cc
new file mode 100644
index 0000000..46f7276
--- /dev/null
+++ b/webkit/glue/plugins/pepper_file_io.cc
@@ -0,0 +1,255 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/pepper_file_io.h"
+
+#include "base/logging.h"
+#include "third_party/ppapi/c/pp_completion_callback.h"
+#include "third_party/ppapi/c/pp_errors.h"
+#include "third_party/ppapi/c/ppb_file_io.h"
+#include "third_party/ppapi/c/ppb_file_io_trusted.h"
+#include "webkit/glue/plugins/pepper_file_ref.h"
+#include "webkit/glue/plugins/pepper_plugin_module.h"
+#include "webkit/glue/plugins/pepper_resource_tracker.h"
+
+namespace pepper {
+
+namespace {
+
+PP_Resource Create(PP_Module module_id) {
+ PluginModule* module = PluginModule::FromPPModule(module_id);
+ if (!module)
+ return 0;
+
+ FileIO* file_io = new FileIO(module);
+ return file_io->GetReference();
+}
+
+bool IsFileIO(PP_Resource resource) {
+ return !!Resource::GetAs<FileIO>(resource);
+}
+
+int32_t Open(PP_Resource file_io_id,
+ PP_Resource file_ref_id,
+ int32_t open_flags,
+ PP_CompletionCallback callback) {
+ scoped_refptr<FileIO> file_io(Resource::GetAs<FileIO>(file_io_id));
+ if (!file_io)
+ return PP_ERROR_BADRESOURCE;
+
+ scoped_refptr<FileRef> file_ref(Resource::GetAs<FileRef>(file_ref_id));
+ if (!file_ref)
+ return PP_ERROR_BADRESOURCE;
+
+ return file_io->Open(file_ref, open_flags, callback);
+}
+
+int32_t Query(PP_Resource file_io_id,
+ PP_FileInfo* info,
+ PP_CompletionCallback callback) {
+ scoped_refptr<FileIO> file_io(Resource::GetAs<FileIO>(file_io_id));
+ if (!file_io)
+ return PP_ERROR_BADRESOURCE;
+
+ return file_io->Query(info, callback);
+}
+
+int32_t Touch(PP_Resource file_io_id,
+ PP_Time last_access_time,
+ PP_Time last_modified_time,
+ PP_CompletionCallback callback) {
+ scoped_refptr<FileIO> file_io(Resource::GetAs<FileIO>(file_io_id));
+ if (!file_io)
+ return PP_ERROR_BADRESOURCE;
+
+ return file_io->Touch(last_access_time, last_modified_time, callback);
+}
+
+int32_t Read(PP_Resource file_io_id,
+ int64_t offset,
+ char* buffer,
+ int32_t bytes_to_read,
+ PP_CompletionCallback callback) {
+ scoped_refptr<FileIO> file_io(Resource::GetAs<FileIO>(file_io_id));
+ if (!file_io)
+ return PP_ERROR_BADRESOURCE;
+
+ return file_io->Read(offset, buffer, bytes_to_read, callback);
+}
+
+int32_t Write(PP_Resource file_io_id,
+ int64_t offset,
+ const char* buffer,
+ int32_t bytes_to_write,
+ PP_CompletionCallback callback) {
+ scoped_refptr<FileIO> file_io(Resource::GetAs<FileIO>(file_io_id));
+ if (!file_io)
+ return PP_ERROR_BADRESOURCE;
+
+ return file_io->Write(offset, buffer, bytes_to_write, callback);
+}
+
+int32_t SetLength(PP_Resource file_io_id,
+ int64_t length,
+ PP_CompletionCallback callback) {
+ scoped_refptr<FileIO> file_io(Resource::GetAs<FileIO>(file_io_id));
+ if (!file_io)
+ return PP_ERROR_BADRESOURCE;
+
+ return file_io->SetLength(length, callback);
+}
+
+int32_t Flush(PP_Resource file_io_id,
+ PP_CompletionCallback callback) {
+ scoped_refptr<FileIO> file_io(Resource::GetAs<FileIO>(file_io_id));
+ if (!file_io)
+ return PP_ERROR_BADRESOURCE;
+
+ return file_io->Flush(callback);
+}
+
+void Close(PP_Resource file_io_id) {
+ scoped_refptr<FileIO> file_io(Resource::GetAs<FileIO>(file_io_id));
+ if (!file_io)
+ return;
+
+ file_io->Close();
+}
+
+const PPB_FileIO ppb_fileio = {
+ &Create,
+ &IsFileIO,
+ &Open,
+ &Query,
+ &Touch,
+ &Read,
+ &Write,
+ &SetLength,
+ &Flush,
+ &Close
+};
+
+int32_t GetOSFileDescriptor(PP_Resource file_io_id) {
+ scoped_refptr<FileIO> file_io(Resource::GetAs<FileIO>(file_io_id));
+ if (!file_io)
+ return PP_ERROR_BADRESOURCE;
+
+ return file_io->GetOSFileDescriptor();
+}
+
+int32_t WillWrite(PP_Resource file_io_id,
+ int64_t offset,
+ int32_t bytes_to_write,
+ PP_CompletionCallback callback) {
+ scoped_refptr<FileIO> file_io(Resource::GetAs<FileIO>(file_io_id));
+ if (!file_io)
+ return PP_ERROR_BADRESOURCE;
+
+ return file_io->WillWrite(offset, bytes_to_write, callback);
+}
+
+int32_t WillSetLength(PP_Resource file_io_id,
+ int64_t length,
+ PP_CompletionCallback callback) {
+ scoped_refptr<FileIO> file_io(Resource::GetAs<FileIO>(file_io_id));
+ if (!file_io)
+ return PP_ERROR_BADRESOURCE;
+
+ return file_io->WillSetLength(length, callback);
+}
+
+const PPB_FileIOTrusted ppb_fileiotrusted = {
+ &GetOSFileDescriptor,
+ &WillWrite,
+ &WillSetLength
+};
+
+} // namespace
+
+FileIO::FileIO(PluginModule* module) : Resource(module) {
+}
+
+FileIO::~FileIO() {
+}
+
+// static
+const PPB_FileIO* FileIO::GetInterface() {
+ return &ppb_fileio;
+}
+
+// static
+const PPB_FileIOTrusted* FileIO::GetTrustedInterface() {
+ return &ppb_fileiotrusted;
+}
+
+int32_t FileIO::Open(FileRef* file_ref,
+ int32_t open_flags,
+ PP_CompletionCallback callback) {
+ NOTIMPLEMENTED(); // TODO(darin): Implement me!
+ return PP_ERROR_FAILED;
+}
+
+int32_t FileIO::Query(PP_FileInfo* info,
+ PP_CompletionCallback callback) {
+ NOTIMPLEMENTED(); // TODO(darin): Implement me!
+ return PP_ERROR_FAILED;
+}
+
+int32_t FileIO::Touch(PP_Time last_access_time,
+ PP_Time last_modified_time,
+ PP_CompletionCallback callback) {
+ NOTIMPLEMENTED(); // TODO(darin): Implement me!
+ return PP_ERROR_FAILED;
+}
+
+int32_t FileIO::Read(int64_t offset,
+ char* buffer,
+ int32_t bytes_to_read,
+ PP_CompletionCallback callback) {
+ NOTIMPLEMENTED(); // TODO(darin): Implement me!
+ return PP_ERROR_FAILED;
+}
+
+int32_t FileIO::Write(int64_t offset,
+ const char* buffer,
+ int32_t bytes_to_write,
+ PP_CompletionCallback callback) {
+ NOTIMPLEMENTED(); // TODO(darin): Implement me!
+ return PP_ERROR_FAILED;
+}
+
+int32_t FileIO::SetLength(int64_t length,
+ PP_CompletionCallback callback) {
+ NOTIMPLEMENTED(); // TODO(darin): Implement me!
+ return PP_ERROR_FAILED;
+}
+
+int32_t FileIO::Flush(PP_CompletionCallback callback) {
+ NOTIMPLEMENTED(); // TODO(darin): Implement me!
+ return PP_ERROR_FAILED;
+}
+
+void FileIO::Close() {
+ NOTIMPLEMENTED(); // TODO(darin): Implement me!
+}
+
+int32_t FileIO::GetOSFileDescriptor() {
+ NOTIMPLEMENTED(); // TODO(darin): Implement me!
+ return PP_ERROR_FAILED;
+}
+
+int32_t FileIO::WillWrite(int64_t offset,
+ int32_t bytes_to_write,
+ PP_CompletionCallback callback) {
+ NOTIMPLEMENTED(); // TODO(darin): Implement me!
+ return PP_ERROR_FAILED;
+}
+
+int32_t FileIO::WillSetLength(int64_t length,
+ PP_CompletionCallback callback) {
+ NOTIMPLEMENTED(); // TODO(darin): Implement me!
+ return PP_ERROR_FAILED;
+}
+
+} // namespace pepper
diff --git a/webkit/glue/plugins/pepper_file_io.h b/webkit/glue/plugins/pepper_file_io.h
new file mode 100644
index 0000000..4af6f2b
--- /dev/null
+++ b/webkit/glue/plugins/pepper_file_io.h
@@ -0,0 +1,69 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_PEPPER_FILE_IO_H_
+#define WEBKIT_GLUE_PLUGINS_PEPPER_FILE_IO_H_
+
+#include "third_party/ppapi/c/pp_time.h"
+#include "webkit/glue/plugins/pepper_resource.h"
+
+typedef struct _pp_CompletionCallback PP_CompletionCallback;
+typedef struct _pp_FileInfo PP_FileInfo;
+typedef struct _ppb_FileIO PPB_FileIO;
+typedef struct _ppb_FileIOTrusted PPB_FileIOTrusted;
+
+namespace pepper {
+
+class PluginModule;
+
+class FileIO : public Resource {
+ public:
+ explicit FileIO(PluginModule* module);
+ virtual ~FileIO();
+
+ // Returns a pointer to the interface implementing PPB_FileIO that is exposed
+ // to the plugin.
+ static const PPB_FileIO* GetInterface();
+
+ // Returns a pointer to the interface implementing PPB_FileIOTrusted that is
+ // exposed to the plugin.
+ static const PPB_FileIOTrusted* GetTrustedInterface();
+
+ // Resource overrides.
+ FileIO* AsFileIO() { return this; }
+
+ // PPB_FileIO implementation.
+ int32_t Open(FileRef* file_ref,
+ int32_t open_flags,
+ PP_CompletionCallback callback);
+ int32_t Query(PP_FileInfo* info,
+ PP_CompletionCallback callback);
+ int32_t Touch(PP_Time last_access_time,
+ PP_Time last_modified_time,
+ PP_CompletionCallback callback);
+ int32_t Read(int64_t offset,
+ char* buffer,
+ int32_t bytes_to_read,
+ PP_CompletionCallback callback);
+ int32_t Write(int64_t offset,
+ const char* buffer,
+ int32_t bytes_to_write,
+ PP_CompletionCallback callback);
+ int32_t SetLength(int64_t length,
+ PP_CompletionCallback callback);
+ int32_t Flush(PP_CompletionCallback callback);
+ void Close();
+
+ // PPB_FileIOTrusted implementation.
+ int32_t GetOSFileDescriptor();
+ int32_t WillWrite(int64_t offset,
+ int32_t bytes_to_write,
+ PP_CompletionCallback callback);
+ int32_t WillSetLength(int64_t length,
+ PP_CompletionCallback callback);
+};
+
+} // namespace pepper
+
+#endif // WEBKIT_GLUE_PLUGINS_PEPPER_FILE_IO_H_
diff --git a/webkit/glue/plugins/pepper_file_ref.cc b/webkit/glue/plugins/pepper_file_ref.cc
new file mode 100644
index 0000000..9b42cff
--- /dev/null
+++ b/webkit/glue/plugins/pepper_file_ref.cc
@@ -0,0 +1,169 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/pepper_file_ref.h"
+
+#include "base/string_util.h"
+#include "webkit/glue/plugins/pepper_plugin_instance.h"
+#include "webkit/glue/plugins/pepper_var.h"
+#include "webkit/glue/plugins/pepper_resource_tracker.h"
+
+namespace pepper {
+
+namespace {
+
+bool IsValidLocalPath(const std::string& path) {
+ // The path must start with '/'
+ if (path.empty() || path[0] != '/')
+ return false;
+
+ // The path must contain valid UTF-8 characters.
+ if (!IsStringUTF8(path))
+ return false;
+
+ return true;
+}
+
+void TrimTrailingSlash(std::string* path) {
+ // If this path ends with a slash, then normalize it away unless path is the
+ // root path.
+ if (path->size() > 1 && path->at(path->size() - 1) == '/')
+ path->erase(path->size() - 1, 1);
+}
+
+PP_Resource CreateFileRef(PP_Instance instance_id,
+ PP_FileSystemType fs_type,
+ const char* path) {
+ PluginInstance* instance = PluginInstance::FromPPInstance(instance_id);
+ if (!instance)
+ return 0;
+
+ std::string origin; // TODO(darin): Extract from PluginInstance.
+
+ std::string validated_path(path);
+ if (!IsValidLocalPath(validated_path))
+ return 0;
+ TrimTrailingSlash(&validated_path);
+
+ FileRef* file_ref = new FileRef(instance->module(),
+ fs_type,
+ validated_path,
+ origin);
+ return file_ref->GetReference();
+}
+
+PP_Resource CreatePersistentFileRef(PP_Instance instance_id, const char* path) {
+ return CreateFileRef(instance_id, PP_FILESYSTEMTYPE_LOCALPERSISTENT, path);
+}
+
+PP_Resource CreateTemporaryFileRef(PP_Instance instance_id, const char* path) {
+ return CreateFileRef(instance_id, PP_FILESYSTEMTYPE_LOCALTEMPORARY, path);
+}
+
+bool IsFileRef(PP_Resource resource) {
+ return !!Resource::GetAs<FileRef>(resource);
+}
+
+PP_FileSystemType GetFileSystemType(PP_Resource file_ref_id) {
+ scoped_refptr<FileRef> file_ref(Resource::GetAs<FileRef>(file_ref_id));
+ if (!file_ref)
+ return PP_FILESYSTEMTYPE_EXTERNAL;
+
+ return file_ref->file_system_type();
+}
+
+PP_Var GetName(PP_Resource file_ref_id) {
+ scoped_refptr<FileRef> file_ref(Resource::GetAs<FileRef>(file_ref_id));
+ if (!file_ref)
+ return PP_MakeVoid();
+
+ return StringToPPVar(file_ref->GetName());
+}
+
+PP_Var GetPath(PP_Resource file_ref_id) {
+ scoped_refptr<FileRef> file_ref(Resource::GetAs<FileRef>(file_ref_id));
+ if (!file_ref)
+ return PP_MakeVoid();
+
+ if (file_ref->file_system_type() == PP_FILESYSTEMTYPE_EXTERNAL)
+ return PP_MakeVoid();
+
+ return StringToPPVar(file_ref->path());
+}
+
+PP_Resource GetParent(PP_Resource file_ref_id) {
+ scoped_refptr<FileRef> file_ref(Resource::GetAs<FileRef>(file_ref_id));
+ if (!file_ref)
+ return 0;
+
+ if (file_ref->file_system_type() == PP_FILESYSTEMTYPE_EXTERNAL)
+ return 0;
+
+ scoped_refptr<FileRef> parent_ref(file_ref->GetParent());
+ if (!parent_ref)
+ return 0;
+
+ return parent_ref->GetReference();
+}
+
+const PPB_FileRef ppb_fileref = {
+ &CreatePersistentFileRef,
+ &CreateTemporaryFileRef,
+ &IsFileRef,
+ &GetFileSystemType,
+ &GetName,
+ &GetPath,
+ &GetParent
+};
+
+} // namespace
+
+FileRef::FileRef(PluginModule* module,
+ PP_FileSystemType file_system_type,
+ const std::string& validated_path,
+ const std::string& origin)
+ : Resource(module),
+ fs_type_(file_system_type),
+ path_(validated_path),
+ origin_(origin) {
+ // TODO(darin): Need to initialize system_path_.
+}
+
+FileRef::~FileRef() {
+}
+
+// static
+const PPB_FileRef* FileRef::GetInterface() {
+ return &ppb_fileref;
+}
+
+std::string FileRef::GetName() const {
+ if (path_.size() == 1 && path_[0] == '/')
+ return path_;
+
+ // There should always be a leading slash at least!
+ size_t pos = path_.rfind('/');
+ DCHECK(pos != std::string::npos);
+
+ return path_.substr(pos + 1);
+}
+
+scoped_refptr<FileRef> FileRef::GetParent() {
+ if (path_.size() == 1 && path_[0] == '/')
+ return this;
+
+ // There should always be a leading slash at least!
+ size_t pos = path_.rfind('/');
+ DCHECK(pos != std::string::npos);
+
+ // If the path is "/foo", then we want to include the slash.
+ if (pos == 0)
+ pos++;
+ std::string parent_path = path_.substr(0, pos);
+
+ FileRef* parent_ref = new FileRef(module(), fs_type_, parent_path, origin_);
+ return parent_ref;
+}
+
+} // namespace pepper
diff --git a/webkit/glue/plugins/pepper_file_ref.h b/webkit/glue/plugins/pepper_file_ref.h
new file mode 100644
index 0000000..34e4c3e
--- /dev/null
+++ b/webkit/glue/plugins/pepper_file_ref.h
@@ -0,0 +1,55 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_PEPPER_FILE_REF_H_
+#define WEBKIT_GLUE_PLUGINS_PEPPER_FILE_REF_H_
+
+#include <string>
+
+#include "base/file_path.h"
+#include "third_party/ppapi/c/ppb_file_ref.h"
+#include "webkit/glue/plugins/pepper_resource.h"
+
+namespace pepper {
+
+class PluginModule;
+
+class FileRef : public Resource {
+ public:
+ FileRef(PluginModule* module,
+ PP_FileSystemType file_system_type,
+ const std::string& validated_path,
+ const std::string& origin);
+ virtual ~FileRef();
+
+ // Returns a pointer to the interface implementing PPB_FileRef that is
+ // exposed to the plugin.
+ static const PPB_FileRef* GetInterface();
+
+ // Resource overrides.
+ FileRef* AsFileRef() { return this; }
+
+ // PPB_FileRef implementation.
+ std::string GetName() const;
+ scoped_refptr<FileRef> GetParent();
+
+ PP_FileSystemType file_system_type() const { return fs_type_; }
+
+ // Returns the virtual path (i.e., the path that the pepper plugin sees)
+ // corresponding to this file.
+ const std::string& path() const { return path_; }
+
+ // Returns the system path corresponding to this file.
+ const FilePath& system_path() const { return system_path_; }
+
+ private:
+ FilePath system_path_;
+ PP_FileSystemType fs_type_;
+ std::string path_; // UTF-8 encoded.
+ std::string origin_;
+};
+
+} // namespace pepper
+
+#endif // WEBKIT_GLUE_PLUGINS_PEPPER_FILE_REF_H_
diff --git a/webkit/glue/plugins/pepper_file_system.cc b/webkit/glue/plugins/pepper_file_system.cc
new file mode 100644
index 0000000..678399e
--- /dev/null
+++ b/webkit/glue/plugins/pepper_file_system.cc
@@ -0,0 +1,59 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/pepper_file_system.h"
+
+#include "third_party/ppapi/c/pp_completion_callback.h"
+#include "third_party/ppapi/c/pp_errors.h"
+#include "third_party/ppapi/c/ppb_file_system.h"
+
+namespace pepper {
+
+namespace {
+
+int32_t MakeDirectory(PP_Resource directory_ref,
+ bool make_ancestors,
+ PP_CompletionCallback callback) {
+ return PP_ERROR_FAILED; // TODO(darin): Implement me!
+}
+
+int32_t Query(PP_Resource file_ref,
+ PP_FileInfo* info,
+ PP_CompletionCallback callback) {
+ return PP_ERROR_FAILED; // TODO(darin): Implement me!
+}
+
+int32_t Touch(PP_Resource file_ref,
+ PP_Time last_access_time,
+ PP_Time last_modified_time,
+ PP_CompletionCallback callback) {
+ return PP_ERROR_FAILED; // TODO(darin): Implement me!
+}
+
+int32_t Delete(PP_Resource file_ref,
+ PP_CompletionCallback callback) {
+ return PP_ERROR_FAILED; // TODO(darin): Implement me!
+}
+
+int32_t Rename(PP_Resource file_ref,
+ PP_Resource new_file_ref,
+ PP_CompletionCallback callback) {
+ return PP_ERROR_FAILED; // TODO(darin): Implement me!
+}
+
+const PPB_FileSystem ppb_filesystem = {
+ &MakeDirectory,
+ &Query,
+ &Touch,
+ &Delete,
+ &Rename
+};
+
+} // namespace
+
+const PPB_FileSystem* FileSystem::GetInterface() {
+ return &ppb_filesystem;
+}
+
+} // namespace pepper
diff --git a/webkit/glue/plugins/pepper_file_system.h b/webkit/glue/plugins/pepper_file_system.h
new file mode 100644
index 0000000..b8ad01a
--- /dev/null
+++ b/webkit/glue/plugins/pepper_file_system.h
@@ -0,0 +1,21 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_PEPPER_FILE_SYSTEM_H_
+#define WEBKIT_GLUE_PLUGINS_PEPPER_FILE_SYSTEM_H_
+
+typedef struct _ppb_FileSystem PPB_FileSystem;
+
+namespace pepper {
+
+class FileSystem {
+ public:
+ // Returns a pointer to the interface implementing PPB_FileSystem that is
+ // exposed to the plugin.
+ static const PPB_FileSystem* GetInterface();
+};
+
+} // namespace pepper
+
+#endif // WEBKIT_GLUE_PLUGINS_PEPPER_FILE_SYSTEM_H_
diff --git a/webkit/glue/plugins/pepper_font.cc b/webkit/glue/plugins/pepper_font.cc
new file mode 100644
index 0000000..af4cb81
--- /dev/null
+++ b/webkit/glue/plugins/pepper_font.cc
@@ -0,0 +1,99 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "build/build_config.h"
+
+#include "webkit/glue/plugins/pepper_font.h"
+
+#if defined(OS_LINUX)
+#include <unistd.h>
+#endif
+
+#include "base/logging.h"
+#include "third_party/ppapi/c/ppb_font.h"
+#include "webkit/glue/plugins/pepper_plugin_module.h"
+#include "webkit/glue/webkit_glue.h"
+
+namespace pepper {
+
+namespace {
+
+PP_Resource MatchFontWithFallback(PP_Module module_id,
+ const PP_FontDescription* description) {
+#if defined(OS_LINUX)
+ PluginModule* module = PluginModule::FromPPModule(module_id);
+ if (!module)
+ return NULL;
+
+ int fd = webkit_glue::MatchFontWithFallback(description->face,
+ description->weight >= 700,
+ description->italic,
+ description->charset);
+ if (fd == -1)
+ return NULL;
+
+ scoped_refptr<Font> font(new Font(module, fd));
+
+ return font->GetReference();
+#else
+ // For trusted pepper plugins, this is only needed in Linux since font loading
+ // on Windows and Mac works through the renderer sandbox.
+ return false;
+#endif
+}
+
+bool IsFont(PP_Resource resource) {
+ return !!Resource::GetAs<Font>(resource);
+}
+
+bool GetFontTable(PP_Resource font_id,
+ uint32_t table,
+ void* output,
+ uint32_t* output_length) {
+ scoped_refptr<Font> font(Resource::GetAs<Font>(font_id));
+ if (!font.get())
+ return false;
+
+ return font->GetFontTable(table, output, output_length);
+}
+
+const PPB_Font ppb_font = {
+ &MatchFontWithFallback,
+ &IsFont,
+ &GetFontTable,
+};
+
+} // namespace
+
+Font::Font(PluginModule* module, int fd)
+ : Resource(module),
+ fd_(fd) {
+}
+
+Font::~Font() {
+#if defined (OS_LINUX)
+ close(fd_);
+#endif
+}
+
+// static
+const PPB_Font* Font::GetInterface() {
+ return &ppb_font;
+}
+
+bool Font::GetFontTable(uint32_t table,
+ void* output,
+ uint32_t* output_length) {
+#if defined(OS_LINUX)
+ size_t temp_size = static_cast<size_t>(*output_length);
+ bool rv = webkit_glue::GetFontTable(
+ fd_, table, static_cast<uint8_t*>(output), &temp_size);
+ *output_length = static_cast<uint32_t>(temp_size);
+ return rv;
+#else
+ return false;
+#endif
+}
+
+} // namespace pepper
diff --git a/webkit/glue/plugins/pepper_font.h b/webkit/glue/plugins/pepper_font.h
new file mode 100644
index 0000000..ad1abba
--- /dev/null
+++ b/webkit/glue/plugins/pepper_font.h
@@ -0,0 +1,39 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_PEPPER_FONT_H_
+#define WEBKIT_GLUE_PLUGINS_PEPPER_FONT_H_
+
+#include "webkit/glue/plugins/pepper_resource.h"
+
+typedef struct _ppb_Font PPB_Font;
+
+namespace pepper {
+
+class PluginInstance;
+
+class Font : public Resource {
+ public:
+ Font(PluginModule* module, int fd);
+ virtual ~Font();
+
+ // Returns a pointer to the interface implementing PPB_Font that is exposed to
+ // the plugin.
+ static const PPB_Font* GetInterface();
+
+ // Resource overrides.
+ Font* AsFont() { return this; }
+
+ // PPB_Font implementation.
+ bool GetFontTable(uint32_t table,
+ void* output,
+ uint32_t* output_length);
+
+ private:
+ int fd_;
+};
+
+} // namespace pepper
+
+#endif // WEBKIT_GLUE_PLUGINS_PEPPER_FONT_H_
diff --git a/webkit/glue/plugins/pepper_image_data.cc b/webkit/glue/plugins/pepper_image_data.cc
new file mode 100644
index 0000000..8288fe2
--- /dev/null
+++ b/webkit/glue/plugins/pepper_image_data.cc
@@ -0,0 +1,159 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/pepper_image_data.h"
+
+#include <algorithm>
+#include <limits>
+
+#include "base/logging.h"
+#include "base/scoped_ptr.h"
+#include "skia/ext/platform_canvas.h"
+#include "third_party/ppapi/c/pp_instance.h"
+#include "third_party/ppapi/c/pp_module.h"
+#include "third_party/ppapi/c/pp_resource.h"
+#include "third_party/ppapi/c/ppb_image_data.h"
+#include "webkit/glue/plugins/pepper_plugin_instance.h"
+#include "webkit/glue/plugins/pepper_plugin_module.h"
+
+namespace pepper {
+
+namespace {
+
+PP_ImageDataFormat GetNativeImageDataFormat() {
+ return PP_IMAGEDATAFORMAT_BGRA_PREMUL;
+}
+
+PP_Resource Create(PP_Module module_id,
+ PP_ImageDataFormat format,
+ const PP_Size* size,
+ bool init_to_zero) {
+ PluginModule* module = PluginModule::FromPPModule(module_id);
+ if (!module)
+ return NULL;
+
+ scoped_refptr<ImageData> data(new ImageData(module));
+ if (!data->Init(format, size->width, size->height, init_to_zero))
+ return NULL;
+
+ return data->GetReference();
+}
+
+bool IsImageData(PP_Resource resource) {
+ return !!Resource::GetAs<ImageData>(resource);
+}
+
+bool Describe(PP_Resource resource, PP_ImageDataDesc* desc) {
+ // Give predictable values on failure.
+ memset(desc, 0, sizeof(PP_ImageDataDesc));
+
+ scoped_refptr<ImageData> image_data(Resource::GetAs<ImageData>(resource));
+ if (!image_data)
+ return false;
+ image_data->Describe(desc);
+ return true;
+}
+
+void* Map(PP_Resource resource) {
+ scoped_refptr<ImageData> image_data(Resource::GetAs<ImageData>(resource));
+ if (!image_data)
+ return NULL;
+ return image_data->Map();
+}
+
+void Unmap(PP_Resource resource) {
+ scoped_refptr<ImageData> image_data(Resource::GetAs<ImageData>(resource));
+ if (image_data)
+ image_data->Unmap();
+}
+
+const PPB_ImageData ppb_imagedata = {
+ &GetNativeImageDataFormat,
+ &Create,
+ &IsImageData,
+ &Describe,
+ &Map,
+ &Unmap,
+};
+
+} // namespace
+
+ImageData::ImageData(PluginModule* module)
+ : Resource(module),
+ width_(0),
+ height_(0) {
+}
+
+ImageData::~ImageData() {
+}
+
+// static
+const PPB_ImageData* ImageData::GetInterface() {
+ return &ppb_imagedata;
+}
+
+bool ImageData::Init(PP_ImageDataFormat format,
+ int width, int height,
+ bool init_to_zero) {
+ // TODO(brettw) this should be called only on the main thread!
+ // TODO(brettw) use init_to_zero when we implement caching.
+ if (format != PP_IMAGEDATAFORMAT_BGRA_PREMUL)
+ return false; // Only support this one format for now.
+ if (width <= 0 || height <= 0)
+ return false;
+ if (static_cast<int64>(width) * static_cast<int64>(height) >=
+ std::numeric_limits<int32>::max())
+ return false; // Prevent overflow of signed 32-bit ints.
+
+ platform_image_.reset(
+ module()->GetSomeInstance()->delegate()->CreateImage2D(width, height));
+ width_ = width;
+ height_ = height;
+ return !!platform_image_.get();
+}
+
+void ImageData::Describe(PP_ImageDataDesc* desc) const {
+ desc->format = PP_IMAGEDATAFORMAT_BGRA_PREMUL;
+ desc->size.width = width_;
+ desc->size.height = height_;
+ desc->stride = width_ * 4;
+}
+
+void* ImageData::Map() {
+ if (!mapped_canvas_.get()) {
+ mapped_canvas_.reset(platform_image_->Map());
+ if (!mapped_canvas_.get())
+ return NULL;
+ }
+ const SkBitmap& bitmap =
+ mapped_canvas_->getTopPlatformDevice().accessBitmap(true);
+
+ // Our platform bitmaps are set to opaque by default, which we don't want.
+ const_cast<SkBitmap&>(bitmap).setIsOpaque(false);
+
+ bitmap.lockPixels();
+ return bitmap.getAddr32(0, 0);
+}
+
+void ImageData::Unmap() {
+ // This is currently unimplemented, which is OK. The data will just always
+ // be around once it's mapped. Chrome's TransportDIB isn't currently
+ // unmappable without freeing it, but this may be something we want to support
+ // in the future to save some memory.
+}
+
+const SkBitmap* ImageData::GetMappedBitmap() const {
+ if (!mapped_canvas_.get())
+ return NULL;
+ return &mapped_canvas_->getTopPlatformDevice().accessBitmap(false);
+}
+
+void ImageData::Swap(ImageData* other) {
+ swap(other->platform_image_, platform_image_);
+ swap(other->mapped_canvas_, mapped_canvas_);
+ std::swap(other->width_, width_);
+ std::swap(other->height_, height_);
+}
+
+} // namespace pepper
diff --git a/webkit/glue/plugins/pepper_image_data.h b/webkit/glue/plugins/pepper_image_data.h
new file mode 100644
index 0000000..7652b80
--- /dev/null
+++ b/webkit/glue/plugins/pepper_image_data.h
@@ -0,0 +1,116 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_PEPPER_IMAGE_DATA_H_
+#define WEBKIT_GLUE_PLUGINS_PEPPER_IMAGE_DATA_H_
+
+#include "base/basictypes.h"
+#include "base/scoped_ptr.h"
+#include "third_party/ppapi/c/ppb_image_data.h"
+#include "webkit/glue/plugins/pepper_plugin_delegate.h"
+#include "webkit/glue/plugins/pepper_resource.h"
+
+namespace skia {
+class PlatformCanvas;
+}
+
+class SkBitmap;
+
+namespace pepper {
+
+class ImageData : public Resource {
+ public:
+ explicit ImageData(PluginModule* module);
+ virtual ~ImageData();
+
+ int width() const { return width_; }
+ int height() const { return height_; }
+
+ // Returns the image format. Currently there is only one format so this
+ // always returns the same thing. But if you care about the formation, you
+ // should probably check this so when we support multiple formats, we can't
+ // forget to update your code.
+ PP_ImageDataFormat format() const { return PP_IMAGEDATAFORMAT_BGRA_PREMUL; }
+
+ // Returns true if this image is mapped. False means that the image is either
+ // invalid or not mapped. See ImageDataAutoMapper below.
+ bool is_mapped() const { return !!mapped_canvas_.get(); }
+
+ // Returns a pointer to the interface implementing PPB_ImageData that is
+ // exposed to the plugin.
+ static const PPB_ImageData* GetInterface();
+
+ // Resource overrides.
+ ImageData* AsImageData() { return this; }
+
+ // PPB_ImageData implementation.
+ bool Init(PP_ImageDataFormat format,
+ int width, int height,
+ bool init_to_zero);
+ void Describe(PP_ImageDataDesc* desc) const;
+ void* Map();
+ void Unmap();
+
+ // The mapped bitmap and canvas will be NULL if the image is not mapped.
+ skia::PlatformCanvas* mapped_canvas() const { return mapped_canvas_.get(); }
+ const SkBitmap* GetMappedBitmap() const;
+
+ // Swaps the guts of this image data with another.
+ void Swap(ImageData* other);
+
+ private:
+ // This will be NULL before initialization, and if this ImageData is
+ // swapped with another.
+ scoped_ptr<PluginDelegate::PlatformImage2D> platform_image_;
+
+ // When the device is mapped, this is the image. Null when umapped.
+ scoped_ptr<skia::PlatformCanvas> mapped_canvas_;
+
+ int width_;
+ int height_;
+
+ DISALLOW_COPY_AND_ASSIGN(ImageData);
+};
+
+// Manages mapping an image resource if necessary. Use this to ensure the
+// image is mapped. The destructor will put the image back into the previous
+// state. You must check is_valid() to make sure the image was successfully
+// mapped before using it.
+//
+// Example:
+// ImageDataAutoMapper mapper(image_data);
+// if (!mapper.is_valid())
+// return utter_failure;
+// image_data->mapped_canvas()->blah(); // Guaranteed valid.
+class ImageDataAutoMapper {
+ public:
+ ImageDataAutoMapper(ImageData* image_data) : image_data_(image_data) {
+ if (image_data_->is_mapped()) {
+ is_valid_ = true;
+ needs_unmap_ = false;
+ } else {
+ is_valid_ = needs_unmap_ = !!image_data_->Map();
+ }
+ }
+
+ ~ImageDataAutoMapper() {
+ if (needs_unmap_)
+ image_data_->Unmap();
+ }
+
+ // Check this to see if the image was successfully mapped. If this is false,
+ // the image could not be mapped and is unusable.
+ bool is_valid() const { return is_valid_; }
+
+ private:
+ ImageData* image_data_;
+ bool is_valid_;
+ bool needs_unmap_;
+
+ DISALLOW_COPY_AND_ASSIGN(ImageDataAutoMapper);
+};
+
+} // namespace pepper
+
+#endif // WEBKIT_GLUE_PLUGINS_PEPPER_IMAGE_DATA_H_
diff --git a/webkit/glue/plugins/pepper_plugin_delegate.h b/webkit/glue/plugins/pepper_plugin_delegate.h
new file mode 100644
index 0000000..ffc9d52
--- /dev/null
+++ b/webkit/glue/plugins/pepper_plugin_delegate.h
@@ -0,0 +1,59 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_PEPPER_PLUGIN_DELEGATE_H_
+#define WEBKIT_GLUE_PLUGINS_PEPPER_PLUGIN_DELEGATE_H_
+
+#include "third_party/ppapi/c/pp_stdint.h"
+
+namespace skia {
+class PlatformCanvas;
+}
+
+namespace pepper {
+
+class PluginInstance;
+
+// Virtual interface that the browser implements to implement features for
+// Pepper plugins.
+class PluginDelegate {
+ public:
+ // Represents an image. This is to allow the browser layer to supply a correct
+ // image representation. In Chrome, this will be a TransportDIB.
+ class PlatformImage2D {
+ public:
+ virtual ~PlatformImage2D() {}
+
+ // Caller will own the returned pointer, returns NULL on failure.
+ virtual skia::PlatformCanvas* Map() = 0;
+
+ // Returns the platform-specific shared memory handle of the data backing
+ // this image. This is used by NativeClient to send the image to the
+ // out-of-process plugin. Returns 0 on failure.
+ virtual intptr_t GetSharedMemoryHandle() const = 0;
+ };
+
+ // Indicates that the given instance has been created.
+ virtual void InstanceCreated(pepper::PluginInstance* instance) = 0;
+
+ // Indicates that the given instance is being destroyed. This is called from
+ // the destructor, so it's important that the instance is not dereferenced
+ // from this call.
+ virtual void InstanceDeleted(pepper::PluginInstance* instance) = 0;
+
+ // The caller will own the pointer returned from this.
+ virtual PlatformImage2D* CreateImage2D(int width, int height) = 0;
+
+ // Notifies that the number of find results has changed.
+ virtual void DidChangeNumberOfFindResults(int identifier,
+ int total,
+ bool final_result) = 0;
+
+ // Notifies that the index of the currently selected item has been updated.
+ virtual void DidChangeSelectedFindResult(int identifier, int index) = 0;
+};
+
+} // namespace pepper
+
+#endif // WEBKIT_GLUE_PLUGINS_PEPPER_PLUGIN_DELEGATE_H_
diff --git a/webkit/glue/plugins/pepper_plugin_instance.cc b/webkit/glue/plugins/pepper_plugin_instance.cc
new file mode 100644
index 0000000..6a7bf54
--- /dev/null
+++ b/webkit/glue/plugins/pepper_plugin_instance.cc
@@ -0,0 +1,828 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/pepper_plugin_instance.h"
+
+#include "base/logging.h"
+#include "base/histogram.h"
+#if defined(OS_MACOSX)
+#include "base/mac_util.h"
+#include "base/scoped_cftyperef.h"
+#endif
+#include "base/scoped_ptr.h"
+#include "base/utf_string_conversions.h"
+#include "gfx/rect.h"
+#if defined(OS_WIN)
+#include "gfx/codec/jpeg_codec.h"
+#include "gfx/gdi_util.h"
+#endif
+#include "gfx/skia_util.h"
+#include "printing/native_metafile.h"
+#include "printing/units.h"
+#include "skia/ext/vector_platform_device.h"
+#include "skia/ext/platform_canvas.h"
+#include "third_party/ppapi/c/pp_instance.h"
+#include "third_party/ppapi/c/pp_event.h"
+#include "third_party/ppapi/c/pp_rect.h"
+#include "third_party/ppapi/c/pp_resource.h"
+#include "third_party/ppapi/c/pp_var.h"
+#include "third_party/ppapi/c/ppb_core.h"
+#include "third_party/ppapi/c/ppb_find.h"
+#include "third_party/ppapi/c/ppb_instance.h"
+#include "third_party/ppapi/c/ppp_find.h"
+#include "third_party/ppapi/c/ppp_instance.h"
+#include "third_party/ppapi/c/ppp_zoom.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebCursorInfo.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebDocument.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebElement.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebFrame.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebInputEvent.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebPluginContainer.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebRect.h"
+#include "webkit/glue/plugins/pepper_buffer.h"
+#include "webkit/glue/plugins/pepper_device_context_2d.h"
+#include "webkit/glue/plugins/pepper_event_conversion.h"
+#include "webkit/glue/plugins/pepper_image_data.h"
+#include "webkit/glue/plugins/pepper_plugin_delegate.h"
+#include "webkit/glue/plugins/pepper_plugin_module.h"
+#include "webkit/glue/plugins/pepper_string.h"
+#include "webkit/glue/plugins/pepper_url_loader.h"
+#include "webkit/glue/plugins/pepper_var.h"
+
+using WebKit::WebCanvas;
+using WebKit::WebCursorInfo;
+using WebKit::WebFrame;
+using WebKit::WebInputEvent;
+using WebKit::WebPluginContainer;
+
+namespace pepper {
+
+#if defined(OS_WIN)
+// Exported by pdf.dll
+typedef bool (*RenderPDFPageToDCProc)(
+ const unsigned char* pdf_buffer, int buffer_size, int page_number, HDC dc,
+ int dpi_x, int dpi_y, int bounds_origin_x, int bounds_origin_y,
+ int bounds_width, int bounds_height, bool fit_to_bounds,
+ bool stretch_to_bounds, bool keep_aspect_ratio, bool center_in_bounds);
+#endif // defined(OS_WIN)
+
+namespace {
+
+#define COMPILE_ASSERT_MATCHING_ENUM(webkit_name, np_name) \
+ COMPILE_ASSERT(int(WebCursorInfo::webkit_name) == int(np_name), \
+ mismatching_enums)
+
+COMPILE_ASSERT_MATCHING_ENUM(TypePointer, PP_CURSORTYPE_POINTER);
+COMPILE_ASSERT_MATCHING_ENUM(TypeCross, PP_CURSORTYPE_CROSS);
+COMPILE_ASSERT_MATCHING_ENUM(TypeHand, PP_CURSORTYPE_HAND);
+COMPILE_ASSERT_MATCHING_ENUM(TypeIBeam, PP_CURSORTYPE_IBEAM);
+COMPILE_ASSERT_MATCHING_ENUM(TypeWait, PP_CURSORTYPE_WAIT);
+COMPILE_ASSERT_MATCHING_ENUM(TypeHelp, PP_CURSORTYPE_HELP);
+COMPILE_ASSERT_MATCHING_ENUM(TypeEastResize, PP_CURSORTYPE_EASTRESIZE);
+COMPILE_ASSERT_MATCHING_ENUM(TypeNorthResize, PP_CURSORTYPE_NORTHRESIZE);
+COMPILE_ASSERT_MATCHING_ENUM(TypeNorthEastResize,
+ PP_CURSORTYPE_NORTHEASTRESIZE);
+COMPILE_ASSERT_MATCHING_ENUM(TypeNorthWestResize,
+ PP_CURSORTYPE_NORTHWESTRESIZE);
+COMPILE_ASSERT_MATCHING_ENUM(TypeSouthResize, PP_CURSORTYPE_SOUTHRESIZE);
+COMPILE_ASSERT_MATCHING_ENUM(TypeSouthEastResize,
+ PP_CURSORTYPE_SOUTHEASTRESIZE);
+COMPILE_ASSERT_MATCHING_ENUM(TypeSouthWestResize,
+ PP_CURSORTYPE_SOUTHWESTRESIZE);
+COMPILE_ASSERT_MATCHING_ENUM(TypeWestResize, PP_CURSORTYPE_WESTRESIZE);
+COMPILE_ASSERT_MATCHING_ENUM(TypeNorthSouthResize,
+ PP_CURSORTYPE_NORTHSOUTHRESIZE);
+COMPILE_ASSERT_MATCHING_ENUM(TypeEastWestResize, PP_CURSORTYPE_EASTWESTRESIZE);
+COMPILE_ASSERT_MATCHING_ENUM(TypeNorthEastSouthWestResize,
+ PP_CURSORTYPE_NORTHEASTSOUTHWESTRESIZE);
+COMPILE_ASSERT_MATCHING_ENUM(TypeNorthWestSouthEastResize,
+ PP_CURSORTYPE_NORTHWESTSOUTHEASTRESIZE);
+COMPILE_ASSERT_MATCHING_ENUM(TypeColumnResize, PP_CURSORTYPE_COLUMNRESIZE);
+COMPILE_ASSERT_MATCHING_ENUM(TypeRowResize, PP_CURSORTYPE_ROWRESIZE);
+COMPILE_ASSERT_MATCHING_ENUM(TypeMiddlePanning, PP_CURSORTYPE_MIDDLEPANNING);
+COMPILE_ASSERT_MATCHING_ENUM(TypeEastPanning, PP_CURSORTYPE_EASTPANNING);
+COMPILE_ASSERT_MATCHING_ENUM(TypeNorthPanning, PP_CURSORTYPE_NORTHPANNING);
+COMPILE_ASSERT_MATCHING_ENUM(TypeNorthEastPanning,
+ PP_CURSORTYPE_NORTHEASTPANNING);
+COMPILE_ASSERT_MATCHING_ENUM(TypeNorthWestPanning,
+ PP_CURSORTYPE_NORTHWESTPANNING);
+COMPILE_ASSERT_MATCHING_ENUM(TypeSouthPanning, PP_CURSORTYPE_SOUTHPANNING);
+COMPILE_ASSERT_MATCHING_ENUM(TypeSouthEastPanning,
+ PP_CURSORTYPE_SOUTHEASTPANNING);
+COMPILE_ASSERT_MATCHING_ENUM(TypeSouthWestPanning,
+ PP_CURSORTYPE_SOUTHWESTPANNING);
+COMPILE_ASSERT_MATCHING_ENUM(TypeWestPanning, PP_CURSORTYPE_WESTPANNING);
+COMPILE_ASSERT_MATCHING_ENUM(TypeMove, PP_CURSORTYPE_MOVE);
+COMPILE_ASSERT_MATCHING_ENUM(TypeVerticalText, PP_CURSORTYPE_VERTICALTEXT);
+COMPILE_ASSERT_MATCHING_ENUM(TypeCell, PP_CURSORTYPE_CELL);
+COMPILE_ASSERT_MATCHING_ENUM(TypeContextMenu, PP_CURSORTYPE_CONTEXTMENU);
+COMPILE_ASSERT_MATCHING_ENUM(TypeAlias, PP_CURSORTYPE_ALIAS);
+COMPILE_ASSERT_MATCHING_ENUM(TypeProgress, PP_CURSORTYPE_PROGRESS);
+COMPILE_ASSERT_MATCHING_ENUM(TypeNoDrop, PP_CURSORTYPE_NODROP);
+COMPILE_ASSERT_MATCHING_ENUM(TypeCopy, PP_CURSORTYPE_COPY);
+COMPILE_ASSERT_MATCHING_ENUM(TypeNone, PP_CURSORTYPE_NONE);
+COMPILE_ASSERT_MATCHING_ENUM(TypeNotAllowed, PP_CURSORTYPE_NOTALLOWED);
+COMPILE_ASSERT_MATCHING_ENUM(TypeZoomIn, PP_CURSORTYPE_ZOOMIN);
+COMPILE_ASSERT_MATCHING_ENUM(TypeZoomOut, PP_CURSORTYPE_ZOOMOUT);
+COMPILE_ASSERT_MATCHING_ENUM(TypeCustom, PP_CURSORTYPE_CUSTOM);
+
+void RectToPPRect(const gfx::Rect& input, PP_Rect* output) {
+ *output = PP_MakeRectFromXYWH(input.x(), input.y(),
+ input.width(), input.height());
+}
+
+PP_Var GetWindowObject(PP_Instance instance_id) {
+ PluginInstance* instance = PluginInstance::FromPPInstance(instance_id);
+ if (!instance)
+ return PP_MakeVoid();
+ return instance->GetWindowObject();
+}
+
+PP_Var GetOwnerElementObject(PP_Instance instance_id) {
+ PluginInstance* instance = PluginInstance::FromPPInstance(instance_id);
+ if (!instance)
+ return PP_MakeVoid();
+ return instance->GetOwnerElementObject();
+}
+
+bool BindGraphicsDeviceContext(PP_Instance instance_id, PP_Resource device_id) {
+ PluginInstance* instance = PluginInstance::FromPPInstance(instance_id);
+ if (!instance)
+ return false;
+ return instance->BindGraphicsDeviceContext(device_id);
+}
+
+bool IsFullFrame(PP_Instance instance_id) {
+ PluginInstance* instance = PluginInstance::FromPPInstance(instance_id);
+ if (!instance)
+ return false;
+ return instance->full_frame();
+}
+
+bool SetCursor(PP_Instance instance_id,
+ PP_CursorType type,
+ PP_Resource custom_image_id,
+ const PP_Point* hot_spot) {
+ PluginInstance* instance = PluginInstance::FromPPInstance(instance_id);
+ if (!instance)
+ return false;
+
+ scoped_refptr<ImageData> custom_image(
+ Resource::GetAs<ImageData>(custom_image_id));
+ if (custom_image.get()) {
+ // TODO: implement custom cursors.
+ NOTIMPLEMENTED();
+ return false;
+ }
+
+ return instance->SetCursor(type);
+}
+
+const PPB_Instance ppb_instance = {
+ &GetWindowObject,
+ &GetOwnerElementObject,
+ &BindGraphicsDeviceContext,
+ &IsFullFrame,
+ &SetCursor,
+};
+
+void NumberOfFindResultsChanged(PP_Instance instance_id,
+ int32_t total,
+ bool final_result) {
+ PluginInstance* instance = PluginInstance::FromPPInstance(instance_id);
+ if (!instance)
+ return;
+
+ DCHECK_NE(instance->find_identifier(), -1);
+ instance->delegate()->DidChangeNumberOfFindResults(
+ instance->find_identifier(), total, final_result);
+}
+
+void SelectedFindResultChanged(PP_Instance instance_id,
+ int32_t index) {
+ PluginInstance* instance = PluginInstance::FromPPInstance(instance_id);
+ if (!instance)
+ return;
+
+ DCHECK_NE(instance->find_identifier(), -1);
+ instance->delegate()->DidChangeSelectedFindResult(
+ instance->find_identifier(), index);
+}
+
+const PPB_Find ppb_find = {
+ &NumberOfFindResultsChanged,
+ &SelectedFindResultChanged,
+};
+
+} // namespace
+
+PluginInstance::PluginInstance(PluginDelegate* delegate,
+ PluginModule* module,
+ const PPP_Instance* instance_interface)
+ : delegate_(delegate),
+ module_(module),
+ instance_interface_(instance_interface),
+ container_(NULL),
+ full_frame_(false),
+ find_identifier_(-1),
+ plugin_find_interface_(NULL),
+ plugin_zoom_interface_(NULL),
+#if defined (OS_LINUX)
+ num_pages_(0),
+ pdf_output_done_(false),
+#endif // defined (OS_LINUX)
+ plugin_print_interface_(NULL) {
+ memset(&current_print_settings_, 0, sizeof(current_print_settings_));
+ DCHECK(delegate);
+ module_->InstanceCreated(this);
+ delegate_->InstanceCreated(this);
+}
+
+PluginInstance::~PluginInstance() {
+ delegate_->InstanceDeleted(this);
+ module_->InstanceDeleted(this);
+}
+
+// static
+const PPB_Instance* PluginInstance::GetInterface() {
+ return &ppb_instance;
+}
+
+// static
+PluginInstance* PluginInstance::FromPPInstance(PP_Instance instance) {
+ return reinterpret_cast<PluginInstance*>(instance);
+}
+
+// static
+const PPB_Find* PluginInstance::GetFindInterface() {
+ return &ppb_find;
+}
+
+PP_Instance PluginInstance::GetPPInstance() {
+ return reinterpret_cast<intptr_t>(this);
+}
+
+void PluginInstance::Paint(WebCanvas* canvas,
+ const gfx::Rect& plugin_rect,
+ const gfx::Rect& paint_rect) {
+ if (device_context_2d_)
+ device_context_2d_->Paint(canvas, plugin_rect, paint_rect);
+}
+
+void PluginInstance::InvalidateRect(const gfx::Rect& rect) {
+ if (!container_ || position_.IsEmpty())
+ return; // Nothing to do.
+ if (rect.IsEmpty())
+ container_->invalidate();
+ else
+ container_->invalidateRect(rect);
+}
+
+PP_Var PluginInstance::GetWindowObject() {
+ if (!container_)
+ return PP_MakeVoid();
+
+ WebFrame* frame = container_->element().document().frame();
+ if (!frame)
+ return PP_MakeVoid();
+
+ return NPObjectToPPVar(frame->windowObject());
+}
+
+PP_Var PluginInstance::GetOwnerElementObject() {
+ if (!container_)
+ return PP_MakeVoid();
+
+ return NPObjectToPPVar(container_->scriptableObjectForElement());
+}
+
+bool PluginInstance::BindGraphicsDeviceContext(PP_Resource device_id) {
+ if (!device_id) {
+ // Special-case clearing the current device.
+ if (device_context_2d_) {
+ device_context_2d_->BindToInstance(NULL);
+ device_context_2d_ = NULL;
+ InvalidateRect(gfx::Rect());
+ }
+ return true;
+ }
+
+ scoped_refptr<DeviceContext2D> device_2d =
+ Resource::GetAs<DeviceContext2D>(device_id);
+
+ if (device_2d) {
+ if (!device_2d->BindToInstance(this))
+ return false; // Can't bind to more than one instance.
+
+ // See http://crbug.com/49403: this can be further optimized by keeping the
+ // old device around and painting from it.
+ if (device_context_2d_.get()) {
+ // Start the new image with the content of the old image until the plugin
+ // repaints.
+ const SkBitmap* old_backing_bitmap =
+ device_context_2d_->image_data()->GetMappedBitmap();
+ SkRect old_size = SkRect::MakeWH(
+ SkScalar(static_cast<float>(old_backing_bitmap->width())),
+ SkScalar(static_cast<float>(old_backing_bitmap->height())));
+
+ SkCanvas canvas(*device_2d->image_data()->GetMappedBitmap());
+ canvas.drawBitmap(*old_backing_bitmap, 0, 0);
+
+ // Fill in any extra space with white.
+ canvas.clipRect(old_size, SkRegion::kDifference_Op);
+ canvas.drawARGB(255, 255, 255, 255);
+ }
+
+ device_context_2d_ = device_2d;
+ // BindToInstance will have invalidated the plugin if necessary.
+ }
+
+ return true;
+}
+
+bool PluginInstance::SetCursor(PP_CursorType type) {
+ cursor_.reset(new WebCursorInfo(static_cast<WebCursorInfo::Type>(type)));
+ return true;
+}
+
+void PluginInstance::Delete() {
+ instance_interface_->Delete(GetPPInstance());
+
+ container_ = NULL;
+}
+
+bool PluginInstance::Initialize(WebPluginContainer* container,
+ const std::vector<std::string>& arg_names,
+ const std::vector<std::string>& arg_values,
+ bool full_frame) {
+ container_ = container;
+ full_frame_ = full_frame;
+
+ if (!instance_interface_->New(GetPPInstance()))
+ return false;
+
+ size_t argc = 0;
+ scoped_array<const char*> argn(new const char*[arg_names.size()]);
+ scoped_array<const char*> argv(new const char*[arg_names.size()]);
+ for (size_t i = 0; i < arg_names.size(); ++i) {
+ argn[argc] = arg_names[i].c_str();
+ argv[argc] = arg_values[i].c_str();
+ argc++;
+ }
+
+ return instance_interface_->Initialize(GetPPInstance(),
+ argc, argn.get(), argv.get());
+}
+
+bool PluginInstance::HandleDocumentLoad(URLLoader* loader) {
+ Resource::ScopedResourceId resource(loader);
+ return instance_interface_->HandleDocumentLoad(GetPPInstance(), resource.id);
+}
+
+bool PluginInstance::HandleInputEvent(const WebKit::WebInputEvent& event,
+ WebCursorInfo* cursor_info) {
+ scoped_ptr<PP_Event> pp_event(CreatePP_Event(event));
+ if (!pp_event.get())
+ return false;
+
+ bool rv = instance_interface_->HandleEvent(GetPPInstance(), pp_event.get());
+ if (cursor_.get())
+ *cursor_info = *cursor_;
+ return rv;
+}
+
+PP_Var PluginInstance::GetInstanceObject() {
+ return instance_interface_->GetInstanceObject(GetPPInstance());
+}
+
+void PluginInstance::ViewChanged(const gfx::Rect& position,
+ const gfx::Rect& clip) {
+ position_ = position;
+ if (clip.IsEmpty()) {
+ // WebKit can give weird (x,y) positions for empty clip rects (since the
+ // position technically doesn't matter). But we want to make these
+ // consistent since this is given to the plugin, so force everything to 0
+ // in the "everything is clipped" case.
+ clip_ = gfx::Rect();
+ } else {
+ clip_ = clip;
+ }
+
+ PP_Rect pp_position, pp_clip;
+ RectToPPRect(position_, &pp_position);
+ RectToPPRect(clip_, &pp_clip);
+ instance_interface_->ViewChanged(GetPPInstance(), &pp_position, &pp_clip);
+}
+
+void PluginInstance::ViewInitiatedPaint() {
+ if (device_context_2d_)
+ device_context_2d_->ViewInitiatedPaint();
+}
+
+void PluginInstance::ViewFlushedPaint() {
+ if (device_context_2d_)
+ device_context_2d_->ViewFlushedPaint();
+}
+
+string16 PluginInstance::GetSelectedText(bool html) {
+ PP_Var rv = instance_interface_->GetSelectedText(GetPPInstance(), html);
+ String* string = GetString(rv);
+ if (!string)
+ return string16();
+ return UTF8ToUTF16(string->value());
+}
+
+void PluginInstance::Zoom(float factor, bool text_only) {
+ if (!LoadZoomInterface())
+ return;
+ plugin_zoom_interface_->Zoom(GetPPInstance(), factor, text_only);
+}
+
+bool PluginInstance::StartFind(const string16& search_text,
+ bool case_sensitive,
+ int identifier) {
+ if (!LoadFindInterface())
+ return false;
+ find_identifier_ = identifier;
+ return plugin_find_interface_->StartFind(
+ GetPPInstance(),
+ UTF16ToUTF8(search_text.c_str()).c_str(),
+ case_sensitive);
+}
+
+void PluginInstance::SelectFindResult(bool forward) {
+ if (LoadFindInterface())
+ plugin_find_interface_->SelectFindResult(GetPPInstance(), forward);
+}
+
+void PluginInstance::StopFind() {
+ if (!LoadFindInterface())
+ return;
+ find_identifier_ = -1;
+ plugin_find_interface_->StopFind(GetPPInstance());
+}
+
+bool PluginInstance::LoadFindInterface() {
+ if (!plugin_find_interface_) {
+ plugin_find_interface_ =
+ reinterpret_cast<const PPP_Find*>(module_->GetPluginInterface(
+ PPP_FIND_INTERFACE));
+ }
+
+ return !!plugin_find_interface_;
+}
+
+bool PluginInstance::LoadZoomInterface() {
+ if (!plugin_zoom_interface_) {
+ plugin_zoom_interface_ =
+ reinterpret_cast<const PPP_Zoom*>(module_->GetPluginInterface(
+ PPP_ZOOM_INTERFACE));
+ }
+
+ return !!plugin_zoom_interface_;
+}
+
+bool PluginInstance::GetPreferredPrintOutputFormat(
+ PP_PrintOutputFormat* format) {
+ if (!plugin_print_interface_) {
+ plugin_print_interface_ =
+ reinterpret_cast<const PPP_Printing*>(module_->GetPluginInterface(
+ PPP_PRINTING_INTERFACE));
+ }
+ if (!plugin_print_interface_)
+ return false;
+ uint32_t format_count = 0;
+ PP_PrintOutputFormat* supported_formats =
+ plugin_print_interface_->QuerySupportedFormats(GetPPInstance(),
+ &format_count);
+ if (!supported_formats)
+ return false;
+
+ bool found_supported_format = false;
+ for (uint32_t index = 0; index < format_count; index++) {
+ if (supported_formats[index] == PP_PRINTOUTPUTFORMAT_PDF) {
+ // If we found PDF, we are done.
+ found_supported_format = true;
+ *format = PP_PRINTOUTPUTFORMAT_PDF;
+ break;
+ } else if (supported_formats[index] == PP_PRINTOUTPUTFORMAT_RASTER) {
+ // We found raster. Keep looking.
+ found_supported_format = true;
+ *format = PP_PRINTOUTPUTFORMAT_RASTER;
+ }
+ }
+ PluginModule::GetCore()->MemFree(supported_formats);
+ return found_supported_format;
+}
+
+bool PluginInstance::SupportsPrintInterface() {
+ PP_PrintOutputFormat format;
+ return GetPreferredPrintOutputFormat(&format);
+}
+
+int PluginInstance::PrintBegin(const gfx::Rect& printable_area,
+ int printer_dpi) {
+ PP_PrintOutputFormat format;
+ if (!GetPreferredPrintOutputFormat(&format)) {
+ // PrintBegin should not have been called since SupportsPrintInterface
+ // would have returned false;
+ NOTREACHED();
+ return 0;
+ }
+
+ PP_PrintSettings print_settings;
+ RectToPPRect(printable_area, &print_settings.printable_area);
+ print_settings.dpi = printer_dpi;
+ print_settings.orientation = PP_PRINTORIENTATION_NORMAL;
+ print_settings.grayscale = false;
+ print_settings.format = format;
+ int num_pages = plugin_print_interface_->Begin(GetPPInstance(),
+ &print_settings);
+ if (!num_pages)
+ return 0;
+ current_print_settings_ = print_settings;
+#if defined (OS_LINUX)
+ num_pages_ = num_pages;
+ pdf_output_done_ = false;
+#endif // (OS_LINUX)
+ return num_pages;
+}
+
+bool PluginInstance::PrintPage(int page_number, WebKit::WebCanvas* canvas) {
+ DCHECK(plugin_print_interface_);
+ PP_PrintPageNumberRange page_range;
+#if defined(OS_LINUX)
+ if (current_print_settings_.format == PP_PRINTOUTPUTFORMAT_PDF) {
+ // On Linux we will try and output all pages as PDF in the first call to
+ // PrintPage. This is a temporary hack.
+ // TODO(sanjeevr): Remove this hack and fix this by changing the print
+ // interfaces for WebFrame and WebPlugin.
+ if (page_number != 0)
+ return pdf_output_done_;
+ page_range.first_page_number = 0;
+ page_range.last_page_number = num_pages_ - 1;
+ }
+#else // defined(OS_LINUX)
+ page_range.first_page_number = page_range.last_page_number = page_number;
+#endif // defined(OS_LINUX)
+
+ PP_Resource print_output =
+ plugin_print_interface_->PrintPages(GetPPInstance(), &page_range, 1);
+
+ if (!print_output)
+ return false;
+
+ bool ret = false;
+
+ if (current_print_settings_.format == PP_PRINTOUTPUTFORMAT_PDF)
+ ret = PrintPDFOutput(print_output, canvas);
+ else if (current_print_settings_.format == PP_PRINTOUTPUTFORMAT_RASTER)
+ ret = PrintRasterOutput(print_output, canvas);
+
+ // Now we need to release the print output resource.
+ PluginModule::GetCore()->ReleaseResource(print_output);
+
+ return ret;
+}
+
+void PluginInstance::PrintEnd() {
+ DCHECK(plugin_print_interface_);
+ if (plugin_print_interface_)
+ plugin_print_interface_->End(GetPPInstance());
+ memset(&current_print_settings_, 0, sizeof(current_print_settings_));
+#if defined(OS_MACOSX)
+ last_printed_page_ = SkBitmap();
+#elif defined(OS_LINUX)
+ num_pages_ = 0;
+ pdf_output_done_ = false;
+#endif // defined(OS_LINUX)
+}
+
+bool PluginInstance::PrintPDFOutput(PP_Resource print_output,
+ WebKit::WebCanvas* canvas) {
+ scoped_refptr<Buffer> buffer(Resource::GetAs<Buffer>(print_output));
+ if (!buffer.get() || !buffer->is_mapped() || !buffer->size()) {
+ NOTREACHED();
+ return false;
+ }
+#if defined(OS_WIN)
+ // For Windows, we need the PDF DLL to render the output PDF to a DC.
+ HMODULE pdf_module = GetModuleHandle(L"pdf.dll");
+ if (!pdf_module)
+ return false;
+ RenderPDFPageToDCProc render_proc =
+ reinterpret_cast<RenderPDFPageToDCProc>(
+ GetProcAddress(pdf_module, "RenderPDFPageToDC"));
+ if (!render_proc)
+ return false;
+#endif // defined(OS_WIN)
+
+ bool ret = false;
+#if defined(OS_LINUX)
+ // On Linux we need to get the backing PdfPsMetafile and write the bits
+ // directly.
+ cairo_t* context = canvas->beginPlatformPaint();
+ printing::NativeMetafile* metafile =
+ printing::NativeMetafile::FromCairoContext(context);
+ DCHECK(metafile);
+ if (metafile) {
+ ret = metafile->SetRawData(buffer->mapped_buffer(), buffer->size());
+ if (ret)
+ pdf_output_done_ = true;
+ }
+ canvas->endPlatformPaint();
+#elif defined(OS_MACOSX)
+ printing::NativeMetafile metafile;
+ // Create a PDF metafile and render from there into the passed in context.
+ if (metafile.Init(buffer->mapped_buffer(), buffer->size())) {
+ // Flip the transform.
+ CGContextSaveGState(canvas);
+ CGContextTranslateCTM(canvas, 0,
+ current_print_settings_.printable_area.size.height);
+ CGContextScaleCTM(canvas, 1.0, -1.0);
+ CGRect page_rect;
+ page_rect.origin.x = current_print_settings_.printable_area.point.x;
+ page_rect.origin.y = current_print_settings_.printable_area.point.y;
+ page_rect.size.width = current_print_settings_.printable_area.size.width;
+ page_rect.size.height = current_print_settings_.printable_area.size.height;
+
+ ret = metafile.RenderPage(1, canvas, page_rect, true, false, true, true);
+ CGContextRestoreGState(canvas);
+ }
+#elif defined(OS_WIN)
+ // On Windows, we now need to render the PDF to the DC that backs the
+ // supplied canvas.
+ skia::VectorPlatformDevice& device =
+ static_cast<skia::VectorPlatformDevice&>(
+ canvas->getTopPlatformDevice());
+ HDC dc = device.getBitmapDC();
+ gfx::Size size_in_pixels;
+ size_in_pixels.set_width(
+ printing::ConvertUnit(current_print_settings_.printable_area.size.width,
+ static_cast<int>(printing::kPointsPerInch),
+ current_print_settings_.dpi));
+ size_in_pixels.set_height(
+ printing::ConvertUnit(current_print_settings_.printable_area.size.height,
+ static_cast<int>(printing::kPointsPerInch),
+ current_print_settings_.dpi));
+ // We need to render using the actual printer DPI (rendering to a smaller
+ // set of pixels leads to a blurry output). However, we need to counter the
+ // scaling up that will happen in the browser.
+ XFORM xform = {0};
+ xform.eM11 = xform.eM22 = static_cast<float>(printing::kPointsPerInch) /
+ static_cast<float>(current_print_settings_.dpi);
+ ModifyWorldTransform(dc, &xform, MWT_LEFTMULTIPLY);
+
+ ret = render_proc(buffer->mapped_buffer(), buffer->size(), 0, dc,
+ current_print_settings_.dpi, current_print_settings_.dpi,
+ 0, 0, size_in_pixels.width(),
+ size_in_pixels.height(), true, false, true, true);
+#endif // defined(OS_WIN)
+
+ return ret;
+}
+
+bool PluginInstance::PrintRasterOutput(PP_Resource print_output,
+ WebKit::WebCanvas* canvas) {
+ scoped_refptr<ImageData> image(Resource::GetAs<ImageData>(print_output));
+ if (!image.get() || !image->is_mapped())
+ return false;
+
+ const SkBitmap* bitmap = image->GetMappedBitmap();
+ if (!bitmap)
+ return false;
+
+ // Draw the printed image into the supplied canvas.
+ SkIRect src_rect;
+ src_rect.set(0, 0, bitmap->width(), bitmap->height());
+ SkRect dest_rect;
+ dest_rect.set(
+ SkIntToScalar(current_print_settings_.printable_area.point.x),
+ SkIntToScalar(current_print_settings_.printable_area.point.y),
+ SkIntToScalar(current_print_settings_.printable_area.point.x +
+ current_print_settings_.printable_area.size.width),
+ SkIntToScalar(current_print_settings_.printable_area.point.y +
+ current_print_settings_.printable_area.size.height));
+ bool draw_to_canvas = true;
+ gfx::Rect dest_rect_gfx;
+ dest_rect_gfx.set_x(current_print_settings_.printable_area.point.x);
+ dest_rect_gfx.set_y(current_print_settings_.printable_area.point.y);
+ dest_rect_gfx.set_width(current_print_settings_.printable_area.size.width);
+ dest_rect_gfx.set_height(current_print_settings_.printable_area.size.height);
+
+#if defined(OS_WIN)
+ // Since this is a raster output, the size of the bitmap can be
+ // huge (especially at high printer DPIs). On Windows, this can
+ // result in a HUGE EMF (on Mac and Linux the output goes to PDF
+ // which appears to Flate compress the bitmap). So, if this bitmap
+ // is larger than 20 MB, we save the bitmap as a JPEG into the EMF
+ // DC. Note: We chose JPEG over PNG because JPEG compression seems
+ // way faster (about 4 times faster).
+ static const int kCompressionThreshold = 20 * 1024 * 1024;
+ if (bitmap->getSize() > kCompressionThreshold) {
+ DrawJPEGToPlatformDC(*bitmap, dest_rect_gfx, canvas);
+ draw_to_canvas = false;
+ }
+#endif // defined(OS_WIN)
+#if defined(OS_MACOSX)
+ draw_to_canvas = false;
+ DrawSkBitmapToCanvas(*bitmap, canvas, dest_rect_gfx,
+ current_print_settings_.printable_area.size.height);
+ // See comments in the header file.
+ last_printed_page_ = *bitmap;
+#else // defined(OS_MACOSX)
+ if (draw_to_canvas)
+ canvas->drawBitmapRect(*bitmap, &src_rect, dest_rect);
+#endif // defined(OS_MACOSX)
+ return true;
+}
+
+#if defined(OS_WIN)
+bool PluginInstance::DrawJPEGToPlatformDC(
+ const SkBitmap& bitmap,
+ const gfx::Rect& printable_area,
+ WebKit::WebCanvas* canvas) {
+ skia::VectorPlatformDevice& device =
+ static_cast<skia::VectorPlatformDevice&>(
+ canvas->getTopPlatformDevice());
+ HDC dc = device.getBitmapDC();
+ // TODO(sanjeevr): This is a temporary hack. If we output a JPEG
+ // to the EMF, the EnumEnhMetaFile call fails in the browser
+ // process. The failure also happens if we output nothing here.
+ // We need to investigate the reason for this failure and fix it.
+ // In the meantime this temporary hack of drawing an empty
+ // rectangle in the DC gets us by.
+ Rectangle(dc, 0, 0, 0, 0);
+
+ // Ideally we should add JPEG compression to the VectorPlatformDevice class
+ // However, Skia currently has no JPEG compression code and we cannot
+ // depend on gfx/jpeg_codec.h in Skia. So we do the compression here.
+ SkAutoLockPixels lock(bitmap);
+ DCHECK(bitmap.getConfig() == SkBitmap::kARGB_8888_Config);
+ const uint32_t* pixels =
+ static_cast<const uint32_t*>(bitmap.getPixels());
+ std::vector<unsigned char> compressed_image;
+ base::TimeTicks start_time = base::TimeTicks::Now();
+ bool encoded = gfx::JPEGCodec::Encode(
+ reinterpret_cast<const unsigned char*>(pixels),
+ gfx::JPEGCodec::FORMAT_BGRA, bitmap.width(), bitmap.height(),
+ static_cast<int>(bitmap.rowBytes()), 100, &compressed_image);
+ UMA_HISTOGRAM_TIMES("PepperPluginPrint.RasterBitmapCompressTime",
+ base::TimeTicks::Now() - start_time);
+ if (!encoded) {
+ NOTREACHED();
+ return false;
+ }
+ BITMAPINFOHEADER bmi = {0};
+ gfx::CreateBitmapHeader(bitmap.width(), bitmap.height(), &bmi);
+ bmi.biCompression = BI_JPEG;
+ bmi.biSizeImage = compressed_image.size();
+ bmi.biHeight = -bmi.biHeight;
+ StretchDIBits(dc, printable_area.x(), printable_area.y(),
+ printable_area.width(), printable_area.height(),
+ 0, 0, bitmap.width(), bitmap.height(),
+ &compressed_image.front(),
+ reinterpret_cast<const BITMAPINFO*>(&bmi),
+ DIB_RGB_COLORS, SRCCOPY);
+ return true;
+}
+#endif // OS_WIN
+
+#if defined(OS_MACOSX)
+void PluginInstance::DrawSkBitmapToCanvas(
+ const SkBitmap& bitmap, WebKit::WebCanvas* canvas,
+ const gfx::Rect& dest_rect,
+ int canvas_height) {
+ SkAutoLockPixels lock(bitmap);
+ DCHECK(bitmap.getConfig() == SkBitmap::kARGB_8888_Config);
+ scoped_cftyperef<CGDataProviderRef> data_provider(
+ CGDataProviderCreateWithData(
+ NULL, bitmap.getAddr32(0, 0),
+ bitmap.rowBytes() * bitmap.height(), NULL));
+ scoped_cftyperef<CGImageRef> image(
+ CGImageCreate(
+ bitmap.width(), bitmap.height(),
+ 8, 32, bitmap.rowBytes(),
+ mac_util::GetSystemColorSpace(),
+ kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host,
+ data_provider, NULL, false, kCGRenderingIntentDefault));
+
+ // Flip the transform
+ CGContextSaveGState(canvas);
+ CGContextTranslateCTM(canvas, 0, canvas_height);
+ CGContextScaleCTM(canvas, 1.0, -1.0);
+
+ CGRect bounds;
+ bounds.origin.x = dest_rect.x();
+ bounds.origin.y = canvas_height - dest_rect.y() - dest_rect.height();
+ bounds.size.width = dest_rect.width();
+ bounds.size.height = dest_rect.height();
+
+ CGContextDrawImage(canvas, bounds, image);
+ CGContextRestoreGState(canvas);
+}
+#endif // defined(OS_MACOSX)
+
+
+} // namespace pepper
diff --git a/webkit/glue/plugins/pepper_plugin_instance.h b/webkit/glue/plugins/pepper_plugin_instance.h
new file mode 100644
index 0000000..4528a99
--- /dev/null
+++ b/webkit/glue/plugins/pepper_plugin_instance.h
@@ -0,0 +1,210 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_PEPPER_PLUGIN_INSTANCE_H_
+#define WEBKIT_GLUE_PLUGINS_PEPPER_PLUGIN_INSTANCE_H_
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/ref_counted.h"
+#include "base/scoped_ptr.h"
+#include "base/string16.h"
+#include "gfx/rect.h"
+#include "third_party/ppapi/c/pp_cursor_type.h"
+#include "third_party/ppapi/c/pp_instance.h"
+#include "third_party/ppapi/c/pp_resource.h"
+#include "third_party/ppapi/c/ppp_printing.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebCanvas.h"
+
+typedef struct _pp_Var PP_Var;
+typedef struct _ppb_Instance PPB_Instance;
+typedef struct _ppb_Find PPB_Find;
+typedef struct _ppp_Find PPP_Find;
+typedef struct _ppp_Instance PPP_Instance;
+typedef struct _ppp_Zoom PPP_Zoom;
+
+class SkBitmap;
+
+namespace gfx {
+class Rect;
+}
+
+namespace WebKit {
+struct WebCursorInfo;
+class WebInputEvent;
+class WebPluginContainer;
+}
+
+namespace pepper {
+
+class DeviceContext2D;
+class PluginDelegate;
+class PluginModule;
+class URLLoader;
+
+class PluginInstance : public base::RefCounted<PluginInstance> {
+ public:
+ PluginInstance(PluginDelegate* delegate,
+ PluginModule* module,
+ const PPP_Instance* instance_interface);
+ ~PluginInstance();
+
+ static const PPB_Instance* GetInterface();
+
+ // Converts the given instance ID to an actual instance object.
+ static PluginInstance* FromPPInstance(PP_Instance instance);
+
+ // Returns a pointer to the interface implementing PPB_Find that is
+ // exposed to the plugin.
+ static const PPB_Find* GetFindInterface();
+
+ PluginDelegate* delegate() const { return delegate_; }
+ PluginModule* module() const { return module_.get(); }
+
+ WebKit::WebPluginContainer* container() const { return container_; }
+
+ const gfx::Rect& position() const { return position_; }
+ const gfx::Rect& clip() const { return clip_; }
+
+ int find_identifier() const { return find_identifier_; }
+
+ PP_Instance GetPPInstance();
+
+ // Paints the current backing store to the web page.
+ void Paint(WebKit::WebCanvas* canvas,
+ const gfx::Rect& plugin_rect,
+ const gfx::Rect& paint_rect);
+
+ // Schedules a paint of the page for the given region. The coordinates are
+ // relative to the top-left of the plugin. This does nothing if the plugin
+ // has not yet been positioned. You can supply an empty gfx::Rect() to
+ // invalidate the entire plugin.
+ void InvalidateRect(const gfx::Rect& rect);
+
+ // PPB_Instance implementation.
+ PP_Var GetWindowObject();
+ PP_Var GetOwnerElementObject();
+ bool BindGraphicsDeviceContext(PP_Resource device_id);
+ bool full_frame() const { return full_frame_; }
+ bool SetCursor(PP_CursorType type);
+
+ // PPP_Instance pass-through.
+ void Delete();
+ bool Initialize(WebKit::WebPluginContainer* container,
+ const std::vector<std::string>& arg_names,
+ const std::vector<std::string>& arg_values,
+ bool full_frame);
+ bool HandleDocumentLoad(URLLoader* loader);
+ bool HandleInputEvent(const WebKit::WebInputEvent& event,
+ WebKit::WebCursorInfo* cursor_info);
+ PP_Var GetInstanceObject();
+ void ViewChanged(const gfx::Rect& position, const gfx::Rect& clip);
+
+ // Notifications that the view has rendered the page and that it has been
+ // flushed to the screen. These messages are used to send Flush callbacks to
+ // the plugin for DeviceContext2D.
+ void ViewInitiatedPaint();
+ void ViewFlushedPaint();
+
+ string16 GetSelectedText(bool html);
+ void Zoom(float factor, bool text_only);
+ bool StartFind(const string16& search_text,
+ bool case_sensitive,
+ int identifier);
+ void SelectFindResult(bool forward);
+ void StopFind();
+
+ bool SupportsPrintInterface();
+ int PrintBegin(const gfx::Rect& printable_area, int printer_dpi);
+ bool PrintPage(int page_number, WebKit::WebCanvas* canvas);
+ void PrintEnd();
+
+ private:
+ bool LoadFindInterface();
+ bool LoadZoomInterface();
+
+ // Queries the plugin for supported print formats and sets |format| to the
+ // best format to use. Returns false if the plugin does not support any
+ // print format that we can handle (we can handle raster and PDF).
+ bool GetPreferredPrintOutputFormat(PP_PrintOutputFormat* format);
+ bool PrintPDFOutput(PP_Resource print_output, WebKit::WebCanvas* canvas);
+ bool PrintRasterOutput(PP_Resource print_output, WebKit::WebCanvas* canvas);
+#if defined(OS_WIN)
+ bool DrawJPEGToPlatformDC(const SkBitmap& bitmap,
+ const gfx::Rect& printable_area,
+ WebKit::WebCanvas* canvas);
+#elif defined(OS_MACOSX)
+ // Draws the given kARGB_8888_Config bitmap to the specified canvas starting
+ // at the specified destination rect.
+ void DrawSkBitmapToCanvas(const SkBitmap& bitmap, WebKit::WebCanvas* canvas,
+ const gfx::Rect& dest_rect, int canvas_height);
+#endif // OS_MACOSX
+
+ PluginDelegate* delegate_;
+ scoped_refptr<PluginModule> module_;
+ const PPP_Instance* instance_interface_;
+
+ // NULL until we have been initialized.
+ WebKit::WebPluginContainer* container_;
+
+ // Indicates whether this is a full frame instance, which means it represents
+ // an entire document rather than an embed tag.
+ bool full_frame_;
+
+ // Position in the viewport (which moves as the page is scrolled) of this
+ // plugin. This will be a 0-sized rectangle if the plugin has not yet been
+ // laid out.
+ gfx::Rect position_;
+
+ // Current clip rect. This will be empty if the plugin is not currently
+ // visible. This is in the plugin's coordinate system, so fully visible will
+ // be (0, 0, w, h) regardless of scroll position.
+ gfx::Rect clip_;
+
+ // The current device context for painting in 2D.
+ scoped_refptr<DeviceContext2D> device_context_2d_;
+
+ // The id of the current find operation, or -1 if none is in process.
+ int find_identifier_;
+
+ // The plugin find and zoom interfaces.
+ const PPP_Find* plugin_find_interface_;
+ const PPP_Zoom* plugin_zoom_interface_;
+
+ // This is only valid between a successful PrintBegin call and a PrintEnd
+ // call.
+ PP_PrintSettings current_print_settings_;
+#if defined(OS_MACOSX)
+ // On the Mac, when we draw the bitmap to the PDFContext, it seems necessary
+ // to keep the pixels valid until CGContextEndPage is called. We use this
+ // variable to hold on to the pixels.
+ SkBitmap last_printed_page_;
+#elif defined(OS_LINUX)
+ // On Linux, we always send all pages from the renderer to the browser.
+ // So, if the plugin supports printPagesAsPDF we print the entire output
+ // in one shot in the first call to PrintPage.
+ // (This is a temporary hack until we change the WebFrame and WebPlugin print
+ // interfaces).
+ // Specifies the total number of pages to be printed. It it set in PrintBegin.
+ int32 num_pages_;
+ // Specifies whether we have already output all pages. This is used to ignore
+ // subsequent PrintPage requests.
+ bool pdf_output_done_;
+#endif // defined(OS_LINUX)
+
+ // The plugin print interface.
+ const PPP_Printing* plugin_print_interface_;
+
+ // Containes the cursor if it's set by the plugin.
+ scoped_ptr<WebKit::WebCursorInfo> cursor_;
+
+ DISALLOW_COPY_AND_ASSIGN(PluginInstance);
+};
+
+} // namespace pepper
+
+#endif // WEBKIT_GLUE_PLUGINS_PEPPER_PLUGIN_INSTANCE_H_
diff --git a/webkit/glue/plugins/pepper_plugin_module.cc b/webkit/glue/plugins/pepper_plugin_module.cc
new file mode 100644
index 0000000..8ffd78b
--- /dev/null
+++ b/webkit/glue/plugins/pepper_plugin_module.cc
@@ -0,0 +1,362 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/pepper_plugin_module.h"
+
+#include <set>
+
+#include "base/command_line.h"
+#include "base/message_loop.h"
+#include "base/message_loop_proxy.h"
+#include "base/logging.h"
+#include "base/scoped_ptr.h"
+#include "base/time.h"
+#include "third_party/ppapi/c/ppb_buffer.h"
+#include "third_party/ppapi/c/ppb_core.h"
+#include "third_party/ppapi/c/ppb_device_context_2d.h"
+#include "third_party/ppapi/c/ppb_file_io.h"
+#include "third_party/ppapi/c/ppb_file_io_trusted.h"
+#include "third_party/ppapi/c/ppb_file_system.h"
+#include "third_party/ppapi/c/ppb_image_data.h"
+#include "third_party/ppapi/c/ppb_instance.h"
+#include "third_party/ppapi/c/ppb_find.h"
+#include "third_party/ppapi/c/ppb_font.h"
+#include "third_party/ppapi/c/ppb_scrollbar.h"
+#include "third_party/ppapi/c/ppb_testing.h"
+#include "third_party/ppapi/c/ppb_url_loader.h"
+#include "third_party/ppapi/c/ppb_url_request_info.h"
+#include "third_party/ppapi/c/ppb_url_response_info.h"
+#include "third_party/ppapi/c/ppb_var.h"
+#include "third_party/ppapi/c/ppb_widget.h"
+#include "third_party/ppapi/c/ppp.h"
+#include "third_party/ppapi/c/ppp_instance.h"
+#include "third_party/ppapi/c/pp_module.h"
+#include "third_party/ppapi/c/pp_resource.h"
+#include "third_party/ppapi/c/pp_var.h"
+#include "webkit/glue/plugins/pepper_buffer.h"
+#include "webkit/glue/plugins/pepper_device_context_2d.h"
+#include "webkit/glue/plugins/pepper_directory_reader.h"
+#include "webkit/glue/plugins/pepper_file_io.h"
+#include "webkit/glue/plugins/pepper_file_ref.h"
+#include "webkit/glue/plugins/pepper_file_system.h"
+#include "webkit/glue/plugins/pepper_font.h"
+#include "webkit/glue/plugins/pepper_image_data.h"
+#include "webkit/glue/plugins/pepper_plugin_instance.h"
+#include "webkit/glue/plugins/pepper_private.h"
+#include "webkit/glue/plugins/pepper_resource_tracker.h"
+#include "webkit/glue/plugins/pepper_scrollbar.h"
+#include "webkit/glue/plugins/pepper_url_loader.h"
+#include "webkit/glue/plugins/pepper_url_request_info.h"
+#include "webkit/glue/plugins/pepper_url_response_info.h"
+#include "webkit/glue/plugins/pepper_var.h"
+#include "webkit/glue/plugins/pepper_widget.h"
+#include "webkit/glue/plugins/ppb_private.h"
+
+namespace pepper {
+
+namespace {
+
+// Maintains all currently loaded plugin libs for validating PP_Module
+// identifiers.
+typedef std::set<PluginModule*> PluginModuleSet;
+
+PluginModuleSet* GetLivePluginSet() {
+ static PluginModuleSet live_plugin_libs;
+ return &live_plugin_libs;
+}
+
+base::MessageLoopProxy* GetMainThreadMessageLoop() {
+ static scoped_refptr<base::MessageLoopProxy> proxy(
+ base::MessageLoopProxy::CreateForCurrentThread());
+ return proxy.get();
+}
+
+// PPB_Core --------------------------------------------------------------------
+
+void AddRefResource(PP_Resource resource) {
+ if (!ResourceTracker::Get()->AddRefResource(resource)) {
+ DLOG(WARNING) << "AddRefResource()ing a nonexistent resource";
+ }
+}
+
+void ReleaseResource(PP_Resource resource) {
+ if (!ResourceTracker::Get()->UnrefResource(resource)) {
+ DLOG(WARNING) << "ReleaseResource()ing a nonexistent resource";
+ }
+}
+
+void* MemAlloc(size_t num_bytes) {
+ return malloc(num_bytes);
+}
+
+void MemFree(void* ptr) {
+ free(ptr);
+}
+
+double GetTime() {
+ return base::Time::Now().ToDoubleT();
+}
+
+void CallOnMainThread(int delay_in_msec,
+ PP_CompletionCallback callback,
+ int32_t result) {
+ GetMainThreadMessageLoop()->PostDelayedTask(
+ FROM_HERE,
+ NewRunnableFunction(callback.func, callback.user_data, result),
+ delay_in_msec);
+}
+
+const PPB_Core core_interface = {
+ &AddRefResource,
+ &ReleaseResource,
+ &MemAlloc,
+ &MemFree,
+ &GetTime,
+ &CallOnMainThread
+};
+
+// PPB_Testing -----------------------------------------------------------------
+
+bool ReadImageData(PP_Resource device_context_2d,
+ PP_Resource image,
+ const PP_Point* top_left) {
+ scoped_refptr<DeviceContext2D> context(
+ Resource::GetAs<DeviceContext2D>(device_context_2d));
+ if (!context.get())
+ return false;
+ return context->ReadImageData(image, top_left);
+}
+
+void RunMessageLoop() {
+ bool old_state = MessageLoop::current()->NestableTasksAllowed();
+ MessageLoop::current()->SetNestableTasksAllowed(true);
+ MessageLoop::current()->Run();
+ MessageLoop::current()->SetNestableTasksAllowed(old_state);
+}
+
+void QuitMessageLoop() {
+ MessageLoop::current()->Quit();
+}
+
+const PPB_Testing testing_interface = {
+ &ReadImageData,
+ &RunMessageLoop,
+ &QuitMessageLoop,
+};
+
+// GetInterface ----------------------------------------------------------------
+
+const void* GetInterface(const char* name) {
+ if (strcmp(name, PPB_CORE_INTERFACE) == 0)
+ return &core_interface;
+ if (strcmp(name, PPB_VAR_INTERFACE) == 0)
+ return GetVarInterface();
+ if (strcmp(name, PPB_INSTANCE_INTERFACE) == 0)
+ return PluginInstance::GetInterface();
+ if (strcmp(name, PPB_IMAGEDATA_INTERFACE) == 0)
+ return ImageData::GetInterface();
+ if (strcmp(name, PPB_DEVICECONTEXT2D_INTERFACE) == 0)
+ return DeviceContext2D::GetInterface();
+ if (strcmp(name, PPB_URLLOADER_INTERFACE) == 0)
+ return URLLoader::GetInterface();
+ if (strcmp(name, PPB_URLREQUESTINFO_INTERFACE) == 0)
+ return URLRequestInfo::GetInterface();
+ if (strcmp(name, PPB_URLRESPONSEINFO_INTERFACE) == 0)
+ return URLResponseInfo::GetInterface();
+ if (strcmp(name, PPB_BUFFER_INTERFACE) == 0)
+ return Buffer::GetInterface();
+ if (strcmp(name, PPB_FILEREF_INTERFACE) == 0)
+ return FileRef::GetInterface();
+ if (strcmp(name, PPB_FILEIO_INTERFACE) == 0)
+ return FileIO::GetInterface();
+ if (strcmp(name, PPB_FILEIOTRUSTED_INTERFACE) == 0)
+ return FileIO::GetTrustedInterface();
+ if (strcmp(name, PPB_FILESYSTEM_INTERFACE) == 0)
+ return FileSystem::GetInterface();
+ if (strcmp(name, PPB_DIRECTORYREADER_INTERFACE) == 0)
+ return DirectoryReader::GetInterface();
+ if (strcmp(name, PPB_WIDGET_INTERFACE) == 0)
+ return Widget::GetInterface();
+ if (strcmp(name, PPB_SCROLLBAR_INTERFACE) == 0)
+ return Scrollbar::GetInterface();
+ if (strcmp(name, PPB_FONT_INTERFACE) == 0)
+ return Font::GetInterface();
+ if (strcmp(name, PPB_FIND_INTERFACE) == 0)
+ return PluginInstance::GetFindInterface();
+ if (strcmp(name, PPB_PRIVATE_INTERFACE) == 0)
+ return Private::GetInterface();
+
+ // Only support the testing interface when the command line switch is
+ // specified. This allows us to prevent people from (ab)using this interface
+ // in production code.
+ if (strcmp(name, PPB_TESTING_INTERFACE) == 0) {
+ if (CommandLine::ForCurrentProcess()->HasSwitch("enable-pepper-testing"))
+ return &testing_interface;
+ }
+ return NULL;
+}
+
+} // namespace
+
+PluginModule::PluginModule()
+ : initialized_(false),
+ library_(NULL) {
+ GetMainThreadMessageLoop(); // Initialize the main thread message loop.
+ GetLivePluginSet()->insert(this);
+}
+
+PluginModule::~PluginModule() {
+ // When the module is being deleted, there should be no more instances still
+ // holding a reference to us.
+ DCHECK(instances_.empty());
+
+ GetLivePluginSet()->erase(this);
+
+ if (entry_points_.shutdown_module)
+ entry_points_.shutdown_module();
+
+ if (library_)
+ base::UnloadNativeLibrary(library_);
+}
+
+// static
+scoped_refptr<PluginModule> PluginModule::CreateModule(
+ const FilePath& path) {
+ // FIXME(brettw) do uniquifying of the plugin here like the NPAPI one.
+
+ scoped_refptr<PluginModule> lib(new PluginModule());
+ if (!lib->InitFromFile(path))
+ return NULL;
+
+ return lib;
+}
+
+scoped_refptr<PluginModule> PluginModule::CreateInternalModule(
+ EntryPoints entry_points) {
+ scoped_refptr<PluginModule> lib(new PluginModule());
+ if (!lib->InitFromEntryPoints(entry_points))
+ return NULL;
+
+ return lib;
+}
+
+// static
+PluginModule* PluginModule::FromPPModule(PP_Module module) {
+ PluginModule* lib = reinterpret_cast<PluginModule*>(module);
+ if (GetLivePluginSet()->find(lib) == GetLivePluginSet()->end())
+ return NULL; // Invalid plugin.
+ return lib;
+}
+
+// static
+const PPB_Core* PluginModule::GetCore() {
+ return &core_interface;
+}
+
+bool PluginModule::InitFromEntryPoints(const EntryPoints& entry_points) {
+ if (initialized_)
+ return true;
+
+ // Attempt to run the initialization funciton.
+ int retval = entry_points.initialize_module(GetPPModule(), &GetInterface);
+ if (retval != 0) {
+ LOG(WARNING) << "PPP_InitializeModule returned failure " << retval;
+ return false;
+ }
+
+ entry_points_ = entry_points;
+ initialized_ = true;
+ return true;
+}
+
+bool PluginModule::InitFromFile(const FilePath& path) {
+ if (initialized_)
+ return true;
+
+ base::NativeLibrary library = base::LoadNativeLibrary(path);
+ if (!library)
+ return false;
+
+ EntryPoints entry_points;
+ if (!LoadEntryPoints(library, &entry_points) ||
+ !InitFromEntryPoints(entry_points)) {
+ base::UnloadNativeLibrary(library);
+ return false;
+ }
+
+ // We let InitFromEntryPoints() handle setting the all the internal state
+ // of the object other than the |library_| reference.
+ library_ = library;
+ return true;
+}
+
+// static
+bool PluginModule::LoadEntryPoints(const base::NativeLibrary& library,
+ EntryPoints* entry_points) {
+
+ entry_points->get_interface =
+ reinterpret_cast<PPP_GetInterfaceFunc>(
+ base::GetFunctionPointerFromNativeLibrary(library,
+ "PPP_GetInterface"));
+ if (!entry_points->get_interface) {
+ LOG(WARNING) << "No PPP_GetInterface in plugin library";
+ return false;
+ }
+
+ entry_points->initialize_module =
+ reinterpret_cast<PPP_InitializeModuleFunc>(
+ base::GetFunctionPointerFromNativeLibrary(library,
+ "PPP_InitializeModule"));
+ if (!entry_points->initialize_module) {
+ LOG(WARNING) << "No PPP_InitializeModule in plugin library";
+ return false;
+ }
+
+ // It's okay for PPP_ShutdownModule to not be defined and shutdown_module to
+ // be NULL.
+ entry_points->shutdown_module =
+ reinterpret_cast<PPP_ShutdownModuleFunc>(
+ base::GetFunctionPointerFromNativeLibrary(library,
+ "PPP_ShutdownModule"));
+
+ return true;
+}
+
+PP_Module PluginModule::GetPPModule() const {
+ return reinterpret_cast<intptr_t>(this);
+}
+
+PluginInstance* PluginModule::CreateInstance(PluginDelegate* delegate) {
+ const PPP_Instance* plugin_instance_interface =
+ reinterpret_cast<const PPP_Instance*>(GetPluginInterface(
+ PPP_INSTANCE_INTERFACE));
+ if (!plugin_instance_interface) {
+ LOG(WARNING) << "Plugin doesn't support instance interface, failing.";
+ return NULL;
+ }
+ return new PluginInstance(delegate, this, plugin_instance_interface);
+}
+
+PluginInstance* PluginModule::GetSomeInstance() const {
+ // This will generally crash later if there is not actually any instance to
+ // return, so we force a crash now to make bugs easier to track down.
+ CHECK(!instances_.empty());
+ return *instances_.begin();
+}
+
+const void* PluginModule::GetPluginInterface(const char* name) const {
+ if (!entry_points_.get_interface)
+ return NULL;
+ return entry_points_.get_interface(name);
+}
+
+void PluginModule::InstanceCreated(PluginInstance* instance) {
+ instances_.insert(instance);
+}
+
+void PluginModule::InstanceDeleted(PluginInstance* instance) {
+ instances_.erase(instance);
+}
+
+} // namespace pepper
diff --git a/webkit/glue/plugins/pepper_plugin_module.h b/webkit/glue/plugins/pepper_plugin_module.h
new file mode 100644
index 0000000..6bfeccf
--- /dev/null
+++ b/webkit/glue/plugins/pepper_plugin_module.h
@@ -0,0 +1,102 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_PEPPER_PLUGIN_MODULE_H_
+#define WEBKIT_GLUE_PLUGINS_PEPPER_PLUGIN_MODULE_H_
+
+#include <set>
+
+#include "base/basictypes.h"
+#include "base/file_path.h"
+#include "base/native_library.h"
+#include "base/ref_counted.h"
+#include "third_party/ppapi/c/pp_module.h"
+#include "third_party/ppapi/c/ppb.h"
+
+typedef struct _ppb_Core PPB_Core;
+
+namespace pepper {
+
+class PluginDelegate;
+class PluginInstance;
+
+class PluginModule : public base::RefCounted<PluginModule> {
+ public:
+ typedef const void* (*PPP_GetInterfaceFunc)(const char*);
+ typedef int (*PPP_InitializeModuleFunc)(PP_Module, PPB_GetInterface);
+ typedef void (*PPP_ShutdownModuleFunc)();
+
+ struct EntryPoints {
+ EntryPoints()
+ : get_interface(NULL),
+ initialize_module(NULL),
+ shutdown_module(NULL) {
+ }
+
+ PPP_GetInterfaceFunc get_interface;
+ PPP_InitializeModuleFunc initialize_module;
+ PPP_ShutdownModuleFunc shutdown_module;
+ };
+
+ ~PluginModule();
+
+ static scoped_refptr<PluginModule> CreateModule(const FilePath& path);
+ static scoped_refptr<PluginModule> CreateInternalModule(
+ EntryPoints entry_points);
+
+ // Converts the given module ID to an actual module object. Will return NULL
+ // if the module is invalid.
+ static PluginModule* FromPPModule(PP_Module module);
+
+ static const PPB_Core* GetCore();
+
+ PP_Module GetPPModule() const;
+
+ PluginInstance* CreateInstance(PluginDelegate* delegate);
+
+ // Returns "some" plugin instance associated with this module. This is not
+ // guaranteed to be any one in particular. This is normally used to execute
+ // callbacks up to the browser layer that are not inherently per-instance,
+ // but the delegate lives only on the plugin instance so we need one of them.
+ PluginInstance* GetSomeInstance() const;
+
+ const void* GetPluginInterface(const char* name) const;
+
+ // This module is associated with a set of instances. The PluginInstance
+ // object declares its association with this module in its destructor and
+ // releases us in its destructor.
+ void InstanceCreated(PluginInstance* instance);
+ void InstanceDeleted(PluginInstance* instance);
+
+ private:
+ PluginModule();
+
+ bool InitFromEntryPoints(const EntryPoints& entry_points);
+ bool InitFromFile(const FilePath& path);
+ static bool LoadEntryPoints(const base::NativeLibrary& library,
+ EntryPoints* entry_points);
+
+ bool initialized_;
+
+ // Holds a reference to the base::NativeLibrary handle if this PluginModule
+ // instance wraps functions loaded from a library. Can be NULL. If
+ // |library_| is non-NULL, PluginModule will attempt to unload the library
+ // during destruction.
+ base::NativeLibrary library_;
+
+ // Contains pointers to the entry points of the actual plugin
+ // implementation.
+ EntryPoints entry_points_;
+
+ // Non-owning pointers to all instances associated with this module. When
+ // there are no more instances, this object should be deleted.
+ typedef std::set<PluginInstance*> PluginInstanceSet;
+ PluginInstanceSet instances_;
+
+ DISALLOW_COPY_AND_ASSIGN(PluginModule);
+};
+
+} // namespace pepper
+
+#endif // WEBKIT_GLUE_PLUGINS_PEPPER_PLUGIN_MODULE_H_
diff --git a/webkit/glue/plugins/pepper_private.cc b/webkit/glue/plugins/pepper_private.cc
new file mode 100644
index 0000000..8d5182f
--- /dev/null
+++ b/webkit/glue/plugins/pepper_private.cc
@@ -0,0 +1,38 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "build/build_config.h"
+
+#include "webkit/glue/plugins/pepper_private.h"
+
+#include "base/utf_string_conversions.h"
+#include "grit/webkit_strings.h"
+#include "webkit/glue/webkit_glue.h"
+#include "webkit/glue/plugins/pepper_var.h"
+#include "webkit/glue/plugins/ppb_private.h"
+
+namespace pepper {
+
+namespace {
+
+PP_Var GetLocalizedString(PP_ResourceString string_id) {
+ std::string rv;
+ if (string_id == PP_RESOURCESTRING_PDFGETPASSWORD)
+ rv = UTF16ToUTF8(webkit_glue::GetLocalizedString(IDS_PDF_NEED_PASSWORD));
+
+ return StringToPPVar(rv);
+}
+
+const PPB_Private ppb_private = {
+ &GetLocalizedString,
+};
+
+} // namespace
+
+// static
+const PPB_Private* Private::GetInterface() {
+ return &ppb_private;
+}
+
+} // namespace pepper
diff --git a/webkit/glue/plugins/pepper_private.h b/webkit/glue/plugins/pepper_private.h
new file mode 100644
index 0000000..fda75a7
--- /dev/null
+++ b/webkit/glue/plugins/pepper_private.h
@@ -0,0 +1,23 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_PEPPER_PRIVATE_H_
+#define WEBKIT_GLUE_PLUGINS_PEPPER_PRIVATE_H_
+
+#include "webkit/glue/plugins/pepper_resource.h"
+
+typedef struct _ppb_Private PPB_Private;
+
+namespace pepper {
+
+class Private {
+ public:
+ // Returns a pointer to the interface implementing PPB_Private that is exposed
+ // to the plugin.
+ static const PPB_Private* GetInterface();
+};
+
+} // namespace pepper
+
+#endif // WEBKIT_GLUE_PLUGINS_PEPPER_PRIVATE_H_
diff --git a/webkit/glue/plugins/pepper_resource.cc b/webkit/glue/plugins/pepper_resource.cc
new file mode 100644
index 0000000..c568183
--- /dev/null
+++ b/webkit/glue/plugins/pepper_resource.cc
@@ -0,0 +1,19 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/pepper_resource.h"
+
+#include "webkit/glue/plugins/pepper_resource_tracker.h"
+
+namespace pepper {
+
+PP_Resource Resource::GetReference() {
+ ResourceTracker *tracker = ResourceTracker::Get();
+ if (resource_id_)
+ tracker->AddRefResource(resource_id_);
+ else
+ resource_id_ = tracker->AddResource(this);
+ return resource_id_;
+}
+} // namespace pepper
diff --git a/webkit/glue/plugins/pepper_resource.h b/webkit/glue/plugins/pepper_resource.h
new file mode 100644
index 0000000..417a06b
--- /dev/null
+++ b/webkit/glue/plugins/pepper_resource.h
@@ -0,0 +1,135 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_PEPPER_RESOURCE_H_
+#define WEBKIT_GLUE_PLUGINS_PEPPER_RESOURCE_H_
+
+#include "base/logging.h"
+#include "base/basictypes.h"
+#include "base/ref_counted.h"
+#include "third_party/ppapi/c/pp_resource.h"
+#include "webkit/glue/plugins/pepper_resource_tracker.h"
+
+namespace pepper {
+
+class Buffer;
+class DeviceContext2D;
+class DirectoryReader;
+class FileChooser;
+class FileIO;
+class FileRef;
+class Font;
+class ImageData;
+class PluginModule;
+class Scrollbar;
+class URLLoader;
+class URLRequestInfo;
+class URLResponseInfo;
+class Widget;
+
+class Resource : public base::RefCountedThreadSafe<Resource> {
+ public:
+ explicit Resource(PluginModule* module) : resource_id_(0), module_(module) {}
+ virtual ~Resource() {}
+
+ // Returns NULL if the resource is invalid or is a different type.
+ template<typename T>
+ static scoped_refptr<T> GetAs(PP_Resource res) {
+ scoped_refptr<Resource> resource = ResourceTracker::Get()->GetResource(res);
+ return resource ? resource->Cast<T>() : NULL;
+ }
+
+ PluginModule* module() const { return module_; }
+
+ // Cast the resource into a specified type. This will return NULL if the
+ // resource does not match the specified type. Specializations of this
+ // template call into As* functions.
+ template <typename T> T* Cast() { return NULL; }
+
+ // Returns an resource id of this object. If the object doesn't have a
+ // resource id, new one is created with plugin refcount of 1. If it does,
+ // the refcount is incremented. Use this when you need to return a new
+ // reference to the plugin.
+ PP_Resource GetReference();
+
+ // When you need to ensure that a resource has a reference, but you do not
+ // want to increase the refcount (for example, if you need to call a plugin
+ // callback function with a reference), you can use this class. For example:
+ //
+ // plugin_callback(.., ScopedResourceId(resource).id, ...);
+ class ScopedResourceId {
+ public:
+ explicit ScopedResourceId(Resource* resource)
+ : id(resource->GetReference()) {}
+ ~ScopedResourceId() {
+ ResourceTracker::Get()->UnrefResource(id);
+ }
+ const PP_Resource id;
+ };
+
+ private:
+ // Type-specific getters for individual resource types. These will return
+ // NULL if the resource does not match the specified type. Used by the Cast()
+ // function.
+ virtual Buffer* AsBuffer() { return NULL; }
+ virtual DeviceContext2D* AsDeviceContext2D() { return NULL; }
+ virtual DirectoryReader* AsDirectoryReader() { return NULL; }
+ virtual FileChooser* AsFileChooser() { return NULL; }
+ virtual FileIO* AsFileIO() { return NULL; }
+ virtual FileRef* AsFileRef() { return NULL; }
+ virtual Font* AsFont() { return NULL; }
+ virtual ImageData* AsImageData() { return NULL; }
+ virtual Scrollbar* AsScrollbar() { return NULL; }
+ virtual URLLoader* AsURLLoader() { return NULL; }
+ virtual URLRequestInfo* AsURLRequestInfo() { return NULL; }
+ virtual URLResponseInfo* AsURLResponseInfo() { return NULL; }
+ virtual Widget* AsWidget() { return NULL; }
+
+ private:
+ // If referenced by a plugin, holds the id of this resource object. Do not
+ // access this member directly, because it is possible that the plugin holds
+ // no references to the object, and therefore the resource_id_ is zero. Use
+ // either GetReference() to obtain a new resource_id and increase the
+ // refcount, or TemporaryReference when you do not want to increase the
+ // refcount.
+ PP_Resource resource_id_;
+
+ // Non-owning pointer to our module.
+ PluginModule* module_;
+
+ // Called by the resource tracker when the last plugin reference has been
+ // dropped.
+ friend class ResourceTracker;
+ void StoppedTracking() {
+ DCHECK(resource_id_ != 0);
+ resource_id_ = 0;
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(Resource);
+};
+
+// Cast() specializations.
+#define DEFINE_RESOURCE_CAST(Type) \
+ template <> inline Type* Resource::Cast<Type>() { \
+ return As##Type(); \
+ }
+
+DEFINE_RESOURCE_CAST(Buffer)
+DEFINE_RESOURCE_CAST(DeviceContext2D)
+DEFINE_RESOURCE_CAST(DirectoryReader)
+DEFINE_RESOURCE_CAST(FileChooser)
+DEFINE_RESOURCE_CAST(FileIO)
+DEFINE_RESOURCE_CAST(FileRef)
+DEFINE_RESOURCE_CAST(Font)
+DEFINE_RESOURCE_CAST(ImageData)
+DEFINE_RESOURCE_CAST(Scrollbar)
+DEFINE_RESOURCE_CAST(URLLoader)
+DEFINE_RESOURCE_CAST(URLRequestInfo)
+DEFINE_RESOURCE_CAST(URLResponseInfo)
+DEFINE_RESOURCE_CAST(Widget)
+
+#undef DEFINE_RESOURCE_CAST
+} // namespace pepper
+
+#endif // WEBKIT_GLUE_PLUGINS_PEPPER_RESOURCE_H_
diff --git a/webkit/glue/plugins/pepper_resource_tracker.cc b/webkit/glue/plugins/pepper_resource_tracker.cc
new file mode 100644
index 0000000..8aa94d2
--- /dev/null
+++ b/webkit/glue/plugins/pepper_resource_tracker.cc
@@ -0,0 +1,61 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/pepper_resource_tracker.h"
+
+#include <limits>
+#include <set>
+
+#include "base/logging.h"
+#include "third_party/ppapi/c/pp_resource.h"
+#include "webkit/glue/plugins/pepper_resource.h"
+
+namespace pepper {
+
+scoped_refptr<Resource> ResourceTracker::GetResource(PP_Resource res) const {
+ ResourceMap::const_iterator result = live_resources_.find(res);
+ if (result == live_resources_.end()) {
+ return scoped_refptr<Resource>();
+ }
+ return result->second.first;
+}
+
+PP_Resource ResourceTracker::AddResource(Resource* resource) {
+ // If the plugin manages to create 4B resources...
+ if (last_id_ == std::numeric_limits<PP_Resource>::max()) {
+ return 0;
+ }
+ // Add the resource with plugin use-count 1.
+ ++last_id_;
+ live_resources_.insert(std::make_pair(last_id_, std::make_pair(resource, 1)));
+ return last_id_;
+}
+
+bool ResourceTracker::AddRefResource(PP_Resource res) {
+ ResourceMap::iterator i = live_resources_.find(res);
+ if (i != live_resources_.end()) {
+ // We don't protect against overflow, since a plugin as malicious as to ref
+ // once per every byte in the address space could have just as well unrefed
+ // one time too many.
+ ++i->second.second;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool ResourceTracker::UnrefResource(PP_Resource res) {
+ ResourceMap::iterator i = live_resources_.find(res);
+ if (i != live_resources_.end()) {
+ if (!--i->second.second) {
+ i->second.first->StoppedTracking();
+ live_resources_.erase(i);
+ }
+ return true;
+ } else {
+ return false;
+ }
+}
+
+} // namespace pepper
diff --git a/webkit/glue/plugins/pepper_resource_tracker.h b/webkit/glue/plugins/pepper_resource_tracker.h
new file mode 100644
index 0000000..d06c9ba
--- /dev/null
+++ b/webkit/glue/plugins/pepper_resource_tracker.h
@@ -0,0 +1,75 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_PEPPER_RESOURCE_TRACKER_H_
+#define WEBKIT_GLUE_PLUGINS_PEPPER_RESOURCE_TRACKER_H_
+
+#include <set>
+
+#include "base/atomic_sequence_num.h"
+#include "base/basictypes.h"
+#include "base/hash_tables.h"
+#include "base/ref_counted.h"
+#include "base/singleton.h"
+#include "third_party/ppapi/c/pp_resource.h"
+
+namespace pepper {
+
+class Resource;
+
+// This class maintains a global list of all live pepper resources. It allows
+// us to check resource ID validity and to map them to a specific module.
+//
+// This object is threadsafe.
+class ResourceTracker {
+ public:
+ // Returns the pointer to the singleton object.
+ static ResourceTracker* Get() {
+ return Singleton<ResourceTracker>::get();
+ }
+
+ // The returned pointer will be NULL if there is no resource. Note that this
+ // return value is a scoped_refptr so that we ensure the resource is valid
+ // from the point of the lookup to the point that the calling code needs it.
+ // Otherwise, the plugin could Release() the resource on another thread and
+ // the object will get deleted out from under us.
+ scoped_refptr<Resource> GetResource(PP_Resource res) const;
+
+ // Increment resource's plugin refcount. See ResourceAndRefCount comments
+ // below.
+ bool AddRefResource(PP_Resource res);
+ bool UnrefResource(PP_Resource res);
+
+ private:
+ friend struct DefaultSingletonTraits<ResourceTracker>;
+ friend class Resource;
+
+ // Prohibit creation other then by the Singleton class.
+ ResourceTracker() : last_id_(0) {}
+ ~ResourceTracker() {}
+
+ // Adds the given resource to the tracker and assigns it a resource ID and
+ // refcount of 1. The assigned resource ID will be returned. Used only by the
+ // Resource class.
+ PP_Resource AddResource(Resource* resource);
+
+ // Last assigned resource ID.
+ PP_Resource last_id_;
+
+ // For each PP_Resource, keep the Resource* (as refptr) and plugin use count.
+ // This use count is different then Resource's RefCount, and is manipulated
+ // using this RefResource/UnrefResource. When it drops to zero, we just remove
+ // the resource from this resource tracker, but the resource object will be
+ // alive so long as some scoped_refptr still holds it's reference. This
+ // prevents plugins from forcing destruction of Resource objects.
+ typedef std::pair<scoped_refptr<Resource>, size_t> ResourceAndRefCount;
+ typedef base::hash_map<PP_Resource, ResourceAndRefCount> ResourceMap;
+ ResourceMap live_resources_;
+
+ DISALLOW_COPY_AND_ASSIGN(ResourceTracker);
+};
+
+} // namespace pepper
+
+#endif // WEBKIT_GLUE_PLUGINS_PEPPER_RESOURCE_TRACKER_H_
diff --git a/webkit/glue/plugins/pepper_scrollbar.cc b/webkit/glue/plugins/pepper_scrollbar.cc
new file mode 100644
index 0000000..48db8d4
--- /dev/null
+++ b/webkit/glue/plugins/pepper_scrollbar.cc
@@ -0,0 +1,228 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/pepper_scrollbar.h"
+
+#include "base/logging.h"
+#include "base/message_loop.h"
+#include "third_party/ppapi/c/ppp_scrollbar.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebInputEvent.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebRect.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebScrollbar.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebVector.h"
+#include "webkit/glue/plugins/pepper_event_conversion.h"
+#include "webkit/glue/plugins/pepper_image_data.h"
+#include "webkit/glue/plugins/pepper_plugin_instance.h"
+#include "webkit/glue/plugins/pepper_plugin_module.h"
+#include "webkit/glue/webkit_glue.h"
+
+using WebKit::WebInputEvent;
+using WebKit::WebRect;
+using WebKit::WebScrollbar;
+
+namespace pepper {
+
+namespace {
+
+PP_Resource Create(PP_Instance instance_id, bool vertical) {
+ PluginInstance* instance = PluginInstance::FromPPInstance(instance_id);
+ if (!instance)
+ return NULL;
+
+ scoped_refptr<Scrollbar> scrollbar(new Scrollbar(instance, vertical));
+ return scrollbar->GetReference();
+}
+
+bool IsScrollbar(PP_Resource resource) {
+ return !!Resource::GetAs<Scrollbar>(resource);
+}
+
+uint32_t GetThickness() {
+ return WebScrollbar::defaultThickness();
+}
+
+uint32_t GetValue(PP_Resource resource) {
+ scoped_refptr<Scrollbar> scrollbar(Resource::GetAs<Scrollbar>(resource));
+ if (!scrollbar)
+ return 0;
+ return scrollbar->GetValue();
+}
+
+void SetValue(PP_Resource resource, uint32_t value) {
+ scoped_refptr<Scrollbar> scrollbar(Resource::GetAs<Scrollbar>(resource));
+ if (scrollbar)
+ scrollbar->SetValue(value);
+}
+
+void SetDocumentSize(PP_Resource resource, uint32_t size) {
+ scoped_refptr<Scrollbar> scrollbar(Resource::GetAs<Scrollbar>(resource));
+ if (scrollbar)
+ scrollbar->SetDocumentSize(size);
+}
+
+void SetTickMarks(PP_Resource resource,
+ const PP_Rect* tick_marks,
+ uint32_t count) {
+ scoped_refptr<Scrollbar> scrollbar(Resource::GetAs<Scrollbar>(resource));
+ if (scrollbar)
+ scrollbar->SetTickMarks(tick_marks, count);
+}
+
+void ScrollBy(PP_Resource resource,
+ PP_ScrollBy unit,
+ int32_t multiplier) {
+ scoped_refptr<Scrollbar> scrollbar(Resource::GetAs<Scrollbar>(resource));
+ if (scrollbar)
+ scrollbar->ScrollBy(unit, multiplier);
+}
+
+const PPB_Scrollbar ppb_scrollbar = {
+ &Create,
+ &IsScrollbar,
+ &GetThickness,
+ &GetValue,
+ &SetValue,
+ &SetDocumentSize,
+ &SetTickMarks,
+ &ScrollBy
+};
+
+} // namespace
+
+Scrollbar::Scrollbar(PluginInstance* instance, bool vertical)
+ : Widget(instance) {
+ scrollbar_.reset(WebScrollbar::create(
+ static_cast<WebKit::WebScrollbarClient*>(this),
+ vertical ? WebScrollbar::Vertical : WebScrollbar::Horizontal));
+}
+
+Scrollbar::~Scrollbar() {
+}
+
+// static
+const PPB_Scrollbar* Scrollbar::GetInterface() {
+ return &ppb_scrollbar;
+}
+
+uint32_t Scrollbar::GetValue() {
+ return scrollbar_->value();
+}
+
+void Scrollbar::SetValue(uint32_t value) {
+ scrollbar_->setValue(value);
+}
+
+void Scrollbar::SetDocumentSize(uint32_t size) {
+ scrollbar_->setDocumentSize(size);
+}
+
+void Scrollbar::SetTickMarks(const PP_Rect* tick_marks, uint32_t count) {
+ tickmarks_.resize(count);
+ for (uint32 i = 0; i < count; ++i) {
+ tickmarks_[i] = WebRect(tick_marks[i].point.x,
+ tick_marks[i].point.y,
+ tick_marks[i].size.width,
+ tick_marks[i].size.height);;
+ }
+ PP_Rect rect = location();
+ Invalidate(&rect);
+}
+
+void Scrollbar::ScrollBy(PP_ScrollBy unit, int32_t multiplier) {
+ WebScrollbar::ScrollDirection direction = multiplier >= 0 ?
+ WebScrollbar::ScrollForward : WebScrollbar::ScrollBackward;
+ float fmultiplier = 1.0;
+
+ WebScrollbar::ScrollGranularity granularity;
+ if (unit == PP_SCROLLBY_LINE) {
+ granularity = WebScrollbar::ScrollByLine;
+ } else if (unit == PP_SCROLLBY_PAGE) {
+ granularity = WebScrollbar::ScrollByPage;
+ } else if (unit == PP_SCROLLBY_DOCUMENT) {
+ granularity = WebScrollbar::ScrollByDocument;
+ } else {
+ granularity = WebScrollbar::ScrollByPixel;
+ fmultiplier = static_cast<float>(multiplier);
+ if (fmultiplier < 0)
+ fmultiplier *= -1;
+ }
+ scrollbar_->scroll(direction, granularity, fmultiplier);
+}
+
+bool Scrollbar::Paint(const PP_Rect* rect, ImageData* image) {
+ gfx::Rect gfx_rect(rect->point.x,
+ rect->point.y,
+ rect->size.width,
+ rect->size.height);
+ skia::PlatformCanvas* canvas = image->mapped_canvas();
+ if (!canvas)
+ return false;
+ scrollbar_->paint(webkit_glue::ToWebCanvas(canvas), gfx_rect);
+ return true;
+}
+
+bool Scrollbar::HandleEvent(const PP_Event* event) {
+ scoped_ptr<WebInputEvent> web_input_event(CreateWebInputEvent(*event));
+ if (!web_input_event.get())
+ return false;
+
+ return scrollbar_->handleInputEvent(*web_input_event.get());
+}
+
+void Scrollbar::SetLocationInternal(const PP_Rect* location) {
+ scrollbar_->setLocation(WebRect(location->point.x,
+ location->point.y,
+ location->size.width,
+ location->size.height));
+}
+
+void Scrollbar::valueChanged(WebKit::WebScrollbar* scrollbar) {
+ const PPP_Scrollbar* ppp_scrollbar = static_cast<const PPP_Scrollbar*>(
+ module()->GetPluginInterface(PPP_SCROLLBAR_INTERFACE));
+ if (!ppp_scrollbar)
+ return;
+ ScopedResourceId resource(this);
+ ppp_scrollbar->ValueChanged(
+ instance()->GetPPInstance(), resource.id, scrollbar_->value());
+}
+
+void Scrollbar::invalidateScrollbarRect(WebKit::WebScrollbar* scrollbar,
+ const WebKit::WebRect& rect) {
+ gfx::Rect gfx_rect(rect.x,
+ rect.y,
+ rect.width,
+ rect.height);
+ dirty_ = dirty_.Union(gfx_rect);
+ // Can't call into the client to tell them about the invalidate right away,
+ // since the Scrollbar code is still in the middle of updating its internal
+ // state.
+ MessageLoop::current()->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(this, &Scrollbar::NotifyInvalidate));
+}
+
+void Scrollbar::getTickmarks(
+ WebKit::WebScrollbar* scrollbar,
+ WebKit::WebVector<WebKit::WebRect>* tick_marks) const {
+ if (tickmarks_.empty()) {
+ WebRect* rects = NULL;
+ tick_marks->assign(rects, 0);
+ } else {
+ tick_marks->assign(&tickmarks_[0], tickmarks_.size());
+ }
+}
+
+void Scrollbar::NotifyInvalidate() {
+ if (dirty_.IsEmpty())
+ return;
+ PP_Rect pp_rect;
+ pp_rect.point.x = dirty_.x();
+ pp_rect.point.y = dirty_.y();
+ pp_rect.size.width = dirty_.width();
+ pp_rect.size.height = dirty_.height();
+ dirty_ = gfx::Rect();
+ Invalidate(&pp_rect);
+}
+
+} // namespace pepper
diff --git a/webkit/glue/plugins/pepper_scrollbar.h b/webkit/glue/plugins/pepper_scrollbar.h
new file mode 100644
index 0000000..bf25136
--- /dev/null
+++ b/webkit/glue/plugins/pepper_scrollbar.h
@@ -0,0 +1,64 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_PEPPER_SCROLLBAR_H_
+#define WEBKIT_GLUE_PLUGINS_PEPPER_SCROLLBAR_H_
+
+#include <vector>
+
+#include "gfx/rect.h"
+#include "third_party/ppapi/c/ppb_scrollbar.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebRect.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebScrollbarClient.h"
+#include "webkit/glue/plugins/pepper_widget.h"
+
+typedef struct _ppb_Scrollbar PPB_Scrollbar;
+
+namespace pepper {
+
+class PluginInstance;
+
+class Scrollbar : public Widget, public WebKit::WebScrollbarClient {
+ public:
+ Scrollbar(PluginInstance* instance, bool vertical);
+ virtual ~Scrollbar();
+
+ // Returns a pointer to the interface implementing PPB_Scrollbar that is
+ // exposed to the plugin.
+ static const PPB_Scrollbar* GetInterface();
+
+ // Resource overrides.
+ Scrollbar* AsScrollbar() { return this; }
+
+ // PPB_Scrollbar implementation.
+ uint32_t GetValue();
+ void SetValue(uint32_t value);
+ void SetDocumentSize(uint32_t size);
+ void SetTickMarks(const PP_Rect* tick_marks, uint32_t count);
+ void ScrollBy(PP_ScrollBy unit, int32_t multiplier);
+
+ // PPB_Widget implementation.
+ virtual bool Paint(const PP_Rect* rect, ImageData* image);
+ virtual bool HandleEvent(const PP_Event* event);
+ virtual void SetLocationInternal(const PP_Rect* location);
+
+ private:
+ // WebKit::WebScrollbarClient implementation.
+ virtual void valueChanged(WebKit::WebScrollbar* scrollbar);
+ virtual void invalidateScrollbarRect(WebKit::WebScrollbar* scrollbar,
+ const WebKit::WebRect& rect);
+ virtual void getTickmarks(
+ WebKit::WebScrollbar* scrollbar,
+ WebKit::WebVector<WebKit::WebRect>* tick_marks) const;
+
+ void NotifyInvalidate();
+
+ gfx::Rect dirty_;
+ std::vector<WebKit::WebRect> tickmarks_;
+ scoped_ptr<WebKit::WebScrollbar> scrollbar_;
+};
+
+} // namespace pepper
+
+#endif // WEBKIT_GLUE_PLUGINS_PEPPER_SCROLLBAR_H_
diff --git a/webkit/glue/plugins/pepper_string.h b/webkit/glue/plugins/pepper_string.h
new file mode 100644
index 0000000..1fc43c4
--- /dev/null
+++ b/webkit/glue/plugins/pepper_string.h
@@ -0,0 +1,30 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_PEPPER_STRING_H_
+#define WEBKIT_GLUE_PLUGINS_PEPPER_STRING_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/ref_counted.h"
+
+namespace pepper {
+
+class String : public base::RefCountedThreadSafe<String> {
+ public:
+ String(const char* str, uint32 len) : value_(str, len) {
+ }
+
+ const std::string& value() const { return value_; }
+
+ private:
+ std::string value_;
+
+ DISALLOW_COPY_AND_ASSIGN(String);
+};
+
+} // namespace pepper
+
+#endif // WEBKIT_GLUE_PLUGINS_PEPPER_STRING_H_
diff --git a/webkit/glue/plugins/pepper_url_loader.cc b/webkit/glue/plugins/pepper_url_loader.cc
new file mode 100644
index 0000000..aa09686
--- /dev/null
+++ b/webkit/glue/plugins/pepper_url_loader.cc
@@ -0,0 +1,299 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/pepper_url_loader.h"
+
+#include "base/logging.h"
+#include "third_party/ppapi/c/pp_completion_callback.h"
+#include "third_party/ppapi/c/pp_errors.h"
+#include "third_party/ppapi/c/ppb_url_loader.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebDocument.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebElement.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebFrame.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebKit.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebKitClient.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebPluginContainer.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURLRequest.h"
+#include "webkit/glue/plugins/pepper_plugin_instance.h"
+#include "webkit/glue/plugins/pepper_url_request_info.h"
+#include "webkit/glue/plugins/pepper_url_response_info.h"
+
+using WebKit::WebFrame;
+using WebKit::WebString;
+using WebKit::WebURL;
+using WebKit::WebURLError;
+using WebKit::WebURLLoader;
+using WebKit::WebURLRequest;
+using WebKit::WebURLResponse;
+
+#ifdef _MSC_VER
+// Do not warn about use of std::copy with raw pointers.
+#pragma warning(disable : 4996)
+#endif
+
+namespace pepper {
+
+namespace {
+
+PP_Resource Create(PP_Instance instance_id) {
+ PluginInstance* instance = PluginInstance::FromPPInstance(instance_id);
+ if (!instance)
+ return 0;
+
+ URLLoader* loader = new URLLoader(instance);
+
+ return loader->GetReference();
+}
+
+bool IsURLLoader(PP_Resource resource) {
+ return !!Resource::GetAs<URLLoader>(resource);
+}
+
+int32_t Open(PP_Resource loader_id,
+ PP_Resource request_id,
+ PP_CompletionCallback callback) {
+ scoped_refptr<URLLoader> loader(Resource::GetAs<URLLoader>(loader_id));
+ if (!loader)
+ return PP_ERROR_BADRESOURCE;
+
+ scoped_refptr<URLRequestInfo> request(
+ Resource::GetAs<URLRequestInfo>(request_id));
+ if (!request)
+ return PP_ERROR_BADRESOURCE;
+
+ return loader->Open(request, callback);
+}
+
+int32_t FollowRedirect(PP_Resource loader_id,
+ PP_CompletionCallback callback) {
+ scoped_refptr<URLLoader> loader(Resource::GetAs<URLLoader>(loader_id));
+ if (!loader)
+ return PP_ERROR_BADRESOURCE;
+
+ return loader->FollowRedirect(callback);
+}
+
+bool GetUploadProgress(PP_Resource loader_id,
+ int64_t* bytes_sent,
+ int64_t* total_bytes_to_be_sent) {
+ scoped_refptr<URLLoader> loader(Resource::GetAs<URLLoader>(loader_id));
+ if (!loader)
+ return false;
+
+ *bytes_sent = loader->bytes_sent();
+ *total_bytes_to_be_sent = loader->total_bytes_to_be_sent();
+ return true;
+}
+
+bool GetDownloadProgress(PP_Resource loader_id,
+ int64_t* bytes_received,
+ int64_t* total_bytes_to_be_received) {
+ scoped_refptr<URLLoader> loader(Resource::GetAs<URLLoader>(loader_id));
+ if (!loader)
+ return false;
+
+ *bytes_received = loader->bytes_received();
+ *total_bytes_to_be_received = loader->total_bytes_to_be_received();
+ return true;
+}
+
+PP_Resource GetResponseInfo(PP_Resource loader_id) {
+ scoped_refptr<URLLoader> loader(Resource::GetAs<URLLoader>(loader_id));
+ if (!loader)
+ return 0;
+
+ URLResponseInfo* response_info = loader->response_info();
+ if (!response_info)
+ return 0;
+
+ return response_info->GetReference();
+}
+
+int32_t ReadResponseBody(PP_Resource loader_id,
+ char* buffer,
+ int32_t bytes_to_read,
+ PP_CompletionCallback callback) {
+ scoped_refptr<URLLoader> loader(Resource::GetAs<URLLoader>(loader_id));
+ if (!loader)
+ return PP_ERROR_BADRESOURCE;
+
+ return loader->ReadResponseBody(buffer, bytes_to_read, callback);
+}
+
+void Close(PP_Resource loader_id) {
+ scoped_refptr<URLLoader> loader(Resource::GetAs<URLLoader>(loader_id));
+ if (!loader)
+ return;
+
+ loader->Close();
+}
+
+const PPB_URLLoader ppb_urlloader = {
+ &Create,
+ &IsURLLoader,
+ &Open,
+ &FollowRedirect,
+ &GetUploadProgress,
+ &GetDownloadProgress,
+ &GetResponseInfo,
+ &ReadResponseBody,
+ &Close
+};
+
+} // namespace
+
+URLLoader::URLLoader(PluginInstance* instance)
+ : Resource(instance->module()),
+ instance_(instance),
+ pending_callback_(),
+ bytes_sent_(0),
+ total_bytes_to_be_sent_(0),
+ bytes_received_(0),
+ total_bytes_to_be_received_(0),
+ user_buffer_(NULL),
+ user_buffer_size_(0),
+ done_(false) {
+}
+
+URLLoader::~URLLoader() {
+}
+
+// static
+const PPB_URLLoader* URLLoader::GetInterface() {
+ return &ppb_urlloader;
+}
+
+int32_t URLLoader::Open(URLRequestInfo* request,
+ PP_CompletionCallback callback) {
+ if (loader_.get())
+ return PP_ERROR_INPROGRESS;
+
+ // We only support non-blocking calls.
+ if (!callback.func)
+ return PP_ERROR_BADARGUMENT;
+
+ WebFrame* frame = instance_->container()->element().document().frame();
+ if (!frame)
+ return PP_ERROR_FAILED;
+ WebURLRequest web_request(request->ToWebURLRequest(frame));
+ frame->dispatchWillSendRequest(web_request);
+
+ loader_.reset(WebKit::webKitClient()->createURLLoader());
+ if (!loader_.get()) {
+ loader_.reset();
+ return PP_ERROR_FAILED;
+ }
+ loader_->loadAsynchronously(web_request, this);
+
+ pending_callback_ = callback;
+
+ // Notify completion when we receive a redirect or response headers.
+ return PP_ERROR_WOULDBLOCK;
+}
+
+int32_t URLLoader::FollowRedirect(PP_CompletionCallback callback) {
+ NOTIMPLEMENTED(); // TODO(darin): Implement me.
+ return PP_ERROR_FAILED;
+}
+
+int32_t URLLoader::ReadResponseBody(char* buffer, int32_t bytes_to_read,
+ PP_CompletionCallback callback) {
+ if (bytes_to_read <= 0 || !buffer)
+ return PP_ERROR_BADARGUMENT;
+ if (pending_callback_.func)
+ return PP_ERROR_INPROGRESS;
+
+ // We only support non-blocking calls.
+ if (!callback.func)
+ return PP_ERROR_BADARGUMENT;
+
+ user_buffer_ = buffer;
+ user_buffer_size_ = bytes_to_read;
+
+ if (!buffer_.empty())
+ return FillUserBuffer();
+
+ if (done_) {
+ user_buffer_ = NULL;
+ user_buffer_size_ = 0;
+ return 0;
+ }
+
+ pending_callback_ = callback;
+ return PP_ERROR_WOULDBLOCK;
+}
+
+void URLLoader::Close() {
+ NOTIMPLEMENTED(); // TODO(darin): Implement me.
+}
+
+void URLLoader::willSendRequest(WebURLLoader* loader,
+ WebURLRequest& new_request,
+ const WebURLResponse& redirect_response) {
+ NOTIMPLEMENTED(); // TODO(darin): Allow the plugin to inspect redirects.
+}
+
+void URLLoader::didSendData(WebURLLoader* loader,
+ unsigned long long bytes_sent,
+ unsigned long long total_bytes_to_be_sent) {
+ // TODO(darin): Bounds check input?
+ bytes_sent_ = static_cast<int64_t>(bytes_sent);
+ total_bytes_to_be_sent_ = static_cast<int64_t>(total_bytes_to_be_sent);
+}
+
+void URLLoader::didReceiveResponse(WebURLLoader* loader,
+ const WebURLResponse& response) {
+ scoped_refptr<URLResponseInfo> response_info(new URLResponseInfo(module()));
+ if (response_info->Initialize(response))
+ response_info_ = response_info;
+
+ RunCallback(PP_OK);
+}
+
+void URLLoader::didReceiveData(WebURLLoader* loader,
+ const char* data,
+ int data_length) {
+ buffer_.insert(buffer_.end(), data, data + data_length);
+ if (user_buffer_) {
+ RunCallback(FillUserBuffer());
+ } else {
+ DCHECK(!pending_callback_.func);
+ }
+}
+
+void URLLoader::didFinishLoading(WebURLLoader* loader) {
+ done_ = true;
+ RunCallback(PP_OK);
+}
+
+void URLLoader::didFail(WebURLLoader* loader, const WebURLError& error) {
+ done_ = true;
+ // TODO(darin): Provide more detailed error information.
+ RunCallback(PP_ERROR_FAILED);
+}
+
+void URLLoader::RunCallback(int32_t result) {
+ if (!pending_callback_.func)
+ return;
+
+ PP_CompletionCallback callback = {0};
+ std::swap(callback, pending_callback_);
+ PP_RunCompletionCallback(&callback, result);
+}
+
+size_t URLLoader::FillUserBuffer() {
+ DCHECK(user_buffer_);
+ DCHECK(user_buffer_size_);
+
+ size_t bytes_to_copy = std::min(buffer_.size(), user_buffer_size_);
+ std::copy(buffer_.begin(), buffer_.begin() + bytes_to_copy, user_buffer_);
+ buffer_.erase(buffer_.begin(), buffer_.begin() + bytes_to_copy);
+
+ // Reset for next time.
+ user_buffer_ = NULL;
+ user_buffer_size_ = 0;
+ return bytes_to_copy;
+}
+
+} // namespace pepper
diff --git a/webkit/glue/plugins/pepper_url_loader.h b/webkit/glue/plugins/pepper_url_loader.h
new file mode 100644
index 0000000..088f220
--- /dev/null
+++ b/webkit/glue/plugins/pepper_url_loader.h
@@ -0,0 +1,89 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_PEPPER_URL_LOADER_H_
+#define WEBKIT_GLUE_PLUGINS_PEPPER_URL_LOADER_H_
+
+#include <deque>
+
+#include "base/scoped_ptr.h"
+#include "third_party/ppapi/c/pp_completion_callback.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURLLoader.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURLLoaderClient.h"
+#include "webkit/glue/plugins/pepper_resource.h"
+
+typedef struct _ppb_URLLoader PPB_URLLoader;
+
+namespace pepper {
+
+class PluginInstance;
+class URLRequestInfo;
+class URLResponseInfo;
+
+class URLLoader : public Resource, public WebKit::WebURLLoaderClient {
+ public:
+ explicit URLLoader(PluginInstance* instance);
+ virtual ~URLLoader();
+
+ // Returns a pointer to the interface implementing PPB_URLLoader that is
+ // exposed to the plugin.
+ static const PPB_URLLoader* GetInterface();
+
+ // Resource overrides.
+ URLLoader* AsURLLoader() { return this; }
+
+ // PPB_URLLoader implementation.
+ int32_t Open(URLRequestInfo* request, PP_CompletionCallback callback);
+ int32_t FollowRedirect(PP_CompletionCallback callback);
+ int32_t ReadResponseBody(char* buffer, int32_t bytes_to_read,
+ PP_CompletionCallback callback);
+ void Close();
+
+ // WebKit::WebURLLoaderClient implementation.
+ virtual void willSendRequest(WebKit::WebURLLoader* loader,
+ WebKit::WebURLRequest& new_request,
+ const WebKit::WebURLResponse& redir_response);
+ virtual void didSendData(WebKit::WebURLLoader* loader,
+ unsigned long long bytes_sent,
+ unsigned long long total_bytes_to_be_sent);
+ virtual void didReceiveResponse(WebKit::WebURLLoader* loader,
+ const WebKit::WebURLResponse& response);
+ virtual void didReceiveData(WebKit::WebURLLoader* loader,
+ const char* data,
+ int data_length);
+ virtual void didFinishLoading(WebKit::WebURLLoader* loader);
+ virtual void didFail(WebKit::WebURLLoader* loader,
+ const WebKit::WebURLError& error);
+
+ URLResponseInfo* response_info() const { return response_info_; }
+
+ // Progress counters.
+ int64_t bytes_sent() const { return bytes_sent_; }
+ int64_t total_bytes_to_be_sent() const { return total_bytes_to_be_sent_; }
+ int64_t bytes_received() const { return bytes_received_; }
+ int64_t total_bytes_to_be_received() const {
+ return total_bytes_to_be_received_;
+ }
+
+ private:
+ void RunCallback(int32_t result);
+ size_t FillUserBuffer();
+
+ scoped_refptr<PluginInstance> instance_;
+ scoped_ptr<WebKit::WebURLLoader> loader_;
+ scoped_refptr<URLResponseInfo> response_info_;
+ PP_CompletionCallback pending_callback_;
+ std::deque<char> buffer_;
+ int64_t bytes_sent_;
+ int64_t total_bytes_to_be_sent_;
+ int64_t bytes_received_;
+ int64_t total_bytes_to_be_received_;
+ char* user_buffer_;
+ size_t user_buffer_size_;
+ bool done_;
+};
+
+} // namespace pepper
+
+#endif // WEBKIT_GLUE_PLUGINS_PEPPER_URL_LOADER_H_
diff --git a/webkit/glue/plugins/pepper_url_request_info.cc b/webkit/glue/plugins/pepper_url_request_info.cc
new file mode 100644
index 0000000..d230f20
--- /dev/null
+++ b/webkit/glue/plugins/pepper_url_request_info.cc
@@ -0,0 +1,220 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/pepper_url_request_info.h"
+
+#include "base/logging.h"
+#include "base/string_util.h"
+#include "googleurl/src/gurl.h"
+#include "net/http/http_util.h"
+#include "third_party/ppapi/c/pp_var.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebData.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebDocument.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebFrame.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebHTTPBody.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURL.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURLRequest.h"
+#include "webkit/glue/plugins/pepper_file_ref.h"
+#include "webkit/glue/plugins/pepper_plugin_module.h"
+#include "webkit/glue/plugins/pepper_string.h"
+#include "webkit/glue/plugins/pepper_var.h"
+#include "webkit/glue/webkit_glue.h"
+
+using WebKit::WebData;
+using WebKit::WebFileInfo;
+using WebKit::WebHTTPBody;
+using WebKit::WebString;
+using WebKit::WebFrame;
+using WebKit::WebURL;
+using WebKit::WebURLRequest;
+
+namespace pepper {
+
+namespace {
+
+// If any of these request headers are specified, they will not be sent.
+// TODO(darin): Add more based on security considerations?
+const char* const kIgnoredRequestHeaders[] = {
+ "content-length"
+};
+
+bool IsIgnoredRequestHeader(const std::string& name) {
+ for (size_t i = 0; i < arraysize(kIgnoredRequestHeaders); ++i) {
+ if (LowerCaseEqualsASCII(name, kIgnoredRequestHeaders[i]))
+ return true;
+ }
+ return false;
+}
+
+PP_Resource Create(PP_Module module_id) {
+ PluginModule* module = PluginModule::FromPPModule(module_id);
+ if (!module)
+ return 0;
+
+ URLRequestInfo* request = new URLRequestInfo(module);
+
+ return request->GetReference();
+}
+
+bool IsURLRequestInfo(PP_Resource resource) {
+ return !!Resource::GetAs<URLRequestInfo>(resource);
+}
+
+bool SetProperty(PP_Resource request_id,
+ PP_URLRequestProperty property,
+ PP_Var var) {
+ scoped_refptr<URLRequestInfo> request(
+ Resource::GetAs<URLRequestInfo>(request_id));
+ if (!request)
+ return false;
+
+ if (var.type == PP_VARTYPE_BOOL)
+ return request->SetBooleanProperty(property, var.value.as_bool);
+
+ if (var.type == PP_VARTYPE_STRING)
+ return request->SetStringProperty(property, GetString(var)->value());
+
+ return false;
+}
+
+bool AppendDataToBody(PP_Resource request_id, PP_Var var) {
+ scoped_refptr<URLRequestInfo> request(
+ Resource::GetAs<URLRequestInfo>(request_id));
+ if (!request)
+ return false;
+
+ String* data = GetString(var);
+ if (!data)
+ return false;
+
+ return request->AppendDataToBody(data->value());
+}
+
+bool AppendFileToBody(PP_Resource request_id,
+ PP_Resource file_ref_id,
+ int64_t start_offset,
+ int64_t number_of_bytes,
+ PP_Time expected_last_modified_time) {
+ scoped_refptr<URLRequestInfo> request(
+ Resource::GetAs<URLRequestInfo>(request_id));
+ if (!request)
+ return false;
+
+ scoped_refptr<FileRef> file_ref(Resource::GetAs<FileRef>(file_ref_id));
+ if (!file_ref)
+ return false;
+
+ return request->AppendFileToBody(file_ref,
+ start_offset,
+ number_of_bytes,
+ expected_last_modified_time);
+}
+
+const PPB_URLRequestInfo ppb_urlrequestinfo = {
+ &Create,
+ &IsURLRequestInfo,
+ &SetProperty,
+ &AppendDataToBody,
+ &AppendFileToBody
+};
+
+} // namespace
+
+URLRequestInfo::URLRequestInfo(PluginModule* module)
+ : Resource(module) {
+}
+
+URLRequestInfo::~URLRequestInfo() {
+}
+
+// static
+const PPB_URLRequestInfo* URLRequestInfo::GetInterface() {
+ return &ppb_urlrequestinfo;
+}
+
+bool URLRequestInfo::SetBooleanProperty(PP_URLRequestProperty property,
+ bool value) {
+ NOTIMPLEMENTED(); // TODO(darin): Implement me!
+ return false;
+}
+
+bool URLRequestInfo::SetStringProperty(PP_URLRequestProperty property,
+ const std::string& value) {
+ // TODO(darin): Validate input. Perhaps at a different layer?
+ switch (property) {
+ case PP_URLREQUESTPROPERTY_URL:
+ url_ = value; // NOTE: This may be a relative URL.
+ return true;
+ case PP_URLREQUESTPROPERTY_METHOD:
+ method_ = value;
+ return true;
+ case PP_URLREQUESTPROPERTY_HEADERS:
+ headers_ = value;
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool URLRequestInfo::AppendDataToBody(const std::string& data) {
+ if (!data.empty())
+ body_.push_back(BodyItem(data));
+ return true;
+}
+
+bool URLRequestInfo::AppendFileToBody(FileRef* file_ref,
+ int64_t start_offset,
+ int64_t number_of_bytes,
+ PP_Time expected_last_modified_time) {
+ body_.push_back(BodyItem(file_ref,
+ start_offset,
+ number_of_bytes,
+ expected_last_modified_time));
+ return true;
+}
+
+WebURLRequest URLRequestInfo::ToWebURLRequest(WebFrame* frame) const {
+ WebURLRequest web_request;
+ web_request.initialize();
+ web_request.setURL(frame->document().completeURL(WebString::fromUTF8(url_)));
+
+ if (!method_.empty())
+ web_request.setHTTPMethod(WebString::fromUTF8(method_));
+
+ if (!headers_.empty()) {
+ net::HttpUtil::HeadersIterator it(headers_.begin(), headers_.end(), "\n");
+ while (it.GetNext()) {
+ if (!IsIgnoredRequestHeader(it.name())) {
+ web_request.addHTTPHeaderField(
+ WebString::fromUTF8(it.name()),
+ WebString::fromUTF8(it.values()));
+ }
+ }
+ }
+
+ if (!body_.empty()) {
+ WebHTTPBody http_body;
+ http_body.initialize();
+ for (size_t i = 0; i < body_.size(); ++i) {
+ if (body_[i].file_ref) {
+ WebFileInfo file_info;
+ file_info.modificationTime = body_[i].expected_last_modified_time;
+ http_body.appendFileRange(
+ webkit_glue::FilePathToWebString(body_[i].file_ref->system_path()),
+ body_[i].start_offset,
+ body_[i].number_of_bytes,
+ file_info);
+ } else {
+ DCHECK(!body_[i].data.empty());
+ http_body.appendData(WebData(body_[i].data));
+ }
+ }
+ web_request.setHTTPBody(http_body);
+ }
+
+ frame->setReferrerForRequest(web_request, WebURL()); // Use default.
+ return web_request;
+}
+
+} // namespace pepper
diff --git a/webkit/glue/plugins/pepper_url_request_info.h b/webkit/glue/plugins/pepper_url_request_info.h
new file mode 100644
index 0000000..ef1452c
--- /dev/null
+++ b/webkit/glue/plugins/pepper_url_request_info.h
@@ -0,0 +1,83 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_PEPPER_URL_REQUEST_INFO_H_
+#define WEBKIT_GLUE_PLUGINS_PEPPER_URL_REQUEST_INFO_H_
+
+#include <string>
+#include <vector>
+
+#include "base/ref_counted.h"
+#include "third_party/ppapi/c/ppb_url_request_info.h"
+#include "webkit/glue/plugins/pepper_file_ref.h"
+#include "webkit/glue/plugins/pepper_resource.h"
+
+namespace WebKit {
+class WebFrame;
+class WebURLRequest;
+}
+
+namespace pepper {
+
+class URLRequestInfo : public Resource {
+ public:
+ explicit URLRequestInfo(PluginModule* module);
+ virtual ~URLRequestInfo();
+
+ // Returns a pointer to the interface implementing PPB_URLRequestInfo that is
+ // exposed to the plugin.
+ static const PPB_URLRequestInfo* GetInterface();
+
+ // Resource overrides.
+ URLRequestInfo* AsURLRequestInfo() { return this; }
+
+ // PPB_URLRequestInfo implementation.
+ bool SetBooleanProperty(PP_URLRequestProperty property, bool value);
+ bool SetStringProperty(PP_URLRequestProperty property,
+ const std::string& value);
+ bool AppendDataToBody(const std::string& data);
+ bool AppendFileToBody(FileRef* file_ref,
+ int64_t start_offset,
+ int64_t number_of_bytes,
+ PP_Time expected_last_modified_time);
+
+ WebKit::WebURLRequest ToWebURLRequest(WebKit::WebFrame* frame) const;
+
+ private:
+ struct BodyItem {
+ BodyItem(const std::string& data)
+ : data(data),
+ start_offset(0),
+ number_of_bytes(-1),
+ expected_last_modified_time(0.0) {
+ }
+
+ BodyItem(FileRef* file_ref,
+ int64_t start_offset,
+ int64_t number_of_bytes,
+ PP_Time expected_last_modified_time)
+ : file_ref(file_ref),
+ start_offset(start_offset),
+ number_of_bytes(number_of_bytes),
+ expected_last_modified_time(expected_last_modified_time) {
+ }
+
+ std::string data;
+ scoped_refptr<FileRef> file_ref;
+ int64_t start_offset;
+ int64_t number_of_bytes;
+ PP_Time expected_last_modified_time;
+ };
+
+ typedef std::vector<BodyItem> Body;
+
+ std::string url_;
+ std::string method_;
+ std::string headers_;
+ Body body_;
+};
+
+} // namespace pepper
+
+#endif // WEBKIT_GLUE_PLUGINS_PEPPER_URL_REQUEST_INFO_H_
diff --git a/webkit/glue/plugins/pepper_url_response_info.cc b/webkit/glue/plugins/pepper_url_response_info.cc
new file mode 100644
index 0000000..bff92aa
--- /dev/null
+++ b/webkit/glue/plugins/pepper_url_response_info.cc
@@ -0,0 +1,113 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/pepper_url_response_info.h"
+
+#include "base/logging.h"
+#include "third_party/ppapi/c/pp_var.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebHTTPHeaderVisitor.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebString.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURL.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURLResponse.h"
+#include "webkit/glue/plugins/pepper_file_ref.h"
+#include "webkit/glue/plugins/pepper_var.h"
+
+using WebKit::WebHTTPHeaderVisitor;
+using WebKit::WebString;
+using WebKit::WebURLResponse;
+
+namespace pepper {
+
+namespace {
+
+class HeaderFlattener : public WebHTTPHeaderVisitor {
+ public:
+ const std::string& buffer() const { return buffer_; }
+
+ virtual void visitHeader(const WebString& name, const WebString& value) {
+ if (!buffer_.empty())
+ buffer_.append("\n");
+ buffer_.append(name.utf8());
+ buffer_.append(": ");
+ buffer_.append(value.utf8());
+ }
+
+ private:
+ std::string buffer_;
+};
+
+bool IsURLResponseInfo(PP_Resource resource) {
+ return !!Resource::GetAs<URLResponseInfo>(resource);
+}
+
+PP_Var GetProperty(PP_Resource response_id,
+ PP_URLResponseProperty property) {
+ scoped_refptr<URLResponseInfo> response(
+ Resource::GetAs<URLResponseInfo>(response_id));
+ if (!response)
+ return PP_MakeVoid();
+
+ return response->GetProperty(property);
+}
+
+PP_Resource GetBody(PP_Resource response_id) {
+ scoped_refptr<URLResponseInfo> response(
+ Resource::GetAs<URLResponseInfo>(response_id));
+ if (!response.get())
+ return 0;
+
+ FileRef* body = response->body();
+ if (!body)
+ return 0;
+ body->AddRef(); // AddRef for the caller.
+
+ return body->GetReference();
+}
+
+const PPB_URLResponseInfo ppb_urlresponseinfo = {
+ &IsURLResponseInfo,
+ &GetProperty,
+ &GetBody
+};
+
+} // namespace
+
+URLResponseInfo::URLResponseInfo(PluginModule* module)
+ : Resource(module),
+ status_code_(-1) {
+}
+
+URLResponseInfo::~URLResponseInfo() {
+}
+
+// static
+const PPB_URLResponseInfo* URLResponseInfo::GetInterface() {
+ return &ppb_urlresponseinfo;
+}
+
+PP_Var URLResponseInfo::GetProperty(PP_URLResponseProperty property) {
+ switch (property) {
+ case PP_URLRESPONSEPROPERTY_URL:
+ return StringToPPVar(url_);
+ case PP_URLRESPONSEPROPERTY_STATUSCODE:
+ return PP_MakeInt32(status_code_);
+ case PP_URLRESPONSEPROPERTY_HEADERS:
+ return StringToPPVar(headers_);
+ default:
+ NOTIMPLEMENTED(); // TODO(darin): Implement me!
+ return PP_MakeVoid();
+ }
+}
+
+bool URLResponseInfo::Initialize(const WebURLResponse& response) {
+ url_ = response.url().spec();
+ status_code_ = response.httpStatusCode();
+
+ HeaderFlattener flattener;
+ response.visitHTTPHeaderFields(&flattener);
+ headers_ = flattener.buffer();
+ return true;
+}
+
+} // namespace pepper
diff --git a/webkit/glue/plugins/pepper_url_response_info.h b/webkit/glue/plugins/pepper_url_response_info.h
new file mode 100644
index 0000000..8874919
--- /dev/null
+++ b/webkit/glue/plugins/pepper_url_response_info.h
@@ -0,0 +1,47 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_PEPPER_URL_RESPONSE_INFO_H_
+#define WEBKIT_GLUE_PLUGINS_PEPPER_URL_RESPONSE_INFO_H_
+
+#include <string>
+
+#include "third_party/ppapi/c/ppb_url_response_info.h"
+#include "webkit/glue/plugins/pepper_resource.h"
+
+namespace WebKit {
+class WebURLResponse;
+}
+
+namespace pepper {
+
+class URLResponseInfo : public Resource {
+ public:
+ explicit URLResponseInfo(PluginModule* module);
+ virtual ~URLResponseInfo();
+
+ // Returns a pointer to the interface implementing PPB_URLResponseInfo that
+ // is exposed to the plugin.
+ static const PPB_URLResponseInfo* GetInterface();
+
+ // Resource overrides.
+ URLResponseInfo* AsURLResponseInfo() { return this; }
+
+ // PPB_URLResponseInfo implementation.
+ PP_Var GetProperty(PP_URLResponseProperty property);
+
+ bool Initialize(const WebKit::WebURLResponse& response);
+
+ FileRef* body() { return body_; }
+
+ private:
+ std::string url_;
+ std::string headers_;
+ int32_t status_code_;
+ scoped_refptr<FileRef> body_;
+};
+
+} // namespace pepper
+
+#endif // WEBKIT_GLUE_PLUGINS_PEPPER_URL_RESPONSE_INFO_H_
diff --git a/webkit/glue/plugins/pepper_var.cc b/webkit/glue/plugins/pepper_var.cc
new file mode 100644
index 0000000..414df7b
--- /dev/null
+++ b/webkit/glue/plugins/pepper_var.cc
@@ -0,0 +1,857 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/pepper_var.h"
+
+#include "base/logging.h"
+#include "base/scoped_ptr.h"
+#include "base/string_util.h"
+#include "third_party/ppapi/c/pp_var.h"
+#include "third_party/ppapi/c/ppb_var.h"
+#include "third_party/ppapi/c/ppp_class.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebBindings.h"
+#include "webkit/glue/plugins/pepper_string.h"
+#include "v8/include/v8.h"
+
+using WebKit::WebBindings;
+
+namespace pepper {
+
+namespace {
+
+void Release(PP_Var var);
+PP_Var VarFromUtf8(const char* data, uint32_t len);
+
+// ---------------------------------------------------------------------------
+// Exceptions
+
+class TryCatch {
+ public:
+ TryCatch(PP_Var* exception) : exception_(exception) {
+ WebBindings::pushExceptionHandler(&TryCatch::Catch, this);
+ }
+
+ ~TryCatch() {
+ WebBindings::popExceptionHandler();
+ }
+
+ bool HasException() const {
+ return exception_ && exception_->type != PP_VARTYPE_VOID;
+ }
+
+ void SetException(const char* message) {
+ DCHECK(!HasException());
+ if (exception_)
+ *exception_ = VarFromUtf8(message, strlen(message));
+ }
+
+ private:
+ static void Catch(void* self, const NPUTF8* message) {
+ static_cast<TryCatch*>(self)->SetException(message);
+ }
+
+ // May be null if the consumer isn't interesting in catching exceptions.
+ PP_Var* exception_;
+};
+
+const char kInvalidObjectException[] = "Error: Invalid object";
+const char kInvalidPropertyException[] = "Error: Invalid property";
+const char kUnableToGetPropertyException[] = "Error: Unable to get property";
+const char kUnableToSetPropertyException[] = "Error: Unable to set property";
+const char kUnableToRemovePropertyException[] =
+ "Error: Unable to remove property";
+const char kUnableToGetAllPropertiesException[] =
+ "Error: Unable to get all properties";
+const char kUnableToCallMethodException[] = "Error: Unable to call method";
+const char kUnableToConstructException[] = "Error: Unable to construct";
+
+// ---------------------------------------------------------------------------
+// Utilities
+
+String* GetStringUnchecked(PP_Var var) {
+ return reinterpret_cast<String*>(var.value.as_id);
+}
+
+NPObject* GetNPObjectUnchecked(PP_Var var) {
+ return reinterpret_cast<NPObject*>(var.value.as_id);
+}
+
+// Returns a PP_Var that corresponds to the given NPVariant. The contents of
+// the NPVariant will be copied unless the NPVariant corresponds to an object.
+PP_Var NPVariantToPPVar(const NPVariant* variant) {
+ switch (variant->type) {
+ case NPVariantType_Void:
+ return PP_MakeVoid();
+ case NPVariantType_Null:
+ return PP_MakeNull();
+ case NPVariantType_Bool:
+ return PP_MakeBool(NPVARIANT_TO_BOOLEAN(*variant));
+ case NPVariantType_Int32:
+ return PP_MakeInt32(NPVARIANT_TO_INT32(*variant));
+ case NPVariantType_Double:
+ return PP_MakeDouble(NPVARIANT_TO_DOUBLE(*variant));
+ case NPVariantType_String:
+ return VarFromUtf8(NPVARIANT_TO_STRING(*variant).UTF8Characters,
+ NPVARIANT_TO_STRING(*variant).UTF8Length);
+ case NPVariantType_Object:
+ return NPObjectToPPVar(NPVARIANT_TO_OBJECT(*variant));
+ }
+ NOTREACHED();
+ return PP_MakeVoid();
+}
+
+// Returns a NPVariant that corresponds to the given PP_Var. The contents of
+// the PP_Var will be copied unless the PP_Var corresponds to an object.
+NPVariant PPVarToNPVariant(PP_Var var) {
+ NPVariant ret;
+ switch (var.type) {
+ case PP_VARTYPE_VOID:
+ VOID_TO_NPVARIANT(ret);
+ break;
+ case PP_VARTYPE_NULL:
+ NULL_TO_NPVARIANT(ret);
+ break;
+ case PP_VARTYPE_BOOL:
+ BOOLEAN_TO_NPVARIANT(var.value.as_bool, ret);
+ break;
+ case PP_VARTYPE_INT32:
+ INT32_TO_NPVARIANT(var.value.as_int, ret);
+ break;
+ case PP_VARTYPE_DOUBLE:
+ DOUBLE_TO_NPVARIANT(var.value.as_double, ret);
+ break;
+ case PP_VARTYPE_STRING: {
+ const std::string& value = GetStringUnchecked(var)->value();
+ STRINGN_TO_NPVARIANT(base::strdup(value.c_str()), value.size(), ret);
+ break;
+ }
+ case PP_VARTYPE_OBJECT: {
+ NPObject* object = GetNPObjectUnchecked(var);
+ OBJECT_TO_NPVARIANT(WebBindings::retainObject(object), ret);
+ break;
+ }
+ }
+ return ret;
+}
+
+// Returns a NPVariant that corresponds to the given PP_Var. The contents of
+// the PP_Var will NOT be copied, so you need to ensure that the PP_Var remains
+// valid while the resultant NPVariant is in use.
+NPVariant PPVarToNPVariantNoCopy(PP_Var var) {
+ NPVariant ret;
+ switch (var.type) {
+ case PP_VARTYPE_VOID:
+ VOID_TO_NPVARIANT(ret);
+ break;
+ case PP_VARTYPE_NULL:
+ NULL_TO_NPVARIANT(ret);
+ break;
+ case PP_VARTYPE_BOOL:
+ BOOLEAN_TO_NPVARIANT(var.value.as_bool, ret);
+ break;
+ case PP_VARTYPE_INT32:
+ INT32_TO_NPVARIANT(var.value.as_int, ret);
+ break;
+ case PP_VARTYPE_DOUBLE:
+ DOUBLE_TO_NPVARIANT(var.value.as_double, ret);
+ break;
+ case PP_VARTYPE_STRING: {
+ const std::string& value = GetStringUnchecked(var)->value();
+ STRINGN_TO_NPVARIANT(value.c_str(), value.size(), ret);
+ break;
+ }
+ case PP_VARTYPE_OBJECT: {
+ OBJECT_TO_NPVARIANT(GetNPObjectUnchecked(var), ret);
+ break;
+ }
+ }
+ return ret;
+}
+
+// Returns a NPIdentifier that corresponds to the given PP_Var. The contents
+// of the PP_Var will be copied. Returns NULL if the given PP_Var is not a a
+// string or integer type.
+NPIdentifier PPVarToNPIdentifier(PP_Var var) {
+ switch (var.type) {
+ case PP_VARTYPE_STRING:
+ return WebBindings::getStringIdentifier(
+ GetStringUnchecked(var)->value().c_str());
+ case PP_VARTYPE_INT32:
+ return WebBindings::getIntIdentifier(var.value.as_int);
+ default:
+ return NULL;
+ }
+}
+
+PP_Var NPIdentifierToPPVar(NPIdentifier id) {
+ const NPUTF8* string_value = NULL;
+ int32_t int_value = 0;
+ bool is_string = false;
+ WebBindings::extractIdentifierData(id, string_value, int_value, is_string);
+ if (is_string)
+ return VarFromUtf8(string_value, strlen(string_value));
+
+ return PP_MakeInt32(int_value);
+}
+
+PP_Var NPIdentifierToPPVarString(NPIdentifier id) {
+ PP_Var var = NPIdentifierToPPVar(id);
+ if (var.type == PP_VARTYPE_STRING)
+ return var;
+ DCHECK(var.type == PP_VARTYPE_INT32);
+ const std::string& str = IntToString(var.value.as_int);
+ return VarFromUtf8(str.data(), str.size());
+}
+
+void ThrowException(NPObject* object, PP_Var exception) {
+ String* str = GetString(exception);
+ if (str)
+ WebBindings::setException(object, str->value().c_str());
+}
+
+// ---------------------------------------------------------------------------
+// NPObject implementation in terms of PPP_Class
+
+struct WrapperObject : NPObject {
+ const PPP_Class* ppp_class;
+ void* ppp_class_data;
+};
+
+static WrapperObject* ToWrapper(NPObject* object) {
+ return static_cast<WrapperObject*>(object);
+}
+
+NPObject* WrapperClass_Allocate(NPP npp, NPClass* unused) {
+ return new WrapperObject;
+}
+
+void WrapperClass_Deallocate(NPObject* object) {
+ WrapperObject* wrapper = ToWrapper(object);
+ wrapper->ppp_class->Deallocate(wrapper->ppp_class_data);
+ delete object;
+}
+
+void WrapperClass_Invalidate(NPObject* object) {
+ // TODO(darin): Do I need to do something here?
+}
+
+bool WrapperClass_HasMethod(NPObject* object, NPIdentifier method_name) {
+ WrapperObject* wrapper = ToWrapper(object);
+
+ PP_Var method_name_var = NPIdentifierToPPVarString(method_name);
+ PP_Var exception = PP_MakeVoid();
+ bool rv = wrapper->ppp_class->HasMethod(wrapper->ppp_class_data,
+ method_name_var,
+ &exception);
+ Release(method_name_var);
+
+ if (exception.type != PP_VARTYPE_VOID) {
+ ThrowException(object, exception);
+ Release(exception);
+ return false;
+ }
+ return rv;
+}
+
+bool WrapperClass_Invoke(NPObject* object, NPIdentifier method_name,
+ const NPVariant* argv, uint32_t argc,
+ NPVariant* result) {
+ WrapperObject* wrapper = ToWrapper(object);
+
+ scoped_array<PP_Var> args;
+ if (argc) {
+ args.reset(new PP_Var[argc]);
+ for (uint32_t i = 0; i < argc; ++i)
+ args[i] = NPVariantToPPVar(&argv[i]);
+ }
+ PP_Var method_name_var = NPIdentifierToPPVarString(method_name);
+ PP_Var exception = PP_MakeVoid();
+ PP_Var result_var = wrapper->ppp_class->Call(wrapper->ppp_class_data,
+ method_name_var, argc,
+ args.get(), &exception);
+ Release(method_name_var);
+ for (uint32_t i = 0; i < argc; ++i)
+ Release(args[i]);
+
+ bool rv;
+ if (exception.type == PP_VARTYPE_VOID) {
+ rv = true;
+ *result = PPVarToNPVariant(result_var);
+ } else {
+ rv = false;
+ ThrowException(object, exception);
+ Release(exception);
+ }
+ Release(result_var);
+ return rv;
+}
+
+bool WrapperClass_InvokeDefault(NPObject* object, const NPVariant* argv,
+ uint32_t argc, NPVariant* result) {
+ WrapperObject* wrapper = ToWrapper(object);
+
+ scoped_array<PP_Var> args;
+ if (argc) {
+ args.reset(new PP_Var[argc]);
+ for (uint32_t i = 0; i < argc; ++i)
+ args[i] = NPVariantToPPVar(&argv[i]);
+ }
+ PP_Var exception = PP_MakeVoid();
+ PP_Var result_var = wrapper->ppp_class->Call(wrapper->ppp_class_data,
+ PP_MakeVoid(), argc, args.get(),
+ &exception);
+ for (uint32_t i = 0; i < argc; ++i)
+ Release(args[i]);
+
+ bool rv;
+ if (exception.type == PP_VARTYPE_VOID) {
+ rv = true;
+ *result = PPVarToNPVariant(result_var);
+ } else {
+ rv = false;
+ ThrowException(object, exception);
+ Release(exception);
+ }
+ Release(result_var);
+ return rv;
+}
+
+bool WrapperClass_HasProperty(NPObject* object, NPIdentifier property_name) {
+ WrapperObject* wrapper = ToWrapper(object);
+
+ PP_Var property_name_var = NPIdentifierToPPVar(property_name);
+ PP_Var exception = PP_MakeVoid();
+ bool rv = wrapper->ppp_class->HasProperty(wrapper->ppp_class_data,
+ property_name_var,
+ &exception);
+ Release(property_name_var);
+
+ if (exception.type != PP_VARTYPE_VOID) {
+ ThrowException(object, exception);
+ Release(exception);
+ return false;
+ }
+ return rv;
+}
+
+bool WrapperClass_GetProperty(NPObject* object, NPIdentifier property_name,
+ NPVariant* result) {
+ WrapperObject* wrapper = ToWrapper(object);
+
+ PP_Var property_name_var = NPIdentifierToPPVar(property_name);
+ PP_Var exception = PP_MakeVoid();
+ PP_Var result_var = wrapper->ppp_class->GetProperty(wrapper->ppp_class_data,
+ property_name_var,
+ &exception);
+ Release(property_name_var);
+
+ bool rv;
+ if (exception.type == PP_VARTYPE_VOID) {
+ rv = true;
+ *result = PPVarToNPVariant(result_var);
+ } else {
+ rv = false;
+ ThrowException(object, exception);
+ Release(exception);
+ }
+ Release(result_var);
+ return rv;
+}
+
+bool WrapperClass_SetProperty(NPObject* object, NPIdentifier property_name,
+ const NPVariant* value) {
+ WrapperObject* wrapper = ToWrapper(object);
+
+ PP_Var property_name_var = NPIdentifierToPPVar(property_name);
+ PP_Var value_var = NPVariantToPPVar(value);
+ PP_Var exception = PP_MakeVoid();
+ wrapper->ppp_class->SetProperty(wrapper->ppp_class_data, property_name_var,
+ value_var, &exception);
+ Release(value_var);
+ Release(property_name_var);
+
+ if (exception.type != PP_VARTYPE_VOID) {
+ ThrowException(object, exception);
+ Release(exception);
+ return false;
+ }
+ return true;
+}
+
+bool WrapperClass_RemoveProperty(NPObject* object, NPIdentifier property_name) {
+ WrapperObject* wrapper = ToWrapper(object);
+
+ PP_Var property_name_var = NPIdentifierToPPVar(property_name);
+ PP_Var exception = PP_MakeVoid();
+ wrapper->ppp_class->RemoveProperty(wrapper->ppp_class_data, property_name_var,
+ &exception);
+ Release(property_name_var);
+
+ if (exception.type != PP_VARTYPE_VOID) {
+ ThrowException(object, exception);
+ Release(exception);
+ return false;
+ }
+ return true;
+}
+
+bool WrapperClass_Enumerate(NPObject* object, NPIdentifier** values,
+ uint32_t* count) {
+ WrapperObject* wrapper = ToWrapper(object);
+
+ uint32_t property_count = 0;
+ PP_Var* properties = NULL;
+ PP_Var exception = PP_MakeVoid();
+ wrapper->ppp_class->GetAllPropertyNames(wrapper->ppp_class_data,
+ &property_count,
+ &properties,
+ &exception);
+
+ bool rv;
+ if (exception.type == PP_VARTYPE_VOID) {
+ rv = true;
+ if (property_count == 0) {
+ *values = NULL;
+ *count = 0;
+ } else {
+ *values = static_cast<NPIdentifier*>(
+ malloc(sizeof(NPIdentifier) * property_count));
+ *count = property_count;
+ for (uint32_t i = 0; i < property_count; ++i)
+ (*values)[i] = PPVarToNPIdentifier(properties[i]);
+ }
+ } else {
+ rv = false;
+ ThrowException(object, exception);
+ Release(exception);
+ }
+
+ for (uint32_t i = 0; i < property_count; ++i)
+ Release(properties[i]);
+ free(properties);
+ return rv;
+}
+
+bool WrapperClass_Construct(NPObject* object, const NPVariant* argv,
+ uint32_t argc, NPVariant* result) {
+ WrapperObject* wrapper = ToWrapper(object);
+
+ scoped_array<PP_Var> args;
+ if (argc) {
+ args.reset(new PP_Var[argc]);
+ for (uint32_t i = 0; i < argc; ++i)
+ args[i] = NPVariantToPPVar(&argv[i]);
+ }
+
+ PP_Var exception = PP_MakeVoid();
+ PP_Var result_var = wrapper->ppp_class->Construct(wrapper->ppp_class_data,
+ argc, args.get(),
+ &exception);
+ for (uint32_t i = 0; i < argc; ++i)
+ Release(args[i]);
+
+ bool rv;
+ if (exception.type == PP_VARTYPE_VOID) {
+ rv = true;
+ *result = PPVarToNPVariant(result_var);
+ } else {
+ rv = false;
+ ThrowException(object, exception);
+ Release(exception);
+ }
+ Release(result_var);
+ return rv;
+}
+
+const NPClass wrapper_class = {
+ NP_CLASS_STRUCT_VERSION,
+ WrapperClass_Allocate,
+ WrapperClass_Deallocate,
+ WrapperClass_Invalidate,
+ WrapperClass_HasMethod,
+ WrapperClass_Invoke,
+ WrapperClass_InvokeDefault,
+ WrapperClass_HasProperty,
+ WrapperClass_GetProperty,
+ WrapperClass_SetProperty,
+ WrapperClass_RemoveProperty,
+ WrapperClass_Enumerate,
+ WrapperClass_Construct
+};
+
+// ---------------------------------------------------------------------------
+// PPB_Var methods
+
+void AddRef(PP_Var var) {
+ if (var.type == PP_VARTYPE_STRING) {
+ GetStringUnchecked(var)->AddRef();
+ } else if (var.type == PP_VARTYPE_OBJECT) {
+ // TODO(darin): Add thread safety check
+ WebBindings::retainObject(GetNPObjectUnchecked(var));
+ }
+}
+
+void Release(PP_Var var) {
+ if (var.type == PP_VARTYPE_STRING) {
+ GetStringUnchecked(var)->Release();
+ } else if (var.type == PP_VARTYPE_OBJECT) {
+ // TODO(darin): Add thread safety check
+ WebBindings::releaseObject(GetNPObjectUnchecked(var));
+ }
+}
+
+PP_Var VarFromUtf8(const char* data, uint32_t len) {
+ String* str = new String(data, len);
+ str->AddRef(); // This is for the caller, we return w/ a refcount of 1.
+ PP_Var ret;
+ ret.type = PP_VARTYPE_STRING;
+ ret.value.as_id = reinterpret_cast<intptr_t>(str);
+ return ret;
+}
+
+const char* VarToUtf8(PP_Var var, uint32_t* len) {
+ if (var.type != PP_VARTYPE_STRING) {
+ *len = 0;
+ return NULL;
+ }
+ const std::string& str = GetStringUnchecked(var)->value();
+ *len = static_cast<uint32_t>(str.size());
+ if (str.empty())
+ return ""; // Don't return NULL on success.
+ return str.data();
+}
+
+bool HasProperty(PP_Var var,
+ PP_Var name,
+ PP_Var* exception) {
+ TryCatch try_catch(exception);
+ if (try_catch.HasException())
+ return false;
+
+ NPObject* object = GetNPObject(var);
+ if (!object) {
+ try_catch.SetException(kInvalidObjectException);
+ return false;
+ }
+
+ NPIdentifier identifier = PPVarToNPIdentifier(name);
+ if (!identifier) {
+ try_catch.SetException(kInvalidPropertyException);
+ return false;
+ }
+
+ return WebBindings::hasProperty(NULL, object, identifier);
+}
+
+bool HasMethod(PP_Var var,
+ PP_Var name,
+ PP_Var* exception) {
+ TryCatch try_catch(exception);
+ if (try_catch.HasException())
+ return false;
+
+ NPObject* object = GetNPObject(var);
+ if (!object) {
+ try_catch.SetException(kInvalidObjectException);
+ return false;
+ }
+
+ NPIdentifier identifier = PPVarToNPIdentifier(name);
+ if (!identifier) {
+ try_catch.SetException(kInvalidPropertyException);
+ return false;
+ }
+
+ return WebBindings::hasMethod(NULL, object, identifier);
+}
+
+PP_Var GetProperty(PP_Var var,
+ PP_Var name,
+ PP_Var* exception) {
+ TryCatch try_catch(exception);
+ if (try_catch.HasException())
+ return PP_MakeVoid();
+
+ NPObject* object = GetNPObject(var);
+ if (!object) {
+ try_catch.SetException(kInvalidObjectException);
+ return PP_MakeVoid();
+ }
+
+ NPIdentifier identifier = PPVarToNPIdentifier(name);
+ if (!identifier) {
+ try_catch.SetException(kInvalidPropertyException);
+ return PP_MakeVoid();
+ }
+
+ NPVariant result;
+ if (!WebBindings::getProperty(NULL, object, identifier, &result)) {
+ // An exception may have been raised.
+ if (!try_catch.HasException())
+ try_catch.SetException(kUnableToGetPropertyException);
+ return PP_MakeVoid();
+ }
+
+ PP_Var ret = NPVariantToPPVar(&result);
+ WebBindings::releaseVariantValue(&result);
+ return ret;
+}
+
+void GetAllPropertyNames(PP_Var var,
+ uint32_t* property_count,
+ PP_Var** properties,
+ PP_Var* exception) {
+ *properties = NULL;
+ *property_count = 0;
+
+ TryCatch try_catch(exception);
+ if (try_catch.HasException())
+ return;
+
+ NPObject* object = GetNPObject(var);
+ if (!object) {
+ try_catch.SetException(kInvalidObjectException);
+ return;
+ }
+
+ NPIdentifier* identifiers = NULL;
+ uint32_t count = 0;
+ if (!WebBindings::enumerate(NULL, object, &identifiers, &count)) {
+ if (!try_catch.HasException())
+ try_catch.SetException(kUnableToGetAllPropertiesException);
+ return;
+ }
+
+ if (count == 0)
+ return;
+
+ *property_count = count;
+ *properties = static_cast<PP_Var*>(malloc(sizeof(PP_Var) * count));
+ for (uint32_t i = 0; i < count; ++i)
+ (*properties)[i] = NPIdentifierToPPVar(identifiers[i]);
+ free(identifiers);
+}
+
+void SetProperty(PP_Var var,
+ PP_Var name,
+ PP_Var value,
+ PP_Var* exception) {
+ TryCatch try_catch(exception);
+ if (try_catch.HasException())
+ return;
+
+ NPObject* object = GetNPObject(var);
+ if (!object) {
+ try_catch.SetException(kInvalidObjectException);
+ return;
+ }
+
+ NPIdentifier identifier = PPVarToNPIdentifier(name);
+ if (!identifier) {
+ try_catch.SetException(kInvalidPropertyException);
+ return;
+ }
+
+ NPVariant variant = PPVarToNPVariantNoCopy(value);
+ if (!WebBindings::setProperty(NULL, object, identifier, &variant)) {
+ if (!try_catch.HasException())
+ try_catch.SetException(kUnableToSetPropertyException);
+ }
+}
+
+void RemoveProperty(PP_Var var,
+ PP_Var name,
+ PP_Var* exception) {
+ TryCatch try_catch(exception);
+ if (try_catch.HasException())
+ return;
+
+ NPObject* object = GetNPObject(var);
+ if (!object) {
+ try_catch.SetException(kInvalidObjectException);
+ return;
+ }
+
+ NPIdentifier identifier = PPVarToNPIdentifier(name);
+ if (!identifier) {
+ try_catch.SetException(kInvalidPropertyException);
+ return;
+ }
+
+ if (!WebBindings::removeProperty(NULL, object, identifier)) {
+ if (!try_catch.HasException())
+ try_catch.SetException(kUnableToRemovePropertyException);
+ }
+}
+
+PP_Var Call(PP_Var var,
+ PP_Var method_name,
+ uint32_t argc,
+ PP_Var* argv,
+ PP_Var* exception) {
+ TryCatch try_catch(exception);
+ if (try_catch.HasException())
+ return PP_MakeVoid();
+
+ NPObject* object = GetNPObject(var);
+ if (!object) {
+ try_catch.SetException(kInvalidObjectException);
+ return PP_MakeVoid();
+ }
+
+ NPIdentifier identifier;
+ if (method_name.type == PP_VARTYPE_VOID) {
+ identifier = NULL;
+ } else if (method_name.type == PP_VARTYPE_STRING) {
+ // Specifically allow only string functions to be called.
+ identifier = PPVarToNPIdentifier(method_name);
+ if (!identifier) {
+ try_catch.SetException(kInvalidPropertyException);
+ return PP_MakeVoid();
+ }
+ } else {
+ try_catch.SetException(kInvalidPropertyException);
+ return PP_MakeVoid();
+ }
+
+ scoped_array<NPVariant> args;
+ if (argc) {
+ args.reset(new NPVariant[argc]);
+ for (uint32_t i = 0; i < argc; ++i)
+ args[i] = PPVarToNPVariantNoCopy(argv[i]);
+ }
+
+ bool ok;
+
+ NPVariant result;
+ if (identifier) {
+ ok = WebBindings::invoke(NULL, object, identifier, args.get(), argc,
+ &result);
+ } else {
+ ok = WebBindings::invokeDefault(NULL, object, args.get(), argc, &result);
+ }
+
+ if (!ok) {
+ // An exception may have been raised.
+ if (!try_catch.HasException())
+ try_catch.SetException(kUnableToCallMethodException);
+ return PP_MakeVoid();
+ }
+
+ PP_Var ret = NPVariantToPPVar(&result);
+ WebBindings::releaseVariantValue(&result);
+ return ret;
+}
+
+PP_Var Construct(PP_Var var,
+ uint32_t argc,
+ PP_Var* argv,
+ PP_Var* exception) {
+ TryCatch try_catch(exception);
+ if (try_catch.HasException())
+ return PP_MakeVoid();
+
+ NPObject* object = GetNPObject(var);
+ if (!object) {
+ try_catch.SetException(kInvalidObjectException);
+ return PP_MakeVoid();
+ }
+
+ scoped_array<NPVariant> args;
+ if (argc) {
+ args.reset(new NPVariant[argc]);
+ for (uint32_t i = 0; i < argc; ++i)
+ args[i] = PPVarToNPVariantNoCopy(argv[i]);
+ }
+
+ NPVariant result;
+ if (!WebBindings::construct(NULL, object, args.get(), argc, &result)) {
+ // An exception may have been raised.
+ if (!try_catch.HasException())
+ try_catch.SetException(kUnableToConstructException);
+ return PP_MakeVoid();
+ }
+
+ PP_Var ret = NPVariantToPPVar(&result);
+ WebBindings::releaseVariantValue(&result);
+ return ret;
+}
+
+bool IsInstanceOf(PP_Var var, const PPP_Class* ppp_class,
+ void** ppp_class_data) {
+ NPObject* object = GetNPObject(var);
+ if (!object)
+ return false;
+
+ if (object->_class != &wrapper_class)
+ return false;
+
+ WrapperObject* wrapper = ToWrapper(object);
+ if (wrapper->ppp_class != ppp_class)
+ return false;
+
+ if (ppp_class_data)
+ *ppp_class_data = wrapper->ppp_class_data;
+ return true;
+}
+
+PP_Var CreateObject(const PPP_Class* ppp_class, void* ppp_class_data) {
+ NPObject* object =
+ WebBindings::createObject(NULL, const_cast<NPClass*>(&wrapper_class));
+ static_cast<WrapperObject*>(object)->ppp_class = ppp_class;
+ static_cast<WrapperObject*>(object)->ppp_class_data = ppp_class_data;
+ PP_Var ret = NPObjectToPPVar(object);
+ WebBindings::releaseObject(object); // Release reference from createObject
+ return ret;
+}
+
+const PPB_Var var_interface = {
+ &AddRef,
+ &Release,
+ &VarFromUtf8,
+ &VarToUtf8,
+ &HasProperty,
+ &HasMethod,
+ &GetProperty,
+ &GetAllPropertyNames,
+ &SetProperty,
+ &RemoveProperty,
+ &Call,
+ &Construct,
+ &IsInstanceOf,
+ &CreateObject
+};
+
+} // namespace
+
+const PPB_Var* GetVarInterface() {
+ return &var_interface;
+}
+
+PP_Var NPObjectToPPVar(NPObject* object) {
+ PP_Var ret;
+ ret.type = PP_VARTYPE_OBJECT;
+ ret.value.as_id = reinterpret_cast<intptr_t>(object);
+ WebBindings::retainObject(object);
+ return ret;
+}
+
+NPObject* GetNPObject(PP_Var var) {
+ if (var.type != PP_VARTYPE_OBJECT)
+ return NULL;
+ return GetNPObjectUnchecked(var);
+}
+
+PP_Var StringToPPVar(const std::string& str) {
+ DCHECK(IsStringUTF8(str));
+ return VarFromUtf8(str.data(), str.size());
+}
+
+String* GetString(PP_Var var) {
+ if (var.type != PP_VARTYPE_STRING)
+ return NULL;
+ return GetStringUnchecked(var);
+}
+
+} // namespace pepper
diff --git a/webkit/glue/plugins/pepper_var.h b/webkit/glue/plugins/pepper_var.h
new file mode 100644
index 0000000..b8c31cc
--- /dev/null
+++ b/webkit/glue/plugins/pepper_var.h
@@ -0,0 +1,46 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_PEPPER_VAR_H_
+#define WEBKIT_GLUE_PLUGINS_PEPPER_VAR_H_
+
+#include <string>
+
+typedef struct _pp_Var PP_Var;
+typedef struct _ppb_Var PPB_Var;
+typedef struct NPObject NPObject;
+typedef struct _NPVariant NPVariant;
+typedef void* NPIdentifier;
+
+namespace pepper {
+
+class String;
+
+// There's no class implementing Var since it could represent a number of
+// objects. Instead, we just expose a getter for the interface implemented in
+// the .cc file here.
+const PPB_Var* GetVarInterface();
+
+// Returns a PP_Var of type object that wraps the given NPObject. Calling this
+// function multiple times given the same NPObject results in the same PP_Var.
+PP_Var NPObjectToPPVar(NPObject* object);
+
+// Returns the NPObject corresponding to the PP_Var. This pointer has not been
+// retained, so you should not call WebBindings::releaseObject unless you first
+// call WebBindings::retainObject. Returns NULL if the PP_Var is not an object
+// type.
+NPObject* GetNPObject(PP_Var var);
+
+// Returns a PP_Var of type string that contains a copy of the given string.
+// The input data must be valid UTF-8 encoded text.
+PP_Var StringToPPVar(const std::string& str);
+
+// Returns the String corresponding to the PP_Var. This pointer has not been
+// AddRef'd, so you should not call Release! Returns NULL if the PP_Var is not
+// a string type.
+String* GetString(PP_Var var);
+
+} // namespace pepper
+
+#endif // WEBKIT_GLUE_PLUGINS_PEPPER_VAR_H_
diff --git a/webkit/glue/plugins/pepper_webplugin_impl.cc b/webkit/glue/plugins/pepper_webplugin_impl.cc
new file mode 100644
index 0000000..df8aae0
--- /dev/null
+++ b/webkit/glue/plugins/pepper_webplugin_impl.cc
@@ -0,0 +1,196 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/pepper_webplugin_impl.h"
+
+#include "base/file_path.h"
+#include "base/message_loop.h"
+#include "third_party/ppapi/c/pp_var.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebPluginParams.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebRect.h"
+#include "webkit/glue/plugins/pepper_plugin_instance.h"
+#include "webkit/glue/plugins/pepper_plugin_module.h"
+#include "webkit/glue/plugins/pepper_url_loader.h"
+#include "webkit/glue/plugins/pepper_var.h"
+
+using WebKit::WebCanvas;
+using WebKit::WebPluginContainer;
+using WebKit::WebPluginParams;
+using WebKit::WebRect;
+using WebKit::WebString;
+using WebKit::WebVector;
+
+namespace pepper {
+
+WebPluginImpl::WebPluginImpl(
+ PluginModule* plugin_module,
+ const WebPluginParams& params,
+ const base::WeakPtr<PluginDelegate>& plugin_delegate)
+ : init_data_(new InitData()),
+ full_frame_(params.loadManually) {
+ DCHECK(plugin_module);
+ init_data_->module = plugin_module;
+ init_data_->delegate = plugin_delegate;
+ for (size_t i = 0; i < params.attributeNames.size(); ++i) {
+ init_data_->arg_names.push_back(params.attributeNames[i].utf8());
+ init_data_->arg_values.push_back(params.attributeValues[i].utf8());
+ }
+}
+
+WebPluginImpl::~WebPluginImpl() {
+}
+
+bool WebPluginImpl::initialize(WebPluginContainer* container) {
+ // The plugin delegate may have gone away.
+ if (!init_data_->delegate)
+ return false;
+
+ instance_ = init_data_->module->CreateInstance(init_data_->delegate);
+ if (!instance_)
+ return false;
+
+ bool success = instance_->Initialize(container,
+ init_data_->arg_names,
+ init_data_->arg_values,
+ full_frame_);
+ if (!success) {
+ instance_->Delete();
+ instance_ = NULL;
+ return false;
+ }
+
+ init_data_.reset();
+ return true;
+}
+
+void WebPluginImpl::destroy() {
+ if (instance_) {
+ instance_->Delete();
+ instance_ = NULL;
+ }
+
+ MessageLoop::current()->DeleteSoon(FROM_HERE, this);
+}
+
+NPObject* WebPluginImpl::scriptableObject() {
+ return GetNPObject(instance_->GetInstanceObject());
+}
+
+void WebPluginImpl::paint(WebCanvas* canvas, const WebRect& rect) {
+ instance_->Paint(canvas, plugin_rect_, rect);
+}
+
+void WebPluginImpl::updateGeometry(
+ const WebRect& window_rect,
+ const WebRect& clip_rect,
+ const WebVector<WebRect>& cut_outs_rects,
+ bool is_visible) {
+ plugin_rect_ = window_rect;
+ instance_->ViewChanged(plugin_rect_, clip_rect);
+}
+
+void WebPluginImpl::updateFocus(bool focused) {
+}
+
+void WebPluginImpl::updateVisibility(bool visible) {
+}
+
+bool WebPluginImpl::acceptsInputEvents() {
+ return true;
+}
+
+bool WebPluginImpl::handleInputEvent(const WebKit::WebInputEvent& event,
+ WebKit::WebCursorInfo& cursor_info) {
+ return instance_->HandleInputEvent(event, &cursor_info);
+}
+
+void WebPluginImpl::didReceiveResponse(
+ const WebKit::WebURLResponse& response) {
+ DCHECK(!document_loader_);
+
+ document_loader_ = new URLLoader(instance_);
+ document_loader_->didReceiveResponse(NULL, response);
+
+ if (!instance_->HandleDocumentLoad(document_loader_))
+ document_loader_ = NULL;
+}
+
+void WebPluginImpl::didReceiveData(const char* data, int data_length) {
+ if (document_loader_)
+ document_loader_->didReceiveData(NULL, data, data_length);
+}
+
+void WebPluginImpl::didFinishLoading() {
+ if (document_loader_) {
+ document_loader_->didFinishLoading(NULL);
+ document_loader_ = NULL;
+ }
+}
+
+void WebPluginImpl::didFailLoading(const WebKit::WebURLError& error) {
+ if (document_loader_) {
+ document_loader_->didFail(NULL, error);
+ document_loader_ = NULL;
+ }
+}
+
+void WebPluginImpl::didFinishLoadingFrameRequest(const WebKit::WebURL& url,
+ void* notify_data) {
+}
+
+void WebPluginImpl::didFailLoadingFrameRequest(
+ const WebKit::WebURL& url,
+ void* notify_data,
+ const WebKit::WebURLError& error) {
+}
+
+bool WebPluginImpl::hasSelection() const {
+ return !selectionAsText().isEmpty();
+}
+
+WebKit::WebString WebPluginImpl::selectionAsText() const {
+ return instance_->GetSelectedText(false);
+}
+
+WebKit::WebString WebPluginImpl::selectionAsMarkup() const {
+ return instance_->GetSelectedText(true);
+}
+
+void WebPluginImpl::setZoomFactor(float scale, bool text_only) {
+ instance_->Zoom(scale, text_only);
+}
+
+bool WebPluginImpl::startFind(const WebKit::WebString& search_text,
+ bool case_sensitive,
+ int identifier) {
+ return instance_->StartFind(search_text, case_sensitive, identifier);
+}
+
+void WebPluginImpl::selectFindResult(bool forward) {
+ instance_->SelectFindResult(forward);
+}
+
+void WebPluginImpl::stopFind() {
+ instance_->StopFind();
+}
+
+bool WebPluginImpl::supportsPaginatedPrint() {
+ return instance_->SupportsPrintInterface();
+}
+
+int WebPluginImpl::printBegin(const WebKit::WebRect& printable_area,
+ int printer_dpi) {
+ return instance_->PrintBegin(printable_area, printer_dpi);
+}
+
+bool WebPluginImpl::printPage(int page_number,
+ WebKit::WebCanvas* canvas) {
+ return instance_->PrintPage(page_number, canvas);
+}
+
+void WebPluginImpl::printEnd() {
+ return instance_->PrintEnd();
+}
+
+} // namespace pepper
diff --git a/webkit/glue/plugins/pepper_webplugin_impl.h b/webkit/glue/plugins/pepper_webplugin_impl.h
new file mode 100644
index 0000000..9d2f313
--- /dev/null
+++ b/webkit/glue/plugins/pepper_webplugin_impl.h
@@ -0,0 +1,98 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_PEPPER_WEBPLUGIN_IMPL_H_
+#define WEBKIT_GLUE_PLUGINS_PEPPER_WEBPLUGIN_IMPL_H_
+
+#include <string>
+#include <vector>
+
+#include "base/weak_ptr.h"
+#include "base/scoped_ptr.h"
+#include "base/task.h"
+#include "gfx/rect.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebPlugin.h"
+
+namespace WebKit {
+struct WebPluginParams;
+}
+
+namespace pepper {
+
+class PluginDelegate;
+class PluginInstance;
+class PluginModule;
+class URLLoader;
+
+class WebPluginImpl : public WebKit::WebPlugin {
+ public:
+ WebPluginImpl(PluginModule* module,
+ const WebKit::WebPluginParams& params,
+ const base::WeakPtr<PluginDelegate>& plugin_delegate);
+
+ private:
+ friend class DeleteTask<WebPluginImpl>;
+
+ ~WebPluginImpl();
+
+ // WebKit::WebPlugin implementation.
+ virtual bool initialize(WebKit::WebPluginContainer* container);
+ virtual void destroy();
+ virtual NPObject* scriptableObject();
+ virtual void paint(WebKit::WebCanvas* canvas, const WebKit::WebRect& rect);
+ virtual void updateGeometry(
+ const WebKit::WebRect& frame_rect,
+ const WebKit::WebRect& clip_rect,
+ const WebKit::WebVector<WebKit::WebRect>& cut_outs_rects,
+ bool is_visible);
+ virtual void updateFocus(bool focused);
+ virtual void updateVisibility(bool visible);
+ virtual bool acceptsInputEvents();
+ virtual bool handleInputEvent(const WebKit::WebInputEvent& event,
+ WebKit::WebCursorInfo& cursor_info);
+ virtual void didReceiveResponse(const WebKit::WebURLResponse& response);
+ virtual void didReceiveData(const char* data, int data_length);
+ virtual void didFinishLoading();
+ virtual void didFailLoading(const WebKit::WebURLError&);
+ virtual void didFinishLoadingFrameRequest(const WebKit::WebURL& url,
+ void* notify_data);
+ virtual void didFailLoadingFrameRequest(const WebKit::WebURL& url,
+ void* notify_data,
+ const WebKit::WebURLError& error);
+ virtual bool hasSelection() const;
+ virtual WebKit::WebString selectionAsText() const;
+ virtual WebKit::WebString selectionAsMarkup() const;
+ virtual void setZoomFactor(float scale, bool text_only);
+ virtual bool startFind(const WebKit::WebString& search_text,
+ bool case_sensitive,
+ int identifier);
+ virtual void selectFindResult(bool forward);
+ virtual void stopFind();
+ virtual bool supportsPaginatedPrint();
+ virtual int printBegin(const WebKit::WebRect& printable_area,
+ int printer_dpi);
+ virtual bool printPage(int page_number, WebKit::WebCanvas* canvas);
+ virtual void printEnd();
+
+ struct InitData {
+ scoped_refptr<PluginModule> module;
+ base::WeakPtr<PluginDelegate> delegate;
+ std::vector<std::string> arg_names;
+ std::vector<std::string> arg_values;
+ };
+
+ scoped_ptr<InitData> init_data_; // Cleared upon successful initialization.
+ // True if the instance represents the entire document in a frame instead of
+ // being an embedded resource.
+ bool full_frame_;
+ scoped_refptr<PluginInstance> instance_;
+ scoped_refptr<URLLoader> document_loader_;
+ gfx::Rect plugin_rect_;
+
+ DISALLOW_COPY_AND_ASSIGN(WebPluginImpl);
+};
+
+} // namespace pepper
+
+#endif // WEBKIT_GLUE_PLUGINS_PEPPER_WEBPLUGIN_IMPL_H_
diff --git a/webkit/glue/plugins/pepper_widget.cc b/webkit/glue/plugins/pepper_widget.cc
new file mode 100644
index 0000000..74b0e40
--- /dev/null
+++ b/webkit/glue/plugins/pepper_widget.cc
@@ -0,0 +1,91 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/pepper_widget.h"
+
+#include "base/logging.h"
+#include "third_party/ppapi/c/pp_completion_callback.h"
+#include "third_party/ppapi/c/pp_errors.h"
+#include "third_party/ppapi/c/ppb_widget.h"
+#include "third_party/ppapi/c/ppp_widget.h"
+#include "webkit/glue/plugins/pepper_image_data.h"
+#include "webkit/glue/plugins/pepper_plugin_instance.h"
+#include "webkit/glue/plugins/pepper_plugin_module.h"
+
+namespace pepper {
+
+namespace {
+
+bool IsWidget(PP_Resource resource) {
+ return !!Resource::GetAs<Widget>(resource);
+}
+
+bool Paint(PP_Resource resource, const PP_Rect* rect, PP_Resource image_id) {
+ scoped_refptr<Widget> widget(Resource::GetAs<Widget>(resource));
+ if (!widget)
+ return false;
+
+ scoped_refptr<ImageData> image(Resource::GetAs<ImageData>(image_id));
+ return widget && widget->Paint(rect, image);
+}
+
+bool HandleEvent(PP_Resource resource, const PP_Event* event) {
+ scoped_refptr<Widget> widget(Resource::GetAs<Widget>(resource));
+ return widget && widget->HandleEvent(event);
+}
+
+bool GetLocation(PP_Resource resource, PP_Rect* location) {
+ scoped_refptr<Widget> widget(Resource::GetAs<Widget>(resource));
+ return widget && widget->GetLocation(location);
+}
+
+void SetLocation(PP_Resource resource, const PP_Rect* location) {
+ scoped_refptr<Widget> widget(Resource::GetAs<Widget>(resource));
+ if (widget)
+ widget->SetLocation(location);
+}
+
+const PPB_Widget ppb_widget = {
+ &IsWidget,
+ &Paint,
+ &HandleEvent,
+ &GetLocation,
+ &SetLocation,
+};
+
+} // namespace
+
+Widget::Widget(PluginInstance* instance)
+ : Resource(instance->module()),
+ instance_(instance) {
+}
+
+Widget::~Widget() {
+}
+
+// static
+const PPB_Widget* Widget::GetInterface() {
+ return &ppb_widget;
+}
+
+bool Widget::GetLocation(PP_Rect* location) {
+ *location = location_;
+ return true;
+}
+
+void Widget::SetLocation(const PP_Rect* location) {
+ location_ = *location;
+ SetLocationInternal(location);
+}
+
+void Widget::Invalidate(const PP_Rect* dirty) {
+ const PPP_Widget* widget = static_cast<const PPP_Widget*>(
+ module()->GetPluginInterface(PPP_WIDGET_INTERFACE));
+ if (!widget)
+ return;
+ ScopedResourceId resource(this);
+ widget->Invalidate(instance_->GetPPInstance(), resource.id, dirty);
+}
+
+} // namespace pepper
diff --git a/webkit/glue/plugins/pepper_widget.h b/webkit/glue/plugins/pepper_widget.h
new file mode 100644
index 0000000..048a718
--- /dev/null
+++ b/webkit/glue/plugins/pepper_widget.h
@@ -0,0 +1,53 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_PEPPER_WIDGET_H_
+#define WEBKIT_GLUE_PLUGINS_PEPPER_WIDGET_H_
+
+#include "base/scoped_ptr.h"
+#include "third_party/ppapi/c/pp_rect.h"
+#include "webkit/glue/plugins/pepper_resource.h"
+
+typedef struct _ppb_Widget PPB_Widget;
+typedef struct _pp_Event PP_Event;
+
+namespace pepper {
+
+class ImageData;
+class PluginInstance;
+
+class Widget : public Resource {
+ public:
+ explicit Widget(PluginInstance* instance);
+ virtual ~Widget();
+
+ // Returns a pointer to the interface implementing PPB_Widget that is
+ // exposed to the plugin.
+ static const PPB_Widget* GetInterface();
+
+ // Resource overrides.
+ Widget* AsWidget() { return this; }
+
+ // PPB_Widget implementation.
+ virtual bool Paint(const PP_Rect* rect, ImageData* image) = 0;
+ virtual bool HandleEvent(const PP_Event* event) = 0;
+ bool GetLocation(PP_Rect* location);
+ void SetLocation(const PP_Rect* location);
+
+ // Notifies the plugin instance that the given rect needs to be repainted.
+ void Invalidate(const PP_Rect* dirty);
+ PluginInstance* instance() { return instance_; }
+
+ protected:
+ virtual void SetLocationInternal(const PP_Rect* location) = 0;
+ PP_Rect location() const { return location_; }
+
+ private:
+ scoped_refptr<PluginInstance> instance_;
+ PP_Rect location_;
+};
+
+} // namespace pepper
+
+#endif // WEBKIT_GLUE_PLUGINS_PEPPER_WIDGET_H_
diff --git a/webkit/glue/plugins/plugin_constants_win.h b/webkit/glue/plugins/plugin_constants_win.h
new file mode 100644
index 0000000..9913e5d
--- /dev/null
+++ b/webkit/glue/plugins/plugin_constants_win.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGIN_CONSTANTS_WIN_H_
+#define WEBKIT_GLUE_PLUGIN_CONSTANTS_WIN_H_
+
+// Used by the plugins_test when testing the older WMP plugin to force the new
+// plugin to not get loaded.
+#define kUseOldWMPPluginSwitch "use-old-wmp"
+
+// The window class name for a plugin window.
+#define kNativeWindowClassName L"NativeWindowClass"
+
+// The name of the window class name for the wrapper HWND around the actual
+// plugin window that's used when running in multi-process mode. This window
+// is created on the browser UI thread.
+#define kWrapperNativeWindowClassName L"WrapperNativeWindowClass"
+
+// The name of the custom window message that the browser uses to tell the
+// plugin process to paint a window.
+#define kPaintMessageName L"Chrome_CustomPaint"
+
+// The name of the registry key which NPAPI plugins update on installation.
+#define kRegistryMozillaPlugins L"SOFTWARE\\MozillaPlugins"
+
+#define kMozillaActiveXPlugin L"npmozax.dll"
+#define kNewWMPPlugin L"np-mswmp.dll"
+#define kOldWMPPlugin L"npdsplay.dll"
+#define kYahooApplicationStatePlugin L"npystate.dll"
+#define kWanWangProtocolHandlerPlugin L"npww.dll"
+#define kFlashPlugin L"npswf32.dll"
+#define kAcrobatReaderPlugin L"nppdf32.dll"
+#define kRealPlayerPlugin L"nppl3260.dll"
+#define kSilverlightPlugin L"npctrl.dll"
+#define kJavaPlugin1 L"npjp2.dll"
+#define kJavaPlugin2 L"npdeploytk.dll"
+
+#define kGPUPluginMimeType "application/vnd.google.chrome.gpu-plugin"
+
+#endif // WEBKIT_GLUE_PLUGIN_PLUGIN_LIST_H_
diff --git a/webkit/glue/plugins/plugin_host.cc b/webkit/glue/plugins/plugin_host.cc
new file mode 100644
index 0000000..fe1d7ef
--- /dev/null
+++ b/webkit/glue/plugins/plugin_host.cc
@@ -0,0 +1,1088 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/plugin_host.h"
+
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/scoped_ptr.h"
+#include "base/string_piece.h"
+#include "base/string_util.h"
+#if defined(OS_MACOSX)
+#include "base/sys_info.h"
+#endif
+#include "base/sys_string_conversions.h"
+#include "net/base/net_util.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebBindings.h"
+#include "webkit/glue/webkit_glue.h"
+#include "webkit/glue/plugins/default_plugin_shared.h"
+#include "webkit/glue/plugins/npapi_extension_thunk.h"
+#include "webkit/glue/plugins/plugin_instance.h"
+#include "webkit/glue/plugins/plugin_lib.h"
+#include "webkit/glue/plugins/plugin_list.h"
+#include "webkit/glue/plugins/plugin_stream_url.h"
+#include "webkit/glue/plugins/webplugin_delegate.h"
+#include "webkit/glue/plugins/webplugininfo.h"
+#include "third_party/npapi/bindings/npapi_extensions.h"
+#include "third_party/npapi/bindings/npruntime.h"
+
+using WebKit::WebBindings;
+
+// Finds a PluginInstance from an NPP.
+// The caller must take a reference if needed.
+static NPAPI::PluginInstance* FindInstance(NPP id) {
+ if (id == NULL) {
+ NOTREACHED();
+ return NULL;
+ }
+ return reinterpret_cast<NPAPI::PluginInstance*>(id->ndata);
+}
+
+#if defined(OS_MACOSX)
+// Returns true if the OS supports shared accelerated surfaces via IOSurface.
+// This is true on Snow Leopard and higher.
+static bool SupportsSharingAcceleratedSurfaces() {
+ int32 major, minor, bugfix;
+ base::SysInfo::OperatingSystemVersionNumbers(&major, &minor, &bugfix);
+ return major > 10 || (major == 10 && minor > 5);
+}
+#endif
+
+namespace NPAPI {
+
+scoped_refptr<PluginHost> PluginHost::singleton_;
+
+PluginHost::PluginHost() {
+ InitializeHostFuncs();
+}
+
+PluginHost::~PluginHost() {
+}
+
+PluginHost *PluginHost::Singleton() {
+ if (singleton_.get() == NULL) {
+ singleton_ = new PluginHost();
+ }
+
+ DCHECK(singleton_.get() != NULL);
+ return singleton_;
+}
+
+void PluginHost::InitializeHostFuncs() {
+ memset(&host_funcs_, 0, sizeof(host_funcs_));
+ host_funcs_.size = sizeof(host_funcs_);
+ host_funcs_.version = (NP_VERSION_MAJOR << 8) | (NP_VERSION_MINOR);
+
+ // The "basic" functions
+ host_funcs_.geturl = &NPN_GetURL;
+ host_funcs_.posturl = &NPN_PostURL;
+ host_funcs_.requestread = &NPN_RequestRead;
+ host_funcs_.newstream = &NPN_NewStream;
+ host_funcs_.write = &NPN_Write;
+ host_funcs_.destroystream = &NPN_DestroyStream;
+ host_funcs_.status = &NPN_Status;
+ host_funcs_.uagent = &NPN_UserAgent;
+ host_funcs_.memalloc = &NPN_MemAlloc;
+ host_funcs_.memfree = &NPN_MemFree;
+ host_funcs_.memflush = &NPN_MemFlush;
+ host_funcs_.reloadplugins = &NPN_ReloadPlugins;
+
+ // We don't implement java yet
+ host_funcs_.getJavaEnv = &NPN_GetJavaEnv;
+ host_funcs_.getJavaPeer = &NPN_GetJavaPeer;
+
+ // Advanced functions we implement
+ host_funcs_.geturlnotify = &NPN_GetURLNotify;
+ host_funcs_.posturlnotify = &NPN_PostURLNotify;
+ host_funcs_.getvalue = &NPN_GetValue;
+ host_funcs_.setvalue = &NPN_SetValue;
+ host_funcs_.invalidaterect = &NPN_InvalidateRect;
+ host_funcs_.invalidateregion = &NPN_InvalidateRegion;
+ host_funcs_.forceredraw = &NPN_ForceRedraw;
+
+ // These come from the Javascript Engine
+ host_funcs_.getstringidentifier = WebBindings::getStringIdentifier;
+ host_funcs_.getstringidentifiers = WebBindings::getStringIdentifiers;
+ host_funcs_.getintidentifier = WebBindings::getIntIdentifier;
+ host_funcs_.identifierisstring = WebBindings::identifierIsString;
+ host_funcs_.utf8fromidentifier = WebBindings::utf8FromIdentifier;
+ host_funcs_.intfromidentifier = WebBindings::intFromIdentifier;
+ host_funcs_.createobject = WebBindings::createObject;
+ host_funcs_.retainobject = WebBindings::retainObject;
+ host_funcs_.releaseobject = WebBindings::releaseObject;
+ host_funcs_.invoke = WebBindings::invoke;
+ host_funcs_.invokeDefault = WebBindings::invokeDefault;
+ host_funcs_.evaluate = WebBindings::evaluate;
+ host_funcs_.getproperty = WebBindings::getProperty;
+ host_funcs_.setproperty = WebBindings::setProperty;
+ host_funcs_.removeproperty = WebBindings::removeProperty;
+ host_funcs_.hasproperty = WebBindings::hasProperty;
+ host_funcs_.hasmethod = WebBindings::hasMethod;
+ host_funcs_.releasevariantvalue = WebBindings::releaseVariantValue;
+ host_funcs_.setexception = WebBindings::setException;
+ host_funcs_.pushpopupsenabledstate = NPN_PushPopupsEnabledState;
+ host_funcs_.poppopupsenabledstate = NPN_PopPopupsEnabledState;
+ host_funcs_.enumerate = WebBindings::enumerate;
+ host_funcs_.pluginthreadasynccall = NPN_PluginThreadAsyncCall;
+ host_funcs_.construct = WebBindings::construct;
+ host_funcs_.getvalueforurl = NPN_GetValueForURL;
+ host_funcs_.setvalueforurl = NPN_SetValueForURL;
+ host_funcs_.getauthenticationinfo = NPN_GetAuthenticationInfo;
+ host_funcs_.scheduletimer = NPN_ScheduleTimer;
+ host_funcs_.unscheduletimer = NPN_UnscheduleTimer;
+ host_funcs_.popupcontextmenu = NPN_PopUpContextMenu;
+ host_funcs_.convertpoint = NPN_ConvertPoint;
+}
+
+void PluginHost::PatchNPNetscapeFuncs(NPNetscapeFuncs* overrides) {
+ // When running in the plugin process, we need to patch the NPN functions
+ // that the plugin calls to interact with NPObjects that we give. Otherwise
+ // the plugin will call the v8 NPN functions, which won't work since we have
+ // an NPObjectProxy and not a real v8 implementation.
+ if (overrides->invoke)
+ host_funcs_.invoke = overrides->invoke;
+
+ if (overrides->invokeDefault)
+ host_funcs_.invokeDefault = overrides->invokeDefault;
+
+ if (overrides->evaluate)
+ host_funcs_.evaluate = overrides->evaluate;
+
+ if (overrides->getproperty)
+ host_funcs_.getproperty = overrides->getproperty;
+
+ if (overrides->setproperty)
+ host_funcs_.setproperty = overrides->setproperty;
+
+ if (overrides->removeproperty)
+ host_funcs_.removeproperty = overrides->removeproperty;
+
+ if (overrides->hasproperty)
+ host_funcs_.hasproperty = overrides->hasproperty;
+
+ if (overrides->hasmethod)
+ host_funcs_.hasmethod = overrides->hasmethod;
+
+ if (overrides->setexception)
+ host_funcs_.setexception = overrides->setexception;
+
+ if (overrides->enumerate)
+ host_funcs_.enumerate = overrides->enumerate;
+}
+
+bool PluginHost::SetPostData(const char* buf,
+ uint32 length,
+ std::vector<std::string>* names,
+ std::vector<std::string>* values,
+ std::vector<char>* body) {
+ // Use a state table to do the parsing. Whitespace must be
+ // trimmed after the fact if desired. In our case, we actually
+ // don't care about the whitespace, because we're just going to
+ // pass this back into another POST. This function strips out the
+ // "Content-length" header and does not append it to the request.
+
+ //
+ // This parser takes action only on state changes.
+ //
+ // Transition table:
+ // : \n NULL Other
+ // 0 GetHeader 1 2 4 0
+ // 1 GetValue 1 0 3 1
+ // 2 GetData 2 2 3 2
+ // 3 DONE
+ // 4 ERR
+ //
+ enum { INPUT_COLON=0, INPUT_NEWLINE, INPUT_NULL, INPUT_OTHER };
+ enum { GETNAME, GETVALUE, GETDATA, DONE, ERR };
+ int statemachine[3][4] = { { GETVALUE, GETDATA, GETDATA, GETNAME },
+ { GETVALUE, GETNAME, DONE, GETVALUE },
+ { GETDATA, GETDATA, DONE, GETDATA } };
+ std::string name, value;
+ const char* ptr = static_cast<const char*>(buf);
+ const char* start = ptr;
+ int state = GETNAME; // initial state
+ bool done = false;
+ bool err = false;
+ do {
+ int input;
+
+ // Translate the current character into an input
+ // for the state table.
+ switch (*ptr) {
+ case ':' :
+ input = INPUT_COLON;
+ break;
+ case '\n':
+ input = INPUT_NEWLINE;
+ break;
+ case 0 :
+ input = INPUT_NULL;
+ break;
+ default :
+ input = INPUT_OTHER;
+ break;
+ }
+
+ int newstate = statemachine[state][input];
+
+ // Take action based on the new state.
+ if (state != newstate) {
+ switch (newstate) {
+ case GETNAME:
+ // Got a value.
+ value = std::string(start, ptr - start);
+ TrimWhitespace(value, TRIM_ALL, &value);
+ // If the name field is empty, we'll skip this header
+ // but we won't error out.
+ if (!name.empty() && name != "content-length") {
+ names->push_back(name);
+ values->push_back(value);
+ }
+ start = ptr + 1;
+ break;
+ case GETVALUE:
+ // Got a header.
+ name = StringToLowerASCII(std::string(start, ptr - start));
+ TrimWhitespace(name, TRIM_ALL, &name);
+ start = ptr + 1;
+ break;
+ case GETDATA: {
+ // Finished headers, now get body
+ if (*ptr)
+ start = ptr + 1;
+ size_t previous_size = body->size();
+ size_t new_body_size = length - static_cast<int>(start - buf);
+ body->resize(previous_size + new_body_size);
+ if (!body->empty())
+ memcpy(&body->front() + previous_size, start, new_body_size);
+ done = true;
+ break;
+ }
+ case ERR:
+ // error
+ err = true;
+ done = true;
+ break;
+ }
+ }
+ state = newstate;
+ ptr++;
+ } while (!done);
+
+ return !err;
+}
+
+} // namespace NPAPI
+
+extern "C" {
+
+// Allocates memory from the host's memory space.
+void* NPN_MemAlloc(uint32_t size) {
+ scoped_refptr<NPAPI::PluginHost> host = NPAPI::PluginHost::Singleton();
+ if (host != NULL) {
+ // Note: We must use the same allocator/deallocator
+ // that is used by the javascript library, as some of the
+ // JS APIs will pass memory to the plugin which the plugin
+ // will attempt to free.
+ return malloc(size);
+ }
+ return NULL;
+}
+
+// Deallocates memory from the host's memory space
+void NPN_MemFree(void* ptr) {
+ scoped_refptr<NPAPI::PluginHost> host = NPAPI::PluginHost::Singleton();
+ if (host != NULL) {
+ if (ptr != NULL && ptr != reinterpret_cast<void*>(-1))
+ free(ptr);
+ }
+}
+
+// Requests that the host free a specified amount of memory.
+uint32_t NPN_MemFlush(uint32_t size) {
+ // This is not relevant on Windows; MAC specific
+ return size;
+}
+
+// This is for dynamic discovery of new plugins.
+// Should force a re-scan of the plugins directory to load new ones.
+void NPN_ReloadPlugins(NPBool reloadPages) {
+ // TODO: implement me
+ DLOG(INFO) << "NPN_ReloadPlugin is not implemented yet.";
+}
+
+// Requests a range of bytes for a seekable stream.
+NPError NPN_RequestRead(NPStream* stream, NPByteRange* range_list) {
+ if (!stream || !range_list)
+ return NPERR_GENERIC_ERROR;
+
+ scoped_refptr<NPAPI::PluginInstance> plugin =
+ reinterpret_cast<NPAPI::PluginInstance*>(stream->ndata);
+ if (!plugin.get())
+ return NPERR_GENERIC_ERROR;
+
+ plugin->RequestRead(stream, range_list);
+ return NPERR_NO_ERROR;
+}
+
+// Generic form of GetURL for common code between GetURL and GetURLNotify.
+static NPError GetURLNotify(NPP id,
+ const char* url,
+ const char* target,
+ bool notify,
+ void* notify_data) {
+ if (!url)
+ return NPERR_INVALID_URL;
+
+ scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
+ if (!plugin.get()) {
+ NOTREACHED();
+ return NPERR_GENERIC_ERROR;
+ }
+
+ plugin->RequestURL(url, "GET", target, NULL, 0, notify, notify_data);
+ return NPERR_NO_ERROR;
+}
+
+// Requests creation of a new stream with the contents of the
+// specified URL; gets notification of the result.
+NPError NPN_GetURLNotify(NPP id,
+ const char* url,
+ const char* target,
+ void* notify_data) {
+ // This is identical to NPN_GetURL, but after finishing, the
+ // browser will call NPP_URLNotify to inform the plugin that
+ // it has completed.
+
+ // According to the NPAPI documentation, if target == _self
+ // or a parent to _self, the browser should return NPERR_INVALID_PARAM,
+ // because it can't notify the plugin once deleted. This is
+ // absolutely false; firefox doesn't do this, and Flash relies on
+ // being able to use this.
+
+ // Also according to the NPAPI documentation, we should return
+ // NPERR_INVALID_URL if the url requested is not valid. However,
+ // this would require that we synchronously start fetching the
+ // URL. That just isn't practical. As such, there really is
+ // no way to return this error. From looking at the Firefox
+ // implementation, it doesn't look like Firefox does this either.
+
+ return GetURLNotify(id, url, target, true, notify_data);
+}
+
+NPError NPN_GetURL(NPP id, const char* url, const char* target) {
+ // Notes:
+ // Request from the Plugin to fetch content either for the plugin
+ // or to be placed into a browser window.
+ //
+ // If target == null, the browser fetches content and streams to plugin.
+ // otherwise, the browser loads content into an existing browser frame.
+ // If the target is the window/frame containing the plugin, the plugin
+ // may be destroyed.
+ // If the target is _blank, a mailto: or news: url open content in a new
+ // browser window
+ // If the target is _self, no other instance of the plugin is created. The
+ // plugin continues to operate in its own window
+
+ return GetURLNotify(id, url, target, false, 0);
+}
+
+// Generic form of PostURL for common code between PostURL and PostURLNotify.
+static NPError PostURLNotify(NPP id,
+ const char* url,
+ const char* target,
+ uint32_t len,
+ const char* buf,
+ NPBool file,
+ bool notify,
+ void* notify_data) {
+ if (!url)
+ return NPERR_INVALID_URL;
+
+ scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
+ if (!plugin.get()) {
+ NOTREACHED();
+ return NPERR_GENERIC_ERROR;
+ }
+
+ std::string post_file_contents;
+
+ if (file) {
+ // Post data to be uploaded from a file. This can be handled in two
+ // ways.
+ // 1. Read entire file and send the contents as if it was a post data
+ // specified in the argument
+ // 2. Send just the file details and read them in the browser at the
+ // time of sending the request.
+ // Approach 2 is more efficient but complicated. Approach 1 has a major
+ // drawback of sending potentially large data over two IPC hops. In a way
+ // 'large data over IPC' problem exists as it is in case of plugin giving
+ // the data directly instead of in a file.
+ // Currently we are going with the approach 1 to get the feature working.
+ // We can optimize this later with approach 2.
+
+ // TODO(joshia): Design a scheme to send a file descriptor instead of
+ // entire file contents across.
+
+ // Security alert:
+ // ---------------
+ // Here we are blindly uploading whatever file requested by a plugin.
+ // This is risky as someone could exploit a plugin to send private
+ // data in arbitrary locations.
+ // A malicious (non-sandboxed) plugin has unfeterred access to OS
+ // resources and can do this anyway without using browser's HTTP stack.
+ // FWIW, Firefox and Safari don't perform any security checks.
+
+ if (!buf)
+ return NPERR_FILE_NOT_FOUND;
+
+ std::string file_path_ascii(buf);
+ FilePath file_path;
+ static const char kFileUrlPrefix[] = "file:";
+ if (StartsWithASCII(file_path_ascii, kFileUrlPrefix, false)) {
+ GURL file_url(file_path_ascii);
+ DCHECK(file_url.SchemeIsFile());
+ net::FileURLToFilePath(file_url, &file_path);
+ } else {
+ file_path = FilePath::FromWStringHack(
+ base::SysNativeMBToWide(file_path_ascii));
+ }
+
+ file_util::FileInfo post_file_info = {0};
+ if (!file_util::GetFileInfo(file_path, &post_file_info) ||
+ post_file_info.is_directory)
+ return NPERR_FILE_NOT_FOUND;
+
+ if (!file_util::ReadFileToString(file_path, &post_file_contents))
+ return NPERR_FILE_NOT_FOUND;
+
+ buf = post_file_contents.c_str();
+ len = post_file_contents.size();
+ }
+
+ // The post data sent by a plugin contains both headers
+ // and post data. Example:
+ // Content-type: text/html
+ // Content-length: 200
+ //
+ // <200 bytes of content here>
+ //
+ // Unfortunately, our stream needs these broken apart,
+ // so we need to parse the data and set headers and data
+ // separately.
+ plugin->RequestURL(url, "POST", target, buf, len, notify, notify_data);
+ return NPERR_NO_ERROR;
+}
+
+NPError NPN_PostURLNotify(NPP id,
+ const char* url,
+ const char* target,
+ uint32_t len,
+ const char* buf,
+ NPBool file,
+ void* notify_data) {
+ return PostURLNotify(id, url, target, len, buf, file, true, notify_data);
+}
+
+NPError NPN_PostURL(NPP id,
+ const char* url,
+ const char* target,
+ uint32_t len,
+ const char* buf,
+ NPBool file) {
+ // POSTs data to an URL, either from a temp file or a buffer.
+ // If file is true, buf contains a temp file (which host will delete after
+ // completing), and len contains the length of the filename.
+ // If file is false, buf contains the data to send, and len contains the
+ // length of the buffer
+ //
+ // If target is null,
+ // server response is returned to the plugin
+ // If target is _current, _self, or _top,
+ // server response is written to the plugin window and plugin is unloaded.
+ // If target is _new or _blank,
+ // server response is written to a new browser window
+ // If target is an existing frame,
+ // server response goes to that frame.
+ //
+ // For protocols other than FTP
+ // file uploads must be line-end converted from \r\n to \n
+ //
+ // Note: you cannot specify headers (even a blank line) in a memory buffer,
+ // use NPN_PostURLNotify
+
+ return PostURLNotify(id, url, target, len, buf, file, false, 0);
+}
+
+NPError NPN_NewStream(NPP id,
+ NPMIMEType type,
+ const char* target,
+ NPStream** stream) {
+ // Requests creation of a new data stream produced by the plugin,
+ // consumed by the browser.
+ //
+ // Browser should put this stream into a window target.
+ //
+ // TODO: implement me
+ DLOG(INFO) << "NPN_NewStream is not implemented yet.";
+ return NPERR_GENERIC_ERROR;
+}
+
+int32_t NPN_Write(NPP id, NPStream* stream, int32_t len, void* buffer) {
+ // Writes data to an existing Plugin-created stream.
+
+ // TODO: implement me
+ DLOG(INFO) << "NPN_Write is not implemented yet.";
+ return NPERR_GENERIC_ERROR;
+}
+
+NPError NPN_DestroyStream(NPP id, NPStream* stream, NPReason reason) {
+ // Destroys a stream (could be created by plugin or browser).
+ //
+ // Reasons:
+ // NPRES_DONE - normal completion
+ // NPRES_USER_BREAK - user terminated
+ // NPRES_NETWORK_ERROR - network error (all errors fit here?)
+ //
+ //
+
+ scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
+ if (plugin.get() == NULL) {
+ NOTREACHED();
+ return NPERR_GENERIC_ERROR;
+ }
+
+ return plugin->NPP_DestroyStream(stream, reason);
+}
+
+const char* NPN_UserAgent(NPP id) {
+#if defined(OS_WIN)
+ // Flash passes in a null id during the NP_initialize call. We need to
+ // default to the Mozilla user agent if we don't have an NPP instance or
+ // else Flash won't request windowless mode.
+ bool use_mozilla_user_agent = true;
+ if (id) {
+ scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
+ if (plugin.get() && !plugin->use_mozilla_user_agent())
+ use_mozilla_user_agent = false;
+ }
+
+ if (use_mozilla_user_agent)
+ return "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9a1) "
+ "Gecko/20061103 Firefox/2.0a1";
+#elif defined(OS_MACOSX)
+ // Silverlight 4 doesn't handle events correctly unless we claim to be Safari.
+ scoped_refptr<NPAPI::PluginInstance> plugin;
+ if (id)
+ plugin = FindInstance(id);
+ if (plugin.get()) {
+ WebPluginInfo plugin_info = plugin->plugin_lib()->plugin_info();
+ if (plugin_info.name == ASCIIToUTF16("Silverlight Plug-In") &&
+ StartsWith(plugin_info.version, ASCIIToUTF16("4."), false)) {
+ return "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_4; en-us) "
+ "AppleWebKit/534.1+ (KHTML, like Gecko) Version/5.0 Safari/533.16";
+ }
+ }
+#endif
+
+ return webkit_glue::GetUserAgent(GURL()).c_str();
+}
+
+void NPN_Status(NPP id, const char* message) {
+ // Displays a message on the status line of the browser window.
+
+ // TODO: implement me
+ DLOG(INFO) << "NPN_Status is not implemented yet.";
+}
+
+void NPN_InvalidateRect(NPP id, NPRect *invalidRect) {
+ // Invalidates specified drawing area prior to repainting or refreshing a
+ // windowless plugin
+
+ // Before a windowless plugin can refresh part of its drawing area, it must
+ // first invalidate it. This function causes the NPP_HandleEvent method to
+ // pass an update event or a paint message to the plug-in. After calling
+ // this method, the plug-in recieves a paint message asynchronously.
+
+ // The browser redraws invalid areas of the document and any windowless
+ // plug-ins at regularly timed intervals. To force a paint message, the
+ // plug-in can call NPN_ForceRedraw after calling this method.
+
+ scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
+ DCHECK(plugin.get() != NULL);
+ if (plugin.get() && plugin->webplugin()) {
+ if (invalidRect) {
+#if defined(OS_WIN)
+ if (!plugin->windowless()) {
+ RECT rect = {0};
+ rect.left = invalidRect->left;
+ rect.right = invalidRect->right;
+ rect.top = invalidRect->top;
+ rect.bottom = invalidRect->bottom;
+ ::InvalidateRect(plugin->window_handle(), &rect, false);
+ return;
+ }
+#endif
+ gfx::Rect rect(invalidRect->left,
+ invalidRect->top,
+ invalidRect->right - invalidRect->left,
+ invalidRect->bottom - invalidRect->top);
+ plugin->webplugin()->InvalidateRect(rect);
+ } else {
+ plugin->webplugin()->Invalidate();
+ }
+ }
+}
+
+void NPN_InvalidateRegion(NPP id, NPRegion invalidRegion) {
+ // Invalidates a specified drawing region prior to repainting
+ // or refreshing a window-less plugin.
+ //
+ // Similar to NPN_InvalidateRect.
+
+ // TODO: this is overkill--add platform-specific region handling (at the
+ // very least, fetch the region's bounding box and pass it to InvalidateRect).
+ scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
+ DCHECK(plugin.get() != NULL);
+ if (plugin.get() && plugin->webplugin())
+ plugin->webplugin()->Invalidate();
+}
+
+void NPN_ForceRedraw(NPP id) {
+ // Forces repaint for a windowless plug-in.
+ //
+ // We deliberately do not implement this; we don't want plugins forcing
+ // synchronous paints.
+}
+
+NPError NPN_GetValue(NPP id, NPNVariable variable, void* value) {
+ // Allows the plugin to query the browser for information
+ //
+ // Variables:
+ // NPNVxDisplay (unix only)
+ // NPNVxtAppContext (unix only)
+ // NPNVnetscapeWindow (win only) - Gets the native window on which the
+ // plug-in drawing occurs, returns HWND
+ // NPNVjavascriptEnabledBool: tells whether Javascript is enabled
+ // NPNVasdEnabledBool: tells whether SmartUpdate is enabled
+ // NPNVOfflineBool: tells whether offline-mode is enabled
+
+ NPError rv = NPERR_GENERIC_ERROR;
+
+ switch (static_cast<int>(variable)) {
+ case NPNVWindowNPObject: {
+ scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
+ NPObject *np_object = plugin->webplugin()->GetWindowScriptNPObject();
+ // Return value is expected to be retained, as
+ // described here:
+ // <http://www.mozilla.org/projects/plugins/npruntime.html#browseraccess>
+ if (np_object) {
+ WebBindings::retainObject(np_object);
+ void **v = (void **)value;
+ *v = np_object;
+ rv = NPERR_NO_ERROR;
+ } else {
+ NOTREACHED();
+ }
+ break;
+ }
+ case NPNVPluginElementNPObject: {
+ scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
+ NPObject *np_object = plugin->webplugin()->GetPluginElement();
+ // Return value is expected to be retained, as
+ // described here:
+ // <http://www.mozilla.org/projects/plugins/npruntime.html#browseraccess>
+ if (np_object) {
+ WebBindings::retainObject(np_object);
+ void** v = static_cast<void**>(value);
+ *v = np_object;
+ rv = NPERR_NO_ERROR;
+ } else {
+ NOTREACHED();
+ }
+ break;
+ }
+ #if !defined(OS_MACOSX) // OS X doesn't have windowed plugins.
+ case NPNVnetscapeWindow: {
+ scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
+ if (!plugin.get()) {
+ NOTREACHED();
+ return NPERR_GENERIC_ERROR;
+ }
+ gfx::PluginWindowHandle handle = plugin->window_handle();
+ *((void**)value) = (void*)handle;
+ rv = NPERR_NO_ERROR;
+ break;
+ }
+ #endif
+ case NPNVjavascriptEnabledBool: {
+ // yes, JS is enabled.
+ *((void**)value) = (void*)1;
+ rv = NPERR_NO_ERROR;
+ break;
+ }
+ #if defined(TOOLKIT_USES_GTK)
+ case NPNVToolkit:
+ // Tell them we are GTK2. (The alternative is GTK 1.2.)
+ *reinterpret_cast<int*>(value) = NPNVGtk2;
+ rv = NPERR_NO_ERROR;
+ break;
+
+ case NPNVSupportsXEmbedBool:
+ *reinterpret_cast<NPBool*>(value) = true;
+ rv = NPERR_NO_ERROR;
+ break;
+ #endif
+ case NPNVSupportsWindowless: {
+ NPBool* supports_windowless = reinterpret_cast<NPBool*>(value);
+ *supports_windowless = true;
+ rv = NPERR_NO_ERROR;
+ break;
+ }
+ case NPNVprivateModeBool: {
+ NPBool* private_mode = reinterpret_cast<NPBool*>(value);
+ scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
+ *private_mode = plugin->webplugin()->IsOffTheRecord();
+ rv = NPERR_NO_ERROR;
+ break;
+ }
+ case default_plugin::kMissingPluginStatusStart +
+ default_plugin::MISSING_PLUGIN_AVAILABLE:
+ // fall through
+ case default_plugin::kMissingPluginStatusStart +
+ default_plugin::MISSING_PLUGIN_USER_STARTED_DOWNLOAD: {
+ // This is a hack for the default plugin to send notification to
+ // renderer. Even though we check if the plugin is the default plugin,
+ // we still need to worry about future standard change that may conflict
+ // with the variable definition, in order to avoid duplicate case clauses
+ // in this big switch statement.
+ scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
+ if (plugin->plugin_lib()->plugin_info().path.value() ==
+ kDefaultPluginLibraryName) {
+ plugin->webplugin()->OnMissingPluginStatus(
+ variable - default_plugin::kMissingPluginStatusStart);
+ }
+ break;
+ }
+ #if defined(OS_MACOSX)
+ case NPNVpluginDrawingModel: {
+ // return the drawing model that was negotiated when we initialized.
+ scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
+ *reinterpret_cast<int*>(value) = plugin->drawing_model();
+ rv = NPERR_NO_ERROR;
+ break;
+ }
+#ifndef NP_NO_QUICKDRAW
+ case NPNVsupportsQuickDrawBool: {
+ // We do not admit to supporting the QuickDraw drawing model. The logic
+ // here is that our QuickDraw plugin support is so rudimentary that we
+ // only want to use it as a fallback to keep plugins from crashing: if a
+ // plugin knows enough to ask, we want them to use CoreGraphics.
+ NPBool* supports_qd = reinterpret_cast<NPBool*>(value);
+ *supports_qd = false;
+ rv = NPERR_NO_ERROR;
+ break;
+ }
+#endif
+ case NPNVsupportsCoreGraphicsBool:
+#ifndef NP_NO_CARBON
+ case NPNVsupportsCarbonBool:
+#endif
+ case NPNVsupportsCocoaBool: {
+ // we do support these drawing and event models.
+ NPBool* supports_model = reinterpret_cast<NPBool*>(value);
+ *supports_model = true;
+ rv = NPERR_NO_ERROR;
+ break;
+ }
+ case NPNVsupportsCoreAnimationBool:
+ case NPNVsupportsInvalidatingCoreAnimationBool: {
+ // We only support the Core Animation model on 10.6 and higher
+ // TODO(stuartmorgan): Once existing CA plugins have implemented the
+ // invalidating version, remove support for the other version.
+ NPBool* supports_model = reinterpret_cast<NPBool*>(value);
+ *supports_model = SupportsSharingAcceleratedSurfaces() ? true : false;
+ rv = NPERR_NO_ERROR;
+ break;
+ }
+ case NPNVsupportsOpenGLBool: {
+ // This drawing model was never widely supported, and we don't plan to
+ // support it.
+ NPBool* supports_model = reinterpret_cast<NPBool*>(value);
+ *supports_model = false;
+ rv = NPERR_NO_ERROR;
+ break;
+ }
+ #endif // OS_MACOSX
+ case NPNVPepperExtensions:
+ // Available for any plugin that attempts to get it.
+ // If the plugin is not started in a Pepper implementation, it
+ // will likely fail when it tries to use any of the functions
+ // attached to the extension vector.
+ rv = NPAPI::GetPepperExtensionsFunctions(value);
+ break;
+ default:
+ DLOG(INFO) << "NPN_GetValue(" << variable << ") is not implemented yet.";
+ break;
+ }
+ return rv;
+}
+
+NPError NPN_SetValue(NPP id, NPPVariable variable, void* value) {
+ // Allows the plugin to set various modes
+
+ scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
+ switch(variable) {
+ case NPPVpluginWindowBool: {
+ // Sets windowless mode for display of the plugin
+ // Note: the documentation at
+ // http://developer.mozilla.org/en/docs/NPN_SetValue is wrong. When
+ // value is NULL, the mode is set to true. This is the same way Mozilla
+ // works.
+ plugin->set_windowless(value == 0);
+ return NPERR_NO_ERROR;
+ }
+ case NPPVpluginTransparentBool: {
+ // Sets transparent mode for display of the plugin
+ //
+ // Transparent plugins require the browser to paint the background
+ // before having the plugin paint. By default, windowless plugins
+ // are transparent. Making a windowless plugin opaque means that
+ // the plugin does not require the browser to paint the background.
+ bool mode = (value != 0);
+ plugin->set_transparent(mode);
+ return NPERR_NO_ERROR;
+ }
+ case NPPVjavascriptPushCallerBool:
+ // Specifies whether you are pushing or popping the JSContext off.
+ // the stack
+ // TODO: implement me
+ DLOG(INFO) <<
+ "NPN_SetValue(NPPVJavascriptPushCallerBool) is not implemented.";
+ return NPERR_GENERIC_ERROR;
+ case NPPVpluginKeepLibraryInMemory:
+ // Tells browser that plugin library should live longer than usual.
+ // TODO: implement me
+ DLOG(INFO) <<
+ "NPN_SetValue(NPPVpluginKeepLibraryInMemory) is not implemented.";
+ return NPERR_GENERIC_ERROR;
+ #if defined(OS_MACOSX)
+ case NPPVpluginDrawingModel: {
+ int model = reinterpret_cast<int>(value);
+ if (model == NPDrawingModelCoreGraphics) {
+ plugin->set_drawing_model(static_cast<NPDrawingModel>(model));
+ return NPERR_NO_ERROR;
+ } else if ((model == NPDrawingModelCoreAnimation ||
+ model == NPDrawingModelInvalidatingCoreAnimation) &&
+ SupportsSharingAcceleratedSurfaces()) {
+ plugin->set_drawing_model(static_cast<NPDrawingModel>(model));
+ return NPERR_NO_ERROR;
+ }
+ return NPERR_GENERIC_ERROR;
+ }
+ case NPPVpluginEventModel: {
+ // we support Carbon and Cocoa event models
+ int model = reinterpret_cast<int>(value);
+ switch (model) {
+#ifndef NP_NO_CARBON
+ case NPEventModelCarbon:
+#endif
+ case NPEventModelCocoa:
+ plugin->set_event_model(static_cast<NPEventModel>(model));
+ return NPERR_NO_ERROR;
+ break;
+ }
+ return NPERR_GENERIC_ERROR;
+ }
+ #endif
+ default:
+ // TODO: implement me
+ DLOG(INFO) << "NPN_SetValue(" << variable << ") is not implemented.";
+ break;
+ }
+
+ NOTREACHED();
+ return NPERR_GENERIC_ERROR;
+}
+
+void* NPN_GetJavaEnv() {
+ // TODO: implement me
+ DLOG(INFO) << "NPN_GetJavaEnv is not implemented.";
+ return NULL;
+}
+
+void* NPN_GetJavaPeer(NPP) {
+ // TODO: implement me
+ DLOG(INFO) << "NPN_GetJavaPeer is not implemented.";
+ return NULL;
+}
+
+void NPN_PushPopupsEnabledState(NPP id, NPBool enabled) {
+ scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
+ if (plugin)
+ plugin->PushPopupsEnabledState(enabled ? true : false);
+}
+
+void NPN_PopPopupsEnabledState(NPP id) {
+ scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
+ if (plugin)
+ plugin->PopPopupsEnabledState();
+}
+
+void NPN_PluginThreadAsyncCall(NPP id,
+ void (*func)(void*),
+ void* user_data) {
+ scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
+ if (plugin)
+ plugin->PluginThreadAsyncCall(func, user_data);
+}
+
+NPError NPN_GetValueForURL(NPP id,
+ NPNURLVariable variable,
+ const char* url,
+ char** value,
+ uint32_t* len) {
+ if (!id)
+ return NPERR_INVALID_PARAM;
+
+ if (!url || !*url || !len)
+ return NPERR_INVALID_URL;
+
+ *len = 0;
+ std::string result;
+
+ switch (variable) {
+ case NPNURLVProxy: {
+ result = "DIRECT";
+ if (!webkit_glue::FindProxyForUrl(GURL((std::string(url))), &result))
+ return NPERR_GENERIC_ERROR;
+
+ break;
+ }
+ case NPNURLVCookie: {
+ scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
+ if (!plugin)
+ return NPERR_GENERIC_ERROR;
+
+ webkit_glue::WebPlugin* webplugin = plugin->webplugin();
+ if (!webplugin)
+ return NPERR_GENERIC_ERROR;
+
+ // Bypass third-party cookie blocking by using the url as the
+ // first_party_for_cookies.
+ GURL cookies_url((std::string(url)));
+ result = webplugin->GetCookies(cookies_url, cookies_url);
+ break;
+ }
+ default:
+ return NPERR_GENERIC_ERROR;
+ }
+
+ // Allocate this using the NPAPI allocator. The plugin will call
+ // NPN_Free to free this.
+ *value = static_cast<char*>(NPN_MemAlloc(result.length() + 1));
+ base::strlcpy(*value, result.c_str(), result.length() + 1);
+ *len = result.length();
+
+ return NPERR_NO_ERROR;
+}
+
+NPError NPN_SetValueForURL(NPP id,
+ NPNURLVariable variable,
+ const char* url,
+ const char* value,
+ uint32_t len) {
+ if (!id)
+ return NPERR_INVALID_PARAM;
+
+ if (!url || !*url)
+ return NPERR_INVALID_URL;
+
+ switch (variable) {
+ case NPNURLVCookie: {
+ scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
+ if (!plugin)
+ return NPERR_GENERIC_ERROR;
+
+ webkit_glue::WebPlugin* webplugin = plugin->webplugin();
+ if (!webplugin)
+ return NPERR_GENERIC_ERROR;
+
+ std::string cookie(value, len);
+ GURL cookies_url((std::string(url)));
+ webplugin->SetCookie(cookies_url, cookies_url, cookie);
+ return NPERR_NO_ERROR;
+ }
+ case NPNURLVProxy:
+ // We don't support setting proxy values, fall through...
+ break;
+ default:
+ // Fall through and return an error...
+ break;
+ }
+
+ return NPERR_GENERIC_ERROR;
+}
+
+NPError NPN_GetAuthenticationInfo(NPP id,
+ const char* protocol,
+ const char* host,
+ int32_t port,
+ const char* scheme,
+ const char* realm,
+ char** username,
+ uint32_t* ulen,
+ char** password,
+ uint32_t* plen) {
+ if (!id || !protocol || !host || !scheme || !realm || !username ||
+ !ulen || !password || !plen)
+ return NPERR_INVALID_PARAM;
+
+ // TODO: implement me (bug 23928)
+ return NPERR_GENERIC_ERROR;
+}
+
+uint32_t NPN_ScheduleTimer(NPP id,
+ uint32_t interval,
+ NPBool repeat,
+ void (*func)(NPP id, uint32_t timer_id)) {
+ scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
+ if (!plugin)
+ return 0;
+
+ return plugin->ScheduleTimer(interval, repeat, func);
+}
+
+void NPN_UnscheduleTimer(NPP id, uint32_t timer_id) {
+ scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
+ if (plugin)
+ plugin->UnscheduleTimer(timer_id);
+}
+
+NPError NPN_PopUpContextMenu(NPP id, NPMenu* menu) {
+ if (!menu)
+ return NPERR_INVALID_PARAM;
+
+ scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
+ if (plugin.get()) {
+ return plugin->PopUpContextMenu(menu);
+ }
+ NOTREACHED();
+ return NPERR_GENERIC_ERROR;
+}
+
+NPBool NPN_ConvertPoint(NPP id, double sourceX, double sourceY,
+ NPCoordinateSpace sourceSpace,
+ double *destX, double *destY,
+ NPCoordinateSpace destSpace) {
+ scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
+ if (plugin.get()) {
+ return plugin->ConvertPoint(sourceX, sourceY, sourceSpace,
+ destX, destY, destSpace);
+ }
+ NOTREACHED();
+ return false;
+}
+
+} // extern "C"
diff --git a/webkit/glue/plugins/plugin_host.h b/webkit/glue/plugins/plugin_host.h
new file mode 100644
index 0000000..4763df1
--- /dev/null
+++ b/webkit/glue/plugins/plugin_host.h
@@ -0,0 +1,63 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// TODO: Need mechanism to cleanup the static instance
+
+#ifndef WEBKIT_GLUE_PLUGIN_PLUGIN_HOST_H__
+#define WEBKIT_GLUE_PLUGIN_PLUGIN_HOST_H__
+
+#include <string>
+#include <vector>
+
+#include "base/ref_counted.h"
+#include "third_party/npapi/bindings/npapi.h"
+#include "third_party/npapi/bindings/nphostapi.h"
+
+namespace NPAPI
+{
+class PluginInstance;
+
+// The Plugin Host implements the NPN_xxx functions for NPAPI plugins.
+// These are the functions exposed from the Plugin Host for use
+// by the Plugin.
+//
+// The PluginHost is managed as a singleton. This isn't strictly
+// necessary, but since the callback functions are all global C
+// functions, there is really no point in having per-instance PluginHosts.
+class PluginHost : public base::RefCounted<PluginHost> {
+ public:
+ // Access the single PluginHost instance. Callers
+ // must call deref() when finished with the object.
+ static PluginHost *Singleton();
+
+ // The table of functions provided to the plugin.
+ NPNetscapeFuncs *host_functions() { return &host_funcs_; }
+
+ // Helper function for parsing post headers, and applying attributes
+ // to the stream. NPAPI post data include headers + data combined.
+ // This function parses it out and adds it to the stream in a WebKit
+ // style.
+ static bool SetPostData(const char *buf,
+ uint32 length,
+ std::vector<std::string>* names,
+ std::vector<std::string>* values,
+ std::vector<char>* body);
+
+ void PatchNPNetscapeFuncs(NPNetscapeFuncs* overrides);
+
+ private:
+ friend class base::RefCounted<PluginHost>;
+
+ virtual ~PluginHost();
+
+ PluginHost();
+ void InitializeHostFuncs();
+ static scoped_refptr<PluginHost> singleton_;
+ NPNetscapeFuncs host_funcs_;
+ DISALLOW_COPY_AND_ASSIGN(PluginHost);
+};
+
+} // namespace NPAPI
+
+#endif // WEBKIT_GLUE_PLUGIN_PLUGIN_HOST_H__
diff --git a/webkit/glue/plugins/plugin_instance.cc b/webkit/glue/plugins/plugin_instance.cc
new file mode 100644
index 0000000..8506623
--- /dev/null
+++ b/webkit/glue/plugins/plugin_instance.cc
@@ -0,0 +1,632 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "build/build_config.h"
+
+#include "webkit/glue/plugins/plugin_instance.h"
+
+#include "base/file_util.h"
+#include "base/message_loop.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "webkit/glue/webkit_glue.h"
+#include "webkit/glue/plugins/plugin_host.h"
+#include "webkit/glue/plugins/plugin_lib.h"
+#include "webkit/glue/plugins/plugin_stream_url.h"
+#include "webkit/glue/plugins/plugin_string_stream.h"
+#include "webkit/glue/plugins/webplugin.h"
+#include "webkit/glue/plugins/webplugin_delegate.h"
+#include "net/base/escape.h"
+
+#if defined(OS_MACOSX)
+#include <ApplicationServices/ApplicationServices.h>
+#endif
+
+namespace NPAPI {
+
+PluginInstance::PluginInstance(PluginLib *plugin, const std::string &mime_type)
+ : plugin_(plugin),
+ npp_(0),
+ host_(PluginHost::Singleton()),
+ npp_functions_(plugin->functions()),
+ window_handle_(0),
+ windowless_(false),
+ transparent_(true),
+ webplugin_(0),
+ mime_type_(mime_type),
+ use_mozilla_user_agent_(false),
+#if defined (OS_MACOSX)
+#ifdef NP_NO_QUICKDRAW
+ drawing_model_(NPDrawingModelCoreGraphics),
+#else
+ drawing_model_(NPDrawingModelQuickDraw),
+#endif
+#ifdef NP_NO_CARBON
+ event_model_(NPEventModelCocoa),
+#else
+ event_model_(NPEventModelCarbon),
+#endif
+ currently_handled_event_(NULL),
+#endif
+ message_loop_(MessageLoop::current()),
+ load_manually_(false),
+ in_close_streams_(false),
+ next_timer_id_(1),
+ next_notify_id_(0),
+ next_range_request_id_(0) {
+ npp_ = new NPP_t();
+ npp_->ndata = 0;
+ npp_->pdata = 0;
+
+ memset(&zero_padding_, 0, sizeof(zero_padding_));
+ DCHECK(message_loop_);
+}
+
+PluginInstance::~PluginInstance() {
+ CloseStreams();
+
+ if (npp_ != 0) {
+ delete npp_;
+ npp_ = 0;
+ }
+
+ if (plugin_)
+ plugin_->CloseInstance();
+}
+
+PluginStreamUrl* PluginInstance::CreateStream(unsigned long resource_id,
+ const GURL& url,
+ const std::string& mime_type,
+ int notify_id) {
+
+ bool notify;
+ void* notify_data;
+ GetNotifyData(notify_id, &notify, &notify_data);
+ PluginStreamUrl* stream = new PluginStreamUrl(
+ resource_id, url, this, notify, notify_data);
+
+ AddStream(stream);
+ return stream;
+}
+
+void PluginInstance::AddStream(PluginStream* stream) {
+ open_streams_.push_back(stream);
+}
+
+void PluginInstance::RemoveStream(PluginStream* stream) {
+ if (in_close_streams_)
+ return;
+
+ std::vector<scoped_refptr<PluginStream> >::iterator stream_index;
+ for (stream_index = open_streams_.begin();
+ stream_index != open_streams_.end(); ++stream_index) {
+ if (*stream_index == stream) {
+ open_streams_.erase(stream_index);
+ break;
+ }
+ }
+}
+
+bool PluginInstance::IsValidStream(const NPStream* stream) {
+ std::vector<scoped_refptr<PluginStream> >::iterator stream_index;
+ for (stream_index = open_streams_.begin();
+ stream_index != open_streams_.end(); ++stream_index) {
+ if ((*stream_index)->stream() == stream)
+ return true;
+ }
+
+ return false;
+}
+
+void PluginInstance::CloseStreams() {
+ in_close_streams_ = true;
+ for (unsigned int index = 0; index < open_streams_.size(); ++index) {
+ // Close all streams on the way down.
+ open_streams_[index]->Close(NPRES_USER_BREAK);
+ }
+ open_streams_.clear();
+ in_close_streams_ = false;
+}
+
+webkit_glue::WebPluginResourceClient* PluginInstance::GetRangeRequest(
+ int id) {
+ PendingRangeRequestMap::iterator iter = pending_range_requests_.find(id);
+ if (iter == pending_range_requests_.end()) {
+ NOTREACHED();
+ return NULL;
+ }
+
+ webkit_glue::WebPluginResourceClient* rv = iter->second->AsResourceClient();
+ pending_range_requests_.erase(iter);
+ return rv;
+}
+
+bool PluginInstance::Start(const GURL& url,
+ char** const param_names,
+ char** const param_values,
+ int param_count,
+ bool load_manually) {
+ load_manually_ = load_manually;
+ unsigned short mode = load_manually_ ? NP_FULL : NP_EMBED;
+ npp_->ndata = this;
+
+ NPError err = NPP_New(mode, param_count,
+ const_cast<char **>(param_names), const_cast<char **>(param_values));
+ return err == NPERR_NO_ERROR;
+}
+
+NPObject *PluginInstance::GetPluginScriptableObject() {
+ NPObject *value = NULL;
+ NPError error = NPP_GetValue(NPPVpluginScriptableNPObject, &value);
+ if (error != NPERR_NO_ERROR || value == NULL)
+ return NULL;
+ return value;
+}
+
+// WebPluginLoadDelegate methods
+void PluginInstance::DidFinishLoadWithReason(
+ const GURL& url, NPReason reason, int notify_id) {
+ bool notify;
+ void* notify_data;
+ GetNotifyData(notify_id, &notify, &notify_data);
+ if (!notify) {
+ NOTREACHED();
+ return;
+ }
+
+ NPP_URLNotify(url.spec().c_str(), reason, notify_data);
+}
+
+// NPAPI methods
+NPError PluginInstance::NPP_New(unsigned short mode,
+ short argc,
+ char *argn[],
+ char *argv[]) {
+ DCHECK(npp_functions_ != 0);
+ DCHECK(npp_functions_->newp != 0);
+ DCHECK(argc >= 0);
+
+ if (npp_functions_->newp != 0) {
+ return npp_functions_->newp(
+ (NPMIMEType)mime_type_.c_str(), npp_, mode, argc, argn, argv, NULL);
+ }
+ return NPERR_INVALID_FUNCTABLE_ERROR;
+}
+
+void PluginInstance::NPP_Destroy() {
+ DCHECK(npp_functions_ != 0);
+ DCHECK(npp_functions_->destroy != 0);
+
+ if (npp_functions_->destroy != 0) {
+ NPSavedData *savedData = 0;
+ npp_functions_->destroy(npp_, &savedData);
+
+ // TODO: Support savedData. Technically, these need to be
+ // saved on a per-URL basis, and then only passed
+ // to new instances of the plugin at the same URL.
+ // Sounds like a huge security risk. When we do support
+ // these, we should pass them back to the PluginLib
+ // to be stored there.
+ DCHECK(savedData == 0);
+ }
+
+ for (unsigned int file_index = 0; file_index < files_created_.size();
+ file_index++) {
+ file_util::Delete(files_created_[file_index], false);
+ }
+
+ // Ensure that no timer callbacks are invoked after NPP_Destroy.
+ timers_.clear();
+}
+
+NPError PluginInstance::NPP_SetWindow(NPWindow *window) {
+ DCHECK(npp_functions_ != 0);
+ DCHECK(npp_functions_->setwindow != 0);
+
+ if (npp_functions_->setwindow != 0) {
+ return npp_functions_->setwindow(npp_, window);
+ }
+ return NPERR_INVALID_FUNCTABLE_ERROR;
+}
+
+NPError PluginInstance::NPP_NewStream(NPMIMEType type,
+ NPStream *stream,
+ NPBool seekable,
+ unsigned short *stype) {
+ DCHECK(npp_functions_ != 0);
+ DCHECK(npp_functions_->newstream != 0);
+ if (npp_functions_->newstream != 0) {
+ return npp_functions_->newstream(npp_, type, stream, seekable, stype);
+ }
+ return NPERR_INVALID_FUNCTABLE_ERROR;
+}
+
+NPError PluginInstance::NPP_DestroyStream(NPStream *stream, NPReason reason) {
+ DCHECK(npp_functions_ != 0);
+ DCHECK(npp_functions_->destroystream != 0);
+
+ if (stream == NULL || !IsValidStream(stream) || (stream->ndata == NULL))
+ return NPERR_INVALID_INSTANCE_ERROR;
+
+ if (npp_functions_->destroystream != 0) {
+ NPError result = npp_functions_->destroystream(npp_, stream, reason);
+ stream->ndata = NULL;
+ return result;
+ }
+ return NPERR_INVALID_FUNCTABLE_ERROR;
+}
+
+int PluginInstance::NPP_WriteReady(NPStream *stream) {
+ DCHECK(npp_functions_ != 0);
+ DCHECK(npp_functions_->writeready != 0);
+ if (npp_functions_->writeready != 0) {
+ return npp_functions_->writeready(npp_, stream);
+ }
+ return 0;
+}
+
+int PluginInstance::NPP_Write(NPStream *stream,
+ int offset,
+ int len,
+ void *buffer) {
+ DCHECK(npp_functions_ != 0);
+ DCHECK(npp_functions_->write != 0);
+ if (npp_functions_->write != 0) {
+ return npp_functions_->write(npp_, stream, offset, len, buffer);
+ }
+ return 0;
+}
+
+void PluginInstance::NPP_StreamAsFile(NPStream *stream, const char *fname) {
+ DCHECK(npp_functions_ != 0);
+ DCHECK(npp_functions_->asfile != 0);
+ if (npp_functions_->asfile != 0) {
+ npp_functions_->asfile(npp_, stream, fname);
+ }
+
+ // Creating a temporary FilePath instance on the stack as the explicit
+ // FilePath constructor with StringType as an argument causes a compiler
+ // error when invoked via vector push back.
+ FilePath file_name = FilePath::FromWStringHack(UTF8ToWide(fname));
+ files_created_.push_back(file_name);
+}
+
+void PluginInstance::NPP_URLNotify(const char *url,
+ NPReason reason,
+ void *notifyData) {
+ DCHECK(npp_functions_ != 0);
+ DCHECK(npp_functions_->urlnotify != 0);
+ if (npp_functions_->urlnotify != 0) {
+ npp_functions_->urlnotify(npp_, url, reason, notifyData);
+ }
+}
+
+NPError PluginInstance::NPP_GetValue(NPPVariable variable, void *value) {
+ DCHECK(npp_functions_ != 0);
+ // getvalue is NULL for Shockwave
+ if (npp_functions_->getvalue != 0) {
+ return npp_functions_->getvalue(npp_, variable, value);
+ }
+ return NPERR_INVALID_FUNCTABLE_ERROR;
+}
+
+NPError PluginInstance::NPP_SetValue(NPNVariable variable, void *value) {
+ DCHECK(npp_functions_ != 0);
+ if (npp_functions_->setvalue != 0) {
+ return npp_functions_->setvalue(npp_, variable, value);
+ }
+ return NPERR_INVALID_FUNCTABLE_ERROR;
+}
+
+short PluginInstance::NPP_HandleEvent(void* event) {
+ DCHECK(npp_functions_ != 0);
+ DCHECK(npp_functions_->event != 0);
+ if (npp_functions_->event != 0) {
+ return npp_functions_->event(npp_, (void*)event);
+ }
+ return false;
+}
+
+bool PluginInstance::NPP_Print(NPPrint* platform_print) {
+ DCHECK(npp_functions_ != 0);
+ if (npp_functions_->print != 0) {
+ npp_functions_->print(npp_, platform_print);
+ return true;
+ }
+ return false;
+}
+
+void PluginInstance::SendJavaScriptStream(const GURL& url,
+ const std::string& result,
+ bool success,
+ int notify_id) {
+ bool notify;
+ void* notify_data;
+ GetNotifyData(notify_id, &notify, &notify_data);
+
+ if (success) {
+ PluginStringStream *stream =
+ new PluginStringStream(this, url, notify, notify_data);
+ AddStream(stream);
+ stream->SendToPlugin(result, "text/html");
+ } else {
+ // NOTE: Sending an empty stream here will crash MacroMedia
+ // Flash 9. Just send the URL Notify.
+ if (notify)
+ NPP_URLNotify(url.spec().c_str(), NPRES_DONE, notify_data);
+ }
+}
+
+void PluginInstance::DidReceiveManualResponse(const GURL& url,
+ const std::string& mime_type,
+ const std::string& headers,
+ uint32 expected_length,
+ uint32 last_modified) {
+ DCHECK(load_manually_);
+
+ plugin_data_stream_ = CreateStream(-1, url, mime_type, 0);
+ plugin_data_stream_->DidReceiveResponse(mime_type, headers, expected_length,
+ last_modified, true);
+}
+
+void PluginInstance::DidReceiveManualData(const char* buffer, int length) {
+ DCHECK(load_manually_);
+ if (plugin_data_stream_.get() != NULL) {
+ plugin_data_stream_->DidReceiveData(buffer, length, 0);
+ }
+}
+
+void PluginInstance::DidFinishManualLoading() {
+ DCHECK(load_manually_);
+ if (plugin_data_stream_.get() != NULL) {
+ plugin_data_stream_->DidFinishLoading();
+ plugin_data_stream_->Close(NPRES_DONE);
+ plugin_data_stream_ = NULL;
+ }
+}
+
+void PluginInstance::DidManualLoadFail() {
+ DCHECK(load_manually_);
+ if (plugin_data_stream_.get() != NULL) {
+ plugin_data_stream_->DidFail();
+ plugin_data_stream_ = NULL;
+ }
+}
+
+void PluginInstance::PluginThreadAsyncCall(void (*func)(void *),
+ void *user_data) {
+ message_loop_->PostTask(
+ FROM_HERE, NewRunnableMethod(
+ this, &PluginInstance::OnPluginThreadAsyncCall, func, user_data));
+}
+
+void PluginInstance::OnPluginThreadAsyncCall(void (*func)(void *),
+ void *user_data) {
+ // Do not invoke the callback if NPP_Destroy has already been invoked.
+ if (webplugin_)
+ func(user_data);
+}
+
+uint32 PluginInstance::ScheduleTimer(uint32 interval,
+ NPBool repeat,
+ void (*func)(NPP id, uint32 timer_id)) {
+ // Use next timer id.
+ uint32 timer_id;
+ timer_id = next_timer_id_;
+ ++next_timer_id_;
+ DCHECK(next_timer_id_ != 0);
+
+ // Record timer interval and repeat.
+ TimerInfo info;
+ info.interval = interval;
+ info.repeat = repeat ? true : false;
+ timers_[timer_id] = info;
+
+ // Schedule the callback.
+ MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ NewRunnableMethod(
+ this, &PluginInstance::OnTimerCall, func, npp_, timer_id),
+ interval);
+ return timer_id;
+}
+
+void PluginInstance::UnscheduleTimer(uint32 timer_id) {
+ // Remove info about the timer.
+ TimerMap::iterator it = timers_.find(timer_id);
+ if (it != timers_.end())
+ timers_.erase(it);
+}
+
+#if !defined(OS_MACOSX)
+NPError PluginInstance::PopUpContextMenu(NPMenu* menu) {
+ NOTIMPLEMENTED();
+ return NPERR_GENERIC_ERROR;
+}
+#endif
+
+void PluginInstance::OnTimerCall(void (*func)(NPP id, uint32 timer_id),
+ NPP id,
+ uint32 timer_id) {
+ // Do not invoke callback if the timer has been unscheduled.
+ TimerMap::iterator it = timers_.find(timer_id);
+ if (it == timers_.end())
+ return;
+
+ // Get all information about the timer before invoking the callback. The
+ // callback might unschedule the timer.
+ TimerInfo info = it->second;
+
+ func(id, timer_id);
+
+ // If the timer was unscheduled by the callback, just free up the timer id.
+ if (timers_.find(timer_id) == timers_.end())
+ return;
+
+ // Reschedule repeating timers after invoking the callback so callback is not
+ // re-entered if it pumps the messager loop.
+ if (info.repeat) {
+ MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ NewRunnableMethod(
+ this, &PluginInstance::OnTimerCall, func, npp_, timer_id),
+ info.interval);
+ } else {
+ timers_.erase(it);
+ }
+}
+
+void PluginInstance::PushPopupsEnabledState(bool enabled) {
+ popups_enabled_stack_.push(enabled);
+}
+
+void PluginInstance::PopPopupsEnabledState() {
+ popups_enabled_stack_.pop();
+}
+
+void PluginInstance::RequestRead(NPStream* stream, NPByteRange* range_list) {
+ std::string range_info = "bytes=";
+
+ while (range_list) {
+ range_info += IntToString(range_list->offset);
+ range_info += "-";
+ range_info += IntToString(range_list->offset + range_list->length - 1);
+ range_list = range_list->next;
+ if (range_list) {
+ range_info += ",";
+ }
+ }
+
+ if (plugin_data_stream_) {
+ if (plugin_data_stream_->stream() == stream) {
+ webplugin_->CancelDocumentLoad();
+ plugin_data_stream_ = NULL;
+ }
+ }
+
+ // The lifetime of a NPStream instance depends on the PluginStream instance
+ // which owns it. When a plugin invokes NPN_RequestRead on a seekable stream,
+ // we don't want to create a new stream when the corresponding response is
+ // received. We send over a cookie which represents the PluginStream
+ // instance which is sent back from the renderer when the response is
+ // received.
+ std::vector<scoped_refptr<PluginStream> >::iterator stream_index;
+ for (stream_index = open_streams_.begin();
+ stream_index != open_streams_.end(); ++stream_index) {
+ PluginStream* plugin_stream = *stream_index;
+ if (plugin_stream->stream() == stream) {
+ // A stream becomes seekable the first time NPN_RequestRead
+ // is called on it.
+ plugin_stream->set_seekable(true);
+
+ pending_range_requests_[++next_range_request_id_] = plugin_stream;
+ webplugin_->InitiateHTTPRangeRequest(
+ stream->url, range_info.c_str(), next_range_request_id_);
+ return;
+ }
+ }
+ NOTREACHED();
+}
+
+void PluginInstance::RequestURL(const char* url,
+ const char* method,
+ const char* target,
+ const char* buf,
+ unsigned int len,
+ bool notify,
+ void* notify_data) {
+ int notify_id = 0;
+ if (notify) {
+ notify_id = ++next_notify_id_;
+ pending_requests_[notify_id] = notify_data;
+ }
+
+ webplugin_->HandleURLRequest(
+ url, method, target, buf, len, notify_id, popups_allowed());
+}
+
+bool PluginInstance::ConvertPoint(double source_x, double source_y,
+ NPCoordinateSpace source_space,
+ double* dest_x, double* dest_y,
+ NPCoordinateSpace dest_space) {
+#if defined(OS_MACOSX)
+ CGRect main_display_bounds = CGDisplayBounds(CGMainDisplayID());
+
+ double flipped_screen_x = source_x;
+ double flipped_screen_y = source_y;
+ switch(source_space) {
+ case NPCoordinateSpacePlugin:
+ flipped_screen_x += plugin_origin_.x();
+ flipped_screen_y += plugin_origin_.y();
+ break;
+ case NPCoordinateSpaceWindow:
+ flipped_screen_x += containing_window_frame_.x();
+ flipped_screen_y = containing_window_frame_.height() - source_y +
+ containing_window_frame_.y();
+ break;
+ case NPCoordinateSpaceFlippedWindow:
+ flipped_screen_x += containing_window_frame_.x();
+ flipped_screen_y += containing_window_frame_.y();
+ break;
+ case NPCoordinateSpaceScreen:
+ flipped_screen_y = main_display_bounds.size.height - flipped_screen_y;
+ break;
+ case NPCoordinateSpaceFlippedScreen:
+ break;
+ default:
+ NOTREACHED();
+ return false;
+ }
+
+ double target_x = flipped_screen_x;
+ double target_y = flipped_screen_y;
+ switch(dest_space) {
+ case NPCoordinateSpacePlugin:
+ target_x -= plugin_origin_.x();
+ target_y -= plugin_origin_.y();
+ break;
+ case NPCoordinateSpaceWindow:
+ target_x -= containing_window_frame_.x();
+ target_y -= containing_window_frame_.y();
+ target_y = containing_window_frame_.height() - target_y;
+ break;
+ case NPCoordinateSpaceFlippedWindow:
+ target_x -= containing_window_frame_.x();
+ target_y -= containing_window_frame_.y();
+ break;
+ case NPCoordinateSpaceScreen:
+ target_y = main_display_bounds.size.height - flipped_screen_y;
+ break;
+ case NPCoordinateSpaceFlippedScreen:
+ break;
+ default:
+ NOTREACHED();
+ return false;
+ }
+
+ if (dest_x)
+ *dest_x = target_x;
+ if (dest_y)
+ *dest_y = target_y;
+ return true;
+#else
+ NOTIMPLEMENTED();
+ return false;
+#endif
+}
+
+void PluginInstance::GetNotifyData(
+ int notify_id, bool* notify, void** notify_data) {
+ PendingRequestMap::iterator iter = pending_requests_.find(notify_id);
+ if (iter != pending_requests_.end()) {
+ *notify = true;
+ *notify_data = iter->second;
+ pending_requests_.erase(iter);
+ } else {
+ *notify = false;
+ *notify_data = NULL;
+ }
+}
+
+} // namespace NPAPI
diff --git a/webkit/glue/plugins/plugin_instance.h b/webkit/glue/plugins/plugin_instance.h
new file mode 100644
index 0000000..36bf601
--- /dev/null
+++ b/webkit/glue/plugins/plugin_instance.h
@@ -0,0 +1,360 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// TODO: Need to deal with NPAPI's NPSavedData.
+// I haven't seen plugins use it yet.
+
+#ifndef WEBKIT_GLUE_PLUGIN_PLUGIN_INSTANCE_H__
+#define WEBKIT_GLUE_PLUGIN_PLUGIN_INSTANCE_H__
+
+#include <map>
+#include <set>
+#include <stack>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/file_path.h"
+#include "base/ref_counted.h"
+#include "base/scoped_ptr.h"
+#include "gfx/native_widget_types.h"
+#include "gfx/point.h"
+#include "gfx/rect.h"
+#include "googleurl/src/gurl.h"
+#include "third_party/npapi/bindings/npapi.h"
+#include "third_party/npapi/bindings/nphostapi.h"
+
+class MessageLoop;
+
+namespace webkit_glue {
+class WebPlugin;
+class WebPluginResourceClient;
+}
+
+namespace NPAPI
+{
+class PluginLib;
+class PluginHost;
+class PluginStream;
+class PluginStreamUrl;
+class PluginDataStream;
+#if defined(OS_MACOSX)
+class ScopedCurrentPluginEvent;
+#endif
+
+// A PluginInstance is an active, running instance of a Plugin.
+// A single plugin may have many PluginInstances.
+class PluginInstance : public base::RefCountedThreadSafe<PluginInstance> {
+ public:
+ // Create a new instance of a plugin. The PluginInstance
+ // will hold a reference to the plugin.
+ PluginInstance(PluginLib *plugin, const std::string &mime_type);
+
+ // Activates the instance by calling NPP_New.
+ // This should be called after our instance is all
+ // setup from the host side and we are ready to receive
+ // requests from the plugin. We must not call any
+ // functions on the plugin instance until start has
+ // been called.
+ //
+ // url: The instance URL.
+ // param_names: the list of names of attributes passed via the
+ // element.
+ // param_values: the list of values corresponding to param_names
+ // param_count: number of attributes
+ // load_manually: if true indicates that the plugin data would be passed
+ // from webkit. if false indicates that the plugin should
+ // download the data.
+ // This also controls whether the plugin is instantiated as
+ // a full page plugin (NP_FULL) or embedded (NP_EMBED)
+ //
+ bool Start(const GURL& url,
+ char** const param_names,
+ char** const param_values,
+ int param_count,
+ bool load_manually);
+
+ // NPAPI's instance identifier for this instance
+ NPP npp() { return npp_; }
+
+ // Get/Set for the instance's window handle.
+ gfx::PluginWindowHandle window_handle() const { return window_handle_; }
+ void set_window_handle(gfx::PluginWindowHandle value) {
+ window_handle_ = value;
+ }
+
+ // Get/Set whether this instance is in Windowless mode.
+ // Default is false.
+ bool windowless() { return windowless_; }
+ void set_windowless(bool value) { windowless_ = value; }
+
+ // Get/Set whether this instance is transparent.
+ // This only applies to windowless plugins. Transparent
+ // plugins require that webkit paint the background.
+ // Default is true.
+ bool transparent() { return transparent_; }
+ void set_transparent(bool value) { transparent_ = value; }
+
+ // Get/Set the WebPlugin associated with this instance
+ webkit_glue::WebPlugin* webplugin() { return webplugin_; }
+ void set_web_plugin(webkit_glue::WebPlugin* webplugin) {
+ webplugin_ = webplugin;
+ }
+
+ // Get the mimeType for this plugin stream
+ const std::string &mime_type() { return mime_type_; }
+
+ NPAPI::PluginLib* plugin_lib() { return plugin_; }
+
+#if defined(OS_MACOSX)
+ // Get/Set the Mac NPAPI drawing and event models
+ NPDrawingModel drawing_model() { return drawing_model_; }
+ void set_drawing_model(NPDrawingModel value) { drawing_model_ = value; }
+ NPEventModel event_model() { return event_model_; }
+ void set_event_model(NPEventModel value) { event_model_ = value; }
+ // Updates the instance's tracking of the location of the plugin location
+ // relative to the upper left of the screen.
+ void set_plugin_origin(const gfx::Point& origin) { plugin_origin_ = origin; }
+ // Updates the instance's tracking of the frame of the containing window
+ // relative to the upper left of the screen.
+ void set_window_frame(const gfx::Rect& frame) {
+ containing_window_frame_ = frame;
+ }
+#endif
+
+ // Creates a stream for sending an URL. If notify_id is non-zero, it will
+ // send a notification to the plugin when the stream is complete; otherwise it
+ // will not. Set object_url to true if the load is for the object tag's url,
+ // or false if it's for a url that the plugin fetched through
+ // NPN_GetUrl[Notify].
+ PluginStreamUrl* CreateStream(unsigned long resource_id,
+ const GURL& url,
+ const std::string& mime_type,
+ int notify_id);
+
+ // For each instance, we track all streams. When the
+ // instance closes, all remaining streams are also
+ // closed. All streams associated with this instance
+ // should call AddStream so that they can be cleaned
+ // up when the instance shuts down.
+ void AddStream(PluginStream* stream);
+
+ // This is called when a stream is closed. We remove the stream from the
+ // list, which releases the reference maintained to the stream.
+ void RemoveStream(PluginStream* stream);
+
+ // Closes all open streams on this instance.
+ void CloseStreams();
+
+ // Returns the WebPluginResourceClient object for a stream that has become
+ // seekable.
+ webkit_glue::WebPluginResourceClient* GetRangeRequest(int id);
+
+ // Have the plugin create it's script object.
+ NPObject *GetPluginScriptableObject();
+
+ // WebViewDelegate methods that we implement. This is for handling
+ // callbacks during getURLNotify.
+ void DidFinishLoadWithReason(const GURL& url, NPReason reason, int notify_id);
+
+ // If true, send the Mozilla user agent instead of Chrome's to the plugin.
+ bool use_mozilla_user_agent() { return use_mozilla_user_agent_; }
+ void set_use_mozilla_user_agent() { use_mozilla_user_agent_ = true; }
+
+ // Helper that implements NPN_PluginThreadAsyncCall semantics
+ void PluginThreadAsyncCall(void (*func)(void *),
+ void *userData);
+
+ uint32 ScheduleTimer(uint32 interval,
+ NPBool repeat,
+ void (*func)(NPP id, uint32 timer_id));
+
+ void UnscheduleTimer(uint32 timer_id);
+
+ bool ConvertPoint(double source_x, double source_y,
+ NPCoordinateSpace source_space,
+ double* dest_x, double* dest_y,
+ NPCoordinateSpace dest_space);
+
+ NPError PopUpContextMenu(NPMenu* menu);
+
+ //
+ // NPAPI methods for calling the Plugin Instance
+ //
+ NPError NPP_New(unsigned short, short, char *[], char *[]);
+ NPError NPP_SetWindow(NPWindow *);
+ NPError NPP_NewStream(NPMIMEType, NPStream *, NPBool, unsigned short *);
+ NPError NPP_DestroyStream(NPStream *, NPReason);
+ int NPP_WriteReady(NPStream *);
+ int NPP_Write(NPStream *, int, int, void *);
+ void NPP_StreamAsFile(NPStream *, const char *);
+ void NPP_URLNotify(const char *, NPReason, void *);
+ NPError NPP_GetValue(NPPVariable, void *);
+ NPError NPP_SetValue(NPNVariable, void *);
+ short NPP_HandleEvent(void*);
+ void NPP_Destroy();
+ bool NPP_Print(NPPrint* platform_print);
+
+ void SendJavaScriptStream(const GURL& url,
+ const std::string& result,
+ bool success,
+ int notify_id);
+
+ void DidReceiveManualResponse(const GURL& url,
+ const std::string& mime_type,
+ const std::string& headers,
+ uint32 expected_length,
+ uint32 last_modified);
+ void DidReceiveManualData(const char* buffer, int length);
+ void DidFinishManualLoading();
+ void DidManualLoadFail();
+
+ void PushPopupsEnabledState(bool enabled);
+ void PopPopupsEnabledState();
+
+ bool popups_allowed() const {
+ return popups_enabled_stack_.empty() ? false : popups_enabled_stack_.top();
+ }
+
+ // Initiates byte range reads for plugins.
+ void RequestRead(NPStream* stream, NPByteRange* range_list);
+
+ // Handles GetURL/GetURLNotify/PostURL/PostURLNotify requests initiated
+ // by plugins.
+ void RequestURL(const char* url,
+ const char* method,
+ const char* target,
+ const char* buf,
+ unsigned int len,
+ bool notify,
+ void* notify_data);
+
+ private:
+ friend class base::RefCountedThreadSafe<PluginInstance>;
+
+#if defined(OS_MACOSX)
+ friend class ScopedCurrentPluginEvent;
+ // Sets the event that the plugin is currently handling. The object is not
+ // owned or copied, so the caller must call this again with NULL before the
+ // event pointer becomes invalid. Clients use ScopedCurrentPluginEvent rather
+ // than calling this directly.
+ void set_currently_handled_event(NPCocoaEvent* event) {
+ currently_handled_event_ = event;
+ }
+#endif
+
+ ~PluginInstance();
+ void OnPluginThreadAsyncCall(void (*func)(void *), void *userData);
+ void OnTimerCall(void (*func)(NPP id, uint32 timer_id),
+ NPP id, uint32 timer_id);
+ bool IsValidStream(const NPStream* stream);
+ void GetNotifyData(int notify_id, bool* notify, void** notify_data);
+
+ // This is a hack to get the real player plugin to work with chrome
+ // The real player plugin dll(nppl3260) when loaded by firefox is loaded via
+ // the NS COM API which is analogous to win32 COM. So the NPAPI functions in
+ // the plugin are invoked via an interface by firefox. The plugin instance
+ // handle which is passed to every NPAPI method is owned by the real player
+ // plugin, i.e. it expects the ndata member to point to a structure which
+ // it knows about. Eventually it dereferences this structure and compares
+ // a member variable at offset 0x24(Version 6.0.11.2888) /2D (Version
+ // 6.0.11.3088) with 0 and on failing this check, takes a different code
+ // path which causes a crash. Safari and Opera work with version 6.0.11.2888
+ // by chance as their ndata structure contains a 0 at the location which real
+ // player checks:(. They crash with version 6.0.11.3088 as well. The
+ // following member just adds a 96 byte padding to our PluginInstance class
+ // which is passed in the ndata member. This magic number works correctly on
+ // Vista with UAC on or off :(.
+ // NOTE: Please dont change the ordering of the member variables
+ // New members should be added after this padding array.
+ // TODO(iyengar) : Disassemble the Realplayer ndata structure and look into
+ // the possiblity of conforming to it (http://b/issue?id=936667). We
+ // could also log a bug with Real, which would save the effort.
+ uint8 zero_padding_[96];
+ scoped_refptr<NPAPI::PluginLib> plugin_;
+ NPP npp_;
+ scoped_refptr<PluginHost> host_;
+ NPPluginFuncs* npp_functions_;
+ std::vector<scoped_refptr<PluginStream> > open_streams_;
+ gfx::PluginWindowHandle window_handle_;
+ bool windowless_;
+ bool transparent_;
+ webkit_glue::WebPlugin* webplugin_;
+ std::string mime_type_;
+ GURL get_url_;
+ intptr_t get_notify_data_;
+ bool use_mozilla_user_agent_;
+#if defined(OS_MACOSX)
+ NPDrawingModel drawing_model_;
+ NPEventModel event_model_;
+ gfx::Point plugin_origin_;
+ gfx::Rect containing_window_frame_;
+ NPCocoaEvent* currently_handled_event_; // weak
+#endif
+ MessageLoop* message_loop_;
+ scoped_refptr<PluginStreamUrl> plugin_data_stream_;
+
+ // This flag if true indicates that the plugin data would be passed from
+ // webkit. if false indicates that the plugin should download the data.
+ bool load_manually_;
+
+ // Stack indicating if popups are to be enabled for the outgoing
+ // NPN_GetURL/NPN_GetURLNotify calls.
+ std::stack<bool> popups_enabled_stack_;
+
+ // True if in CloseStreams().
+ bool in_close_streams_;
+
+ // List of files created for the current plugin instance. File names are
+ // added to the list every time the NPP_StreamAsFile function is called.
+ std::vector<FilePath> files_created_;
+
+ // Next unusued timer id.
+ uint32 next_timer_id_;
+
+ // Map of timer id to settings for timer.
+ struct TimerInfo {
+ uint32 interval;
+ bool repeat;
+ };
+ typedef std::map<uint32, TimerInfo> TimerMap;
+ TimerMap timers_;
+
+ // Tracks pending GET/POST requests so that the plugin-given data doesn't
+ // cross process boundaries to an untrusted process.
+ typedef std::map<int, void*> PendingRequestMap;
+ PendingRequestMap pending_requests_;
+ int next_notify_id_;
+
+ // Used to track pending range requests so that when WebPlugin replies to us
+ // we can match the reply to the stream.
+ typedef std::map<int, scoped_refptr<PluginStream> > PendingRangeRequestMap;
+ PendingRangeRequestMap pending_range_requests_;
+ int next_range_request_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(PluginInstance);
+};
+
+#if defined(OS_MACOSX)
+// Helper to simplify correct usage of set_currently_handled_event.
+// Instantiating will set |instance|'s currently handled to |event| for the
+// lifetime of the object, then NULL when it goes out of scope.
+class ScopedCurrentPluginEvent {
+ public:
+ ScopedCurrentPluginEvent(PluginInstance* instance, NPCocoaEvent* event)
+ : instance_(instance) {
+ instance_->set_currently_handled_event(event);
+ }
+ ~ScopedCurrentPluginEvent() {
+ instance_->set_currently_handled_event(NULL);
+ }
+ private:
+ scoped_refptr<PluginInstance> instance_;
+ DISALLOW_COPY_AND_ASSIGN(ScopedCurrentPluginEvent);
+};
+#endif
+
+} // namespace NPAPI
+
+#endif // WEBKIT_GLUE_PLUGIN_PLUGIN_INSTANCE_H__
diff --git a/webkit/glue/plugins/plugin_instance_mac.mm b/webkit/glue/plugins/plugin_instance_mac.mm
new file mode 100644
index 0000000..9800198
--- /dev/null
+++ b/webkit/glue/plugins/plugin_instance_mac.mm
@@ -0,0 +1,133 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "build/build_config.h"
+
+#import <AppKit/AppKit.h>
+
+#include "base/logging.h"
+#include "webkit/glue/plugins/plugin_instance.h"
+
+// When C++ exceptions are disabled, the C++ library defines |try| and
+// |catch| so as to allow exception-expecting C++ code to build properly when
+// language support for exceptions is not present. These macros interfere
+// with the use of |@try| and |@catch| in Objective-C files such as this one.
+// Undefine these macros here, after everything has been #included, since
+// there will be no C++ uses and only Objective-C uses from this point on.
+#undef try
+#undef catch
+
+#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_5
+@interface NSMenu (SnowLeopardMenuPopUpDeclaration)
+- (BOOL)popUpMenuPositioningItem:(NSMenuItem*)item
+ atLocation:(NSPoint)location
+ inView:(NSView*)view;
+@end
+#endif
+
+namespace {
+
+// Returns an autoreleased NSEvent constructed from the given np_event,
+// targeting the given window.
+NSEvent* NSEventForNPCocoaEvent(NPCocoaEvent* np_event, NSWindow* window) {
+ bool mouse_down = 1;
+ switch (np_event->type) {
+ case NPCocoaEventMouseDown:
+ mouse_down = 1;
+ break;
+ case NPCocoaEventMouseUp:
+ mouse_down = 0;
+ break;
+ default:
+ // If plugins start bringing up context menus for things other than
+ // clicks, this will need more plumbing; for now just log it and proceed
+ // as if it were a mouse down.
+ NOTREACHED();
+ }
+ NSEventType event_type = NSLeftMouseDown;
+ switch (np_event->data.mouse.buttonNumber) {
+ case 0:
+ event_type = mouse_down ? NSLeftMouseDown : NSLeftMouseUp;
+ break;
+ case 1:
+ event_type = mouse_down ? NSRightMouseDown : NSRightMouseUp;
+ break;
+ default:
+ event_type = mouse_down ? NSOtherMouseDown : NSOtherMouseUp;
+ break;
+ }
+
+ NSInteger click_count = np_event->data.mouse.clickCount;
+ NSInteger modifiers = np_event->data.mouse.modifierFlags;
+ // NPCocoaEvent doesn't have a timestamp, so just use the current time.
+ NSEvent* event =
+ [NSEvent mouseEventWithType:event_type
+ location:NSMakePoint(0, 0)
+ modifierFlags:modifiers
+ timestamp:[[NSApp currentEvent] timestamp]
+ windowNumber:[window windowNumber]
+ context:[NSGraphicsContext currentContext]
+ eventNumber:0
+ clickCount:click_count
+ pressure:1.0];
+ return event;
+}
+
+} // namespace
+
+namespace NPAPI {
+
+NPError PluginInstance::PopUpContextMenu(NPMenu* menu) {
+ if (!currently_handled_event_)
+ return NPERR_GENERIC_ERROR;
+
+ CGRect main_display_bounds = CGDisplayBounds(CGMainDisplayID());
+ NSPoint screen_point = NSMakePoint(
+ plugin_origin_.x() + currently_handled_event_->data.mouse.pluginX,
+ plugin_origin_.y() + currently_handled_event_->data.mouse.pluginY);
+ // Plugin offsets are upper-left based, so flip vertically for Cocoa.
+ screen_point.y = main_display_bounds.size.height - screen_point.y;
+
+ NSMenu* nsmenu = reinterpret_cast<NSMenu*>(menu);
+ NPError return_val = NPERR_NO_ERROR;
+ NSWindow* window = nil;
+ @try {
+ if ([nsmenu respondsToSelector:
+ @selector(popUpMenuPositioningItem:atLocation:inView:)]) {
+ [nsmenu popUpMenuPositioningItem:nil atLocation:screen_point inView:nil];
+ } else {
+ NSRect dummy_window_rect = NSMakeRect(screen_point.x, screen_point.y,
+ 1, 1);
+ window = [[NSWindow alloc] initWithContentRect:dummy_window_rect
+ styleMask:NSBorderlessWindowMask
+ backing:NSBackingStoreNonretained
+ defer:YES];
+ [window setTitle:@"PopupMenuDummy"]; // Lets interposing identify it.
+ [window setAlphaValue:0];
+ [window makeKeyAndOrderFront:nil];
+ [NSMenu popUpContextMenu:nsmenu
+ withEvent:NSEventForNPCocoaEvent(currently_handled_event_,
+ window)
+ forView:[window contentView]];
+ }
+ }
+ @catch (NSException* e) {
+ NSLog(@"Caught exception while handling PopUpContextMenu: %@", e);
+ return_val = NPERR_GENERIC_ERROR;
+ }
+
+ if (window) {
+ @try {
+ [window orderOut:nil];
+ [window release];
+ }
+ @catch (NSException* e) {
+ NSLog(@"Caught exception while cleaning up in PopUpContextMenu: %@", e);
+ }
+ }
+
+ return return_val;
+}
+
+} // namespace NPAPI
diff --git a/webkit/glue/plugins/plugin_lib.cc b/webkit/glue/plugins/plugin_lib.cc
new file mode 100644
index 0000000..877548e
--- /dev/null
+++ b/webkit/glue/plugins/plugin_lib.cc
@@ -0,0 +1,315 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/plugin_lib.h"
+
+#include "base/logging.h"
+#include "base/message_loop.h"
+#include "base/stats_counters.h"
+#include "base/string_util.h"
+#include "webkit/glue/webkit_glue.h"
+#include "webkit/glue/plugins/plugin_instance.h"
+#include "webkit/glue/plugins/plugin_host.h"
+#include "webkit/glue/plugins/plugin_list.h"
+
+namespace NPAPI {
+
+const char kPluginLibrariesLoadedCounter[] = "PluginLibrariesLoaded";
+const char kPluginInstancesActiveCounter[] = "PluginInstancesActive";
+
+// A list of all the instantiated plugins.
+static std::vector<scoped_refptr<PluginLib> >* g_loaded_libs;
+
+PluginLib* PluginLib::CreatePluginLib(const FilePath& filename) {
+ // We can only have one PluginLib object per plugin as it controls the per
+ // instance function calls (i.e. NP_Initialize and NP_Shutdown). So we keep
+ // a map of PluginLib objects.
+ if (!g_loaded_libs)
+ g_loaded_libs = new std::vector<scoped_refptr<PluginLib> >;
+
+ for (size_t i = 0; i < g_loaded_libs->size(); ++i) {
+ if ((*g_loaded_libs)[i]->plugin_info().path == filename)
+ return (*g_loaded_libs)[i];
+ }
+
+ WebPluginInfo info;
+ const PluginEntryPoints* entry_points = NULL;
+ if (!PluginList::Singleton()->ReadPluginInfo(filename, &info, &entry_points))
+ return NULL;
+
+ return new PluginLib(info, entry_points);
+}
+
+void PluginLib::UnloadAllPlugins() {
+ if (g_loaded_libs) {
+ // PluginLib::Unload() can remove items from the list and even delete
+ // the list when it removes the last item, so we must work with a copy
+ // of the list so that we don't get the carpet removed under our feet.
+ std::vector<scoped_refptr<PluginLib> > loaded_libs(*g_loaded_libs);
+ for (size_t i = 0; i < loaded_libs.size(); ++i)
+ loaded_libs[i]->Unload();
+
+ if (g_loaded_libs && g_loaded_libs->empty()) {
+ delete g_loaded_libs;
+ g_loaded_libs = NULL;
+ }
+ }
+}
+
+void PluginLib::ShutdownAllPlugins() {
+ if (g_loaded_libs) {
+ for (size_t i = 0; i < g_loaded_libs->size(); ++i)
+ (*g_loaded_libs)[i]->Shutdown();
+ }
+}
+
+PluginLib::PluginLib(const WebPluginInfo& info,
+ const PluginEntryPoints* entry_points)
+ : web_plugin_info_(info),
+ library_(NULL),
+ initialized_(false),
+ saved_data_(0),
+ instance_count_(0),
+ skip_unload_(false) {
+ StatsCounter(kPluginLibrariesLoadedCounter).Increment();
+ memset(static_cast<void*>(&plugin_funcs_), 0, sizeof(plugin_funcs_));
+ g_loaded_libs->push_back(this);
+
+ if (entry_points) {
+ internal_ = true;
+ entry_points_ = *entry_points;
+ } else {
+ internal_ = false;
+ // We will read the entry points from the plugin directly.
+ memset(&entry_points_, 0, sizeof(entry_points_));
+ }
+}
+
+PluginLib::~PluginLib() {
+ StatsCounter(kPluginLibrariesLoadedCounter).Decrement();
+ if (saved_data_ != 0) {
+ // TODO - delete the savedData object here
+ }
+}
+
+NPPluginFuncs* PluginLib::functions() {
+ return &plugin_funcs_;
+}
+
+NPError PluginLib::NP_Initialize() {
+ LOG(INFO) << "PluginLib::NP_Initialize(" << web_plugin_info_.path.value() <<
+ "): initialized=" << initialized_;
+ if (initialized_)
+ return NPERR_NO_ERROR;
+
+ if (!Load())
+ return NPERR_MODULE_LOAD_FAILED_ERROR;
+
+ PluginHost* host = PluginHost::Singleton();
+ if (host == 0)
+ return NPERR_GENERIC_ERROR;
+
+#if defined(OS_POSIX) && !defined(OS_MACOSX)
+ NPError rv = entry_points_.np_initialize(host->host_functions(),
+ &plugin_funcs_);
+#else
+ NPError rv = entry_points_.np_initialize(host->host_functions());
+#if defined(OS_MACOSX)
+ // On the Mac, we need to get entry points after calling np_initialize to
+ // match the behavior of other browsers.
+ if (rv == NPERR_NO_ERROR) {
+ rv = entry_points_.np_getentrypoints(&plugin_funcs_);
+ }
+#endif // OS_MACOSX
+#endif
+ LOG(INFO) << "PluginLib::NP_Initialize(" << web_plugin_info_.path.value() <<
+ "): result=" << rv;
+ initialized_ = (rv == NPERR_NO_ERROR);
+ return rv;
+}
+
+void PluginLib::NP_Shutdown(void) {
+ DCHECK(initialized_);
+ entry_points_.np_shutdown();
+}
+
+void PluginLib::PreventLibraryUnload() {
+ skip_unload_ = true;
+}
+
+PluginInstance* PluginLib::CreateInstance(const std::string& mime_type) {
+ PluginInstance* new_instance = new PluginInstance(this, mime_type);
+ instance_count_++;
+ StatsCounter(kPluginInstancesActiveCounter).Increment();
+ DCHECK_NE(static_cast<PluginInstance*>(NULL), new_instance);
+ return new_instance;
+}
+
+void PluginLib::CloseInstance() {
+ StatsCounter(kPluginInstancesActiveCounter).Decrement();
+ instance_count_--;
+ // If a plugin is running in its own process it will get unloaded on process
+ // shutdown.
+ if ((instance_count_ == 0) && webkit_glue::IsPluginRunningInRendererProcess())
+ Unload();
+}
+
+bool PluginLib::Load() {
+ if (library_)
+ return true;
+
+ bool rv = false;
+ base::NativeLibrary library = 0;
+
+ if (!internal_) {
+ library = base::LoadNativeLibrary(web_plugin_info_.path);
+ if (library == 0) {
+ LOG_IF(INFO, PluginList::DebugPluginLoading())
+ << "Couldn't load plugin " << web_plugin_info_.path.value();
+ return rv;
+ }
+
+#if defined(OS_MACOSX)
+ // According to the WebKit source, QuickTime at least requires us to call
+ // UseResFile on the plugin resources before loading.
+ if (library->bundle_resource_ref != -1)
+ UseResFile(library->bundle_resource_ref);
+#endif
+
+ rv = true; // assume success now
+
+ entry_points_.np_initialize =
+ (NP_InitializeFunc)base::GetFunctionPointerFromNativeLibrary(library,
+ "NP_Initialize");
+ if (entry_points_.np_initialize == 0)
+ rv = false;
+
+#if defined(OS_WIN) || defined(OS_MACOSX)
+ entry_points_.np_getentrypoints =
+ (NP_GetEntryPointsFunc)base::GetFunctionPointerFromNativeLibrary(
+ library, "NP_GetEntryPoints");
+ if (entry_points_.np_getentrypoints == 0)
+ rv = false;
+#endif
+
+ entry_points_.np_shutdown =
+ (NP_ShutdownFunc)base::GetFunctionPointerFromNativeLibrary(library,
+ "NP_Shutdown");
+ if (entry_points_.np_shutdown == 0)
+ rv = false;
+ } else {
+ rv = true;
+ }
+
+ if (rv) {
+ plugin_funcs_.size = sizeof(plugin_funcs_);
+ plugin_funcs_.version = (NP_VERSION_MAJOR << 8) | NP_VERSION_MINOR;
+#if !defined(OS_POSIX)
+ if (entry_points_.np_getentrypoints(&plugin_funcs_) != NPERR_NO_ERROR)
+ rv = false;
+#else
+ // On Linux and Mac, we get the plugin entry points during NP_Initialize.
+#endif
+ }
+
+ if (!internal_) {
+ if (rv) {
+ LOG_IF(INFO, PluginList::DebugPluginLoading())
+ << "Plugin " << web_plugin_info_.path.value()
+ << " loaded successfully.";
+ library_ = library;
+ } else {
+ LOG_IF(INFO, PluginList::DebugPluginLoading())
+ << "Plugin " << web_plugin_info_.path.value()
+ << " failed to load, unloading.";
+ base::UnloadNativeLibrary(library);
+ }
+ }
+
+ return rv;
+}
+
+// This class implements delayed NP_Shutdown and FreeLibrary on the plugin dll.
+class FreePluginLibraryTask : public Task {
+ public:
+ FreePluginLibraryTask(base::NativeLibrary library,
+ NP_ShutdownFunc shutdown_func)
+ : library_(library),
+ NP_Shutdown_(shutdown_func) {
+ }
+
+ ~FreePluginLibraryTask() {}
+
+ void Run() {
+ if (NP_Shutdown_)
+ NP_Shutdown_();
+
+ if (library_) {
+ base::UnloadNativeLibrary(library_);
+ library_ = NULL;
+ }
+ }
+
+ private:
+ base::NativeLibrary library_;
+ NP_ShutdownFunc NP_Shutdown_;
+ DISALLOW_COPY_AND_ASSIGN(FreePluginLibraryTask);
+};
+
+void PluginLib::Unload() {
+ if (!internal_ && library_) {
+ // In case of single process mode, a plugin can delete itself
+ // by executing a script. So delay the unloading of the library
+ // so that the plugin will have a chance to unwind.
+ bool defer_unload = webkit_glue::IsPluginRunningInRendererProcess();
+
+/* TODO(dglazkov): Revisit when re-enabling the JSC build.
+#if USE(JSC)
+ // The plugin NPAPI instances may still be around. Delay the
+ // NP_Shutdown and FreeLibrary calls at least till the next
+ // peek message.
+ defer_unload = true;
+#endif
+*/
+
+ if (defer_unload) {
+ FreePluginLibraryTask* free_library_task =
+ new FreePluginLibraryTask(skip_unload_ ? NULL : library_,
+ entry_points_.np_shutdown);
+ LOG_IF(INFO, PluginList::DebugPluginLoading())
+ << "Scheduling delayed unload for plugin "
+ << web_plugin_info_.path.value();
+ MessageLoop::current()->PostTask(FROM_HERE, free_library_task);
+ } else {
+ Shutdown();
+ if (!skip_unload_) {
+ LOG_IF(INFO, PluginList::DebugPluginLoading())
+ << "Unloading plugin " << web_plugin_info_.path.value();
+ base::UnloadNativeLibrary(library_);
+ }
+ }
+
+ library_ = NULL;
+ }
+
+ for (size_t i = 0; i < g_loaded_libs->size(); ++i) {
+ if ((*g_loaded_libs)[i].get() == this) {
+ g_loaded_libs->erase(g_loaded_libs->begin() + i);
+ break;
+ }
+ }
+ if (g_loaded_libs->empty()) {
+ delete g_loaded_libs;
+ g_loaded_libs = NULL;
+ }
+}
+
+void PluginLib::Shutdown() {
+ if (initialized_ && !internal_) {
+ NP_Shutdown();
+ initialized_ = false;
+ }
+}
+
+} // namespace NPAPI
diff --git a/webkit/glue/plugins/plugin_lib.h b/webkit/glue/plugins/plugin_lib.h
new file mode 100644
index 0000000..647916e
--- /dev/null
+++ b/webkit/glue/plugins/plugin_lib.h
@@ -0,0 +1,120 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_PLUGIN_LIB_H_
+#define WEBKIT_GLUE_PLUGINS_PLUGIN_LIB_H_
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/file_path.h"
+#include "base/native_library.h"
+#include "base/ref_counted.h"
+#include "build/build_config.h"
+#include "webkit/glue/plugins/plugin_list.h"
+#include "webkit/glue/plugins/webplugin.h"
+
+struct WebPluginInfo;
+
+namespace NPAPI {
+
+class PluginInstance;
+
+// A PluginLib is a single NPAPI Plugin Library, and is the lifecycle
+// manager for new PluginInstances.
+class PluginLib : public base::RefCounted<PluginLib> {
+ public:
+ static PluginLib* CreatePluginLib(const FilePath& filename);
+
+ // Creates a WebPluginInfo structure given a plugin's path. On success
+ // returns true, with the information being put into "info".
+ // Returns false if the library couldn't be found, or if it's not a plugin.
+ static bool ReadWebPluginInfo(const FilePath& filename, WebPluginInfo* info);
+
+#if defined(OS_POSIX) && !defined(OS_MACOSX)
+ // Parse the result of an NP_GetMIMEDescription() call.
+ // This API is only used on Unixes, and is exposed here for testing.
+ static void ParseMIMEDescription(const std::string& description,
+ std::vector<WebPluginMimeType>* mime_types);
+#endif
+
+ // Unloads all the loaded plugin libraries and cleans up the plugin map.
+ static void UnloadAllPlugins();
+
+ // Shuts down all loaded plugin instances.
+ static void ShutdownAllPlugins();
+
+ // Get the Plugin's function pointer table.
+ NPPluginFuncs* functions();
+
+ // Creates a new instance of this plugin.
+ PluginInstance* CreateInstance(const std::string& mime_type);
+
+ // Called by the instance when the instance is tearing down.
+ void CloseInstance();
+
+ // Gets information about this plugin and the mime types that it
+ // supports.
+ const WebPluginInfo& plugin_info() { return web_plugin_info_; }
+
+ bool internal() { return internal_; }
+
+ //
+ // NPAPI functions
+ //
+
+ // NPAPI method to initialize a Plugin.
+ // Initialize can be safely called multiple times
+ NPError NP_Initialize();
+
+ // NPAPI method to shutdown a Plugin.
+ void NP_Shutdown(void);
+
+ int instance_count() const { return instance_count_; }
+
+ // Prevents the library code from being unload when Unload() is called (since
+ // some plugins crash if unloaded).
+ void PreventLibraryUnload();
+
+ // protected for testability.
+ protected:
+ friend class base::RefCounted<PluginLib>;
+
+ // Creates a new PluginLib.
+ // |entry_points| is non-NULL for internal plugins.
+ PluginLib(const WebPluginInfo& info,
+ const PluginEntryPoints* entry_points);
+
+ virtual ~PluginLib();
+
+ // Attempts to load the plugin from the library.
+ // Returns true if it is a legitimate plugin, false otherwise
+ bool Load();
+
+ // Unloads the plugin library.
+ void Unload();
+
+ // Shutdown the plugin library.
+ void Shutdown();
+
+ private:
+ bool internal_; // True for plugins that are built-in into chrome binaries.
+ WebPluginInfo web_plugin_info_; // Supported mime types, description
+ base::NativeLibrary library_; // The opened library reference.
+ NPPluginFuncs plugin_funcs_; // The struct of plugin side functions.
+ bool initialized_; // Is the plugin initialized?
+ NPSavedData *saved_data_; // Persisted plugin info for NPAPI.
+ int instance_count_; // Count of plugins in use.
+ bool skip_unload_; // True if library_ should not be unloaded.
+
+ // Function pointers to entry points into the plugin.
+ PluginEntryPoints entry_points_;
+
+ DISALLOW_COPY_AND_ASSIGN(PluginLib);
+};
+
+} // namespace NPAPI
+
+#endif // WEBKIT_GLUE_PLUGINS_PLUGIN_LIB_H_
diff --git a/webkit/glue/plugins/plugin_lib_mac.mm b/webkit/glue/plugins/plugin_lib_mac.mm
new file mode 100644
index 0000000..263b3b4
--- /dev/null
+++ b/webkit/glue/plugins/plugin_lib_mac.mm
@@ -0,0 +1,346 @@
+// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import <Carbon/Carbon.h>
+
+#include "webkit/glue/plugins/plugin_lib.h"
+
+#include "base/native_library.h"
+#include "base/scoped_cftyperef.h"
+#include "base/scoped_ptr.h"
+#include "base/string_util.h"
+#include "base/sys_string_conversions.h"
+#include "base/utf_string_conversions.h"
+#include "webkit/glue/plugins/plugin_list.h"
+
+static const short kSTRTypeDefinitionResourceID = 128;
+static const short kSTRTypeDescriptionResourceID = 127;
+static const short kSTRPluginDescriptionResourceID = 126;
+
+namespace NPAPI
+{
+
+namespace {
+
+NSDictionary* GetMIMETypes(CFBundleRef bundle) {
+ NSString* mime_filename =
+ (NSString*)CFBundleGetValueForInfoDictionaryKey(bundle,
+ CFSTR("WebPluginMIMETypesFilename"));
+
+ if (mime_filename) {
+
+ // get the file
+
+ NSString* mime_path =
+ [NSString stringWithFormat:@"%@/Library/Preferences/%@",
+ NSHomeDirectory(), mime_filename];
+ NSDictionary* mime_file_dict =
+ [NSDictionary dictionaryWithContentsOfFile:mime_path];
+
+ // is it valid?
+
+ bool valid_file = false;
+ if (mime_file_dict) {
+ NSString* l10n_name =
+ [mime_file_dict objectForKey:@"WebPluginLocalizationName"];
+ NSString* preferred_l10n = [[NSLocale currentLocale] localeIdentifier];
+ if ([l10n_name isEqualToString:preferred_l10n])
+ valid_file = true;
+ }
+
+ if (valid_file)
+ return [mime_file_dict objectForKey:@"WebPluginMIMETypes"];
+
+ // dammit, I didn't want to have to do this
+
+ typedef void (*CreateMIMETypesPrefsPtr)(void);
+ CreateMIMETypesPrefsPtr create_prefs_file =
+ (CreateMIMETypesPrefsPtr)CFBundleGetFunctionPointerForName(
+ bundle, CFSTR("BP_CreatePluginMIMETypesPreferences"));
+ if (!create_prefs_file)
+ return nil;
+ create_prefs_file();
+
+ // one more time
+
+ mime_file_dict = [NSDictionary dictionaryWithContentsOfFile:mime_path];
+ if (mime_file_dict)
+ return [mime_file_dict objectForKey:@"WebPluginMIMETypes"];
+ else
+ return nil;
+
+ } else {
+ return (NSDictionary*)CFBundleGetValueForInfoDictionaryKey(bundle,
+ CFSTR("WebPluginMIMETypes"));
+ }
+}
+
+bool ReadPlistPluginInfo(const FilePath& filename, CFBundleRef bundle,
+ WebPluginInfo* info) {
+ NSDictionary* mime_types = GetMIMETypes(bundle);
+ if (!mime_types)
+ return false; // no type info here; try elsewhere
+
+ for (NSString* mime_type in [mime_types allKeys]) {
+ NSDictionary* mime_dict = [mime_types objectForKey:mime_type];
+ NSString* mime_desc = [mime_dict objectForKey:@"WebPluginTypeDescription"];
+ NSArray* mime_exts = [mime_dict objectForKey:@"WebPluginExtensions"];
+
+ WebPluginMimeType mime;
+ mime.mime_type = base::SysNSStringToUTF8([mime_type lowercaseString]);
+ // Remove PDF from the list of types handled by QuickTime, since it provides
+ // a worse experience than just downloading the PDF.
+ if (mime.mime_type == "application/pdf" &&
+ StartsWithASCII(filename.BaseName().value(), "QuickTime", false)) {
+ continue;
+ }
+
+ if (mime_desc)
+ mime.description = base::SysNSStringToUTF16(mime_desc);
+ for (NSString* ext in mime_exts)
+ mime.file_extensions.push_back(
+ base::SysNSStringToUTF8([ext lowercaseString]));
+
+ info->mime_types.push_back(mime);
+ }
+
+ NSString* plugin_name =
+ (NSString*)CFBundleGetValueForInfoDictionaryKey(bundle,
+ CFSTR("WebPluginName"));
+ NSString* plugin_vers =
+ (NSString*)CFBundleGetValueForInfoDictionaryKey(bundle,
+ CFSTR("CFBundleShortVersionString"));
+ NSString* plugin_desc =
+ (NSString*)CFBundleGetValueForInfoDictionaryKey(bundle,
+ CFSTR("WebPluginDescription"));
+
+ if (plugin_name)
+ info->name = base::SysNSStringToUTF16(plugin_name);
+ else
+ info->name = UTF8ToUTF16(filename.BaseName().value());
+ info->path = filename;
+ if (plugin_vers)
+ info->version = base::SysNSStringToUTF16(plugin_vers);
+ if (plugin_desc)
+ info->desc = base::SysNSStringToUTF16(plugin_desc);
+ else
+ info->desc = UTF8ToUTF16(filename.BaseName().value());
+ info->enabled = true;
+
+ return true;
+}
+
+class ScopedBundleResourceFile {
+ public:
+ ScopedBundleResourceFile(CFBundleRef bundle) : bundle_(bundle) {
+ old_ref_num_ = CurResFile();
+ bundle_ref_num_ = CFBundleOpenBundleResourceMap(bundle);
+ UseResFile(bundle_ref_num_);
+ }
+ ~ScopedBundleResourceFile() {
+ UseResFile(old_ref_num_);
+ CFBundleCloseBundleResourceMap(bundle_, bundle_ref_num_);
+ }
+
+ private:
+ CFBundleRef bundle_;
+ CFBundleRefNum bundle_ref_num_;
+ ResFileRefNum old_ref_num_;
+};
+
+bool GetSTRResource(CFBundleRef bundle, short res_id,
+ std::vector<std::string>* contents) {
+ Handle res_handle = Get1Resource('STR#', res_id);
+ if (!res_handle || !*res_handle)
+ return false;
+
+ char* pointer = *res_handle;
+ short num_strings = *(short*)pointer;
+ pointer += sizeof(short);
+ for (short i = 0; i < num_strings; ++i) {
+ // Despite being 8-bits wide, these are legacy encoded. Make a round trip.
+ scoped_cftyperef<CFStringRef> str(CFStringCreateWithPascalStringNoCopy(
+ kCFAllocatorDefault,
+ (unsigned char*)pointer,
+ GetApplicationTextEncoding(), // is this right?
+ kCFAllocatorNull)); // perhaps CFStringGetSystemEncoding?
+ if (!str.get())
+ return false;
+ contents->push_back(base::SysCFStringRefToUTF8(str.get()));
+ pointer += 1+*reinterpret_cast<unsigned char*>(pointer);
+ }
+
+ return true;
+}
+
+bool ReadSTRPluginInfo(const FilePath& filename, CFBundleRef bundle,
+ WebPluginInfo* info) {
+ ScopedBundleResourceFile res_file(bundle);
+
+ std::vector<std::string> type_strings;
+ if (!GetSTRResource(bundle, kSTRTypeDefinitionResourceID, &type_strings))
+ return false;
+
+ std::vector<std::string> type_descs;
+ bool have_type_descs = GetSTRResource(bundle,
+ kSTRTypeDescriptionResourceID,
+ &type_descs);
+
+ std::vector<std::string> plugin_descs;
+ bool have_plugin_descs = GetSTRResource(bundle,
+ kSTRPluginDescriptionResourceID,
+ &plugin_descs);
+
+ size_t num_types = type_strings.size()/2;
+
+ for (size_t i = 0; i < num_types; ++i) {
+ WebPluginMimeType mime;
+ mime.mime_type = StringToLowerASCII(type_strings[2*i]);
+ if (have_type_descs && i < type_descs.size())
+ mime.description = UTF8ToUTF16(type_descs[i]);
+ SplitString(StringToLowerASCII(type_strings[2*i+1]), ',',
+ &mime.file_extensions);
+
+ info->mime_types.push_back(mime);
+ }
+
+ NSString* plugin_vers =
+ (NSString*)CFBundleGetValueForInfoDictionaryKey(bundle,
+ CFSTR("CFBundleShortVersionString"));
+
+ if (have_plugin_descs && plugin_descs.size() > 1)
+ info->name = UTF8ToUTF16(plugin_descs[1]);
+ else
+ info->name = UTF8ToUTF16(filename.BaseName().value());
+ info->path = filename;
+ if (plugin_vers)
+ info->version = base::SysNSStringToUTF16(plugin_vers);
+ if (have_plugin_descs && plugin_descs.size() > 0)
+ info->desc = UTF8ToUTF16(plugin_descs[0]);
+ else
+ info->desc = UTF8ToUTF16(filename.BaseName().value());
+ info->enabled = true;
+
+ return true;
+}
+
+} // anonymous namespace
+
+bool PluginLib::ReadWebPluginInfo(const FilePath &filename,
+ WebPluginInfo* info) {
+ // There are two ways to get information about plugin capabilities. One is an
+ // Info.plist set of keys, documented at
+ // http://developer.apple.com/documentation/InternetWeb/Conceptual/WebKit_PluginProgTopic/Concepts/AboutPlugins.html .
+ // The other is a set of STR# resources, documented at
+ // https://developer.mozilla.org/En/Gecko_Plugin_API_Reference/Plug-in_Development_Overview .
+ //
+ // Historically, the data was maintained in the STR# resources. Apple, with
+ // the introduction of WebKit, noted the weaknesses of resources and moved the
+ // information into the Info.plist. Mozilla had always supported a
+ // NP_GetMIMEDescription() entry point for Unix plugins and also supports it
+ // on the Mac to supplement the STR# format. WebKit does not support
+ // NP_GetMIMEDescription() and neither do we. (That entry point is documented
+ // at https://developer.mozilla.org/en/NP_GetMIMEDescription .) We prefer the
+ // Info.plist format because it's more extensible and has a defined encoding,
+ // but will fall back to the STR# format of the data if it is not present in
+ // the Info.plist.
+ //
+ // The parsing of the data lives in the two functions ReadSTRPluginInfo() and
+ // ReadPlistPluginInfo(), but a summary of the formats follows.
+ //
+ // Each data type handled by a plugin has several properties:
+ // - <<type0mimetype>>
+ // - <<type0fileextension0>>..<<type0fileextensionk>>
+ // - <<type0description>>
+ //
+ // Each plugin may have any number of types defined. In addition, the plugin
+ // itself has properties:
+ // - <<plugindescription>>
+ // - <<pluginname>>
+ //
+ // For the Info.plist version, the data is formatted as follows (in text plist
+ // format):
+ // {
+ // ... the usual plist keys ...
+ // WebPluginDescription = <<plugindescription>>;
+ // WebPluginMIMETypes = {
+ // <<type0mimetype>> = {
+ // WebPluginExtensions = (
+ // <<type0fileextension0>>,
+ // ...
+ // <<type0fileextensionk>>,
+ // );
+ // WebPluginTypeDescription = <<type0description>>;
+ // };
+ // <<type1mimetype>> = { ... };
+ // ...
+ // <<typenmimetype>> = { ... };
+ // };
+ // WebPluginName = <<pluginname>>;
+ // }
+ //
+ // Alternatively (and this is undocumented), rather than a WebPluginMIMETypes
+ // key, there may be a WebPluginMIMETypesFilename key. If it is present, then
+ // it is the name of a file in the user's preferences folder in which to find
+ // the WebPluginMIMETypes key. If the key is present but the file doesn't
+ // exist, we must load the plugin and call a specific function to have the
+ // plugin create the file.
+ //
+ // If we do not find those keys in the Info.plist, we fall back to the STR#
+ // resources. In them, the data is formatted as follows:
+ // STR# 128
+ // (1) <<type0mimetype>>
+ // (2) <<type0fileextension0>>,...,<<type0fileextensionk>>
+ // (3) <<type1mimetype>>
+ // (4) <<type1fileextension0>>,...,<<type1fileextensionk>>
+ // (...)
+ // (2n+1) <<typenmimetype>>
+ // (2n+2) <<typenfileextension0>>,...,<<typenfileextensionk>>
+ // STR# 127
+ // (1) <<type0description>>
+ // (2) <<type1description>>
+ // (...)
+ // (n+1) <<typendescription>>
+ // STR# 126
+ // (1) <<plugindescription>>
+ // (2) <<pluginname>>
+ //
+ // Strictly speaking, only STR# 128 is required.
+
+ scoped_cftyperef<CFURLRef> bundle_url(CFURLCreateFromFileSystemRepresentation(
+ kCFAllocatorDefault, (const UInt8*)filename.value().c_str(),
+ filename.value().length(), true));
+ if (!bundle_url)
+ return false;
+ scoped_cftyperef<CFBundleRef> bundle(CFBundleCreate(kCFAllocatorDefault,
+ bundle_url.get()));
+ if (!bundle)
+ return false;
+
+ // preflight
+
+ OSType type = 0;
+ CFBundleGetPackageInfo(bundle.get(), &type, NULL);
+ if (type != FOUR_CHAR_CODE('BRPL'))
+ return false;
+
+ CFErrorRef error;
+ Boolean would_load = CFBundlePreflightExecutable(bundle.get(), &error);
+ if (!would_load)
+ return false;
+
+ // get the info
+
+ if (ReadPlistPluginInfo(filename, bundle.get(), info))
+ return true;
+
+ if (ReadSTRPluginInfo(filename, bundle.get(), info))
+ return true;
+
+ // ... or not
+
+ return false;
+}
+
+} // namespace NPAPI
diff --git a/webkit/glue/plugins/plugin_lib_posix.cc b/webkit/glue/plugins/plugin_lib_posix.cc
new file mode 100644
index 0000000..dbc64ed
--- /dev/null
+++ b/webkit/glue/plugins/plugin_lib_posix.cc
@@ -0,0 +1,255 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/plugin_lib.h"
+
+#include <dlfcn.h>
+#if defined(OS_OPENBSD)
+#include <sys/exec_elf.h>
+#else
+#include <elf.h>
+#include <fcntl.h>
+#endif
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "base/eintr_wrapper.h"
+#include "base/file_util.h"
+#include "base/string_util.h"
+#include "base/sys_string_conversions.h"
+#include "base/utf_string_conversions.h"
+#include "webkit/glue/plugins/plugin_list.h"
+
+// These headers must be included in this order to make the declaration gods
+// happy.
+#include "base/third_party/nspr/prcpucfg_linux.h"
+
+namespace {
+
+using NPAPI::PluginList;
+
+// Copied from nsplugindefs.h instead of including the file since it has a bunch
+// of dependencies.
+enum nsPluginVariable {
+ nsPluginVariable_NameString = 1,
+ nsPluginVariable_DescriptionString = 2
+};
+
+// Read the ELF header and return true if it is usable on
+// the current architecture (e.g. 32-bit ELF on 32-bit build).
+// Returns false on other errors as well.
+bool ELFMatchesCurrentArchitecture(const FilePath& filename) {
+ // First make sure we can open the file and it is in fact, a regular file.
+ struct stat stat_buf;
+ // Open with O_NONBLOCK so we don't block on pipes.
+ int fd = open(filename.value().c_str(), O_RDONLY|O_NONBLOCK);
+ if (fd < 0)
+ return false;
+ bool ret = (fstat(fd, &stat_buf) >= 0 && S_ISREG(stat_buf.st_mode));
+ if (HANDLE_EINTR(close(fd)) < 0)
+ return false;
+ if (!ret)
+ return false;
+
+ const size_t kELFBufferSize = 5;
+ char buffer[kELFBufferSize];
+ if (!file_util::ReadFile(filename, buffer, kELFBufferSize))
+ return false;
+
+ if (buffer[0] != ELFMAG0 ||
+ buffer[1] != ELFMAG1 ||
+ buffer[2] != ELFMAG2 ||
+ buffer[3] != ELFMAG3) {
+ // Not an ELF file, perhaps?
+ return false;
+ }
+
+ int elf_class = buffer[EI_CLASS];
+#if defined(ARCH_CPU_32_BITS)
+ if (elf_class == ELFCLASS32)
+ return true;
+#elif defined(ARCH_CPU_64_BITS)
+ if (elf_class == ELFCLASS64)
+ return true;
+#endif
+
+ return false;
+}
+
+// This structure matches enough of nspluginwrapper's NPW_PluginInfo
+// for us to extract the real plugin path.
+struct __attribute__((packed)) NSPluginWrapperInfo {
+ char ident[32]; // NSPluginWrapper magic identifier (includes version).
+ char path[PATH_MAX]; // Path to wrapped plugin.
+};
+
+// Test a plugin for whether it's been wrapped by NSPluginWrapper, and
+// if so attempt to unwrap it. Pass in an opened plugin handle; on
+// success, |dl| and |unwrapped_path| will be filled in with the newly
+// opened plugin. On failure, params are left unmodified.
+void UnwrapNSPluginWrapper(void **dl, FilePath* unwrapped_path) {
+ NSPluginWrapperInfo* info =
+ reinterpret_cast<NSPluginWrapperInfo*>(dlsym(*dl, "NPW_Plugin"));
+ if (!info)
+ return; // Not a NSPW plugin.
+
+ // Here we could check the NSPW ident field for the versioning
+ // information, but the path field is available in all versions
+ // anyway.
+
+ // Grab the path to the wrapped plugin. Just in case the structure
+ // format changes, protect against the path not being null-terminated.
+ char* path_end = static_cast<char*>(memchr(info->path, '\0',
+ sizeof(info->path)));
+ if (!path_end)
+ path_end = info->path + sizeof(info->path);
+ FilePath path = FilePath(std::string(info->path, path_end - info->path));
+
+ if (!ELFMatchesCurrentArchitecture(path)) {
+ LOG(WARNING) << path.value() << " is nspluginwrapper wrapping a "
+ << "plugin for a different architecture; it will "
+ << "work better if you instead use a native plugin.";
+ return;
+ }
+
+ void* newdl = base::LoadNativeLibrary(path);
+ if (!newdl) {
+ // We couldn't load the unwrapped plugin for some reason, despite
+ // being able to load the wrapped one. Just use the wrapped one.
+ LOG_IF(INFO, PluginList::DebugPluginLoading())
+ << "Could not use unwrapped nspluginwrapper plugin "
+ << unwrapped_path->value() << ", using the wrapped one.";
+ return;
+ }
+
+ // Unload the wrapped plugin, and use the wrapped plugin instead.
+ LOG_IF(INFO, PluginList::DebugPluginLoading())
+ << "Using unwrapped version " << unwrapped_path->value()
+ << " of nspluginwrapper-wrapped plugin.";
+ base::UnloadNativeLibrary(*dl);
+ *dl = newdl;
+ *unwrapped_path = path;
+}
+
+} // anonymous namespace
+
+namespace NPAPI {
+
+bool PluginLib::ReadWebPluginInfo(const FilePath& filename,
+ WebPluginInfo* info) {
+ // The file to reference is:
+ // http://mxr.mozilla.org/firefox/source/modules/plugin/base/src/nsPluginsDirUnix.cpp
+
+ // Skip files that aren't appropriate for our architecture.
+ if (!ELFMatchesCurrentArchitecture(filename)) {
+ LOG_IF(INFO, PluginList::DebugPluginLoading())
+ << "Skipping plugin " << filename.value()
+ << " because it doesn't match the current architecture.";
+ return false;
+ }
+
+ void* dl = base::LoadNativeLibrary(filename);
+ if (!dl) {
+ LOG_IF(INFO, PluginList::DebugPluginLoading())
+ << "While reading plugin info, unable to load library "
+ << filename.value() << ", skipping.";
+ return false;
+ }
+
+ info->path = filename;
+ info->enabled = true;
+
+ // Attempt to swap in the wrapped plugin if this is nspluginwrapper.
+ UnwrapNSPluginWrapper(&dl, &info->path);
+
+ // See comments in plugin_lib_mac regarding this symbol.
+ typedef const char* (*NP_GetMimeDescriptionType)();
+ NP_GetMimeDescriptionType NP_GetMIMEDescription =
+ reinterpret_cast<NP_GetMimeDescriptionType>(
+ dlsym(dl, "NP_GetMIMEDescription"));
+ const char* mime_description = NULL;
+ if (NP_GetMIMEDescription)
+ mime_description = NP_GetMIMEDescription();
+
+ if (mime_description)
+ ParseMIMEDescription(mime_description, &info->mime_types);
+
+ // The plugin name and description live behind NP_GetValue calls.
+ typedef NPError (*NP_GetValueType)(void* unused,
+ nsPluginVariable variable,
+ void* value_out);
+ NP_GetValueType NP_GetValue =
+ reinterpret_cast<NP_GetValueType>(dlsym(dl, "NP_GetValue"));
+ if (NP_GetValue) {
+ const char* name = NULL;
+ NP_GetValue(NULL, nsPluginVariable_NameString, &name);
+ if (name)
+ info->name = UTF8ToUTF16(name);
+
+ const char* description = NULL;
+ NP_GetValue(NULL, nsPluginVariable_DescriptionString, &description);
+ if (description)
+ info->desc = UTF8ToUTF16(description);
+
+ LOG_IF(INFO, PluginList::DebugPluginLoading())
+ << "Got info for plugin " << filename.value()
+ << " Name = \"" << UTF16ToUTF8(info->name)
+ << "\", Description = \"" << UTF16ToUTF8(info->desc) << "\".";
+ } else {
+ LOG_IF(INFO, PluginList::DebugPluginLoading())
+ << "Plugin " << filename.value()
+ << " has no GetValue() and probably won't work.";
+ }
+
+ // Intentionally not unloading the plugin here, it can lead to crashes.
+
+ return true;
+}
+
+// static
+void PluginLib::ParseMIMEDescription(
+ const std::string& description,
+ std::vector<WebPluginMimeType>* mime_types) {
+ // We parse the description here into WebPluginMimeType structures.
+ // Naively from the NPAPI docs you'd think you could use
+ // string-splitting, but the Firefox parser turns out to do something
+ // different: find the first colon, then the second, then a semi.
+ //
+ // See ParsePluginMimeDescription near
+ // http://mxr.mozilla.org/firefox/source/modules/plugin/base/src/nsPluginsDirUtils.h#53
+
+ std::string::size_type ofs = 0;
+ for (;;) {
+ WebPluginMimeType mime_type;
+
+ std::string::size_type end = description.find(':', ofs);
+ if (end == std::string::npos)
+ break;
+ mime_type.mime_type = description.substr(ofs, end - ofs);
+ ofs = end + 1;
+
+ end = description.find(':', ofs);
+ if (end == std::string::npos)
+ break;
+ const std::string extensions = description.substr(ofs, end - ofs);
+ SplitString(extensions, ',', &mime_type.file_extensions);
+ ofs = end + 1;
+
+ end = description.find(';', ofs);
+ // It's ok for end to run off the string here. If there's no
+ // trailing semicolon we consume the remainder of the string.
+ if (end != std::string::npos) {
+ mime_type.description = UTF8ToUTF16(description.substr(ofs, end - ofs));
+ } else {
+ mime_type.description = UTF8ToUTF16(description.substr(ofs));
+ }
+ mime_types->push_back(mime_type);
+ if (end == std::string::npos)
+ break;
+ ofs = end + 1;
+ }
+}
+
+} // namespace NPAPI
diff --git a/webkit/glue/plugins/plugin_lib_unittest.cc b/webkit/glue/plugins/plugin_lib_unittest.cc
new file mode 100644
index 0000000..a52510b
--- /dev/null
+++ b/webkit/glue/plugins/plugin_lib_unittest.cc
@@ -0,0 +1,151 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/plugin_lib.h"
+
+#include "base/string_util.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// Test the unloading of plugin libs. Bug http://crbug.com/46526 showed that
+// if UnloadAllPlugins() simply iterates through the g_loaded_libs global
+// variable, we can get a crash if no plugin libs were marked as always loaded.
+class PluginLibTest : public NPAPI::PluginLib {
+ public:
+ PluginLibTest() : NPAPI::PluginLib(WebPluginInfo(), NULL) {
+ }
+ using NPAPI::PluginLib::Unload;
+};
+
+TEST(PluginLibLoading, UnloadAllPlugins) {
+ // For the creation of the g_loaded_libs global variable.
+ ASSERT_EQ(static_cast<PluginLibTest*>(NULL),
+ PluginLibTest::CreatePluginLib(FilePath()));
+
+ // Try with a single plugin lib.
+ scoped_refptr<PluginLibTest> plugin_lib1 = new PluginLibTest();
+ NPAPI::PluginLib::UnloadAllPlugins();
+
+ // Need to create it again, it should have been destroyed above.
+ ASSERT_EQ(static_cast<PluginLibTest*>(NULL),
+ PluginLibTest::CreatePluginLib(FilePath()));
+
+ // Try with two plugin libs.
+ plugin_lib1 = new PluginLibTest();
+ scoped_refptr<PluginLibTest> plugin_lib2 = new PluginLibTest();
+ NPAPI::PluginLib::UnloadAllPlugins();
+
+ // Need to create it again, it should have been destroyed above.
+ ASSERT_EQ(static_cast<PluginLibTest*>(NULL),
+ PluginLibTest::CreatePluginLib(FilePath()));
+
+ // Now try to manually Unload one and then UnloadAll.
+ plugin_lib1 = new PluginLibTest();
+ plugin_lib2 = new PluginLibTest();
+ plugin_lib1->Unload();
+ NPAPI::PluginLib::UnloadAllPlugins();
+
+ // Need to create it again, it should have been destroyed above.
+ ASSERT_EQ(static_cast<PluginLibTest*>(NULL),
+ PluginLibTest::CreatePluginLib(FilePath()));
+
+ // Now try to manually Unload the only one and then UnloadAll.
+ plugin_lib1 = new PluginLibTest();
+ plugin_lib1->Unload();
+ NPAPI::PluginLib::UnloadAllPlugins();
+}
+
+#if defined(OS_LINUX)
+
+// Test parsing a simple description: Real Audio.
+TEST(MIMEDescriptionParse, Simple) {
+ std::vector<WebPluginMimeType> types;
+ NPAPI::PluginLib::ParseMIMEDescription(
+ "audio/x-pn-realaudio-plugin:rpm:RealAudio document;",
+ &types);
+ ASSERT_EQ(1U, types.size());
+ const WebPluginMimeType& type = types[0];
+ EXPECT_EQ("audio/x-pn-realaudio-plugin", type.mime_type);
+ ASSERT_EQ(1U, type.file_extensions.size());
+ EXPECT_EQ("rpm", type.file_extensions[0]);
+ EXPECT_EQ(ASCIIToUTF16("RealAudio document"), type.description);
+}
+
+// Test parsing a multi-entry description: QuickTime as provided by Totem.
+TEST(MIMEDescriptionParse, Multi) {
+ std::vector<WebPluginMimeType> types;
+ NPAPI::PluginLib::ParseMIMEDescription(
+ "video/quicktime:mov:QuickTime video;video/mp4:mp4:MPEG-4 "
+ "video;image/x-macpaint:pntg:MacPaint Bitmap image;image/x"
+ "-quicktime:pict, pict1, pict2:QuickTime image;video/x-m4v"
+ ":m4v:MPEG-4 video;",
+ &types);
+
+ ASSERT_EQ(5U, types.size());
+
+ // Check the x-quicktime one, since it looks tricky with spaces in the
+ // extension list.
+ const WebPluginMimeType& type = types[3];
+ EXPECT_EQ("image/x-quicktime", type.mime_type);
+ ASSERT_EQ(3U, type.file_extensions.size());
+ EXPECT_EQ("pict2", type.file_extensions[2]);
+ EXPECT_EQ(ASCIIToUTF16("QuickTime image"), type.description);
+}
+
+// Test parsing a Japanese description, since we got this wrong in the past.
+// This comes from loading Totem with LANG=ja_JP.UTF-8.
+TEST(MIMEDescriptionParse, JapaneseUTF8) {
+ std::vector<WebPluginMimeType> types;
+ NPAPI::PluginLib::ParseMIMEDescription(
+ "audio/x-ogg:ogg:Ogg \xe3\x82\xaa\xe3\x83\xbc\xe3\x83\x87"
+ "\xe3\x82\xa3\xe3\x83\xaa",
+ &types);
+
+ ASSERT_EQ(1U, types.size());
+ // Check we got the right number of Unicode characters out of the parse.
+ EXPECT_EQ(9U, types[0].description.size());
+}
+
+// Test that we handle corner cases gracefully.
+TEST(MIMEDescriptionParse, CornerCases) {
+ std::vector<WebPluginMimeType> types;
+ NPAPI::PluginLib::ParseMIMEDescription("mime/type:", &types);
+ EXPECT_TRUE(types.empty());
+
+ types.clear();
+ NPAPI::PluginLib::ParseMIMEDescription("mime/type:ext1:", &types);
+ ASSERT_EQ(1U, types.size());
+ EXPECT_EQ("mime/type", types[0].mime_type);
+ EXPECT_EQ(1U, types[0].file_extensions.size());
+ EXPECT_EQ("ext1", types[0].file_extensions[0]);
+ EXPECT_EQ(string16(), types[0].description);
+}
+
+// This Java plugin has embedded semicolons in the mime type.
+TEST(MIMEDescriptionParse, ComplicatedJava) {
+ std::vector<WebPluginMimeType> types;
+ NPAPI::PluginLib::ParseMIMEDescription(
+ "application/x-java-vm:class,jar:IcedTea;application/x-java"
+ "-applet:class,jar:IcedTea;application/x-java-applet;versio"
+ "n=1.1:class,jar:IcedTea;application/x-java-applet;version="
+ "1.1.1:class,jar:IcedTea;application/x-java-applet;version="
+ "1.1.2:class,jar:IcedTea;application/x-java-applet;version="
+ "1.1.3:class,jar:IcedTea;application/x-java-applet;version="
+ "1.2:class,jar:IcedTea;application/x-java-applet;version=1."
+ "2.1:class,jar:IcedTea;application/x-java-applet;version=1."
+ "2.2:class,jar:IcedTea;application/x-java-applet;version=1."
+ "3:class,jar:IcedTea;application/x-java-applet;version=1.3."
+ "1:class,jar:IcedTea;application/x-java-applet;version=1.4:"
+ "class,jar:IcedTea",
+ &types);
+
+ ASSERT_EQ(12U, types.size());
+ for (size_t i = 0; i < types.size(); ++i)
+ EXPECT_EQ(ASCIIToUTF16("IcedTea"), types[i].description);
+
+ // Verify that the mime types with semis are coming through ok.
+ EXPECT_TRUE(types[4].mime_type.find(';') != std::string::npos);
+}
+
+#endif // defined(OS_LINUX)
diff --git a/webkit/glue/plugins/plugin_lib_win.cc b/webkit/glue/plugins/plugin_lib_win.cc
new file mode 100644
index 0000000..00f6243
--- /dev/null
+++ b/webkit/glue/plugins/plugin_lib_win.cc
@@ -0,0 +1,41 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/plugin_lib.h"
+
+#include "base/file_version_info.h"
+#include "base/file_version_info_win.h"
+#include "base/path_service.h"
+#include "webkit/glue/plugins/plugin_constants_win.h"
+#include "webkit/glue/plugins/plugin_list.h"
+
+namespace NPAPI
+{
+bool PluginLib::ReadWebPluginInfo(const FilePath &filename,
+ WebPluginInfo* info) {
+ // On windows, the way we get the mime types for the library is
+ // to check the version information in the DLL itself. This
+ // will be a string of the format: <type1>|<type2>|<type3>|...
+ // For example:
+ // video/quicktime|audio/aiff|image/jpeg
+ scoped_ptr<FileVersionInfo> version_info(
+ FileVersionInfo::CreateFileVersionInfo(filename.value()));
+ if (!version_info.get())
+ return false;
+
+ FileVersionInfoWin* version_info_win =
+ static_cast<FileVersionInfoWin*>(version_info.get());
+ PluginVersionInfo pvi;
+ pvi.mime_types = version_info_win->GetStringValue(L"MIMEType");
+ pvi.file_extensions = version_info_win->GetStringValue(L"FileExtents");
+ pvi.type_descriptions = version_info_win->GetStringValue(L"FileOpenName");
+ pvi.product_name = version_info->product_name();
+ pvi.file_description = version_info->file_description();
+ pvi.file_version = version_info->file_version();
+ pvi.path = filename;
+
+ return PluginList::CreateWebPluginInfo(pvi, info);
+}
+
+} // namespace NPAPI
diff --git a/webkit/glue/plugins/plugin_list.cc b/webkit/glue/plugins/plugin_list.cc
new file mode 100644
index 0000000..a3412d7
--- /dev/null
+++ b/webkit/glue/plugins/plugin_list.cc
@@ -0,0 +1,460 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/plugin_list.h"
+
+#include <algorithm>
+
+#include "base/command_line.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "base/time.h"
+#include "googleurl/src/gurl.h"
+#include "net/base/mime_util.h"
+#include "webkit/glue/plugins/plugin_constants_win.h"
+#include "webkit/glue/plugins/plugin_lib.h"
+#include "webkit/glue/plugins/plugin_switches.h"
+#include "webkit/glue/webkit_glue.h"
+
+namespace NPAPI {
+
+base::LazyInstance<PluginList> g_singleton(base::LINKER_INITIALIZED);
+
+// static
+PluginList* PluginList::Singleton() {
+ return g_singleton.Pointer();
+}
+
+// static
+bool PluginList::DebugPluginLoading() {
+ return CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kDebugPluginLoading);
+}
+
+bool PluginList::PluginsLoaded() {
+ AutoLock lock(lock_);
+ return plugins_loaded_;
+}
+
+void PluginList::RefreshPlugins() {
+ AutoLock lock(lock_);
+ plugins_need_refresh_ = true;
+}
+
+void PluginList::AddExtraPluginPath(const FilePath& plugin_path) {
+ AutoLock lock(lock_);
+ extra_plugin_paths_.push_back(plugin_path);
+}
+
+void PluginList::RemoveExtraPluginPath(const FilePath& plugin_path) {
+ AutoLock lock(lock_);
+ std::vector<FilePath>::iterator it =
+ std::find(extra_plugin_paths_.begin(), extra_plugin_paths_.end(),
+ plugin_path);
+ if (it != extra_plugin_paths_.end())
+ extra_plugin_paths_.erase(it);
+}
+
+void PluginList::AddExtraPluginDir(const FilePath& plugin_dir) {
+ AutoLock lock(lock_);
+ extra_plugin_dirs_.push_back(plugin_dir);
+}
+
+void PluginList::RegisterInternalPlugin(const PluginVersionInfo& info) {
+ AutoLock lock(lock_);
+ internal_plugins_.push_back(info);
+}
+
+void PluginList::UnregisterInternalPlugin(const FilePath& path) {
+ AutoLock lock(lock_);
+ for (size_t i = 0; i < internal_plugins_.size(); i++) {
+ if (internal_plugins_[i].path == path) {
+ internal_plugins_.erase(internal_plugins_.begin() + i);
+ return;
+ }
+ }
+ NOTREACHED();
+}
+
+bool PluginList::ReadPluginInfo(const FilePath& filename,
+ WebPluginInfo* info,
+ const PluginEntryPoints** entry_points) {
+ {
+ AutoLock lock(lock_);
+ for (size_t i = 0; i < internal_plugins_.size(); ++i) {
+ if (filename == internal_plugins_[i].path) {
+ *entry_points = &internal_plugins_[i].entry_points;
+ return CreateWebPluginInfo(internal_plugins_[i], info);
+ }
+ }
+ }
+
+ // Not an internal plugin.
+ *entry_points = NULL;
+
+ return PluginLib::ReadWebPluginInfo(filename, info);
+}
+
+bool PluginList::CreateWebPluginInfo(const PluginVersionInfo& pvi,
+ WebPluginInfo* info) {
+ std::vector<std::string> mime_types, file_extensions;
+ std::vector<string16> descriptions;
+ SplitString(WideToUTF8(pvi.mime_types), '|', &mime_types);
+ SplitString(WideToUTF8(pvi.file_extensions), '|', &file_extensions);
+ SplitString(WideToUTF16(pvi.type_descriptions), '|', &descriptions);
+
+ info->mime_types.clear();
+
+ if (mime_types.empty())
+ return false;
+
+ info->name = WideToUTF16(pvi.product_name);
+ info->desc = WideToUTF16(pvi.file_description);
+ info->version = WideToUTF16(pvi.file_version);
+ info->path = pvi.path;
+ info->enabled = true;
+
+ for (size_t i = 0; i < mime_types.size(); ++i) {
+ WebPluginMimeType mime_type;
+ mime_type.mime_type = StringToLowerASCII(mime_types[i]);
+ if (file_extensions.size() > i)
+ SplitString(file_extensions[i], ',', &mime_type.file_extensions);
+
+ if (descriptions.size() > i) {
+ mime_type.description = descriptions[i];
+
+ // On Windows, the description likely has a list of file extensions
+ // embedded in it (e.g. "SurfWriter file (*.swr)"). Remove an extension
+ // list from the description if it is present.
+ size_t ext = mime_type.description.find(ASCIIToUTF16("(*"));
+ if (ext != string16::npos) {
+ if (ext > 1 && mime_type.description[ext -1] == ' ')
+ ext--;
+
+ mime_type.description.erase(ext);
+ }
+ }
+
+ info->mime_types.push_back(mime_type);
+ }
+
+ return true;
+}
+
+PluginList::PluginList()
+ : plugins_loaded_(false), plugins_need_refresh_(false) {
+ PlatformInit();
+}
+
+void PluginList::LoadPlugins(bool refresh) {
+ // Don't want to hold the lock while loading new plugins, so we don't block
+ // other methods if they're called on other threads.
+ std::vector<FilePath> extra_plugin_paths;
+ std::vector<FilePath> extra_plugin_dirs;
+ std::vector<PluginVersionInfo> internal_plugins;
+ {
+ AutoLock lock(lock_);
+ if (plugins_loaded_ && !refresh && !plugins_need_refresh_)
+ return;
+
+ // Clear the refresh bit now, because it might get set again before we
+ // reach the end of the method.
+ plugins_need_refresh_ = false;
+ extra_plugin_paths = extra_plugin_paths_;
+ extra_plugin_dirs = extra_plugin_dirs_;
+ internal_plugins = internal_plugins_;
+ }
+
+ base::TimeTicks start_time = base::TimeTicks::Now();
+
+ std::vector<WebPluginInfo> new_plugins;
+ std::set<FilePath> visited_plugins;
+
+ std::vector<FilePath> directories_to_scan;
+ GetPluginDirectories(&directories_to_scan);
+
+ // Load internal plugins first so that, if both an internal plugin and a
+ // "discovered" plugin want to handle the same type, the internal plugin
+ // will have precedence.
+ for (size_t i = 0; i < internal_plugins.size(); ++i) {
+ if (internal_plugins[i].path.value() == kDefaultPluginLibraryName)
+ continue;
+ LoadPlugin(internal_plugins[i].path, &new_plugins);
+ }
+
+ for (size_t i = 0; i < extra_plugin_paths.size(); ++i) {
+ const FilePath& path = extra_plugin_paths[i];
+ if (visited_plugins.find(path) != visited_plugins.end())
+ continue;
+ LoadPlugin(path, &new_plugins);
+ visited_plugins.insert(path);
+ }
+
+ for (size_t i = 0; i < extra_plugin_dirs.size(); ++i) {
+ LoadPluginsFromDir(extra_plugin_dirs[i], &new_plugins, &visited_plugins);
+ }
+
+ for (size_t i = 0; i < directories_to_scan.size(); ++i) {
+ LoadPluginsFromDir(directories_to_scan[i], &new_plugins, &visited_plugins);
+ }
+
+#if defined OS_WIN
+ LoadPluginsFromRegistry(&new_plugins, &visited_plugins);
+#endif
+
+ // Load the default plugin last.
+ if (webkit_glue::IsDefaultPluginEnabled())
+ LoadPlugin(FilePath(kDefaultPluginLibraryName), &new_plugins);
+
+ base::TimeTicks end_time = base::TimeTicks::Now();
+ base::TimeDelta elapsed = end_time - start_time;
+ DLOG(INFO) << "Loaded plugin list in " << elapsed.InMilliseconds() << " ms.";
+
+ // Only update the data now since loading plugins can take a while.
+ AutoLock lock(lock_);
+
+ // Go through and mark new plugins in the disabled list as, well, disabled.
+ for (std::vector<WebPluginInfo>::iterator it = new_plugins.begin();
+ it != new_plugins.end();
+ ++it) {
+ if (disabled_plugins_.find(it->path) != disabled_plugins_.end())
+ it->enabled = false;
+ }
+
+ plugins_ = new_plugins;
+ plugins_loaded_ = true;
+}
+
+void PluginList::LoadPlugin(const FilePath& path,
+ std::vector<WebPluginInfo>* plugins) {
+ WebPluginInfo plugin_info;
+ const PluginEntryPoints* entry_points;
+
+ if (!ReadPluginInfo(path, &plugin_info, &entry_points))
+ return;
+
+ if (!ShouldLoadPlugin(plugin_info, plugins))
+ return;
+
+ if (path.value() != kDefaultPluginLibraryName
+#if defined(OS_WIN) && !defined(NDEBUG)
+ && path.BaseName().value() != L"npspy.dll" // Make an exception for NPSPY
+#endif
+ ) {
+ for (size_t i = 0; i < plugin_info.mime_types.size(); ++i) {
+ // TODO: don't load global handlers for now.
+ // WebKit hands to the Plugin before it tries
+ // to handle mimeTypes on its own.
+ const std::string &mime_type = plugin_info.mime_types[i].mime_type;
+ if (mime_type == "*" )
+ return;
+ }
+ }
+
+ plugins->push_back(plugin_info);
+}
+
+bool PluginList::FindPlugin(const std::string& mime_type,
+ bool allow_wildcard,
+ WebPluginInfo* info) {
+ DCHECK(mime_type == StringToLowerASCII(mime_type));
+
+ LoadPlugins(false);
+ AutoLock lock(lock_);
+ for (size_t i = 0; i < plugins_.size(); ++i) {
+ if (plugins_[i].enabled &&
+ SupportsType(plugins_[i], mime_type, allow_wildcard)) {
+ *info = plugins_[i];
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool PluginList::FindDisabledPlugin(const std::string& mime_type,
+ bool allow_wildcard,
+ WebPluginInfo* info) {
+ DCHECK(mime_type == StringToLowerASCII(mime_type));
+
+ LoadPlugins(false);
+ AutoLock lock(lock_);
+ for (size_t i = 0; i < plugins_.size(); ++i) {
+ if (!plugins_[i].enabled &&
+ SupportsType(plugins_[i], mime_type, allow_wildcard)) {
+ *info = plugins_[i];
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool PluginList::FindPlugin(const GURL &url,
+ std::string* actual_mime_type,
+ WebPluginInfo* info) {
+ LoadPlugins(false);
+ AutoLock lock(lock_);
+ std::string path = url.path();
+ std::string::size_type last_dot = path.rfind('.');
+ if (last_dot == std::string::npos)
+ return false;
+
+ std::string extension = StringToLowerASCII(std::string(path, last_dot+1));
+
+ for (size_t i = 0; i < plugins_.size(); ++i) {
+ if (plugins_[i].enabled &&
+ SupportsExtension(plugins_[i], extension, actual_mime_type)) {
+ *info = plugins_[i];
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool PluginList::SupportsType(const WebPluginInfo& info,
+ const std::string &mime_type,
+ bool allow_wildcard) {
+ // Webkit will ask for a plugin to handle empty mime types.
+ if (mime_type.empty())
+ return false;
+
+ for (size_t i = 0; i < info.mime_types.size(); ++i) {
+ const WebPluginMimeType& mime_info = info.mime_types[i];
+ if (net::MatchesMimeType(mime_info.mime_type, mime_type)) {
+ if (!allow_wildcard && mime_info.mime_type == "*") {
+ continue;
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+bool PluginList::SupportsExtension(const WebPluginInfo& info,
+ const std::string &extension,
+ std::string* actual_mime_type) {
+ for (size_t i = 0; i < info.mime_types.size(); ++i) {
+ const WebPluginMimeType& mime_type = info.mime_types[i];
+ for (size_t j = 0; j < mime_type.file_extensions.size(); ++j) {
+ if (mime_type.file_extensions[j] == extension) {
+ if (actual_mime_type)
+ *actual_mime_type = mime_type.mime_type;
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+
+void PluginList::GetPlugins(bool refresh, std::vector<WebPluginInfo>* plugins) {
+ LoadPlugins(refresh);
+
+ AutoLock lock(lock_);
+ *plugins = plugins_;
+}
+
+void PluginList::GetEnabledPlugins(bool refresh,
+ std::vector<WebPluginInfo>* plugins) {
+ LoadPlugins(refresh);
+
+ plugins->clear();
+ AutoLock lock(lock_);
+ for (std::vector<WebPluginInfo>::const_iterator it = plugins_.begin();
+ it != plugins_.end();
+ ++it) {
+ if (it->enabled)
+ plugins->push_back(*it);
+ }
+}
+
+bool PluginList::GetPluginInfo(const GURL& url,
+ const std::string& mime_type,
+ bool allow_wildcard,
+ WebPluginInfo* info,
+ std::string* actual_mime_type) {
+ bool found = FindPlugin(mime_type, allow_wildcard, info);
+ if (!found || (info->path.value() == kDefaultPluginLibraryName)) {
+ if (FindPlugin(url, actual_mime_type, info) ||
+ FindDisabledPlugin(mime_type, allow_wildcard, info)) {
+ found = true;
+ }
+ }
+
+ return found;
+}
+
+bool PluginList::GetPluginInfoByPath(const FilePath& plugin_path,
+ WebPluginInfo* info) {
+ LoadPlugins(false);
+ AutoLock lock(lock_);
+ for (size_t i = 0; i < plugins_.size(); ++i) {
+ if (plugins_[i].path == plugin_path) {
+ *info = plugins_[i];
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool PluginList::EnablePlugin(const FilePath& filename) {
+ AutoLock lock(lock_);
+
+ bool did_enable = false;
+
+ std::set<FilePath>::iterator entry = disabled_plugins_.find(filename);
+ if (entry == disabled_plugins_.end())
+ return did_enable; // Early exit if plugin not in disabled list.
+
+ disabled_plugins_.erase(entry); // Remove from disabled list.
+
+ // Set enabled flags if necessary.
+ for (std::vector<WebPluginInfo>::iterator it = plugins_.begin();
+ it != plugins_.end();
+ ++it) {
+ if (it->path == filename) {
+ DCHECK(!it->enabled); // Should have been disabled.
+ it->enabled = true;
+ did_enable = true;
+ }
+ }
+
+ return did_enable;
+}
+
+bool PluginList::DisablePlugin(const FilePath& filename) {
+ AutoLock lock(lock_);
+
+ bool did_disable = false;
+
+ if (disabled_plugins_.find(filename) != disabled_plugins_.end())
+ return did_disable; // Early exit if plugin already in disabled list.
+
+ disabled_plugins_.insert(filename); // Add to disabled list.
+
+ // Unset enabled flags if necessary.
+ for (std::vector<WebPluginInfo>::iterator it = plugins_.begin();
+ it != plugins_.end();
+ ++it) {
+ if (it->path == filename) {
+ DCHECK(it->enabled); // Should have been enabled.
+ it->enabled = false;
+ did_disable = true;
+ }
+ }
+
+ return did_disable;
+}
+
+void PluginList::Shutdown() {
+ // TODO
+}
+
+} // namespace NPAPI
diff --git a/webkit/glue/plugins/plugin_list.h b/webkit/glue/plugins/plugin_list.h
new file mode 100644
index 0000000..ce9da28
--- /dev/null
+++ b/webkit/glue/plugins/plugin_list.h
@@ -0,0 +1,275 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_PLUGIN_LIST_H_
+#define WEBKIT_GLUE_PLUGINS_PLUGIN_LIST_H_
+
+#include <set>
+#include <string>
+#include <vector>
+#include <set>
+
+#include "base/basictypes.h"
+#include "base/file_path.h"
+#include "base/lock.h"
+#include "third_party/npapi/bindings/nphostapi.h"
+#include "webkit/glue/plugins/webplugininfo.h"
+
+class GURL;
+
+namespace base {
+
+template <typename T>
+struct DefaultLazyInstanceTraits;
+
+} // namespace base
+
+namespace NPAPI {
+
+#define kDefaultPluginLibraryName FILE_PATH_LITERAL("default_plugin")
+#define kGearsPluginLibraryName FILE_PATH_LITERAL("gears")
+
+class PluginInstance;
+
+// This struct holds entry points into a plugin. The entry points are
+// slightly different between Win/Mac and Unixes.
+struct PluginEntryPoints {
+#if !defined(OS_POSIX) || defined(OS_MACOSX)
+ NP_GetEntryPointsFunc np_getentrypoints;
+#endif
+ NP_InitializeFunc np_initialize;
+ NP_ShutdownFunc np_shutdown;
+};
+
+// This struct fully describes a plugin. For external plugins, it's read in from
+// the version info of the dll; For internal plugins, it's predefined and
+// includes addresses of entry functions. (Yes, it's Win32 NPAPI-centric, but
+// it'll do for holding descriptions of internal plugins cross-platform.)
+struct PluginVersionInfo {
+ FilePath path;
+ // Info about the plugin itself.
+ std::wstring product_name;
+ std::wstring file_description;
+ std::wstring file_version;
+ // Info about the data types that the plugin supports.
+ std::wstring mime_types;
+ std::wstring file_extensions;
+ std::wstring type_descriptions;
+ // Entry points for internal plugins. Pointers are NULL for external plugins.
+ PluginEntryPoints entry_points;
+};
+
+// The PluginList is responsible for loading our NPAPI based plugins. It does
+// so in whatever manner is appropriate for the platform. On Windows, it loads
+// plugins from a known directory by looking for DLLs which start with "NP",
+// and checking to see if they are valid NPAPI libraries. On the Mac, it walks
+// the machine-wide and user plugin directories and loads anything that has
+// the correct types. On Linux, it walks the plugin directories as well
+// (e.g. /usr/lib/browser-plugins/).
+// This object is thread safe.
+class PluginList {
+ public:
+ // Gets the one instance of the PluginList.
+ static PluginList* Singleton();
+
+ // Returns true if we're in debug-plugin-loading mode. This is controlled
+ // by a command line switch.
+ static bool DebugPluginLoading();
+
+ // Returns true iff the plugin list has been loaded already.
+ bool PluginsLoaded();
+
+ // Cause the plugin list to refresh next time they are accessed, regardless
+ // of whether they are already loaded.
+ void RefreshPlugins();
+
+ // Add/Remove an extra plugin to load when we actually do the loading. Must
+ // be called before the plugins have been loaded.
+ void AddExtraPluginPath(const FilePath& plugin_path);
+ void RemoveExtraPluginPath(const FilePath& plugin_path);
+
+ // Same as above, but specifies a directory in which to search for plugins.
+ void AddExtraPluginDir(const FilePath& plugin_dir);
+
+ // Register an internal plugin with the specified plugin information and
+ // function pointers. An internal plugin must be registered before it can
+ // be loaded using PluginList::LoadPlugin().
+ void RegisterInternalPlugin(const PluginVersionInfo& info);
+
+ // Removes a specified internal plugin from the list. The search will match
+ // on the path from the version info previously registered.
+ //
+ // This is generally only necessary for tests.
+ void UnregisterInternalPlugin(const FilePath& path);
+
+ // Creates a WebPluginInfo structure given a plugin's path. On success
+ // returns true, with the information being put into "info". If it's an
+ // internal plugin, "entry_points" is filled in as well with a
+ // internally-owned PluginEntryPoints pointer.
+ // Returns false if the library couldn't be found, or if it's not a plugin.
+ bool ReadPluginInfo(const FilePath& filename,
+ WebPluginInfo* info,
+ const PluginEntryPoints** entry_points);
+
+ // Populate a WebPluginInfo from a PluginVersionInfo.
+ static bool CreateWebPluginInfo(const PluginVersionInfo& pvi,
+ WebPluginInfo* info);
+
+ // Shutdown all plugins. Should be called at process teardown.
+ void Shutdown();
+
+ // Get all the plugins.
+ void GetPlugins(bool refresh, std::vector<WebPluginInfo>* plugins);
+
+ // Get all the enabled plugins.
+ void GetEnabledPlugins(bool refresh, std::vector<WebPluginInfo>* plugins);
+
+ // Returns true if a plugin is found for the given url and mime type
+ // (including disabled plugins, for which |info->enabled| is false).
+ // The mime type which corresponds to the URL is optionally returned
+ // back.
+ // The allow_wildcard parameter controls whether this function returns
+ // plugins which support wildcard mime types (* as the mime type).
+ bool GetPluginInfo(const GURL& url,
+ const std::string& mime_type,
+ bool allow_wildcard,
+ WebPluginInfo* info,
+ std::string* actual_mime_type);
+
+ // Get plugin info by plugin path (including disabled plugins). Returns true
+ // if the plugin is found and WebPluginInfo has been filled in |info|.
+ bool GetPluginInfoByPath(const FilePath& plugin_path,
+ WebPluginInfo* info);
+
+ // Load a specific plugin with full path.
+ void LoadPlugin(const FilePath& filename,
+ std::vector<WebPluginInfo>* plugins);
+
+ // Enable a specific plugin, specified by path. Returns |true| iff a plugin
+ // currently in the plugin list was actually enabled as a result; regardless
+ // of return value, if a plugin is found in the future with the given name, it
+ // will be enabled. Note that plugins are enabled by default as far as
+ // |PluginList| is concerned.
+ bool EnablePlugin(const FilePath& filename);
+
+ // Disable a specific plugin, specified by path. Returns |true| iff a plugin
+ // currently in the plugin list was actually disabled as a result; regardless
+ // of return value, if a plugin is found in the future with the given name, it
+ // will be disabled.
+ bool DisablePlugin(const FilePath& filename);
+
+ private:
+ // Constructors are private for singletons
+ PluginList();
+
+ // Load all plugins from the default plugins directory
+ void LoadPlugins(bool refresh);
+
+ // Load all plugins from a specific directory.
+ // |plugins| is updated with loaded plugin information.
+ // |visited_plugins| is updated with paths to all plugins that were considered
+ // (including those we didn't load)
+ void LoadPluginsFromDir(const FilePath& path,
+ std::vector<WebPluginInfo>* plugins,
+ std::set<FilePath>* visited_plugins);
+
+ // Returns true if we should load the given plugin, or false otherwise.
+ // plugins is the list of plugins we have crawled in the current plugin
+ // loading run.
+ bool ShouldLoadPlugin(const WebPluginInfo& info,
+ std::vector<WebPluginInfo>* plugins);
+
+ // Find a plugin by mime type; only searches enabled plugins.
+ // The allow_wildcard parameter controls whether this function returns
+ // plugins which support wildcard mime types (* as the mime type)
+ bool FindPlugin(const std::string &mime_type,
+ bool allow_wildcard,
+ WebPluginInfo* info);
+
+ // Just like |FindPlugin| but it only looks at the disabled plug-ins.
+ bool FindDisabledPlugin(const std::string &mime_type,
+ bool allow_wildcard,
+ WebPluginInfo* info);
+
+ // Find a plugin by extension; only searches enabled plugins. Returns the
+ // corresponding mime type.
+ bool FindPlugin(const GURL &url,
+ std::string* actual_mime_type,
+ WebPluginInfo* info);
+
+ // Returns true if the given WebPluginInfo supports "mime-type".
+ // mime_type should be all lower case.
+ static bool SupportsType(const WebPluginInfo& info,
+ const std::string &mime_type,
+ bool allow_wildcard);
+
+ // Returns true if the given WebPluginInfo supports a given file extension.
+ // extension should be all lower case.
+ // If mime_type is not NULL, it will be set to the mime type if found.
+ // The mime type which corresponds to the extension is optionally returned
+ // back.
+ static bool SupportsExtension(const WebPluginInfo& info,
+ const std::string &extension,
+ std::string* actual_mime_type);
+
+ //
+ // Platform functions
+ //
+
+ // Do any initialization.
+ void PlatformInit();
+
+ // Get the ordered list of directories from which to load plugins
+ void GetPluginDirectories(std::vector<FilePath>* plugin_dirs);
+
+ //
+ // Command-line switches
+ //
+
+#if defined(OS_WIN)
+ // true if we shouldn't load the new WMP plugin.
+ bool dont_load_new_wmp_;
+
+ // Loads plugins registered under HKCU\Software\MozillaPlugins and
+ // HKLM\Software\MozillaPlugins.
+ void LoadPluginsFromRegistry(std::vector<WebPluginInfo>* plugins,
+ std::set<FilePath>* visited_plugins);
+#endif
+
+ //
+ // Internals
+ //
+
+ bool plugins_loaded_;
+
+ // If true, we reload plugins even if they've been loaded already.
+ bool plugins_need_refresh_;
+
+ // Contains information about the available plugins.
+ std::vector<WebPluginInfo> plugins_;
+
+ // Extra plugin paths that we want to search when loading.
+ std::vector<FilePath> extra_plugin_paths_;
+
+ // Extra plugin directories that we want to search when loading.
+ std::vector<FilePath> extra_plugin_dirs_;
+
+ // Holds information about internal plugins.
+ std::vector<PluginVersionInfo> internal_plugins_;
+
+ // Path names of plugins to disable (the default is to enable them all).
+ std::set<FilePath> disabled_plugins_;
+
+ // Need synchronization for the above members since this object can be
+ // accessed on multiple threads.
+ Lock lock_;
+
+ friend struct base::DefaultLazyInstanceTraits<PluginList>;
+
+ DISALLOW_COPY_AND_ASSIGN(PluginList);
+};
+
+} // namespace NPAPI
+
+#endif // WEBKIT_GLUE_PLUGINS_PLUGIN_LIST_H_
diff --git a/webkit/glue/plugins/plugin_list_mac.mm b/webkit/glue/plugins/plugin_list_mac.mm
new file mode 100644
index 0000000..16bde9d
--- /dev/null
+++ b/webkit/glue/plugins/plugin_list_mac.mm
@@ -0,0 +1,107 @@
+// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/plugin_list.h"
+
+#import <Foundation/Foundation.h>
+
+#include "base/file_util.h"
+#include "base/mac_util.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "webkit/glue/plugins/plugin_lib.h"
+
+namespace {
+
+void GetPluginCommonDirectory(std::vector<FilePath>* plugin_dirs,
+ bool user) {
+ // Note that there are no NSSearchPathDirectory constants for these
+ // directories so we can't use Cocoa's NSSearchPathForDirectoriesInDomains().
+ // Interestingly, Safari hard-codes the location (see
+ // WebKit/WebKit/mac/Plugins/WebPluginDatabase.mm's +_defaultPlugInPaths).
+ FSRef ref;
+ OSErr err = FSFindFolder(user ? kUserDomain : kLocalDomain,
+ kInternetPlugInFolderType, false, &ref);
+
+ if (err)
+ return;
+
+ plugin_dirs->push_back(FilePath(mac_util::PathFromFSRef(ref)));
+}
+
+void GetPluginPrivateDirectory(std::vector<FilePath>* plugin_dirs) {
+ NSString* plugin_path = [[NSBundle mainBundle] builtInPlugInsPath];
+ if (!plugin_path)
+ return;
+
+ plugin_dirs->push_back(FilePath([plugin_path fileSystemRepresentation]));
+}
+
+// Returns true if the plugin should be prevented from loading.
+bool IsBlacklistedPlugin(const WebPluginInfo& info) {
+ // We blacklist Gears by included MIME type, since that is more stable than
+ // its name. Be careful about adding any more plugins to this list though,
+ // since it's easy to accidentally blacklist plugins that support lots of
+ // MIME types.
+ for (std::vector<WebPluginMimeType>::const_iterator i =
+ info.mime_types.begin(); i != info.mime_types.end(); ++i) {
+ // The Gears plugin is Safari-specific, so don't load it.
+ if (i->mime_type == "application/x-googlegears")
+ return true;
+ }
+
+ return false;
+}
+
+} // namespace
+
+namespace NPAPI
+{
+
+void PluginList::PlatformInit() {
+}
+
+void PluginList::GetPluginDirectories(std::vector<FilePath>* plugin_dirs) {
+ // Load from the user's area
+ GetPluginCommonDirectory(plugin_dirs, true);
+
+ // Load from the machine-wide area
+ GetPluginCommonDirectory(plugin_dirs, false);
+
+ // Load any bundled plugins (deprecated)
+ // TODO(stuartmorgan): Remove this once it's not used in TestShell.
+ GetPluginPrivateDirectory(plugin_dirs);
+}
+
+void PluginList::LoadPluginsFromDir(const FilePath &path,
+ std::vector<WebPluginInfo>* plugins,
+ std::set<FilePath>* visited_plugins) {
+ file_util::FileEnumerator enumerator(path,
+ false, // not recursive
+ file_util::FileEnumerator::DIRECTORIES);
+ for (FilePath path = enumerator.Next(); !path.value().empty();
+ path = enumerator.Next()) {
+ LoadPlugin(path, plugins);
+ visited_plugins->insert(path);
+ }
+}
+
+bool PluginList::ShouldLoadPlugin(const WebPluginInfo& info,
+ std::vector<WebPluginInfo>* plugins) {
+ if (IsBlacklistedPlugin(info))
+ return false;
+
+ // Hierarchy check
+ // (we're loading plugins hierarchically from Library folders, so plugins we
+ // encounter earlier must override plugins we encounter later)
+ for (size_t i = 0; i < plugins->size(); ++i) {
+ if ((*plugins)[i].path.BaseName() == info.path.BaseName()) {
+ return false; // We already have a loaded plugin higher in the hierarchy.
+ }
+ }
+
+ return true;
+}
+
+} // namespace NPAPI
diff --git a/webkit/glue/plugins/plugin_list_posix.cc b/webkit/glue/plugins/plugin_list_posix.cc
new file mode 100644
index 0000000..1fbd76f
--- /dev/null
+++ b/webkit/glue/plugins/plugin_list_posix.cc
@@ -0,0 +1,267 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/plugin_list.h"
+
+#include "base/file_util.h"
+#include "base/path_service.h"
+#include "base/sha1.h"
+#include "base/string_util.h"
+#include "build/build_config.h"
+
+namespace {
+
+// We build up a list of files and mtimes so we can sort them.
+typedef std::pair<FilePath, base::Time> FileAndTime;
+typedef std::vector<FileAndTime> FileTimeList;
+
+// Comparator used to sort by descending mtime then ascending filename.
+bool CompareTime(const FileAndTime& a, const FileAndTime& b) {
+ if (a.second == b.second) {
+ // Fall back on filename sorting, just to make the predicate valid.
+ return a.first < b.first;
+ }
+
+ // Sort by mtime, descending.
+ return a.second > b.second;
+}
+
+// Return true if |path| matches a known (file size, sha1sum) pair.
+// The use of the file size is an optimization so we don't have to read in
+// the entire file unless we have to.
+bool IsBlacklistedBySha1sum(const FilePath& path) {
+ const struct BadEntry {
+ int64 size;
+ std::string sha1;
+ } bad_entries[] = {
+ // Flash 9 r31 - http://crbug.com/29237
+ { 7040080, "fa5803061125ca47846713b34a26a42f1f1e98bb" },
+ // Flash 9 r48 - http://crbug.com/29237
+ { 7040036, "0c4b3768a6d4bfba003088e4b9090d381de1af2b" },
+ };
+
+ int64 size;
+ if (!file_util::GetFileSize(path, &size))
+ return false;
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(bad_entries); i++) {
+ if (bad_entries[i].size != size)
+ continue;
+
+ std::string file_content;
+ if (!file_util::ReadFileToString(path, &file_content))
+ continue;
+ std::string sha1 = base::SHA1HashString(file_content);
+ std::string sha1_readable;
+ for (size_t j = 0; j < sha1.size(); j++)
+ StringAppendF(&sha1_readable, "%02x", sha1[j] & 0xFF);
+ if (bad_entries[i].sha1 == sha1_readable)
+ return true;
+ }
+ return false;
+}
+
+// Some plugins are shells around other plugins; we prefer to use the
+// real plugin directly, if it's available. This function returns
+// true if we should prefer other plugins over this one. We'll still
+// use a "undesirable" plugin if no other option is available.
+bool IsUndesirablePlugin(const WebPluginInfo& info) {
+ std::string filename = info.path.BaseName().value();
+ const char* kUndesiredPlugins[] = {
+ "npcxoffice", // Crossover
+ "npwrapper", // nspluginwrapper
+ };
+ for (size_t i = 0; i < arraysize(kUndesiredPlugins); i++) {
+ if (filename.find(kUndesiredPlugins[i]) != std::string::npos) {
+ return true;
+ }
+ }
+ return false;
+}
+
+// Return true if we shouldn't load a plugin at all.
+// This is an ugly hack to blacklist Adobe Acrobat due to not supporting
+// its Xt-based mainloop.
+// http://code.google.com/p/chromium/issues/detail?id=38229
+// The gecko-mediaplayer plugins also crashes the entire browser sometimes.
+// http://code.google.com/p/chromium/issues/detail?id=24507
+bool IsBlacklistedPlugin(const FilePath& path) {
+ const char* kBlackListedPlugins[] = {
+ "nppdf.so", // Adobe PDF
+ "gecko-mediaplayer", // Gecko Media Player
+ };
+ std::string filename = path.BaseName().value();
+ for (size_t i = 0; i < arraysize(kBlackListedPlugins); i++) {
+ if (filename.find(kBlackListedPlugins[i]) != std::string::npos) {
+ return true;
+ }
+ }
+ return IsBlacklistedBySha1sum(path);
+}
+
+} // anonymous namespace
+
+namespace NPAPI {
+
+void PluginList::PlatformInit() {
+}
+
+void PluginList::GetPluginDirectories(std::vector<FilePath>* plugin_dirs) {
+ // See http://groups.google.com/group/chromium-dev/browse_thread/thread/7a70e5fcbac786a9
+ // for discussion.
+ // We first consult Chrome-specific dirs, then fall back on the logic
+ // Mozilla uses.
+
+ // Note: "extra" plugin dirs, including the Plugins subdirectory of
+ // your Chrome config, are examined before these. See the logic
+ // related to extra_plugin_dirs in plugin_list.cc.
+
+ // The Chrome binary dir + "plugins/".
+ FilePath dir;
+ PathService::Get(base::DIR_EXE, &dir);
+ plugin_dirs->push_back(dir.Append("plugins"));
+
+ // Mozilla code to reference:
+ // http://mxr.mozilla.org/firefox/ident?i=NS_APP_PLUGINS_DIR_LIST
+ // and tens of accompanying files (mxr is very helpful).
+ // This code carefully matches their behavior for compat reasons.
+
+ // 1) MOZ_PLUGIN_PATH env variable.
+ const char* moz_plugin_path = getenv("MOZ_PLUGIN_PATH");
+ if (moz_plugin_path) {
+ std::vector<std::string> paths;
+ SplitString(moz_plugin_path, ':', &paths);
+ for (size_t i = 0; i < paths.size(); ++i)
+ plugin_dirs->push_back(FilePath(paths[i]));
+ }
+
+ // 2) NS_USER_PLUGINS_DIR: ~/.mozilla/plugins.
+ // This is a de-facto standard, so even though we're not Mozilla, let's
+ // look in there too.
+ FilePath home = file_util::GetHomeDir();
+ if (!home.empty())
+ plugin_dirs->push_back(home.Append(".mozilla/plugins"));
+
+ // 3) NS_SYSTEM_PLUGINS_DIR:
+ // This varies across different browsers and versions, so check 'em all.
+ plugin_dirs->push_back(FilePath("/usr/lib/browser-plugins"));
+ plugin_dirs->push_back(FilePath("/usr/lib/mozilla/plugins"));
+ plugin_dirs->push_back(FilePath("/usr/lib/firefox/plugins"));
+ plugin_dirs->push_back(FilePath("/usr/lib/xulrunner-addons/plugins"));
+
+#if defined(ARCH_CPU_64_BITS)
+ // On my Ubuntu system, /usr/lib64 is a symlink to /usr/lib.
+ // But a user reported on their Fedora system they are separate.
+ plugin_dirs->push_back(FilePath("/usr/lib64/browser-plugins"));
+ plugin_dirs->push_back(FilePath("/usr/lib64/mozilla/plugins"));
+ plugin_dirs->push_back(FilePath("/usr/lib64/firefox/plugins"));
+ plugin_dirs->push_back(FilePath("/usr/lib64/xulrunner-addons/plugins"));
+#endif
+}
+
+void PluginList::LoadPluginsFromDir(const FilePath& dir_path,
+ std::vector<WebPluginInfo>* plugins,
+ std::set<FilePath>* visited_plugins) {
+ // See ScanPluginsDirectory near
+ // http://mxr.mozilla.org/firefox/source/modules/plugin/base/src/nsPluginHostImpl.cpp#5052
+
+ // Construct and stat a list of all filenames under consideration, for
+ // later sorting by mtime.
+ FileTimeList files;
+ file_util::FileEnumerator enumerator(dir_path,
+ false, // not recursive
+ file_util::FileEnumerator::FILES);
+ for (FilePath path = enumerator.Next(); !path.value().empty();
+ path = enumerator.Next()) {
+ // Skip over Mozilla .xpt files.
+ if (path.MatchesExtension(FILE_PATH_LITERAL(".xpt")))
+ continue;
+
+ // Java doesn't like being loaded through a symlink, since it uses
+ // its path to find dependent data files.
+ // file_util::AbsolutePath calls through to realpath(), which resolves
+ // symlinks.
+ FilePath orig_path = path;
+ file_util::AbsolutePath(&path);
+ LOG_IF(INFO, PluginList::DebugPluginLoading())
+ << "Resolved " << orig_path.value() << " -> " << path.value();
+
+ if (visited_plugins->find(path) != visited_plugins->end()) {
+ LOG_IF(INFO, PluginList::DebugPluginLoading())
+ << "Skipping duplicate instance of " << path.value();
+ continue;
+ }
+ visited_plugins->insert(path);
+
+ if (IsBlacklistedPlugin(path)) {
+ LOG_IF(INFO, PluginList::DebugPluginLoading())
+ << "Skipping blacklisted plugin " << path.value();
+ continue;
+ }
+
+ // Flash stops working if the containing directory involves 'netscape'.
+ // No joke. So use the other path if it's better.
+ static const char kFlashPlayerFilename[] = "libflashplayer.so";
+ static const char kNetscapeInPath[] = "/netscape/";
+ if (path.BaseName().value() == kFlashPlayerFilename &&
+ path.value().find(kNetscapeInPath) != std::string::npos) {
+ if (orig_path.value().find(kNetscapeInPath) == std::string::npos) {
+ // Go back to the old path.
+ path = orig_path;
+ } else {
+ LOG(ERROR) << "Flash misbehaves when used from a directory containing "
+ << kNetscapeInPath << ", so skipping " << orig_path.value();
+ continue;
+ }
+ }
+
+ // Get mtime.
+ file_util::FileInfo info;
+ if (!file_util::GetFileInfo(path, &info))
+ continue;
+
+ files.push_back(std::make_pair(path, info.last_modified));
+ }
+
+ // Sort the file list by time (and filename).
+ std::sort(files.begin(), files.end(), CompareTime);
+
+ // Load the files in order.
+ for (FileTimeList::const_iterator i = files.begin(); i != files.end(); ++i) {
+ LoadPlugin(i->first, plugins);
+ }
+}
+
+
+bool PluginList::ShouldLoadPlugin(const WebPluginInfo& info,
+ std::vector<WebPluginInfo>* plugins) {
+ LOG_IF(INFO, PluginList::DebugPluginLoading())
+ << "Considering " << info.path.value() << " (" << info.name << ")";
+
+ if (IsUndesirablePlugin(info)) {
+ LOG_IF(INFO, PluginList::DebugPluginLoading())
+ << info.path.value() << " is undesirable.";
+
+ // See if we have a better version of this plugin.
+ for (size_t i = 0; i < plugins->size(); ++i) {
+ if (plugins->at(i).name == info.name &&
+ !IsUndesirablePlugin(plugins->at(i))) {
+ // Skip the current undesirable one so we can use the better one
+ // we just found.
+ LOG_IF(INFO, PluginList::DebugPluginLoading())
+ << "Skipping " << info.path.value() << ", preferring "
+ << plugins->at(i).path.value();
+ return false;
+ }
+ }
+ }
+
+ // TODO(evanm): prefer the newest version of flash, etc. here?
+
+ LOG_IF(INFO, PluginList::DebugPluginLoading())
+ << "Using " << info.path.value();
+
+ return true;
+}
+
+} // namespace NPAPI
diff --git a/webkit/glue/plugins/plugin_list_win.cc b/webkit/glue/plugins/plugin_list_win.cc
new file mode 100644
index 0000000..1c91916
--- /dev/null
+++ b/webkit/glue/plugins/plugin_list_win.cc
@@ -0,0 +1,404 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/plugin_list.h"
+
+#include <tchar.h>
+
+#include <set>
+
+#include "base/basictypes.h"
+#include "base/command_line.h"
+#include "base/file_util.h"
+#include "base/path_service.h"
+#include "base/registry.h"
+#include "base/scoped_ptr.h"
+#include "base/string_util.h"
+#include "webkit/glue/plugins/plugin_constants_win.h"
+#include "webkit/glue/plugins/plugin_lib.h"
+#include "webkit/glue/webkit_glue.h"
+
+namespace {
+
+const TCHAR kRegistryApps[] =
+ _T("Software\\Microsoft\\Windows\\CurrentVersion\\App Paths");
+const TCHAR kRegistryFirefox[] = _T("firefox.exe");
+const TCHAR kRegistryAcrobat[] = _T("Acrobat.exe");
+const TCHAR kRegistryAcrobatReader[] = _T("AcroRd32.exe");
+const TCHAR kRegistryWindowsMedia[] = _T("wmplayer.exe");
+const TCHAR kRegistryQuickTime[] = _T("QuickTimePlayer.exe");
+const TCHAR kRegistryPath[] = _T("Path");
+const TCHAR kRegistryFirefoxInstalled[] =
+ _T("SOFTWARE\\Mozilla\\Mozilla Firefox");
+const TCHAR kRegistryJava[] =
+ _T("Software\\JavaSoft\\Java Runtime Environment");
+const TCHAR kRegistryBrowserJavaVersion[] = _T("BrowserJavaVersion");
+const TCHAR kRegistryCurrentJavaVersion[] = _T("CurrentVersion");
+const TCHAR kRegistryJavaHome[] = _T("JavaHome");
+const TCHAR kJavaDeploy1[] = _T("npdeploytk.dll");
+const TCHAR kJavaDeploy2[] = _T("npdeployjava1.dll");
+
+// The application path where we expect to find plugins.
+void GetAppDirectory(std::set<FilePath>* plugin_dirs) {
+ FilePath app_path;
+ if (!webkit_glue::GetApplicationDirectory(&app_path))
+ return;
+
+ app_path = app_path.AppendASCII("plugins");
+ plugin_dirs->insert(app_path);
+}
+
+// The executable path where we expect to find plugins.
+void GetExeDirectory(std::set<FilePath>* plugin_dirs) {
+ FilePath exe_path;
+ if (!webkit_glue::GetExeDirectory(&exe_path))
+ return;
+
+ exe_path = exe_path.AppendASCII("plugins");
+ plugin_dirs->insert(exe_path);
+}
+
+// Gets the installed path for a registered app.
+bool GetInstalledPath(const TCHAR* app, FilePath* out) {
+ std::wstring reg_path(kRegistryApps);
+ reg_path.append(L"\\");
+ reg_path.append(app);
+
+ RegKey key(HKEY_LOCAL_MACHINE, reg_path.c_str());
+ std::wstring path;
+ if (key.ReadValue(kRegistryPath, &path)) {
+ *out = FilePath(path);
+ return true;
+ }
+
+ return false;
+}
+
+// Search the registry at the given path and detect plugin directories.
+void GetPluginsInRegistryDirectory(
+ HKEY root_key,
+ const std::wstring& registry_folder,
+ std::set<FilePath>* plugin_dirs) {
+ for (RegistryKeyIterator iter(root_key, registry_folder.c_str());
+ iter.Valid(); ++iter) {
+ // Use the registry to gather plugin across the file system.
+ std::wstring reg_path = registry_folder;
+ reg_path.append(L"\\");
+ reg_path.append(iter.Name());
+ RegKey key(root_key, reg_path.c_str());
+
+ std::wstring path;
+ if (key.ReadValue(kRegistryPath, &path))
+ plugin_dirs->insert(FilePath(path));
+ }
+}
+
+// Enumerate through the registry key to find all installed FireFox paths.
+// FireFox 3 beta and version 2 can coexist. See bug: 1025003
+void GetFirefoxInstalledPaths(std::vector<FilePath>* out) {
+ RegistryKeyIterator it(HKEY_LOCAL_MACHINE, kRegistryFirefoxInstalled);
+ for (; it.Valid(); ++it) {
+ std::wstring full_path = std::wstring(kRegistryFirefoxInstalled) + L"\\" +
+ it.Name() + L"\\Main";
+ RegKey key(HKEY_LOCAL_MACHINE, full_path.c_str(), KEY_READ);
+ std::wstring install_dir;
+ if (!key.ReadValue(L"Install Directory", &install_dir))
+ continue;
+ out->push_back(FilePath(install_dir));
+ }
+}
+
+// Get plugin directory locations from the Firefox install path. This is kind
+// of a kludge, but it helps us locate the flash player for users that
+// already have it for firefox. Not having to download yet-another-plugin
+// is a good thing.
+void GetFirefoxDirectory(std::set<FilePath>* plugin_dirs) {
+ std::vector<FilePath> paths;
+ GetFirefoxInstalledPaths(&paths);
+ for (unsigned int i = 0; i < paths.size(); ++i) {
+ plugin_dirs->insert(paths[i].Append(L"plugins"));
+ }
+
+ FilePath firefox_app_data_plugin_path;
+ if (PathService::Get(base::DIR_APP_DATA, &firefox_app_data_plugin_path)) {
+ firefox_app_data_plugin_path =
+ firefox_app_data_plugin_path.AppendASCII("Mozilla")
+ .AppendASCII("plugins");
+ plugin_dirs->insert(firefox_app_data_plugin_path);
+ }
+}
+
+// Hardcoded logic to detect Acrobat plugins locations.
+void GetAcrobatDirectory(std::set<FilePath>* plugin_dirs) {
+ FilePath path;
+ if (!GetInstalledPath(kRegistryAcrobatReader, &path) &&
+ !GetInstalledPath(kRegistryAcrobat, &path)) {
+ return;
+ }
+
+ plugin_dirs->insert(path.Append(L"Browser"));
+}
+
+// Hardcoded logic to detect QuickTime plugin location.
+void GetQuicktimeDirectory(std::set<FilePath>* plugin_dirs) {
+ FilePath path;
+ if (GetInstalledPath(kRegistryQuickTime, &path))
+ plugin_dirs->insert(path.Append(L"plugins"));
+}
+
+// Hardcoded logic to detect Windows Media Player plugin location.
+void GetWindowsMediaDirectory(std::set<FilePath>* plugin_dirs) {
+ FilePath path;
+ if (GetInstalledPath(kRegistryWindowsMedia, &path))
+ plugin_dirs->insert(path);
+
+ // If the Windows Media Player Firefox plugin is installed before Firefox,
+ // the plugin will get written under PFiles\Plugins on one the drives
+ // (usually, but not always, the last letter).
+ int size = GetLogicalDriveStrings(0, NULL);
+ if (size) {
+ scoped_array<wchar_t> strings(new wchar_t[size]);
+ if (GetLogicalDriveStrings(size, strings.get())) {
+ wchar_t* next_drive = strings.get();
+ while (*next_drive) {
+ if (GetDriveType(next_drive) == DRIVE_FIXED) {
+ FilePath pfiles(next_drive);
+ pfiles = pfiles.Append(L"PFiles\\Plugins");
+ if (file_util::PathExists(pfiles))
+ plugin_dirs->insert(pfiles);
+ }
+ next_drive = &next_drive[wcslen(next_drive) + 1];
+ }
+ }
+ }
+}
+
+// Hardcoded logic to detect Java plugin location.
+void GetJavaDirectory(std::set<FilePath>* plugin_dirs) {
+ // Load the new NPAPI Java plugin
+ // 1. Open the main JRE key under HKLM
+ RegKey java_key(HKEY_LOCAL_MACHINE, kRegistryJava, KEY_QUERY_VALUE);
+
+ // 2. Read the current Java version
+ std::wstring java_version;
+ if (!java_key.ReadValue(kRegistryBrowserJavaVersion, &java_version))
+ java_key.ReadValue(kRegistryCurrentJavaVersion, &java_version);
+
+ if (!java_version.empty()) {
+ java_key.OpenKey(java_version.c_str(), KEY_QUERY_VALUE);
+
+ // 3. Install path of the JRE binaries is specified in "JavaHome"
+ // value under the Java version key.
+ std::wstring java_plugin_directory;
+ if (java_key.ReadValue(kRegistryJavaHome, &java_plugin_directory)) {
+ // 4. The new plugin resides under the 'bin/new_plugin'
+ // subdirectory.
+ DCHECK(!java_plugin_directory.empty());
+ java_plugin_directory.append(L"\\bin\\new_plugin");
+
+ // 5. We don't know the exact name of the DLL but it's in the form
+ // NP*.dll so just invoke LoadPlugins on this path.
+ plugin_dirs->insert(FilePath(java_plugin_directory));
+ }
+ }
+}
+
+} // anonymous namespace
+
+namespace NPAPI {
+
+void PluginList::PlatformInit() {
+ const CommandLine& command_line = *CommandLine::ForCurrentProcess();
+ dont_load_new_wmp_ = command_line.HasSwitch(kUseOldWMPPluginSwitch);
+}
+
+void PluginList::GetPluginDirectories(std::vector<FilePath>* plugin_dirs) {
+ // We use a set for uniqueness, which we require, over order, which we do not.
+ std::set<FilePath> dirs;
+
+ // Load from the application-specific area
+ GetAppDirectory(&dirs);
+
+ // Load from the executable area
+ GetExeDirectory(&dirs);
+
+ // Load Java
+ GetJavaDirectory(&dirs);
+
+ // Load firefox plugins too. This is mainly to try to locate
+ // a pre-installed Flash player.
+ GetFirefoxDirectory(&dirs);
+
+ // Firefox hard-codes the paths of some popular plugins to ensure that
+ // the plugins are found. We are going to copy this as well.
+ GetAcrobatDirectory(&dirs);
+ GetQuicktimeDirectory(&dirs);
+ GetWindowsMediaDirectory(&dirs);
+
+ for (std::set<FilePath>::iterator i = dirs.begin(); i != dirs.end(); ++i)
+ plugin_dirs->push_back(*i);
+}
+
+void PluginList::LoadPluginsFromDir(const FilePath &path,
+ std::vector<WebPluginInfo>* plugins,
+ std::set<FilePath>* visited_plugins) {
+ WIN32_FIND_DATA find_file_data;
+ HANDLE find_handle;
+
+ std::wstring dir = path.value();
+ // FindFirstFile requires that you specify a wildcard for directories.
+ dir.append(L"\\NP*.DLL");
+
+ find_handle = FindFirstFile(dir.c_str(), &find_file_data);
+ if (find_handle == INVALID_HANDLE_VALUE)
+ return;
+
+ do {
+ if (!(find_file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
+ FilePath filename = path.Append(find_file_data.cFileName);
+ LoadPlugin(filename, plugins);
+ visited_plugins->insert(filename);
+ }
+ } while (FindNextFile(find_handle, &find_file_data) != 0);
+
+ DCHECK(GetLastError() == ERROR_NO_MORE_FILES);
+ FindClose(find_handle);
+}
+
+void PluginList::LoadPluginsFromRegistry(
+ std::vector<WebPluginInfo>* plugins,
+ std::set<FilePath>* visited_plugins) {
+ std::set<FilePath> plugin_dirs;
+
+ GetPluginsInRegistryDirectory(
+ HKEY_CURRENT_USER, kRegistryMozillaPlugins, &plugin_dirs);
+ GetPluginsInRegistryDirectory(
+ HKEY_LOCAL_MACHINE, kRegistryMozillaPlugins, &plugin_dirs);
+
+ for (std::set<FilePath>::iterator i = plugin_dirs.begin();
+ i != plugin_dirs.end(); ++i) {
+ LoadPlugin(*i, plugins);
+ visited_plugins->insert(*i);
+ }
+}
+
+// Returns true if the given plugins share at least one mime type. This is used
+// to differentiate newer versions of a plugin vs two plugins which happen to
+// have the same filename.
+bool HaveSharedMimeType(const WebPluginInfo& plugin1,
+ const WebPluginInfo& plugin2) {
+ for (size_t i = 0; i < plugin1.mime_types.size(); ++i) {
+ for (size_t j = 0; j < plugin2.mime_types.size(); ++j) {
+ if (plugin1.mime_types[i].mime_type == plugin2.mime_types[j].mime_type)
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// Compares Windows style version strings (i.e. 1,2,3,4). Returns true if b's
+// version is newer than a's, or false if it's equal or older.
+bool IsNewerVersion(const std::wstring& a, const std::wstring& b) {
+ std::vector<std::wstring> a_ver, b_ver;
+ SplitString(a, ',', &a_ver);
+ SplitString(b, ',', &b_ver);
+ if (a_ver.size() == 1 && b_ver.size() == 1) {
+ a_ver.clear();
+ b_ver.clear();
+ SplitString(a, '.', &a_ver);
+ SplitString(b, '.', &b_ver);
+ }
+ if (a_ver.size() != b_ver.size())
+ return false;
+ for (size_t i = 0; i < a_ver.size(); i++) {
+ int cur_a = StringToInt(a_ver[i]);
+ int cur_b = StringToInt(b_ver[i]);
+ if (cur_a > cur_b)
+ return false;
+ if (cur_a < cur_b)
+ return true;
+ }
+ return false;
+}
+
+bool PluginList::ShouldLoadPlugin(const WebPluginInfo& info,
+ std::vector<WebPluginInfo>* plugins) {
+ // Version check
+
+ for (size_t i = 0; i < plugins->size(); ++i) {
+ std::wstring plugin1 =
+ StringToLowerASCII((*plugins)[i].path.BaseName().ToWStringHack());
+ std::wstring plugin2 =
+ StringToLowerASCII(info.path.BaseName().ToWStringHack());
+ if ((plugin1 == plugin2 && HaveSharedMimeType((*plugins)[i], info)) ||
+ (plugin1 == kJavaDeploy1 && plugin2 == kJavaDeploy2) ||
+ (plugin1 == kJavaDeploy2 && plugin2 == kJavaDeploy1)) {
+ if (!IsNewerVersion((*plugins)[i].version, info.version))
+ return false; // We have loaded a plugin whose version is newer.
+
+ plugins->erase(plugins->begin() + i);
+ break;
+ }
+ }
+
+ // Troublemakers
+
+ std::wstring filename = StringToLowerASCII(info.path.BaseName().value());
+ // Depends on XPCOM.
+ if (filename == kMozillaActiveXPlugin)
+ return false;
+
+ // Disable the Yahoo Application State plugin as it crashes the plugin
+ // process on return from NPObjectStub::OnInvoke. Please refer to
+ // http://b/issue?id=1372124 for more information.
+ if (filename == kYahooApplicationStatePlugin)
+ return false;
+
+ // Disable the WangWang protocol handler plugin (npww.dll) as it crashes
+ // chrome during shutdown. Firefox also disables this plugin.
+ // Please refer to http://code.google.com/p/chromium/issues/detail?id=3953
+ // for more information.
+ if (filename == kWanWangProtocolHandlerPlugin)
+ return false;
+
+ // We only work with newer versions of the Java plugin which use NPAPI only
+ // and don't depend on XPCOM.
+ if (filename == kJavaPlugin1 || filename == kJavaPlugin2) {
+ std::vector<std::wstring> ver;
+ SplitString(info.version, '.', &ver);
+ int major, minor, update;
+ if (ver.size() == 4 &&
+ StringToInt(ver[0], &major) &&
+ StringToInt(ver[1], &minor) &&
+ StringToInt(ver[2], &update)) {
+ if (major == 6 && minor == 0 && update < 120)
+ return false; // Java SE6 Update 11 or older.
+ }
+ }
+
+ // Special WMP handling
+
+ // If both the new and old WMP plugins exist, only load the new one.
+ if (filename == kNewWMPPlugin) {
+ if (dont_load_new_wmp_)
+ return false;
+
+ for (size_t i = 0; i < plugins->size(); ++i) {
+ if ((*plugins)[i].path.BaseName().value() == kOldWMPPlugin) {
+ plugins->erase(plugins->begin() + i);
+ break;
+ }
+ }
+ } else if (filename == kOldWMPPlugin) {
+ for (size_t i = 0; i < plugins->size(); ++i) {
+ if ((*plugins)[i].path.BaseName().value() == kNewWMPPlugin)
+ return false;
+ }
+ }
+
+ return true;
+}
+
+} // namespace NPAPI
diff --git a/webkit/glue/plugins/plugin_stream.cc b/webkit/glue/plugins/plugin_stream.cc
new file mode 100644
index 0000000..728b180
--- /dev/null
+++ b/webkit/glue/plugins/plugin_stream.cc
@@ -0,0 +1,254 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// TODO : Support NP_ASFILEONLY mode
+// TODO : Support NP_SEEK mode
+// TODO : Support SEEKABLE=true in NewStream
+
+#include "webkit/glue/plugins/plugin_stream.h"
+
+#include "base/message_loop.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "net/base/mime_util.h"
+#include "webkit/glue/plugins/plugin_instance.h"
+#include "googleurl/src/gurl.h"
+
+namespace NPAPI {
+
+PluginStream::~PluginStream() {
+ // always close our temporary files.
+ CloseTempFile();
+ free(const_cast<char*>(stream_.url));
+}
+
+bool PluginStream::Open(const std::string &mime_type,
+ const std::string &headers,
+ uint32 length,
+ uint32 last_modified,
+ bool request_is_seekable) {
+ headers_ = headers;
+ NPP id = instance_->npp();
+ stream_.end = length;
+ stream_.lastmodified = last_modified;
+ stream_.pdata = 0;
+ stream_.ndata = id->ndata;
+ stream_.notifyData = notify_data_;
+ if (!headers_.empty())
+ stream_.headers = headers_.c_str();
+
+ bool seekable_stream = false;
+ if (request_is_seekable) {
+ std::string headers_lc = StringToLowerASCII(headers);
+ if (headers_lc.find("accept-ranges: bytes") != std::string::npos) {
+ seekable_stream = true;
+ }
+ }
+
+ const char *char_mime_type = "application/x-unknown-content-type";
+ std::string temp_mime_type;
+ if (!mime_type.empty()) {
+ char_mime_type = mime_type.c_str();
+ } else {
+ GURL gurl(stream_.url);
+
+#if defined(OS_WIN)
+ FilePath path(UTF8ToWide(gurl.path()));
+#elif defined(OS_POSIX)
+ FilePath path(gurl.path());
+#endif
+ if (net::GetMimeTypeFromFile(path, &temp_mime_type))
+ char_mime_type = temp_mime_type.c_str();
+ }
+
+ // Silverlight expects a valid mime type
+ DCHECK(strlen(char_mime_type) != 0);
+ NPError err = instance_->NPP_NewStream((NPMIMEType)char_mime_type,
+ &stream_, seekable_stream,
+ &requested_plugin_mode_);
+ if (err != NPERR_NO_ERROR) {
+ Notify(err);
+ return false;
+ }
+
+ opened_ = true;
+
+ if (requested_plugin_mode_ == NP_SEEK) {
+ seekable_stream_ = true;
+ }
+ // If the plugin has requested certain modes, then we need a copy
+ // of this file on disk. Open it and save it as we go.
+ if (requested_plugin_mode_ == NP_ASFILEONLY ||
+ requested_plugin_mode_ == NP_ASFILE) {
+ if (OpenTempFile() == false)
+ return false;
+ }
+
+ mime_type_ = char_mime_type;
+ return true;
+}
+
+int PluginStream::Write(const char *buffer, const int length,
+ int data_offset) {
+ // There may be two streams to write to - the plugin and the file.
+ // It is unclear what to do if we cannot write to both. The rules of
+ // this function are that the plugin must consume at least as many
+ // bytes as returned by the WriteReady call. So, we will attempt to
+ // write that many to both streams. If we can't write that many bytes
+ // to each stream, we'll return failure.
+
+ DCHECK(opened_);
+ if (WriteToFile(buffer, length) &&
+ WriteToPlugin(buffer, length, data_offset))
+ return length;
+
+ return -1;
+}
+
+bool PluginStream::WriteToFile(const char *buf, size_t length) {
+ // For ASFILEONLY, ASFILE, and SEEK modes, we need to write
+ // to the disk
+ if (TempFileIsValid() &&
+ (requested_plugin_mode_ == NP_ASFILE ||
+ requested_plugin_mode_ == NP_ASFILEONLY) ) {
+ size_t totalBytesWritten = 0, bytes;
+ do {
+ bytes = WriteBytes(buf, length);
+ totalBytesWritten += bytes;
+ } while (bytes > 0U && totalBytesWritten < length);
+
+ if (totalBytesWritten != length)
+ return false;
+ }
+
+ return true;
+}
+
+bool PluginStream::WriteToPlugin(const char *buf, const int length,
+ const int data_offset) {
+ // For NORMAL and ASFILE modes, we send the data to the plugin now
+ if (requested_plugin_mode_ != NP_NORMAL &&
+ requested_plugin_mode_ != NP_ASFILE &&
+ requested_plugin_mode_ != NP_SEEK)
+ return true;
+
+ int written = TryWriteToPlugin(buf, length, data_offset);
+ if (written == -1)
+ return false;
+
+ if (written < length) {
+ // Buffer the remaining data.
+ size_t remaining = length - written;
+ size_t previous_size = delivery_data_.size();
+ delivery_data_.resize(previous_size + remaining);
+ data_offset_ = data_offset;
+ memcpy(&delivery_data_[previous_size], buf + written, remaining);
+ MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
+ this, &PluginStream::OnDelayDelivery));
+ }
+
+ return true;
+}
+
+void PluginStream::OnDelayDelivery() {
+ // It is possible that the plugin stream may have closed before the task
+ // was hit.
+ if (!opened_) {
+ return;
+ }
+
+ int size = static_cast<int>(delivery_data_.size());
+ int written = TryWriteToPlugin(&delivery_data_.front(), size,
+ data_offset_);
+ if (written > 0) {
+ // Remove the data that we already wrote.
+ delivery_data_.erase(delivery_data_.begin(),
+ delivery_data_.begin() + written);
+ }
+}
+
+int PluginStream::TryWriteToPlugin(const char *buf, const int length,
+ const int data_offset) {
+ int byte_offset = 0;
+
+ if (data_offset > 0)
+ data_offset_ = data_offset;
+
+ while (byte_offset < length) {
+ int bytes_remaining = length - byte_offset;
+ int bytes_to_write = instance_->NPP_WriteReady(&stream_);
+ if (bytes_to_write > bytes_remaining)
+ bytes_to_write = bytes_remaining;
+
+ if (bytes_to_write == 0)
+ return byte_offset;
+
+ int bytes_consumed = instance_->NPP_Write(
+ &stream_, data_offset_, bytes_to_write,
+ const_cast<char*>(buf + byte_offset));
+ if (bytes_consumed < 0) {
+ // The plugin failed, which means that we need to close the stream.
+ Close(NPRES_NETWORK_ERR);
+ return -1;
+ }
+ if (bytes_consumed == 0) {
+ // The plugin couldn't take all of the data now.
+ return byte_offset;
+ }
+
+ // The plugin might report more that we gave it.
+ bytes_consumed = std::min(bytes_consumed, bytes_to_write);
+
+ data_offset_ += bytes_consumed;
+ byte_offset += bytes_consumed;
+ }
+
+ if (close_on_write_data_)
+ Close(NPRES_DONE);
+
+ return length;
+}
+
+bool PluginStream::Close(NPReason reason) {
+ if (opened_ == true) {
+ opened_ = false;
+
+ if (delivery_data_.size()) {
+ if (reason == NPRES_DONE) {
+ // There is more data to be streamed, don't destroy the stream now.
+ close_on_write_data_ = true;
+ return true;
+ } else {
+ // Stop any pending data from being streamed
+ delivery_data_.resize(0);
+ }
+ }
+
+ // If we have a temp file, be sure to close it.
+ // Also, allow the plugin to access it now.
+ if (TempFileIsValid()) {
+ CloseTempFile();
+ if (reason == NPRES_DONE)
+ WriteAsFile();
+ }
+
+ if (stream_.ndata != NULL) {
+ // Stream hasn't been closed yet.
+ NPError err = instance_->NPP_DestroyStream(&stream_, reason);
+ DCHECK(err == NPERR_NO_ERROR);
+ }
+ }
+
+ Notify(reason);
+ return true;
+}
+
+void PluginStream::Notify(NPReason reason) {
+ if (notify_needed_) {
+ instance_->NPP_URLNotify(stream_.url, reason, notify_data_);
+ notify_needed_ = false;
+ }
+}
+
+} // namespace NPAPI
diff --git a/webkit/glue/plugins/plugin_stream.h b/webkit/glue/plugins/plugin_stream.h
new file mode 100644
index 0000000..f15ea66
--- /dev/null
+++ b/webkit/glue/plugins/plugin_stream.h
@@ -0,0 +1,146 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGIN_PLUGIN_STREAM_H__
+#define WEBKIT_GLUE_PLUGIN_PLUGIN_STREAM_H__
+
+#include <string>
+#include <vector>
+
+#include "base/file_path.h"
+#include "base/ref_counted.h"
+#include "third_party/npapi/bindings/npapi.h"
+
+namespace webkit_glue {
+class WebPluginResourceClient;
+}
+
+namespace NPAPI {
+
+class PluginInstance;
+
+// Base class for a NPAPI stream. Tracks basic elements
+// of a stream for NPAPI notifications and stream position.
+class PluginStream : public base::RefCounted<PluginStream> {
+ public:
+ // Create a new PluginStream object. If needNotify is true, then the
+ // plugin will be notified when the stream has been fully sent.
+ PluginStream(PluginInstance *instance,
+ const char *url,
+ bool need_notify,
+ void *notify_data);
+
+ // In case of a redirect, this can be called to update the url. But it must
+ // be called before Open().
+ void UpdateUrl(const char* url);
+
+ // Opens the stream to the Plugin.
+ // If the mime-type is not specified, we'll try to find one based on the
+ // mime-types table and the extension (if any) in the URL.
+ // If the size of the stream is known, use length to set the size. If
+ // not known, set length to 0.
+ // The request_is_seekable parameter indicates whether byte range requests
+ // can be issued on the stream.
+ bool Open(const std::string &mime_type,
+ const std::string &headers,
+ uint32 length,
+ uint32 last_modified,
+ bool request_is_seekable);
+
+ // Writes to the stream.
+ int Write(const char *buf, const int len, int data_offset);
+
+ // Write the result as a file.
+ void WriteAsFile();
+
+ // Notify the plugin that a stream is complete.
+ void Notify(NPReason reason);
+
+ // Close the stream.
+ virtual bool Close(NPReason reason);
+
+ virtual webkit_glue::WebPluginResourceClient* AsResourceClient() {
+ return NULL;
+ }
+
+ // Cancels any HTTP requests initiated by the stream.
+ virtual void CancelRequest() {}
+
+ const NPStream* stream() const { return &stream_; }
+
+ // setter/getter for the seekable attribute on the stream.
+ bool seekable() const { return seekable_stream_; }
+
+ void set_seekable(bool seekable) { seekable_stream_ = seekable; }
+
+ // getters for reading the notification related attributes on the stream.
+ bool notify_needed() const { return notify_needed_; }
+
+ void* notify_data() const { return notify_data_; }
+
+ protected:
+ friend class base::RefCounted<PluginStream>;
+
+ virtual ~PluginStream();
+
+ PluginInstance* instance() { return instance_.get(); }
+ // Check if the stream is open.
+ bool open() { return opened_; }
+
+ private:
+
+ // Open a temporary file for this stream.
+ // If successful, will set temp_file_name_, temp_file_handle_, and
+ // return true.
+ bool OpenTempFile();
+
+ // Closes the temporary file if it is open.
+ void CloseTempFile();
+
+ // Sends the data to the file. Called From WriteToFile.
+ size_t WriteBytes(const char *buf, size_t length);
+
+ // Sends the data to the file if it's open.
+ bool WriteToFile(const char *buf, size_t length);
+
+ // Sends the data to the plugin. If it's not ready, handles buffering it
+ // and retrying later.
+ bool WriteToPlugin(const char *buf, const int length, const int data_offset);
+
+ // Send the data to the plugin, returning how many bytes it accepted, or -1
+ // if an error occurred.
+ int TryWriteToPlugin(const char *buf, const int length, const int data_offset);
+
+ // The callback which calls TryWriteToPlugin.
+ void OnDelayDelivery();
+
+ // Returns true if the temp file is valid and open for writing.
+ bool TempFileIsValid();
+
+ private:
+ NPStream stream_;
+ std::string headers_;
+ scoped_refptr<PluginInstance> instance_;
+ bool notify_needed_;
+ void * notify_data_;
+ bool close_on_write_data_;
+ uint16 requested_plugin_mode_;
+ bool opened_;
+#if defined(OS_WIN)
+ char temp_file_name_[MAX_PATH];
+ HANDLE temp_file_handle_;
+#elif defined(OS_POSIX)
+ FILE* temp_file_;
+ FilePath temp_file_path_;
+#endif
+ std::vector<char> delivery_data_;
+ int data_offset_;
+ bool seekable_stream_;
+ std::string mime_type_;
+ DISALLOW_COPY_AND_ASSIGN(PluginStream);
+};
+
+} // namespace NPAPI
+
+#endif // WEBKIT_GLUE_PLUGIN_PLUGIN_STREAM_H__
diff --git a/webkit/glue/plugins/plugin_stream_posix.cc b/webkit/glue/plugins/plugin_stream_posix.cc
new file mode 100644
index 0000000..d0e2291
--- /dev/null
+++ b/webkit/glue/plugins/plugin_stream_posix.cc
@@ -0,0 +1,74 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/plugin_stream.h"
+
+#include <string.h>
+
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "webkit/glue/plugins/plugin_instance.h"
+
+namespace NPAPI {
+
+PluginStream::PluginStream(
+ PluginInstance *instance,
+ const char *url,
+ bool need_notify,
+ void *notify_data)
+ : instance_(instance),
+ notify_needed_(need_notify),
+ notify_data_(notify_data),
+ close_on_write_data_(false),
+ requested_plugin_mode_(NP_NORMAL),
+ opened_(false),
+ temp_file_(NULL),
+ temp_file_path_(),
+ data_offset_(0),
+ seekable_stream_(false) {
+ memset(&stream_, 0, sizeof(stream_));
+ stream_.url = strdup(url);
+}
+
+void PluginStream::UpdateUrl(const char* url) {
+ DCHECK(!opened_);
+ free(const_cast<char*>(stream_.url));
+ stream_.url = strdup(url);
+}
+
+void PluginStream::WriteAsFile() {
+ if (requested_plugin_mode_ == NP_ASFILE ||
+ requested_plugin_mode_ == NP_ASFILEONLY)
+ instance_->NPP_StreamAsFile(&stream_, temp_file_path_.value().c_str());
+}
+
+size_t PluginStream::WriteBytes(const char *buf, size_t length) {
+ return fwrite(buf, sizeof(char), length, temp_file_);
+}
+
+bool PluginStream::OpenTempFile() {
+ DCHECK(temp_file_ == NULL);
+
+ if (file_util::CreateTemporaryFile(&temp_file_path_))
+ temp_file_ = file_util::OpenFile(temp_file_path_, "a");
+
+ if (!temp_file_) {
+ temp_file_path_ = FilePath("");
+ return false;
+ }
+
+ return true;
+}
+
+void PluginStream::CloseTempFile() {
+ file_util::CloseFile(temp_file_);
+ temp_file_ = NULL;
+}
+
+bool PluginStream::TempFileIsValid() {
+ return temp_file_ != NULL;
+}
+
+} // namespace NPAPI
diff --git a/webkit/glue/plugins/plugin_stream_url.cc b/webkit/glue/plugins/plugin_stream_url.cc
new file mode 100644
index 0000000..6694139
--- /dev/null
+++ b/webkit/glue/plugins/plugin_stream_url.cc
@@ -0,0 +1,106 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/plugin_stream_url.h"
+
+#include "webkit/glue/plugins/plugin_host.h"
+#include "webkit/glue/plugins/plugin_instance.h"
+#include "webkit/glue/plugins/webplugin.h"
+
+namespace NPAPI {
+
+PluginStreamUrl::PluginStreamUrl(
+ unsigned long resource_id,
+ const GURL &url,
+ PluginInstance *instance,
+ bool notify_needed,
+ void *notify_data)
+ : PluginStream(instance, url.spec().c_str(), notify_needed, notify_data),
+ url_(url),
+ id_(resource_id) {
+}
+
+PluginStreamUrl::~PluginStreamUrl() {
+ if (instance() && instance()->webplugin()) {
+ instance()->webplugin()->ResourceClientDeleted(AsResourceClient());
+ }
+}
+
+bool PluginStreamUrl::Close(NPReason reason) {
+ // Protect the stream against it being destroyed or the whole plugin instance
+ // being destroyed within the destroy stream handler.
+ scoped_refptr<PluginStream> protect(this);
+ CancelRequest();
+ bool result = PluginStream::Close(reason);
+ instance()->RemoveStream(this);
+ return result;
+}
+
+void PluginStreamUrl::WillSendRequest(const GURL& url) {
+ url_ = url;
+ UpdateUrl(url.spec().c_str());
+}
+
+void PluginStreamUrl::DidReceiveResponse(const std::string& mime_type,
+ const std::string& headers,
+ uint32 expected_length,
+ uint32 last_modified,
+ bool request_is_seekable) {
+ // Protect the stream against it being destroyed or the whole plugin instance
+ // being destroyed within the new stream handler.
+ scoped_refptr<PluginStream> protect(this);
+
+ bool opened = Open(mime_type,
+ headers,
+ expected_length,
+ last_modified,
+ request_is_seekable);
+ if (!opened) {
+ CancelRequest();
+ instance()->RemoveStream(this);
+ } else {
+ if (id_ > 0)
+ instance()->webplugin()->SetDeferResourceLoading(id_, false);
+ }
+}
+
+void PluginStreamUrl::DidReceiveData(const char* buffer, int length,
+ int data_offset) {
+ if (!open())
+ return;
+
+ // Protect the stream against it being destroyed or the whole plugin instance
+ // being destroyed within the write handlers
+ scoped_refptr<PluginStream> protect(this);
+
+ if (length > 0) {
+ // The PluginStreamUrl instance could get deleted if the plugin fails to
+ // accept data in NPP_Write.
+ if (Write(const_cast<char*>(buffer), length, data_offset) > 0) {
+ if (id_ > 0)
+ instance()->webplugin()->SetDeferResourceLoading(id_, false);
+ }
+ }
+}
+
+void PluginStreamUrl::DidFinishLoading() {
+ if (!seekable()) {
+ Close(NPRES_DONE);
+ }
+}
+
+void PluginStreamUrl::DidFail() {
+ Close(NPRES_NETWORK_ERR);
+}
+
+void PluginStreamUrl::CancelRequest() {
+ if (id_ > 0) {
+ if (instance()->webplugin()) {
+ instance()->webplugin()->CancelResource(id_);
+ }
+ id_ = 0;
+ }
+}
+
+} // namespace NPAPI
diff --git a/webkit/glue/plugins/plugin_stream_url.h b/webkit/glue/plugins/plugin_stream_url.h
new file mode 100644
index 0000000..01a6da9
--- /dev/null
+++ b/webkit/glue/plugins/plugin_stream_url.h
@@ -0,0 +1,69 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGIN_PLUGIN_STREAM_URL_H__
+#define WEBKIT_GLUE_PLUGIN_PLUGIN_STREAM_URL_H__
+
+
+#include "webkit/glue/plugins/plugin_stream.h"
+#include "webkit/glue/plugins/webplugin.h"
+#include "googleurl/src/gurl.h"
+
+namespace NPAPI {
+
+class PluginInstance;
+
+// A NPAPI Stream based on a URL.
+class PluginStreamUrl : public PluginStream,
+ public webkit_glue::WebPluginResourceClient {
+ public:
+ // Create a new stream for sending to the plugin by fetching
+ // a URL. If notifyNeeded is set, then the plugin will be notified
+ // when the stream has been fully sent to the plugin. Initialize
+ // must be called before the object is used.
+ PluginStreamUrl(unsigned long resource_id,
+ const GURL &url,
+ PluginInstance *instance,
+ bool notify_needed,
+ void *notify_data);
+ virtual ~PluginStreamUrl();
+
+ // Stop sending the stream to the client.
+ // Overrides the base Close so we can cancel our fetching the URL if
+ // it is still loading.
+ virtual bool Close(NPReason reason);
+
+ virtual webkit_glue::WebPluginResourceClient* AsResourceClient() {
+ return static_cast<webkit_glue::WebPluginResourceClient*>(this);
+ }
+
+ virtual void CancelRequest();
+
+ //
+ // WebPluginResourceClient methods
+ //
+ void WillSendRequest(const GURL& url);
+ void DidReceiveResponse(const std::string& mime_type,
+ const std::string& headers,
+ uint32 expected_length,
+ uint32 last_modified,
+ bool request_is_seekable);
+ void DidReceiveData(const char* buffer, int length, int data_offset);
+ void DidFinishLoading();
+ void DidFail();
+ bool IsMultiByteResponseExpected() {
+ return seekable();
+ }
+
+
+ private:
+ GURL url_;
+ unsigned long id_;
+
+ DISALLOW_COPY_AND_ASSIGN(PluginStreamUrl);
+};
+
+} // namespace NPAPI
+
+#endif // WEBKIT_GLUE_PLUGIN_PLUGIN_STREAM_URL_H__
diff --git a/webkit/glue/plugins/plugin_stream_win.cc b/webkit/glue/plugins/plugin_stream_win.cc
new file mode 100644
index 0000000..fffa6fe
--- /dev/null
+++ b/webkit/glue/plugins/plugin_stream_win.cc
@@ -0,0 +1,96 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/plugin_stream.h"
+
+#include "base/logging.h"
+#include "webkit/glue/plugins/plugin_instance.h"
+
+namespace NPAPI {
+
+PluginStream::PluginStream(
+ PluginInstance *instance,
+ const char *url,
+ bool need_notify,
+ void *notify_data)
+ : instance_(instance),
+ notify_needed_(need_notify),
+ notify_data_(notify_data),
+ close_on_write_data_(false),
+ opened_(false),
+ requested_plugin_mode_(NP_NORMAL),
+ temp_file_handle_(INVALID_HANDLE_VALUE),
+ seekable_stream_(false),
+ data_offset_(0) {
+ memset(&stream_, 0, sizeof(stream_));
+ stream_.url = _strdup(url);
+ temp_file_name_[0] = '\0';
+}
+
+void PluginStream::UpdateUrl(const char* url) {
+ DCHECK(!opened_);
+ free(const_cast<char*>(stream_.url));
+ stream_.url = _strdup(url);
+}
+
+void PluginStream::WriteAsFile() {
+ if (requested_plugin_mode_ == NP_ASFILE ||
+ requested_plugin_mode_ == NP_ASFILEONLY)
+ instance_->NPP_StreamAsFile(&stream_, temp_file_name_);
+}
+
+size_t PluginStream::WriteBytes(const char *buf, size_t length) {
+ DWORD bytes;
+
+ if (!WriteFile(temp_file_handle_, buf, length, &bytes, 0))
+ return 0U;
+
+ return static_cast<size_t>(bytes);
+}
+
+bool PluginStream::OpenTempFile() {
+ DCHECK(temp_file_handle_ == INVALID_HANDLE_VALUE);
+
+ // The reason for using all the Ascii versions of these filesystem
+ // calls is that the filename which we pass back to the plugin
+ // via NPAPI is an ascii filename. Otherwise, we'd use wide-chars.
+ //
+ // TODO:
+ // This is a bug in NPAPI itself, and it needs to be fixed.
+ // The case which will fail is if a user has a multibyte name,
+ // but has the system locale set to english. GetTempPathA will
+ // return junk in this case, causing us to be unable to open the
+ // file.
+
+ char temp_directory[MAX_PATH];
+ if (GetTempPathA(MAX_PATH, temp_directory) == 0)
+ return false;
+ if (GetTempFileNameA(temp_directory, "npstream", 0, temp_file_name_) == 0)
+ return false;
+ temp_file_handle_ = CreateFileA(temp_file_name_,
+ FILE_ALL_ACCESS,
+ FILE_SHARE_READ,
+ 0,
+ CREATE_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL,
+ 0);
+ if (temp_file_handle_ == INVALID_HANDLE_VALUE) {
+ temp_file_name_[0] = '\0';
+ return false;
+ }
+ return true;
+}
+
+void PluginStream::CloseTempFile() {
+ if (temp_file_handle_ != INVALID_HANDLE_VALUE) {
+ CloseHandle(temp_file_handle_);
+ temp_file_handle_ = INVALID_HANDLE_VALUE;
+ }
+}
+
+bool PluginStream::TempFileIsValid() {
+ return temp_file_handle_ != INVALID_HANDLE_VALUE;
+}
+
+} // namespace NPAPI
diff --git a/webkit/glue/plugins/plugin_string_stream.cc b/webkit/glue/plugins/plugin_string_stream.cc
new file mode 100644
index 0000000..f174267
--- /dev/null
+++ b/webkit/glue/plugins/plugin_string_stream.cc
@@ -0,0 +1,37 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/plugin_string_stream.h"
+
+#include "googleurl/src/gurl.h"
+
+namespace NPAPI {
+
+PluginStringStream::PluginStringStream(
+ PluginInstance* instance,
+ const GURL& url,
+ bool notify_needed,
+ void* notify_data)
+ : PluginStream(instance, url.spec().c_str(), notify_needed, notify_data) {
+}
+
+PluginStringStream::~PluginStringStream() {
+}
+
+void PluginStringStream::SendToPlugin(const std::string &data,
+ const std::string &mime_type) {
+ // Protect the stream against it being destroyed or the whole plugin instance
+ // being destroyed within the plugin stream callbacks.
+ scoped_refptr<PluginStringStream> protect(this);
+
+ int length = static_cast<int>(data.length());
+ if (Open(mime_type, std::string(), length, 0, false)) {
+ // TODO - check if it was not fully sent, and figure out a backup plan.
+ int written = Write(data.c_str(), length, 0);
+ NPReason reason = written == length ? NPRES_DONE : NPRES_NETWORK_ERR;
+ Close(reason);
+ }
+}
+
+}
diff --git a/webkit/glue/plugins/plugin_string_stream.h b/webkit/glue/plugins/plugin_string_stream.h
new file mode 100644
index 0000000..68db2bf
--- /dev/null
+++ b/webkit/glue/plugins/plugin_string_stream.h
@@ -0,0 +1,39 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGIN_PLUGIN_STRING_STREAM_H_
+#define WEBKIT_GLUE_PLUGIN_PLUGIN_STRING_STREAM_H_
+
+#include "webkit/glue/plugins/plugin_stream.h"
+
+class GURL;
+
+namespace NPAPI {
+
+class PluginInstance;
+
+// An NPAPI stream from a string.
+class PluginStringStream : public PluginStream {
+ public:
+ // Create a new stream for sending to the plugin.
+ // If notify_needed, will notify the plugin after the data has
+ // all been sent.
+ PluginStringStream(PluginInstance* instance,
+ const GURL& url,
+ bool notify_needed,
+ void* notify_data);
+
+ // Initiates the sending of data to the plugin.
+ void SendToPlugin(const std::string& data,
+ const std::string& mime_type);
+
+ private:
+ virtual ~PluginStringStream();
+
+ DISALLOW_COPY_AND_ASSIGN(PluginStringStream);
+};
+
+} // namespace NPAPI
+
+#endif // WEBKIT_GLUE_PLUGIN_PLUGIN_STRING_STREAM_H_
diff --git a/webkit/glue/plugins/plugin_stubs.cc b/webkit/glue/plugins/plugin_stubs.cc
new file mode 100644
index 0000000..f8210c3
--- /dev/null
+++ b/webkit/glue/plugins/plugin_stubs.cc
@@ -0,0 +1,30 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file stubs out some functions needed to make the linker happy
+// without linking in all the plugin code. It should be removed once
+// we have plugins working on all platforms.
+
+// TODO(port): remove this file.
+
+#include "base/logging.h"
+#include "webkit/glue/plugins/plugin_instance.h"
+#include "webkit/glue/plugins/plugin_stream.h"
+
+namespace NPAPI {
+
+PluginStream::~PluginStream() {
+ NOTIMPLEMENTED();
+}
+
+bool PluginStream::Close(NPReason reason) {
+ NOTIMPLEMENTED();
+ return false;
+}
+
+void PluginInstance::NPP_StreamAsFile(NPStream*, const char*) {
+ NOTIMPLEMENTED();
+}
+
+} // namespace NPAPI
diff --git a/webkit/glue/plugins/plugin_switches.cc b/webkit/glue/plugins/plugin_switches.cc
new file mode 100644
index 0000000..eb5c958
--- /dev/null
+++ b/webkit/glue/plugins/plugin_switches.cc
@@ -0,0 +1,15 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/plugin_switches.h"
+
+namespace switches {
+
+// Enables the testing interface for PPAPI.
+const char kEnablePepperTesting[] = "enable-pepper-testing";
+
+// Dumps extra logging about plugin loading to the log file.
+const char kDebugPluginLoading[] = "debug-plugin-loading";
+
+} // namespace switches
diff --git a/webkit/glue/plugins/plugin_switches.h b/webkit/glue/plugins/plugin_switches.h
new file mode 100644
index 0000000..772c047
--- /dev/null
+++ b/webkit/glue/plugins/plugin_switches.h
@@ -0,0 +1,15 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_PLUGIN_SWITCHES_H_
+#define WEBKIT_GLUE_PLUGINS_PLUGIN_SWITCHES_H_
+
+namespace switches {
+
+extern const char kDebugPluginLoading[];
+extern const char kEnablePepperTesting[];
+
+} // namespace switches
+
+#endif // WEBKIT_GLUE_PLUGINS_PLUGIN_SWITCHES_H_
diff --git a/webkit/glue/plugins/plugin_web_event_converter_mac.h b/webkit/glue/plugins/plugin_web_event_converter_mac.h
new file mode 100644
index 0000000..02f113a
--- /dev/null
+++ b/webkit/glue/plugins/plugin_web_event_converter_mac.h
@@ -0,0 +1,66 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGIN_PLUGIN_WEB_EVENT_CONVERTER_MAC_H_
+#define WEBKIT_GLUE_PLUGIN_PLUGIN_WEB_EVENT_CONVERTER_MAC_H_
+
+#include "third_party/npapi/bindings/npapi.h"
+
+namespace WebKit {
+class WebInputEvent;
+class WebKeyboardEvent;
+class WebMouseEvent;
+class WebMouseWheelEvent;
+}
+
+// Utility class to translating WebInputEvent structs to equivalent structures
+// suitable for sending to Mac plugins (via NPP_HandleEvent).
+class PluginWebEventConverter {
+ public:
+ PluginWebEventConverter() {}
+ virtual ~PluginWebEventConverter() {}
+
+ // Initializes a converter for the given web event. Returns false if the event
+ // could not be converted.
+ virtual bool InitWithEvent(const WebKit::WebInputEvent& web_event);
+
+ // Sets a zoom level to apply to mouse coordinates. Must be called after
+ // InitWithEvent.
+ // TODO(stuartmorgan): Re-evaluate whether this is necessary when
+ // http://crbug.com/9996 is fixed.
+ virtual void SetZoomLevel(float zoom) = 0;
+
+ // Returns a pointer to a plugin event--suitable for passing to
+ // NPP_HandleEvent--corresponding to the the web event this converter was
+ // created with. The pointer is valid only as long as this object is.
+ // Returns NULL iff InitWithEvent returned false.
+ virtual void* plugin_event() = 0;
+
+protected:
+ // To be overridden by subclasses to store a converted plugin representation
+ // of the given web event, suitable for returning from plugin_event.
+ // Returns true if the event was successfully converted.
+ virtual bool ConvertKeyboardEvent(
+ const WebKit::WebKeyboardEvent& web_event) = 0;
+ virtual bool ConvertMouseEvent(const WebKit::WebMouseEvent& web_event) = 0;
+ virtual bool ConvertMouseWheelEvent(
+ const WebKit::WebMouseWheelEvent& web_event) = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PluginWebEventConverter);
+};
+
+// Factory for generating PluginWebEventConverter objects by event model.
+class PluginWebEventConverterFactory {
+ public:
+ // Returns a new PluginWebEventConverter corresponding to the given plugin
+ // event model.
+ static PluginWebEventConverter*
+ CreateConverterForModel(NPEventModel event_model);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PluginWebEventConverterFactory);
+};
+
+#endif // WEBKIT_GLUE_PLUGIN_PLUGIN_WEB_EVENT_CONVERTER_MAC_H_
diff --git a/webkit/glue/plugins/plugin_web_event_converter_mac.mm b/webkit/glue/plugins/plugin_web_event_converter_mac.mm
new file mode 100644
index 0000000..d8b5c05
--- /dev/null
+++ b/webkit/glue/plugins/plugin_web_event_converter_mac.mm
@@ -0,0 +1,396 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import <Cocoa/Cocoa.h>
+
+#include "base/logging.h"
+#include "webkit/glue/plugins/plugin_web_event_converter_mac.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebInputEvent.h"
+
+using WebKit::WebInputEvent;
+using WebKit::WebKeyboardEvent;
+using WebKit::WebMouseEvent;
+using WebKit::WebMouseWheelEvent;
+
+namespace {
+
+// Returns true if the caps lock flag should be set for the given event.
+// TODO: Ideally the event itself would know about the caps lock key; see
+// <http://crbug.com/38226>. This function is only a temporary workaround that
+// guesses based on live state.
+bool CapsLockIsActive(const WebInputEvent& event) {
+ NSUInteger current_flags = [[NSApp currentEvent] modifierFlags];
+ bool caps_lock_on = (current_flags & NSAlphaShiftKeyMask) ? true : false;
+ // If this a caps lock keypress, then the event stream state can be wrong.
+ // Luckily, the weird event stream for caps lock makes it easy to tell whether
+ // caps lock is being turned on or off.
+ if (event.type == WebInputEvent::KeyDown ||
+ event.type == WebInputEvent::KeyUp) {
+ const WebKeyboardEvent* key_event =
+ static_cast<const WebKeyboardEvent*>(&event);
+ if (key_event->nativeKeyCode == 57)
+ caps_lock_on = (event.type == WebInputEvent::KeyDown);
+ }
+ return caps_lock_on;
+}
+
+} // namespace
+
+#pragma mark -
+
+#ifndef NP_NO_CARBON
+
+// Converter implementation for the Carbon event model.
+class CarbonPluginWebEventConverter : public PluginWebEventConverter {
+ public:
+ CarbonPluginWebEventConverter() {}
+ virtual ~CarbonPluginWebEventConverter() {}
+
+ virtual bool InitWithEvent(const WebInputEvent& web_event);
+
+ virtual void SetZoomLevel(float zooom);
+
+ virtual void* plugin_event() { return &carbon_event_; }
+
+ protected:
+ virtual bool ConvertKeyboardEvent(const WebKeyboardEvent& key_event);
+ virtual bool ConvertMouseEvent(const WebMouseEvent& mouse_event);
+ virtual bool ConvertMouseWheelEvent(const WebMouseWheelEvent& wheel_event);
+
+ private:
+ // Returns the Carbon translation of web_event's modifiers.
+ static EventModifiers CarbonModifiers(const WebInputEvent& web_event);
+
+ NPEvent carbon_event_;
+
+ DISALLOW_COPY_AND_ASSIGN(CarbonPluginWebEventConverter);
+};
+
+bool CarbonPluginWebEventConverter::InitWithEvent(
+ const WebInputEvent& web_event) {
+ memset(&carbon_event_, 0, sizeof(carbon_event_));
+ // Set the fields common to all event types.
+ carbon_event_.when = TickCount();
+ carbon_event_.modifiers |= CarbonModifiers(web_event);
+
+ return PluginWebEventConverter::InitWithEvent(web_event);
+}
+
+void CarbonPluginWebEventConverter::SetZoomLevel(float zoom) {
+ // Nothing to do here; because the event is built using the WebMouseEvent's
+ // global coordinates, and the dummy window is in the right place, zoom has
+ // no effect.
+}
+
+bool CarbonPluginWebEventConverter::ConvertKeyboardEvent(
+ const WebKeyboardEvent& key_event) {
+ // TODO: Figure out how to handle Unicode input to plugins, if that's
+ // even possible in the NPAPI Carbon event model.
+ carbon_event_.message = (key_event.nativeKeyCode << 8) & keyCodeMask;
+ carbon_event_.message |= key_event.text[0] & charCodeMask;
+ carbon_event_.modifiers |= btnState;
+
+ switch (key_event.type) {
+ case WebInputEvent::KeyDown:
+ if (key_event.modifiers & WebInputEvent::IsAutoRepeat)
+ carbon_event_.what = autoKey;
+ else
+ carbon_event_.what = keyDown;
+ return true;
+ case WebInputEvent::KeyUp:
+ carbon_event_.what = keyUp;
+ return true;
+ case WebInputEvent::RawKeyDown:
+ case WebInputEvent::Char:
+ // May be used eventually for IME, but currently not needed.
+ return false;
+ default:
+ NOTREACHED();
+ return false;
+ }
+}
+
+bool CarbonPluginWebEventConverter::ConvertMouseEvent(
+ const WebMouseEvent& mouse_event) {
+ carbon_event_.where.h = mouse_event.globalX;
+ carbon_event_.where.v = mouse_event.globalY;
+
+ // Default to "button up"; override this for mouse down events below.
+ carbon_event_.modifiers |= btnState;
+
+ switch (mouse_event.button) {
+ case WebMouseEvent::ButtonLeft:
+ break;
+ case WebMouseEvent::ButtonMiddle:
+ carbon_event_.modifiers |= cmdKey;
+ break;
+ case WebMouseEvent::ButtonRight:
+ carbon_event_.modifiers |= controlKey;
+ break;
+ default:
+ NOTIMPLEMENTED();
+ }
+ switch (mouse_event.type) {
+ case WebInputEvent::MouseMove:
+ carbon_event_.what = nullEvent;
+ return true;
+ case WebInputEvent::MouseLeave:
+ case WebInputEvent::MouseEnter:
+ carbon_event_.what = NPEventType_AdjustCursorEvent;
+ return true;
+ case WebInputEvent::MouseDown:
+ carbon_event_.modifiers &= ~btnState;
+ carbon_event_.what = mouseDown;
+ return true;
+ case WebInputEvent::MouseUp:
+ carbon_event_.what = mouseUp;
+ return true;
+ default:
+ NOTREACHED();
+ return false;
+ }
+}
+
+bool CarbonPluginWebEventConverter::ConvertMouseWheelEvent(
+ const WebMouseWheelEvent& wheel_event) {
+ return false; // The Carbon NPAPI event model has no "mouse wheel" concept.
+}
+
+EventModifiers CarbonPluginWebEventConverter::CarbonModifiers(
+ const WebInputEvent& web_event) {
+ NSInteger modifiers = 0;
+ if (web_event.modifiers & WebInputEvent::ControlKey)
+ modifiers |= controlKey;
+ if (web_event.modifiers & WebInputEvent::ShiftKey)
+ modifiers |= shiftKey;
+ if (web_event.modifiers & WebInputEvent::AltKey)
+ modifiers |= optionKey;
+ if (web_event.modifiers & WebInputEvent::MetaKey)
+ modifiers |= cmdKey;
+ if (CapsLockIsActive(web_event))
+ modifiers |= alphaLock;
+ return modifiers;
+}
+
+#endif // !NP_NO_CARBON
+
+#pragma mark -
+
+// Converter implementation for the Cocoa event model.
+class CocoaPluginWebEventConverter : public PluginWebEventConverter {
+public:
+ CocoaPluginWebEventConverter() {}
+ virtual ~CocoaPluginWebEventConverter() {}
+
+ virtual bool InitWithEvent(const WebInputEvent& web_event);
+
+ virtual void SetZoomLevel(float zoom);
+
+ virtual void* plugin_event() { return &cocoa_event_; }
+
+protected:
+ virtual bool ConvertKeyboardEvent(const WebKeyboardEvent& key_event);
+ virtual bool ConvertMouseEvent(const WebMouseEvent& mouse_event);
+ virtual bool ConvertMouseWheelEvent(const WebMouseWheelEvent& wheel_event);
+
+private:
+ // Returns the Cocoa translation of web_event's modifiers.
+ static NSUInteger CocoaModifiers(const WebInputEvent& web_event);
+
+ // Returns true if the given key is a modifier key.
+ static bool KeyIsModifier(int native_key_code);
+
+ NPCocoaEvent cocoa_event_;
+
+ DISALLOW_COPY_AND_ASSIGN(CocoaPluginWebEventConverter);
+};
+
+bool CocoaPluginWebEventConverter::InitWithEvent(
+ const WebInputEvent& web_event) {
+ memset(&cocoa_event_, 0, sizeof(cocoa_event_));
+ return PluginWebEventConverter::InitWithEvent(web_event);
+}
+
+void CocoaPluginWebEventConverter::SetZoomLevel(float zoom) {
+ // Make sure we are dealing with a mouse event.
+ if (!(cocoa_event_.type != NPCocoaEventMouseDown ||
+ cocoa_event_.type != NPCocoaEventMouseUp ||
+ cocoa_event_.type != NPCocoaEventMouseMoved ||
+ cocoa_event_.type != NPCocoaEventMouseEntered ||
+ cocoa_event_.type != NPCocoaEventMouseExited ||
+ cocoa_event_.type != NPCocoaEventMouseDragged ||
+ cocoa_event_.type != NPCocoaEventScrollWheel)) {
+ NOTREACHED();
+ return;
+ }
+ cocoa_event_.data.mouse.pluginX =
+ round(cocoa_event_.data.mouse.pluginX * zoom);
+ cocoa_event_.data.mouse.pluginY =
+ round(cocoa_event_.data.mouse.pluginY * zoom);
+}
+
+bool CocoaPluginWebEventConverter::ConvertKeyboardEvent(
+ const WebKeyboardEvent& key_event) {
+ cocoa_event_.data.key.keyCode = key_event.nativeKeyCode;
+
+ cocoa_event_.data.key.modifierFlags |= CocoaModifiers(key_event);
+
+ // Modifier keys have their own event type, and don't get character or
+ // repeat data.
+ if (KeyIsModifier(key_event.nativeKeyCode)) {
+ cocoa_event_.type = NPCocoaEventFlagsChanged;
+ return true;
+ }
+
+ cocoa_event_.data.key.characters = reinterpret_cast<NPNSString*>(
+ [NSString stringWithFormat:@"%S", key_event.text]);
+ cocoa_event_.data.key.charactersIgnoringModifiers =
+ reinterpret_cast<NPNSString*>(
+ [NSString stringWithFormat:@"%S", key_event.unmodifiedText]);
+
+ if (key_event.modifiers & WebInputEvent::IsAutoRepeat)
+ cocoa_event_.data.key.isARepeat = true;
+
+ switch (key_event.type) {
+ case WebInputEvent::KeyDown:
+ cocoa_event_.type = NPCocoaEventKeyDown;
+ return true;
+ case WebInputEvent::KeyUp:
+ cocoa_event_.type = NPCocoaEventKeyUp;
+ return true;
+ case WebInputEvent::RawKeyDown:
+ case WebInputEvent::Char:
+ // May be used eventually for IME, but currently not needed.
+ return false;
+ default:
+ NOTREACHED();
+ return false;
+ }
+}
+
+bool CocoaPluginWebEventConverter::ConvertMouseEvent(
+ const WebMouseEvent& mouse_event) {
+ cocoa_event_.data.mouse.pluginX = mouse_event.x;
+ cocoa_event_.data.mouse.pluginY = mouse_event.y;
+ cocoa_event_.data.mouse.modifierFlags |= CocoaModifiers(mouse_event);
+ cocoa_event_.data.mouse.clickCount = mouse_event.clickCount;
+ switch (mouse_event.button) {
+ case WebMouseEvent::ButtonLeft:
+ cocoa_event_.data.mouse.buttonNumber = 0;
+ break;
+ case WebMouseEvent::ButtonMiddle:
+ cocoa_event_.data.mouse.buttonNumber = 2;
+ break;
+ case WebMouseEvent::ButtonRight:
+ cocoa_event_.data.mouse.buttonNumber = 1;
+ break;
+ default:
+ cocoa_event_.data.mouse.buttonNumber = mouse_event.button;
+ break;
+ }
+ switch (mouse_event.type) {
+ case WebInputEvent::MouseDown:
+ cocoa_event_.type = NPCocoaEventMouseDown;
+ return true;
+ case WebInputEvent::MouseUp:
+ cocoa_event_.type = NPCocoaEventMouseUp;
+ return true;
+ case WebInputEvent::MouseMove: {
+ bool mouse_is_down =
+ (mouse_event.modifiers & WebInputEvent::LeftButtonDown) ||
+ (mouse_event.modifiers & WebInputEvent::RightButtonDown) ||
+ (mouse_event.modifiers & WebInputEvent::MiddleButtonDown);
+ cocoa_event_.type = mouse_is_down ? NPCocoaEventMouseDragged
+ : NPCocoaEventMouseMoved;
+ return true;
+ }
+ case WebInputEvent::MouseEnter:
+ cocoa_event_.type = NPCocoaEventMouseEntered;
+ return true;
+ case WebInputEvent::MouseLeave:
+ cocoa_event_.type = NPCocoaEventMouseExited;
+ return true;
+ default:
+ NOTREACHED();
+ return false;
+ }
+}
+
+bool CocoaPluginWebEventConverter::ConvertMouseWheelEvent(
+ const WebMouseWheelEvent& wheel_event) {
+ cocoa_event_.type = NPCocoaEventScrollWheel;
+ cocoa_event_.data.mouse.pluginX = wheel_event.x;
+ cocoa_event_.data.mouse.pluginY = wheel_event.y;
+ cocoa_event_.data.mouse.modifierFlags |= CocoaModifiers(wheel_event);
+ cocoa_event_.data.mouse.deltaX = wheel_event.deltaX;
+ cocoa_event_.data.mouse.deltaY = wheel_event.deltaY;
+ return true;
+}
+
+NSUInteger CocoaPluginWebEventConverter::CocoaModifiers(
+ const WebInputEvent& web_event) {
+ NSInteger modifiers = 0;
+ if (web_event.modifiers & WebInputEvent::ControlKey)
+ modifiers |= NSControlKeyMask;
+ if (web_event.modifiers & WebInputEvent::ShiftKey)
+ modifiers |= NSShiftKeyMask;
+ if (web_event.modifiers & WebInputEvent::AltKey)
+ modifiers |= NSAlternateKeyMask;
+ if (web_event.modifiers & WebInputEvent::MetaKey)
+ modifiers |= NSCommandKeyMask;
+ if (CapsLockIsActive(web_event))
+ modifiers |= NSAlphaShiftKeyMask;
+ return modifiers;
+}
+
+bool CocoaPluginWebEventConverter::KeyIsModifier(int native_key_code) {
+ switch (native_key_code) {
+ case 55: // Left command
+ case 54: // Right command
+ case 58: // Left option
+ case 61: // Right option
+ case 59: // Left control
+ case 62: // Right control
+ case 56: // Left shift
+ case 60: // Right shift
+ case 57: // Caps lock
+ return true;
+ default:
+ return false;
+ }
+}
+
+#pragma mark -
+
+bool PluginWebEventConverter::InitWithEvent(const WebInputEvent& web_event) {
+ if (web_event.type == WebInputEvent::MouseWheel) {
+ return ConvertMouseWheelEvent(
+ *static_cast<const WebMouseWheelEvent*>(&web_event));
+ } else if (WebInputEvent::isMouseEventType(web_event.type)) {
+ return ConvertMouseEvent(*static_cast<const WebMouseEvent*>(&web_event));
+ } else if (WebInputEvent::isKeyboardEventType(web_event.type)) {
+ return ConvertKeyboardEvent(
+ *static_cast<const WebKeyboardEvent*>(&web_event));
+ }
+ DLOG(WARNING) << "Unknown event type " << web_event.type;
+ return false;
+}
+
+#pragma mark -
+
+PluginWebEventConverter*
+ PluginWebEventConverterFactory::CreateConverterForModel(
+ NPEventModel event_model) {
+ switch (event_model) {
+ case NPEventModelCocoa:
+ return new CocoaPluginWebEventConverter();
+#ifndef NP_NO_CARBON
+ case NPEventModelCarbon:
+ return new CarbonPluginWebEventConverter();
+#endif
+ default:
+ NOTIMPLEMENTED();
+ return NULL;
+ }
+}
diff --git a/webkit/glue/plugins/ppb_private.h b/webkit/glue/plugins/ppb_private.h
new file mode 100644
index 0000000..a0956f0
--- /dev/null
+++ b/webkit/glue/plugins/ppb_private.h
@@ -0,0 +1,21 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_PPB_PRIVATE_H_
+#define WEBKIT_GLUE_PLUGINS_PPB_PRIVATE_H_
+
+#include "third_party/ppapi/c/pp_var.h"
+
+#define PPB_PRIVATE_INTERFACE "PPB_Private;1"
+
+typedef enum _pp_ResourceString {
+ PP_RESOURCESTRING_PDFGETPASSWORD = 0,
+} PP_ResourceString;
+
+typedef struct _ppb_Private {
+ // Returns a localized string.
+ PP_Var (*GetLocalizedString)(PP_ResourceString string_id);
+} PPB_Private;
+
+#endif // WEBKIT_GLUE_PLUGINS_PPB_PRIVATE_H_
diff --git a/webkit/glue/plugins/quickdraw_drawing_manager_mac.cc b/webkit/glue/plugins/quickdraw_drawing_manager_mac.cc
new file mode 100644
index 0000000..424cc1e
--- /dev/null
+++ b/webkit/glue/plugins/quickdraw_drawing_manager_mac.cc
@@ -0,0 +1,154 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NP_NO_QUICKDRAW
+
+#include "webkit/glue/plugins/quickdraw_drawing_manager_mac.h"
+
+#include "webkit/glue/plugins/coregraphics_private_symbols_mac.h"
+
+// Turn off GCC warnings about deprecated functions (since QuickDraw is a
+// deprecated API). According to the GCC documentation, this can only be done
+// per file, not pushed and popped like some options can be.
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+
+QuickDrawDrawingManager::QuickDrawDrawingManager()
+ : plugin_window_(NULL), target_context_(NULL), fast_path_enabled_(false),
+ current_port_(NULL), target_world_(NULL), plugin_world_(NULL) {}
+
+QuickDrawDrawingManager::~QuickDrawDrawingManager() {
+ DestroyGWorlds();
+}
+
+void QuickDrawDrawingManager::SetFastPathEnabled(bool enabled) {
+ if (fast_path_enabled_ == enabled)
+ return;
+
+ fast_path_enabled_ = enabled;
+ if (enabled) {
+ if (!target_world_)
+ UpdateGWorlds();
+ // Copy our last window snapshot into our new source, since the plugin
+ // may not repaint everything.
+ CopyGWorldBits(target_world_, plugin_world_, plugin_size_);
+ current_port_ = plugin_world_;
+ } else {
+ current_port_ = GetWindowPort(plugin_window_);
+ }
+}
+
+void QuickDrawDrawingManager::SetTargetContext(CGContextRef context,
+ const gfx::Size& plugin_size) {
+ target_context_ = context;
+ if (plugin_size != plugin_size_) {
+ plugin_size_ = plugin_size;
+ // Pitch the old GWorlds, since they are the wrong size now.
+ DestroyGWorlds();
+ if (fast_path_enabled_)
+ UpdateGWorlds();
+ }
+}
+
+void QuickDrawDrawingManager::SetPluginWindow(WindowRef window) {
+ plugin_window_ = window;
+ if (!fast_path_enabled_)
+ current_port_ = GetWindowPort(window);
+}
+
+void QuickDrawDrawingManager::UpdateContext() {
+ if (fast_path_enabled_)
+ CopyGWorldBits(plugin_world_, target_world_, plugin_size_);
+ else
+ ScrapeWindow(plugin_window_, target_context_, plugin_size_);
+}
+
+bool QuickDrawDrawingManager::IsFastPathEnabled() {
+ return fast_path_enabled_;
+}
+
+void QuickDrawDrawingManager::MakePortCurrent() {
+ if (fast_path_enabled_)
+ SetGWorld(current_port_, NULL);
+ else
+ SetPort(current_port_);
+}
+
+void QuickDrawDrawingManager::DestroyGWorlds() {
+ if (plugin_world_) {
+ DisposeGWorld(plugin_world_);
+ plugin_world_ = NULL;
+ }
+ if (target_world_) {
+ DisposeGWorld(target_world_);
+ target_world_ = NULL;
+ }
+}
+
+void QuickDrawDrawingManager::UpdateGWorlds() {
+ DestroyGWorlds();
+ if (!target_context_)
+ return;
+
+ Rect window_bounds = {
+ 0, 0, plugin_size_.height(), plugin_size_.width()
+ };
+ // Create a GWorld pointing at the same bits as our target context.
+ if (target_context_) {
+ NewGWorldFromPtr(
+ &target_world_, k32BGRAPixelFormat, &window_bounds, NULL, NULL, 0,
+ static_cast<Ptr>(CGBitmapContextGetData(target_context_)),
+ static_cast<SInt32>(CGBitmapContextGetBytesPerRow(target_context_)));
+ }
+ // Create a GWorld for the plugin to paint into whenever it wants; since
+ // QuickDraw plugins don't draw at known times, they can't be allowed to draw
+ // directly into the shared memory.
+ NewGWorld(&plugin_world_, k32ARGBPixelFormat, &window_bounds,
+ NULL, NULL, kNativeEndianPixMap);
+ if (fast_path_enabled_)
+ current_port_ = plugin_world_;
+}
+
+void QuickDrawDrawingManager::ScrapeWindow(WindowRef window,
+ CGContextRef target_context,
+ const gfx::Size& plugin_size) {
+ if (!target_context)
+ return;
+
+ CGRect window_bounds = CGRectMake(0, 0,
+ plugin_size.width(),
+ plugin_size.height());
+ CGWindowID window_id = HIWindowGetCGWindowID(window);
+ CGContextSaveGState(target_context);
+ CGContextTranslateCTM(target_context, 0, plugin_size.height());
+ CGContextScaleCTM(target_context, 1.0, -1.0);
+ CGContextCopyWindowCaptureContentsToRect(target_context, window_bounds,
+ _CGSDefaultConnection(),
+ window_id, 0);
+ CGContextRestoreGState(target_context);
+}
+
+void QuickDrawDrawingManager::CopyGWorldBits(GWorldPtr source, GWorldPtr dest,
+ const gfx::Size& plugin_size) {
+ if (!(source && dest))
+ return;
+
+ Rect window_bounds = { 0, 0, plugin_size.height(), plugin_size.width() };
+ PixMapHandle source_pixmap = GetGWorldPixMap(source);
+ if (LockPixels(source_pixmap)) {
+ PixMapHandle dest_pixmap = GetGWorldPixMap(dest);
+ if (LockPixels(dest_pixmap)) {
+ SetGWorld(dest, NULL);
+ // Set foreground and background colors to avoid "colorizing" the image.
+ ForeColor(blackColor);
+ BackColor(whiteColor);
+ CopyBits(reinterpret_cast<BitMap*>(*source_pixmap),
+ reinterpret_cast<BitMap*>(*dest_pixmap),
+ &window_bounds, &window_bounds, srcCopy, NULL);
+ UnlockPixels(dest_pixmap);
+ }
+ UnlockPixels(source_pixmap);
+ }
+}
+
+#endif // !NP_NO_QUICKDRAW
diff --git a/webkit/glue/plugins/quickdraw_drawing_manager_mac.h b/webkit/glue/plugins/quickdraw_drawing_manager_mac.h
new file mode 100644
index 0000000..8163f92
--- /dev/null
+++ b/webkit/glue/plugins/quickdraw_drawing_manager_mac.h
@@ -0,0 +1,83 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_QUICKDRAW_DRAWING_MANAGER_MAC_H_
+#define WEBKIT_GLUE_QUICKDRAW_DRAWING_MANAGER_MAC_H_
+
+#ifndef NP_NO_QUICKDRAW
+
+#import <Carbon/Carbon.h>
+
+#include "gfx/rect.h"
+
+// Plugin helper class encapsulating the details of capturing what a QuickDraw
+// drawing model plugin draws, then drawing it into a CGContext.
+class QuickDrawDrawingManager {
+ public:
+ QuickDrawDrawingManager();
+ ~QuickDrawDrawingManager();
+
+ // Sets the mode used for plugin drawing. If enabled is true the plugin draws
+ // into a GWorld that's not connected to a window, otherwise the plugin draws
+ // into our the plugin's dummy window (which is slower, since the call we use
+ // to scrape the window contents is much more expensive than copying between
+ // GWorlds).
+ void SetFastPathEnabled(bool enabled);
+
+ // Returns true if the fast path is currently enabled.
+ bool IsFastPathEnabled();
+
+ // Sets the context that the plugin bits should be copied into when
+ // UpdateContext is called. This object does not retain |context|, so the
+ // caller must call SetTargetContext again if the context changes.
+ // If the fast path is currently enabled, this call will cause the port to
+ // change.
+ void SetTargetContext(CGContextRef context, const gfx::Size& plugin_size);
+
+ // Sets the window that is used by the plugin. This object does not own the
+ // window, so the caler must call SetPluginWindow again if the window changes.
+ void SetPluginWindow(WindowRef window);
+
+ // Updates the target context with the current plugin bits.
+ void UpdateContext();
+
+ // Returns the port that the plugin should draw into. This returned port is
+ // only valid until the next call to SetFastPathEnabled (or SetTargetContext
+ // while the fast path is enabled).
+ CGrafPtr port() { return current_port_; }
+
+ // Makes the QuickDraw port current; should be called before calls where the
+ // plugin might draw.
+ void MakePortCurrent();
+
+ private:
+ // Updates the GWorlds used by the faster path.
+ void UpdateGWorlds();
+
+ // Deletes the GWorlds used by the faster path.
+ void DestroyGWorlds();
+
+ // Scrapes the contents of the window into the given context.
+ // Used for the slower path.
+ static void ScrapeWindow(WindowRef window, CGContextRef target_context,
+ const gfx::Size& plugin_size);
+
+ // Copies the source GWorld's bits into the target GWorld.
+ // Used for the faster path.
+ static void CopyGWorldBits(GWorldPtr source, GWorldPtr dest,
+ const gfx::Size& plugin_size);
+
+ WindowRef plugin_window_; // Weak reference.
+ CGContextRef target_context_; // Weak reference.
+ gfx::Size plugin_size_;
+ bool fast_path_enabled_;
+ CGrafPtr current_port_;
+ // Variables used for the faster path:
+ GWorldPtr target_world_; // Created lazily; may be NULL.
+ GWorldPtr plugin_world_; // Created lazily; may be NULL.
+};
+
+#endif // !NP_NO_QUICKDRAW
+
+#endif // QUICKDRAW_DRAWING_MANAGER_MAC
diff --git a/webkit/glue/plugins/test/Info.plist b/webkit/glue/plugins/test/Info.plist
new file mode 100644
index 0000000..37145fd
--- /dev/null
+++ b/webkit/glue/plugins/test/Info.plist
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleExecutable</key>
+ <string>NPAPITestPlugIn</string>
+ <key>CFBundleIdentifier</key>
+ <string>org.chromium.npapi_test_plugin</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleExecutable</key>
+ <string>${EXECUTABLE_NAME}</string>
+ <key>CFBundleName</key>
+ <string>${PRODUCT_NAME}</string>
+ <key>CFBundlePackageType</key>
+ <string>BRPL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>1.0</string>
+ <key>CFPlugInDynamicRegisterFunction</key>
+ <string></string>
+ <key>CFPlugInDynamicRegistration</key>
+ <string>NO</string>
+ <key>WebPluginDescription</key>
+ <string>Simple NPAPI plug-in for Chromium unit tests</string>
+ <key>WebPluginMIMETypes</key>
+ <dict>
+ <key>application/vnd.npapi-test</key>
+ <dict>
+ <key>WebPluginExtensions</key>
+ <array>
+ <string>npapitest</string>
+ </array>
+ <key>WebPluginTypeDescription</key>
+ <string>test npapi</string>
+ </dict>
+ </dict>
+ <key>WebPluginName</key>
+ <string>Chromium NPAPI Test Plugin</string>
+</dict>
+</plist>
diff --git a/webkit/glue/plugins/test/npapi_constants.cc b/webkit/glue/plugins/test/npapi_constants.cc
new file mode 100644
index 0000000..75cc68f
--- /dev/null
+++ b/webkit/glue/plugins/test/npapi_constants.cc
@@ -0,0 +1,10 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/test/npapi_constants.h"
+
+namespace NPAPIClient {
+const char kTestCompleteCookie[] = "status";
+const char kTestCompleteSuccess[] = "OK";
+}
diff --git a/webkit/glue/plugins/test/npapi_constants.h b/webkit/glue/plugins/test/npapi_constants.h
new file mode 100644
index 0000000..6570c35
--- /dev/null
+++ b/webkit/glue/plugins/test/npapi_constants.h
@@ -0,0 +1,19 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Constants for the NPAPI test
+
+#ifndef WEBKIT_PORT_PLUGINS_TEST_NPAPI_CONSTANTS_H__
+#define WEBKIT_PORT_PLUGINS_TEST_NPAPI_CONSTANTS_H__
+
+namespace NPAPIClient {
+// The name of the cookie which will be used to communicate between
+// the plugin and the test harness.
+extern const char kTestCompleteCookie[];
+
+// The cookie value which will be sent to the client upon successful
+// test.
+extern const char kTestCompleteSuccess[];
+}
+#endif // WEBKIT_PORT_PLUGINS_TEST_NPAPI_CONSTANTS_H__
diff --git a/webkit/glue/plugins/test/npapi_test.cc b/webkit/glue/plugins/test/npapi_test.cc
new file mode 100644
index 0000000..d7c4fa7
--- /dev/null
+++ b/webkit/glue/plugins/test/npapi_test.cc
@@ -0,0 +1,80 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+//
+// npapitest
+//
+// This is a NPAPI Plugin Program which is used to test the Browser's NPAPI
+// host implementation. It is used in conjunction with the npapi_unittest.
+//
+// As a NPAPI Plugin, you can invoke it by creating a web page of the following
+// type:
+//
+// <embed src="content-to-load" type="application/vnd.npapi-test"
+// name="test-name">
+//
+// arguments:
+// src: This is the initial content which will be sent to the plugin.
+// type: Must be "application/vnd.npapi-test"
+// name: The testcase to run when invoked
+// id: The id of the test being run (for testing concurrent plugins)
+//
+// The Plugin drives the actual test, calling host functions and validating the
+// Host callbacks which it receives. It is the duty of the plugin to record
+// all errors.
+//
+// To indicate test completion, the plugin expects the containing HTML page to
+// implement two javascript functions:
+// onSuccess(string testname);
+// onFailure(string testname, string results);
+// The HTML host pages used in this test will then set a document cookie
+// which the automated test framework can poll for and discover that the
+// test has completed.
+//
+//
+// TESTS
+// When the PluginClient receives a NPP_New callback from the browser,
+// it looks at the "name" argument which is passed in. It verifies that
+// the name matches a known test, and instantiates that test. The test is
+// a subclass of PluginTest.
+//
+//
+
+#include "base/basictypes.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#endif
+
+#if defined(__GNUC__) && __GNUC__ >= 4
+#define EXPORT __attribute__((visibility ("default")))
+#else
+#define EXPORT
+#endif
+
+#include "webkit/glue/plugins/test/plugin_client.h"
+
+#if defined(OS_WIN)
+BOOL API_CALL DllMain(HINSTANCE hDll, DWORD dwReason, LPVOID lpReserved) {
+ return TRUE;
+}
+#endif
+
+extern "C" {
+EXPORT NPError API_CALL NP_GetEntryPoints(NPPluginFuncs* pFuncs) {
+ return NPAPIClient::PluginClient::GetEntryPoints(pFuncs);
+}
+
+EXPORT NPError API_CALL NP_Initialize(NPNetscapeFuncs* pFuncs) {
+ return NPAPIClient::PluginClient::Initialize(pFuncs);
+}
+
+EXPORT NPError API_CALL NP_Shutdown() {
+ return NPAPIClient::PluginClient::Shutdown();
+}
+} // extern "C"
+
+namespace WebCore {
+ const char* currentTextBreakLocaleID() { return "en_us"; }
+}
diff --git a/webkit/glue/plugins/test/npapi_test.def b/webkit/glue/plugins/test/npapi_test.def
new file mode 100644
index 0000000..4481c16
--- /dev/null
+++ b/webkit/glue/plugins/test/npapi_test.def
@@ -0,0 +1,6 @@
+LIBRARY npapi_test_plugin
+
+EXPORTS
+ NP_GetEntryPoints @1
+ NP_Initialize @2
+ NP_Shutdown @3
diff --git a/webkit/glue/plugins/test/npapi_test.rc b/webkit/glue/plugins/test/npapi_test.rc
new file mode 100644
index 0000000..524dda4
--- /dev/null
+++ b/webkit/glue/plugins/test/npapi_test.rc
@@ -0,0 +1,102 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#include ""afxres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 1,0,0,1
+ PRODUCTVERSION 1,0,0,1
+ FILEFLAGSMASK 0x17L
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x4L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904e4"
+ BEGIN
+ VALUE "FileDescription", "NPAPI Test Plugin"
+ VALUE "FileVersion", "1, 0, 0, 1"
+ VALUE "InternalName", "npapi_test_plugin"
+ VALUE "LegalCopyright", "Copyright (C) 2007"
+ VALUE "MIMEType", "application/vnd.npapi-test"
+ VALUE "OriginalFilename", "npapi_test_plugin.dll"
+ VALUE "ProductName", "NPAPI Test Plugin"
+ VALUE "ProductVersion", "1, 0, 0, 1"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1252
+ END
+END
+
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/webkit/glue/plugins/test/plugin_arguments_test.cc b/webkit/glue/plugins/test/plugin_arguments_test.cc
new file mode 100644
index 0000000..ee6d2c0
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_arguments_test.cc
@@ -0,0 +1,68 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/basictypes.h"
+#include "base/string_util.h"
+
+#include "webkit/glue/plugins/test/plugin_arguments_test.h"
+
+namespace NPAPIClient {
+
+PluginArgumentsTest::PluginArgumentsTest(NPP id,
+ NPNetscapeFuncs *host_functions)
+ : PluginTest(id, host_functions) {
+}
+
+NPError PluginArgumentsTest::New(uint16 mode, int16 argc,
+ const char* argn[], const char* argv[],
+ NPSavedData* saved) {
+ // mode: should be the string either "NP_EMBED" or "NP_FULL",
+ // depending on the mode passed in.
+ // count: the count of "val" arguments. If the value is
+ // 2, then we'll find arguments "val1" and "val2". If
+ // the value is 0, then there will be no "val" arguments.
+ // size: each val string will be this size * the value's
+ // index. E.g if size is "10", val1 will be 10bytes,
+ // and val2 will be 20bytes.
+ const char *mode_string = GetArgValue("mode", argc, argn, argv);
+ ExpectAsciiStringNotEqual(mode_string, (const char *)NULL);
+ if (mode_string != NULL) {
+ std::string mode_dep_string = mode_string;
+ if (mode == NP_EMBED)
+ ExpectStringLowerCaseEqual(mode_dep_string, "np_embed");
+ else if (mode == NP_FULL)
+ ExpectStringLowerCaseEqual(mode_dep_string, "np_full");
+ }
+
+ const char *count_string = GetArgValue("count", argc, argn, argv);
+ if (count_string != NULL) {
+ int max_args = atoi(count_string);
+
+ const char *size_string = GetArgValue("size", argc, argn, argv);
+ ExpectAsciiStringNotEqual(size_string, (const char *)NULL);
+ if (size_string != NULL) {
+ int size = atoi(size_string);
+
+ for (int index = 1; index <= max_args; index++) {
+ std::string arg_name = StringPrintf("%s%d", "val", index);
+ const char *val_string = GetArgValue(arg_name.c_str(), argc, argn,
+ argv);
+ ExpectAsciiStringNotEqual(val_string, (const char*)NULL);
+ if (val_string != NULL)
+ ExpectIntegerEqual((int)strlen(val_string), (index*size));
+ }
+ }
+ }
+
+ return PluginTest::New(mode, argc, argn, argv, saved);
+}
+
+NPError PluginArgumentsTest::SetWindow(NPWindow* pNPWindow) {
+ // This test just tests the arguments. We're done now.
+ this->SignalTestCompleted();
+
+ return NPERR_NO_ERROR;
+}
+
+} // namespace NPAPIClient
diff --git a/webkit/glue/plugins/test/plugin_arguments_test.h b/webkit/glue/plugins/test/plugin_arguments_test.h
new file mode 100644
index 0000000..aa05f19
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_arguments_test.h
@@ -0,0 +1,43 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_PORT_PLUGINS_TEST_PLUGIN_ARGUMENTS_TEST_H__
+#define WEBKIT_PORT_PLUGINS_TEST_PLUGIN_ARGUMENTS_TEST_H__
+
+#include "webkit/glue/plugins/test/plugin_test.h"
+
+namespace NPAPIClient {
+
+// The PluginArgumentsTest test that we properly receive arguments
+// intended for the plugin.
+//
+// This is basically overkill for testing that the arguments passed
+// to the plugin match what we expect.
+//
+// We expect to find the following arguments:
+// mode: should be the string either "NP_EMBED" or "NP_FULL",
+// depending on the mode passed in.
+// count: the count of "val" arguments. If the value is
+// 2, then we'll find arguments "val1" and "val2". If
+// the value is 0, then there will be no "val" arguments.
+// size: each val string will be this size * the value's
+// index. E.g if size is "10", val1 will be 10bytes,
+// and val2 will be 20bytes.
+//
+class PluginArgumentsTest : public PluginTest {
+ public:
+ // Constructor.
+ PluginArgumentsTest(NPP id, NPNetscapeFuncs *host_functions);
+
+ // Initialize this PluginTest based on the arguments from NPP_New.
+ virtual NPError New(uint16 mode, int16 argc, const char* argn[],
+ const char* argv[], NPSavedData* saved);
+
+ // NPAPI SetWindow handler.
+ virtual NPError SetWindow(NPWindow* pNPWindow);
+};
+
+} // namespace NPAPIClient
+
+#endif // WEBKIT_PORT_PLUGINS_TEST_PLUGIN_ARGUMENTS_TEST_H__
diff --git a/webkit/glue/plugins/test/plugin_client.cc b/webkit/glue/plugins/test/plugin_client.cc
new file mode 100644
index 0000000..d617e16
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_client.cc
@@ -0,0 +1,230 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/test/plugin_client.h"
+
+#include "base/string_util.h"
+#include "webkit/glue/plugins/test/plugin_test.h"
+#include "webkit/glue/plugins/test/plugin_test_factory.h"
+
+namespace NPAPIClient {
+
+NPNetscapeFuncs* PluginClient::host_functions_;
+
+NPError PluginClient::GetEntryPoints(NPPluginFuncs* pFuncs) {
+ if (pFuncs == NULL)
+ return NPERR_INVALID_FUNCTABLE_ERROR;
+
+ if (pFuncs->size < sizeof(NPPluginFuncs))
+ return NPERR_INVALID_FUNCTABLE_ERROR;
+
+ pFuncs->version = (NP_VERSION_MAJOR << 8) | NP_VERSION_MINOR;
+ pFuncs->newp = NPP_New;
+ pFuncs->destroy = NPP_Destroy;
+ pFuncs->setwindow = NPP_SetWindow;
+ pFuncs->newstream = NPP_NewStream;
+ pFuncs->destroystream = NPP_DestroyStream;
+ pFuncs->asfile = NPP_StreamAsFile;
+ pFuncs->writeready = NPP_WriteReady;
+ pFuncs->write = NPP_Write;
+ pFuncs->print = NPP_Print;
+ pFuncs->event = NPP_HandleEvent;
+ pFuncs->urlnotify = NPP_URLNotify;
+ pFuncs->getvalue = NPP_GetValue;
+ pFuncs->setvalue = NPP_SetValue;
+ pFuncs->javaClass = NULL;
+
+ return NPERR_NO_ERROR;
+}
+
+NPError PluginClient::Initialize(NPNetscapeFuncs* pFuncs) {
+ if (pFuncs == NULL) {
+ return NPERR_INVALID_FUNCTABLE_ERROR;
+ }
+
+ if (static_cast<unsigned char>((pFuncs->version >> 8) & 0xff) >
+ NP_VERSION_MAJOR) {
+ return NPERR_INCOMPATIBLE_VERSION_ERROR;
+ }
+
+#if defined(OS_WIN)
+ // Check if we should crash.
+ HANDLE crash_event = CreateEvent(NULL, TRUE, FALSE, L"TestPluginCrashOnInit");
+ if (WaitForSingleObject(crash_event, 0) == WAIT_OBJECT_0) {
+ int *zero = NULL;
+ *zero = 0;
+ }
+ CloseHandle(crash_event);
+#endif
+
+ host_functions_ = pFuncs;
+
+ return NPERR_NO_ERROR;
+}
+
+NPError PluginClient::Shutdown() {
+ return NPERR_NO_ERROR;
+}
+
+} // namespace NPAPIClient
+
+extern "C" {
+NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16 mode,
+ int16 argc, char* argn[], char* argv[], NPSavedData* saved) {
+ if (instance == NULL)
+ return NPERR_INVALID_INSTANCE_ERROR;
+
+ // We look at the test name requested via the plugin arguments. We match
+ // that against a given test and try to instantiate it.
+
+ // lookup the name parameter
+ std::string test_name;
+ for (int name_index = 0; name_index < argc; name_index++) {
+ if (base::strcasecmp(argn[name_index], "name") == 0) {
+ test_name = argv[name_index];
+ break;
+ }
+ }
+ if (test_name.empty())
+ return NPERR_GENERIC_ERROR; // no name found
+
+ NPAPIClient::PluginTest* new_test = NPAPIClient::CreatePluginTest(test_name,
+ instance, NPAPIClient::PluginClient::HostFunctions());
+ if (new_test == NULL) {
+ // If we don't have a test case for this, create a
+ // generic one which basically never fails.
+ LOG(WARNING) << "Unknown test name '" << test_name
+ << "'; using default test.";
+ new_test = new NPAPIClient::PluginTest(instance,
+ NPAPIClient::PluginClient::HostFunctions());
+ }
+
+ NPError ret = new_test->New(mode, argc, (const char**)argn,
+ (const char**)argv, saved);
+ if ((ret == NPERR_NO_ERROR) && new_test->IsWindowless()) {
+ NPAPIClient::PluginClient::HostFunctions()->setvalue(
+ instance, NPPVpluginWindowBool, NULL);
+ }
+
+ return ret;
+}
+
+NPError NPP_Destroy(NPP instance, NPSavedData** save) {
+ if (instance == NULL)
+ return NPERR_INVALID_INSTANCE_ERROR;
+
+ NPAPIClient::PluginTest *plugin =
+ (NPAPIClient::PluginTest*)instance->pdata;
+
+ NPError rv = plugin->Destroy();
+ delete plugin;
+ return rv;
+}
+
+NPError NPP_SetWindow(NPP instance, NPWindow* pNPWindow) {
+ if (instance == NULL)
+ return NPERR_INVALID_INSTANCE_ERROR;
+
+ NPAPIClient::PluginTest *plugin =
+ (NPAPIClient::PluginTest*)instance->pdata;
+
+ return plugin->SetWindow(pNPWindow);
+}
+
+NPError NPP_NewStream(NPP instance, NPMIMEType type,
+ NPStream* stream, NPBool seekable, uint16* stype) {
+ if (instance == NULL)
+ return NPERR_INVALID_INSTANCE_ERROR;
+
+ NPAPIClient::PluginTest *plugin =
+ (NPAPIClient::PluginTest*)instance->pdata;
+
+ return plugin->NewStream(type, stream, seekable, stype);
+}
+
+int32 NPP_WriteReady(NPP instance, NPStream *stream) {
+ if (instance == NULL)
+ return NPERR_INVALID_INSTANCE_ERROR;
+
+ NPAPIClient::PluginTest *plugin =
+ (NPAPIClient::PluginTest*)instance->pdata;
+
+ return plugin->WriteReady(stream);
+}
+
+int32 NPP_Write(NPP instance, NPStream *stream, int32 offset,
+ int32 len, void *buffer) {
+ if (instance == NULL)
+ return NPERR_INVALID_INSTANCE_ERROR;
+
+ NPAPIClient::PluginTest *plugin =
+ (NPAPIClient::PluginTest*)instance->pdata;
+
+ return plugin->Write(stream, offset, len, buffer);
+}
+
+NPError NPP_DestroyStream(NPP instance, NPStream *stream, NPError reason) {
+ if (instance == NULL)
+ return NPERR_INVALID_INSTANCE_ERROR;
+
+ NPAPIClient::PluginTest *plugin =
+ (NPAPIClient::PluginTest*)instance->pdata;
+
+ return plugin->DestroyStream(stream, reason);
+}
+
+void NPP_StreamAsFile(NPP instance, NPStream* stream, const char* fname) {
+ if (instance == NULL)
+ return;
+
+ NPAPIClient::PluginTest *plugin =
+ (NPAPIClient::PluginTest*)instance->pdata;
+
+ return plugin->StreamAsFile(stream, fname);
+}
+
+void NPP_Print(NPP instance, NPPrint* printInfo) {
+ if (instance == NULL)
+ return;
+
+ // XXXMB - do work here.
+}
+
+void NPP_URLNotify(NPP instance, const char* url, NPReason reason,
+ void* notifyData) {
+ if (instance == NULL)
+ return;
+
+ NPAPIClient::PluginTest *plugin =
+ (NPAPIClient::PluginTest*)instance->pdata;
+
+ return plugin->URLNotify(url, reason, notifyData);
+}
+
+NPError NPP_GetValue(NPP instance, NPPVariable variable, void *value) {
+ if (instance == NULL)
+ return NPERR_INVALID_INSTANCE_ERROR;
+
+ // XXXMB - do work here.
+ return NPERR_GENERIC_ERROR;
+}
+
+NPError NPP_SetValue(NPP instance, NPNVariable variable, void *value) {
+ if (instance == NULL)
+ return NPERR_INVALID_INSTANCE_ERROR;
+
+ // XXXMB - do work here.
+ return NPERR_GENERIC_ERROR;
+}
+
+int16 NPP_HandleEvent(NPP instance, void* event) {
+ if (instance == NULL)
+ return 0;
+
+ NPAPIClient::PluginTest *plugin =
+ (NPAPIClient::PluginTest*)instance->pdata;
+
+ return plugin->HandleEvent(event);
+}
+} // extern "C"
diff --git a/webkit/glue/plugins/test/plugin_client.h b/webkit/glue/plugins/test/plugin_client.h
new file mode 100644
index 0000000..a6291b0
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_client.h
@@ -0,0 +1,45 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_PORT_PLUGINS_TEST_PLUGIN_CLIENT_H__
+#define WEBKIT_PORT_PLUGINS_TEST_PLUGIN_CLIENT_H__
+
+#include "third_party/npapi/bindings/npapi.h"
+#include "third_party/npapi/bindings/nphostapi.h"
+
+namespace NPAPIClient {
+
+// A PluginClient is a NPAPI Plugin. This class contains
+// the bootstrapping functions used by the browser to load
+// the plugin.
+class PluginClient {
+ public:
+ // Although not documented in the NPAPI specification, this function
+ // gets the list of entry points in the NPAPI Plugin (client) for the
+ // NPAPI Host to call.
+ static NPError GetEntryPoints(NPPluginFuncs* pFuncs);
+
+ // The browser calls this function only once: when a plug-in is loaded,
+ // before the first instance is created. This is the first function that
+ // the browser calls. NP_Initialize tells the plug-in that the browser has
+ // loaded it and provides global initialization. Allocate any memory or
+ // resources shared by all instances of your plug-in at this time.
+ static NPError Initialize(NPNetscapeFuncs* pFuncs);
+
+ // The browser calls this function once after the last instance of your
+ // plug-in is destroyed, before unloading the plug-in library itself. Use
+ // NP_Shutdown to delete any data allocated in NP_Initialize to be shared
+ // by all instances of a plug-in.
+ static NPError Shutdown();
+
+ // The table of functions provided by the host.
+ static NPNetscapeFuncs *HostFunctions() { return host_functions_; }
+
+ private:
+ static NPNetscapeFuncs* host_functions_;
+};
+
+} // namespace NPAPIClient
+
+#endif // WEBKIT_PORT_PLUGINS_TEST_PLUGIN_CLIENT_H__
diff --git a/webkit/glue/plugins/test/plugin_create_instance_in_paint.cc b/webkit/glue/plugins/test/plugin_create_instance_in_paint.cc
new file mode 100644
index 0000000..0bea703
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_create_instance_in_paint.cc
@@ -0,0 +1,76 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/test/plugin_create_instance_in_paint.h"
+
+#include "webkit/glue/plugins/test/plugin_client.h"
+
+namespace NPAPIClient {
+
+CreateInstanceInPaintTest::CreateInstanceInPaintTest(
+ NPP id, NPNetscapeFuncs *host_functions)
+ : PluginTest(id, host_functions),
+ window_(NULL), created_(false) {
+}
+
+NPError CreateInstanceInPaintTest::SetWindow(NPWindow* pNPWindow) {
+ if (pNPWindow->window == NULL)
+ return NPERR_NO_ERROR;
+
+ if (test_id() == "1") {
+ if (!window_) {
+ static ATOM window_class = 0;
+ if (!window_class) {
+ WNDCLASSEX wcex;
+ wcex.cbSize = sizeof(WNDCLASSEX);
+ wcex.style = CS_DBLCLKS;
+ wcex.lpfnWndProc =
+ &NPAPIClient::CreateInstanceInPaintTest::WindowProc;
+ wcex.cbClsExtra = 0;
+ wcex.cbWndExtra = 0;
+ wcex.hInstance = GetModuleHandle(NULL);
+ wcex.hIcon = 0;
+ wcex.hCursor = 0;
+ wcex.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_WINDOW+1);
+ wcex.lpszMenuName = 0;
+ wcex.lpszClassName = L"CreateInstanceInPaintTestWindowClass";
+ wcex.hIconSm = 0;
+ window_class = RegisterClassEx(&wcex);
+ }
+
+ HWND parent = reinterpret_cast<HWND>(pNPWindow->window);
+ window_ = CreateWindowEx(
+ WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR,
+ MAKEINTATOM(window_class), 0,
+ WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_VISIBLE ,
+ 0, 0, 100, 100, parent, 0, GetModuleHandle(NULL), 0);
+ DCHECK(window_);
+ ::SetProp(window_, L"Plugin_Instance", this);
+ }
+ } else if (test_id() == "2") {
+ SignalTestCompleted();
+ } else {
+ NOTREACHED();
+ }
+ return NPERR_NO_ERROR;
+}
+
+LRESULT CALLBACK CreateInstanceInPaintTest::WindowProc(
+ HWND window, UINT message, WPARAM wparam, LPARAM lparam) {
+ if (message == WM_PAINT) {
+ CreateInstanceInPaintTest* this_instance =
+ reinterpret_cast<CreateInstanceInPaintTest*>
+ (::GetProp(window, L"Plugin_Instance"));
+ if (this_instance->test_id() == "1" && !this_instance->created_) {
+ this_instance->created_ = true;
+ this_instance->HostFunctions()->geturlnotify(
+ this_instance->id(), "javascript:CreateNewInstance()", NULL,
+ reinterpret_cast<void*>(1));
+ }
+ }
+
+ return DefWindowProc(window, message, wparam, lparam);
+}
+
+} // namespace NPAPIClient
diff --git a/webkit/glue/plugins/test/plugin_create_instance_in_paint.h b/webkit/glue/plugins/test/plugin_create_instance_in_paint.h
new file mode 100644
index 0000000..84d7a94
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_create_instance_in_paint.h
@@ -0,0 +1,33 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_CREATE_INSTANCE_IN_PAINT_H
+#define WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_CREATE_INSTANCE_IN_PAINT_H
+
+#include "webkit/glue/plugins/test/plugin_test.h"
+
+namespace NPAPIClient {
+
+// This class tests that creating a new plugin via script while handling a
+// Windows message doesn't cause a deadlock.
+class CreateInstanceInPaintTest : public PluginTest {
+ public:
+ // Constructor.
+ CreateInstanceInPaintTest(NPP id, NPNetscapeFuncs *host_functions);
+ //
+ // NPAPI functions
+ //
+ virtual NPError SetWindow(NPWindow* pNPWindow);
+
+ private:
+ static LRESULT CALLBACK WindowProc(
+ HWND window, UINT message, WPARAM wparam, LPARAM lparam);
+
+ HWND window_;
+ bool created_;
+};
+
+} // namespace NPAPIClient
+
+#endif // WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_CREATE_INSTANCE_IN_PAINT_H
diff --git a/webkit/glue/plugins/test/plugin_delete_plugin_in_stream_test.cc b/webkit/glue/plugins/test/plugin_delete_plugin_in_stream_test.cc
new file mode 100644
index 0000000..15318b4
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_delete_plugin_in_stream_test.cc
@@ -0,0 +1,45 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/test/plugin_delete_plugin_in_stream_test.h"
+
+#include "webkit/glue/plugins/test/plugin_client.h"
+
+namespace NPAPIClient {
+
+#define kUrl "javascript:window.location+\"\""
+#define kUrlStreamId 1
+
+DeletePluginInStreamTest::DeletePluginInStreamTest(NPP id, NPNetscapeFuncs *host_functions)
+ : PluginTest(id, host_functions),
+ test_started_(false) {
+}
+
+NPError DeletePluginInStreamTest::SetWindow(NPWindow* pNPWindow) {
+ if (pNPWindow->window == NULL)
+ return NPERR_NO_ERROR;
+
+ if (!test_started_) {
+ std::string url = "self_delete_plugin_stream.html";
+ HostFunctions()->geturlnotify(id(), url.c_str(), NULL,
+ reinterpret_cast<void*>(kUrlStreamId));
+ test_started_ = true;
+ }
+ return NPERR_NO_ERROR;
+}
+
+NPError DeletePluginInStreamTest::NewStream(NPMIMEType type, NPStream* stream,
+ NPBool seekable, uint16* stype) {
+ NPIdentifier delete_id = HostFunctions()->getstringidentifier("DeletePluginWithinScript");
+
+ NPObject *window_obj = NULL;
+ HostFunctions()->getvalue(id(), NPNVWindowNPObject, &window_obj);
+
+ NPVariant rv;
+ HostFunctions()->invoke(id(), window_obj, delete_id, NULL, 0, &rv);
+
+ return NPERR_NO_ERROR;
+}
+
+} // namespace NPAPIClient
diff --git a/webkit/glue/plugins/test/plugin_delete_plugin_in_stream_test.h b/webkit/glue/plugins/test/plugin_delete_plugin_in_stream_test.h
new file mode 100644
index 0000000..418e976
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_delete_plugin_in_stream_test.h
@@ -0,0 +1,30 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_DELETE_PLUGIN_IN_STREAM_TEST_H
+#define WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_DELETE_PLUGIN_IN_STREAM_TEST_H
+
+#include "webkit/glue/plugins/test/plugin_test.h"
+
+namespace NPAPIClient {
+
+// This class tests
+class DeletePluginInStreamTest : public PluginTest {
+ public:
+ // Constructor.
+ DeletePluginInStreamTest(NPP id, NPNetscapeFuncs *host_functions);
+ //
+ // NPAPI functions
+ //
+ virtual NPError SetWindow(NPWindow* pNPWindow);
+ virtual NPError NewStream(NPMIMEType type, NPStream* stream,
+ NPBool seekable, uint16* stype);
+ private:
+ bool test_started_;
+ std::string self_url_;
+};
+
+} // namespace NPAPIClient
+
+#endif // WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_DELETE_PLUGIN_IN_STREAM_TEST_H
diff --git a/webkit/glue/plugins/test/plugin_get_javascript_url2_test.cc b/webkit/glue/plugins/test/plugin_get_javascript_url2_test.cc
new file mode 100644
index 0000000..d17dced
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_get_javascript_url2_test.cc
@@ -0,0 +1,134 @@
+// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/test/plugin_get_javascript_url2_test.h"
+
+#include "base/basictypes.h"
+
+// url for "self".
+#define SELF_URL "javascript:window.location+\"\""
+// The identifier for the self url stream.
+#define SELF_URL_STREAM_ID 1
+
+// The identifier for the fetched url stream.
+#define FETCHED_URL_STREAM_ID 2
+
+// The maximum chunk size of stream data.
+#define STREAM_CHUNK 197
+
+const int kNPNEvaluateTimerID = 100;
+const int kNPNEvaluateTimerElapse = 50;
+
+namespace NPAPIClient {
+
+ExecuteGetJavascriptUrl2Test::ExecuteGetJavascriptUrl2Test(
+ NPP id, NPNetscapeFuncs *host_functions)
+ : PluginTest(id, host_functions),
+ test_started_(false) {
+}
+
+NPError ExecuteGetJavascriptUrl2Test::SetWindow(NPWindow* pNPWindow) {
+ if (pNPWindow->window == NULL)
+ return NPERR_NO_ERROR;
+
+ if (!test_started_) {
+ std::string url = SELF_URL;
+ HostFunctions()->geturlnotify(id(), url.c_str(), "_self",
+ reinterpret_cast<void*>(SELF_URL_STREAM_ID));
+ test_started_ = true;
+ }
+ return NPERR_NO_ERROR;
+}
+
+NPError ExecuteGetJavascriptUrl2Test::NewStream(NPMIMEType type, NPStream* stream,
+ NPBool seekable, uint16* stype) {
+ if (stream == NULL) {
+ SetError("NewStream got null stream");
+ return NPERR_INVALID_PARAM;
+ }
+
+ COMPILE_ASSERT(sizeof(unsigned long) <= sizeof(stream->notifyData),
+ cast_validity_check);
+ unsigned long stream_id = reinterpret_cast<unsigned long>(stream->notifyData);
+ switch (stream_id) {
+ case SELF_URL_STREAM_ID:
+ break;
+ default:
+ SetError("Unexpected NewStream callback");
+ break;
+ }
+ return NPERR_NO_ERROR;
+}
+
+int32 ExecuteGetJavascriptUrl2Test::WriteReady(NPStream *stream) {
+ return STREAM_CHUNK;
+}
+
+int32 ExecuteGetJavascriptUrl2Test::Write(NPStream *stream, int32 offset, int32 len,
+ void *buffer) {
+ if (stream == NULL) {
+ SetError("Write got null stream");
+ return -1;
+ }
+ if (len < 0 || len > STREAM_CHUNK) {
+ SetError("Write got bogus stream chunk size");
+ return -1;
+ }
+
+ COMPILE_ASSERT(sizeof(unsigned long) <= sizeof(stream->notifyData),
+ cast_validity_check);
+ unsigned long stream_id = reinterpret_cast<unsigned long>(stream->notifyData);
+ switch (stream_id) {
+ case SELF_URL_STREAM_ID:
+ self_url_.append(static_cast<char*>(buffer), len);
+ break;
+ default:
+ SetError("Unexpected write callback");
+ break;
+ }
+ // Pretend that we took all the data.
+ return len;
+}
+
+
+NPError ExecuteGetJavascriptUrl2Test::DestroyStream(NPStream *stream, NPError reason) {
+ if (stream == NULL) {
+ SetError("NewStream got null stream");
+ return NPERR_INVALID_PARAM;
+ }
+
+ COMPILE_ASSERT(sizeof(unsigned long) <= sizeof(stream->notifyData),
+ cast_validity_check);
+ unsigned long stream_id = reinterpret_cast<unsigned long>(stream->notifyData);
+ switch (stream_id) {
+ case SELF_URL_STREAM_ID:
+ // don't care
+ break;
+ default:
+ SetError("Unexpected NewStream callback");
+ break;
+ }
+ return NPERR_NO_ERROR;
+}
+
+void ExecuteGetJavascriptUrl2Test::URLNotify(const char* url, NPReason reason, void* data) {
+ COMPILE_ASSERT(sizeof(unsigned long) <= sizeof(data),
+ cast_validity_check);
+
+ unsigned long stream_id = reinterpret_cast<unsigned long>(data);
+ switch (stream_id) {
+ case SELF_URL_STREAM_ID:
+ if (strcmp(url, SELF_URL) != 0)
+ SetError("URLNotify reported incorrect url for SELF_URL");
+ if (self_url_.empty())
+ SetError("Failed to obtain window location.");
+ SignalTestCompleted();
+ break;
+ default:
+ SetError("Unexpected NewStream callback");
+ break;
+ }
+}
+
+} // namespace NPAPIClient
diff --git a/webkit/glue/plugins/test/plugin_get_javascript_url2_test.h b/webkit/glue/plugins/test/plugin_get_javascript_url2_test.h
new file mode 100644
index 0000000..557da76
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_get_javascript_url2_test.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_GET_JAVASCRIPT_URL2_H
+#define WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_GET_JAVASCRIPT_URL2_H
+
+#include "webkit/glue/plugins/test/plugin_test.h"
+
+namespace NPAPIClient {
+
+// This class tests NPP_GetURLNotify for a javascript URL with _top
+// as the target frame.
+class ExecuteGetJavascriptUrl2Test : public PluginTest {
+ public:
+ // Constructor.
+ ExecuteGetJavascriptUrl2Test(NPP id, NPNetscapeFuncs *host_functions);
+
+ //
+ // NPAPI functions
+ //
+ virtual NPError SetWindow(NPWindow* pNPWindow);
+ virtual NPError NewStream(NPMIMEType type, NPStream* stream,
+ NPBool seekable, uint16* stype);
+ virtual int32 WriteReady(NPStream *stream);
+ virtual int32 Write(NPStream *stream, int32 offset, int32 len,
+ void *buffer);
+ virtual NPError DestroyStream(NPStream *stream, NPError reason);
+ virtual void URLNotify(const char* url, NPReason reason, void* data);
+
+ private:
+ bool test_started_;
+ std::string self_url_;
+};
+
+} // namespace NPAPIClient
+
+#endif // WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_GET_JAVASCRIPT_URL2_H
diff --git a/webkit/glue/plugins/test/plugin_get_javascript_url_test.cc b/webkit/glue/plugins/test/plugin_get_javascript_url_test.cc
new file mode 100644
index 0000000..cc6bc19
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_get_javascript_url_test.cc
@@ -0,0 +1,210 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/test/plugin_get_javascript_url_test.h"
+
+#include "base/basictypes.h"
+
+// url for "self".
+#define SELF_URL "javascript:window.location+\"\""
+// The identifier for the self url stream.
+#define SELF_URL_STREAM_ID 1
+
+// The identifier for the fetched url stream.
+#define FETCHED_URL_STREAM_ID 2
+
+// The maximum chunk size of stream data.
+#define STREAM_CHUNK 197
+
+const int kNPNEvaluateTimerID = 100;
+const int kNPNEvaluateTimerElapse = 50;
+
+
+namespace NPAPIClient {
+
+ExecuteGetJavascriptUrlTest::ExecuteGetJavascriptUrlTest(NPP id, NPNetscapeFuncs *host_functions)
+ : PluginTest(id, host_functions),
+ test_started_(false),
+#ifdef OS_WIN
+ window_(NULL),
+#endif
+ npn_evaluate_context_(false) {
+}
+
+NPError ExecuteGetJavascriptUrlTest::SetWindow(NPWindow* pNPWindow) {
+ if (pNPWindow->window == NULL)
+ return NPERR_NO_ERROR;
+
+ if (!test_started_) {
+ std::string url = SELF_URL;
+ HostFunctions()->geturlnotify(id(), url.c_str(), "_top",
+ reinterpret_cast<void*>(SELF_URL_STREAM_ID));
+ test_started_ = true;
+
+#ifdef OS_WIN
+ HWND window_handle = reinterpret_cast<HWND>(pNPWindow->window);
+ if (!::GetProp(window_handle, L"Plugin_Instance")) {
+ ::SetProp(window_handle, L"Plugin_Instance", this);
+ // We attempt to retreive the NPObject for the plugin instance identified
+ // by the NPObjectLifetimeTestInstance2 class as it may not have been
+ // instantiated yet.
+ SetTimer(window_handle, kNPNEvaluateTimerID, kNPNEvaluateTimerElapse,
+ TimerProc);
+ }
+ window_ = window_handle;
+#endif
+ }
+
+ return NPERR_NO_ERROR;
+}
+
+#ifdef OS_WIN
+void CALLBACK ExecuteGetJavascriptUrlTest::TimerProc(
+ HWND window, UINT message, UINT timer_id, unsigned long elapsed_time) {
+ ExecuteGetJavascriptUrlTest* this_instance =
+ reinterpret_cast<ExecuteGetJavascriptUrlTest*>
+ (::GetProp(window, L"Plugin_Instance"));
+
+ NPObject *window_obj = NULL;
+ this_instance->HostFunctions()->getvalue(this_instance->id(),
+ NPNVWindowNPObject,
+ &window_obj);
+ if (!window_obj) {
+ this_instance->SetError("Failed to get NPObject for plugin instance2");
+ this_instance->SignalTestCompleted();
+ return;
+ }
+
+ std::string script = "javascript:window.location";
+ NPString script_string;
+ script_string.UTF8Characters = script.c_str();
+ script_string.UTF8Length = static_cast<unsigned int>(script.length());
+ NPVariant result_var;
+
+ this_instance->npn_evaluate_context_ = true;
+ NPError result = this_instance->HostFunctions()->evaluate(
+ this_instance->id(), window_obj, &script_string, &result_var);
+ this_instance->npn_evaluate_context_ = false;
+}
+#endif
+
+NPError ExecuteGetJavascriptUrlTest::NewStream(NPMIMEType type, NPStream* stream,
+ NPBool seekable, uint16* stype) {
+ if (stream == NULL) {
+ SetError("NewStream got null stream");
+ return NPERR_INVALID_PARAM;
+ }
+
+ if (npn_evaluate_context_) {
+ SetError("NewStream received in context of NPN_Evaluate");
+ return NPERR_NO_ERROR;
+ }
+
+ COMPILE_ASSERT(sizeof(unsigned long) <= sizeof(stream->notifyData),
+ cast_validity_check);
+ unsigned long stream_id = reinterpret_cast<unsigned long>(stream->notifyData);
+ switch (stream_id) {
+ case SELF_URL_STREAM_ID:
+ break;
+ default:
+ SetError("Unexpected NewStream callback");
+ break;
+ }
+ return NPERR_NO_ERROR;
+}
+
+int32 ExecuteGetJavascriptUrlTest::WriteReady(NPStream *stream) {
+ if (npn_evaluate_context_) {
+ SetError("WriteReady received in context of NPN_Evaluate");
+ return NPERR_NO_ERROR;
+ }
+ return STREAM_CHUNK;
+}
+
+int32 ExecuteGetJavascriptUrlTest::Write(NPStream *stream, int32 offset, int32 len,
+ void *buffer) {
+ if (stream == NULL) {
+ SetError("Write got null stream");
+ return -1;
+ }
+ if (len < 0 || len > STREAM_CHUNK) {
+ SetError("Write got bogus stream chunk size");
+ return -1;
+ }
+
+ if (npn_evaluate_context_) {
+ SetError("Write received in context of NPN_Evaluate");
+ return len;
+ }
+
+ COMPILE_ASSERT(sizeof(unsigned long) <= sizeof(stream->notifyData),
+ cast_validity_check);
+ unsigned long stream_id = reinterpret_cast<unsigned long>(stream->notifyData);
+ switch (stream_id) {
+ case SELF_URL_STREAM_ID:
+ self_url_.append(static_cast<char*>(buffer), len);
+ break;
+ default:
+ SetError("Unexpected write callback");
+ break;
+ }
+ // Pretend that we took all the data.
+ return len;
+}
+
+
+NPError ExecuteGetJavascriptUrlTest::DestroyStream(NPStream *stream, NPError reason) {
+ if (stream == NULL) {
+ SetError("NewStream got null stream");
+ return NPERR_INVALID_PARAM;
+ }
+
+#ifdef OS_WIN
+ KillTimer(window_, kNPNEvaluateTimerID);
+#endif
+
+ if (npn_evaluate_context_) {
+ SetError("DestroyStream received in context of NPN_Evaluate");
+ return NPERR_NO_ERROR;
+ }
+
+ COMPILE_ASSERT(sizeof(unsigned long) <= sizeof(stream->notifyData),
+ cast_validity_check);
+ unsigned long stream_id = reinterpret_cast<unsigned long>(stream->notifyData);
+ switch (stream_id) {
+ case SELF_URL_STREAM_ID:
+ // don't care
+ break;
+ default:
+ SetError("Unexpected NewStream callback");
+ break;
+ }
+ return NPERR_NO_ERROR;
+}
+
+void ExecuteGetJavascriptUrlTest::URLNotify(const char* url, NPReason reason, void* data) {
+ COMPILE_ASSERT(sizeof(unsigned long) <= sizeof(data),
+ cast_validity_check);
+
+ if (npn_evaluate_context_) {
+ SetError("URLNotify received in context of NPN_Evaluate");
+ return;
+ }
+
+ unsigned long stream_id = reinterpret_cast<unsigned long>(data);
+ switch (stream_id) {
+ case SELF_URL_STREAM_ID:
+ if (strcmp(url, SELF_URL) != 0)
+ SetError("URLNotify reported incorrect url for SELF_URL");
+ if (self_url_.empty())
+ SetError("Failed to obtain window location.");
+ SignalTestCompleted();
+ break;
+ default:
+ SetError("Unexpected NewStream callback");
+ break;
+ }
+}
+
+} // namespace NPAPIClient
diff --git a/webkit/glue/plugins/test/plugin_get_javascript_url_test.h b/webkit/glue/plugins/test/plugin_get_javascript_url_test.h
new file mode 100644
index 0000000..5c2540d
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_get_javascript_url_test.h
@@ -0,0 +1,47 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_GET_JAVASCRIPT_URL_H
+#define WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_GET_JAVASCRIPT_URL_H
+
+#include "webkit/glue/plugins/test/plugin_test.h"
+
+namespace NPAPIClient {
+
+// This class tests NPP_GetURLNotify for a javascript URL with _top
+// as the target frame.
+class ExecuteGetJavascriptUrlTest : public PluginTest {
+ public:
+ // Constructor.
+ ExecuteGetJavascriptUrlTest(NPP id, NPNetscapeFuncs *host_functions);
+ //
+ // NPAPI functions
+ //
+ virtual NPError SetWindow(NPWindow* pNPWindow);
+ virtual NPError NewStream(NPMIMEType type, NPStream* stream,
+ NPBool seekable, uint16* stype);
+ virtual int32 WriteReady(NPStream *stream);
+ virtual int32 Write(NPStream *stream, int32 offset, int32 len,
+ void *buffer);
+ virtual NPError DestroyStream(NPStream *stream, NPError reason);
+ virtual void URLNotify(const char* url, NPReason reason, void* data);
+
+ private:
+#if defined(OS_WIN)
+ static void CALLBACK TimerProc(HWND window, UINT message, UINT timer_id,
+ unsigned long elapsed_time);
+#endif
+ bool test_started_;
+ // This flag is set to true in the context of the NPN_Evaluate call.
+ bool npn_evaluate_context_;
+ std::string self_url_;
+
+#if defined(OS_WIN)
+ HWND window_;
+#endif
+};
+
+} // namespace NPAPIClient
+
+#endif // WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_GET_JAVASCRIPT_URL_H
diff --git a/webkit/glue/plugins/test/plugin_geturl_test.cc b/webkit/glue/plugins/test/plugin_geturl_test.cc
new file mode 100644
index 0000000..f321b4b
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_geturl_test.cc
@@ -0,0 +1,374 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/test/plugin_geturl_test.h"
+
+#include <stdio.h>
+
+#include "base/basictypes.h"
+#include "base/file_util.h"
+
+// url for "self". The %22%22 is to make a statement for javascript to
+// evaluate and return.
+#define SELF_URL "javascript:window.location+\"\""
+
+// The identifier for the self url stream.
+#define SELF_URL_STREAM_ID 1
+
+// The identifier for the fetched url stream.
+#define FETCHED_URL_STREAM_ID 2
+
+// url for testing GetURL with a bogus URL.
+#define BOGUS_URL "bogoproto:///x:/asdf.xysdhffieasdf.asdhj/"
+
+// The identifier for the bogus url stream.
+#define BOGUS_URL_STREAM_ID 3
+
+// The maximum chunk size of stream data.
+#define STREAM_CHUNK 197
+
+namespace NPAPIClient {
+
+PluginGetURLTest::PluginGetURLTest(NPP id, NPNetscapeFuncs *host_functions)
+ : PluginTest(id, host_functions),
+ tests_started_(false),
+ tests_in_progress_(0),
+ test_file_(NULL),
+ expect_404_response_(false),
+ npn_evaluate_context_(false) {
+}
+
+NPError PluginGetURLTest::New(uint16 mode, int16 argc, const char* argn[],
+ const char* argv[], NPSavedData* saved) {
+ const char* page_not_found_url = GetArgValue("page_not_found_url", argc,
+ argn, argv);
+ if (page_not_found_url) {
+ page_not_found_url_ = page_not_found_url;
+ expect_404_response_ = true;
+ }
+
+ const char* fail_write_url = GetArgValue("fail_write_url", argc,
+ argn, argv);
+ if (fail_write_url) {
+ fail_write_url_ = fail_write_url;
+ }
+
+ const char* referrer_target_url = GetArgValue("ref_target", argc,
+ argn, argv);
+ if (referrer_target_url) {
+ referrer_target_url_ = referrer_target_url;
+ }
+
+ return PluginTest::New(mode, argc, argn, argv, saved);
+}
+
+NPError PluginGetURLTest::SetWindow(NPWindow* pNPWindow) {
+ if (pNPWindow->window == NULL)
+ return NPERR_NO_ERROR;
+
+ if (!tests_started_) {
+ tests_started_ = true;
+
+ tests_in_progress_++;
+
+ if (expect_404_response_) {
+ HostFunctions()->geturl(id(), page_not_found_url_.c_str(), NULL);
+ return NPERR_NO_ERROR;
+ } else if (!fail_write_url_.empty()) {
+ HostFunctions()->geturl(id(), fail_write_url_.c_str(), NULL);
+ return NPERR_NO_ERROR;
+ } else if (!referrer_target_url_.empty()) {
+ HostFunctions()->pushpopupsenabledstate(id(), true);
+ HostFunctions()->geturl(id(), referrer_target_url_.c_str(), "_blank");
+ HostFunctions()->poppopupsenabledstate(id());
+ return NPERR_NO_ERROR;
+ }
+
+ std::string url = SELF_URL;
+ HostFunctions()->geturlnotify(id(), url.c_str(), NULL,
+ reinterpret_cast<void*>(SELF_URL_STREAM_ID));
+
+ tests_in_progress_++;
+ std::string bogus_url = BOGUS_URL;
+ HostFunctions()->geturlnotify(id(), bogus_url.c_str(), NULL,
+ reinterpret_cast<void*>(BOGUS_URL_STREAM_ID));
+ }
+ return NPERR_NO_ERROR;
+}
+
+NPError PluginGetURLTest::NewStream(NPMIMEType type, NPStream* stream,
+ NPBool seekable, uint16* stype) {
+ if (stream == NULL) {
+ SetError("NewStream got null stream");
+ return NPERR_INVALID_PARAM;
+ }
+
+ if (test_completed()) {
+ return PluginTest::NewStream(type, stream, seekable, stype);
+ }
+
+ if (!referrer_target_url_.empty()) {
+ return NPERR_NO_ERROR;
+ }
+
+ COMPILE_ASSERT(sizeof(unsigned long) <= sizeof(stream->notifyData),
+ cast_validity_check);
+
+ if (expect_404_response_) {
+ NPObject *window_obj = NULL;
+ HostFunctions()->getvalue(id(), NPNVWindowNPObject, &window_obj);
+ if (!window_obj) {
+ SetError("Failed to get NPObject for plugin instance2");
+ SignalTestCompleted();
+ return NPERR_NO_ERROR;
+ }
+
+ std::string script = "javascript:alert('Hi there from plugin');";
+ NPString script_string;
+ script_string.UTF8Characters = script.c_str();
+ script_string.UTF8Length = static_cast<unsigned int>(script.length());
+ NPVariant result_var;
+
+ npn_evaluate_context_ = true;
+ HostFunctions()->evaluate(id(), window_obj, &script_string, &result_var);
+ npn_evaluate_context_ = false;
+ return NPERR_NO_ERROR;
+ }
+
+ if (!fail_write_url_.empty()) {
+ return NPERR_NO_ERROR;
+ }
+
+
+ unsigned long stream_id = reinterpret_cast<unsigned long>(
+ stream->notifyData);
+
+ switch (stream_id) {
+ case SELF_URL_STREAM_ID:
+ break;
+ case FETCHED_URL_STREAM_ID:
+ {
+ std::string filename = self_url_;
+ if (filename.find("file:///", 0) != 0) {
+ SetError("Test expects a file-url.");
+ break;
+ }
+
+ // TODO(evanm): use the net:: functions to convert file:// URLs to
+ // on-disk file paths. But it probably doesn't actually matter in
+ // this test.
+
+#if defined(OS_WIN)
+ filename = filename.substr(8); // remove "file:///"
+ // Assume an ASCII path on Windows.
+ FilePath path = FilePath(ASCIIToWide(filename));
+#else
+ filename = filename.substr(7); // remove "file://"
+ FilePath path = FilePath(filename);
+#endif
+
+ test_file_ = file_util::OpenFile(path, "r");
+ if (!test_file_) {
+ SetError("Could not open source file");
+ }
+ }
+ break;
+ case BOGUS_URL_STREAM_ID:
+ SetError("Unexpected NewStream for BOGUS_URL");
+ break;
+ default:
+ SetError("Unexpected NewStream callback");
+ break;
+ }
+ return NPERR_NO_ERROR;
+}
+
+int32 PluginGetURLTest::WriteReady(NPStream *stream) {
+ if (test_completed()) {
+ return PluginTest::WriteReady(stream);
+ }
+
+ if (!referrer_target_url_.empty()) {
+ return STREAM_CHUNK;
+ }
+
+ COMPILE_ASSERT(sizeof(unsigned long) <= sizeof(stream->notifyData),
+ cast_validity_check);
+ unsigned long stream_id = reinterpret_cast<unsigned long>(
+ stream->notifyData);
+ if (stream_id == BOGUS_URL_STREAM_ID)
+ SetError("Received WriteReady for BOGUS_URL");
+
+ return STREAM_CHUNK;
+}
+
+int32 PluginGetURLTest::Write(NPStream *stream, int32 offset, int32 len,
+ void *buffer) {
+ if (test_completed()) {
+ return PluginTest::Write(stream, offset, len, buffer);
+ }
+
+ if (!fail_write_url_.empty()) {
+ SignalTestCompleted();
+ return -1;
+ }
+
+ if (!referrer_target_url_.empty()) {
+ return len;
+ }
+
+ if (stream == NULL) {
+ SetError("Write got null stream");
+ return -1;
+ }
+ if (len < 0 || len > STREAM_CHUNK) {
+ SetError("Write got bogus stream chunk size");
+ return -1;
+ }
+
+ COMPILE_ASSERT(sizeof(unsigned long) <= sizeof(stream->notifyData),
+ cast_validity_check);
+ unsigned long stream_id = reinterpret_cast<unsigned long>(
+ stream->notifyData);
+ switch (stream_id) {
+ case SELF_URL_STREAM_ID:
+ self_url_.append(static_cast<char*>(buffer), len);
+ break;
+ case FETCHED_URL_STREAM_ID:
+ {
+ char read_buffer[STREAM_CHUNK];
+ int32 bytes = fread(read_buffer, 1, len, test_file_);
+ // Technically, fread could return fewer than len
+ // bytes. But this is not likely.
+ if (bytes != len)
+ SetError("Did not read correct bytelength from source file");
+ if (memcmp(read_buffer, buffer, len))
+ SetError("Content mismatch between data and source!");
+ }
+ break;
+ case BOGUS_URL_STREAM_ID:
+ SetError("Unexpected write callback for BOGUS_URL");
+ break;
+ default:
+ SetError("Unexpected write callback");
+ break;
+ }
+ // Pretend that we took all the data.
+ return len;
+}
+
+
+NPError PluginGetURLTest::DestroyStream(NPStream *stream, NPError reason) {
+ if (test_completed()) {
+ return PluginTest::DestroyStream(stream, reason);
+ }
+
+ if (stream == NULL) {
+ SetError("NewStream got null stream");
+ return NPERR_INVALID_PARAM;
+ }
+
+ COMPILE_ASSERT(sizeof(unsigned long) <= sizeof(stream->notifyData),
+ cast_validity_check);
+
+ if (expect_404_response_) {
+ if (npn_evaluate_context_) {
+ SetError("Received destroyStream in the context of NPN_Evaluate.");
+ }
+
+ SignalTestCompleted();
+ return NPERR_NO_ERROR;
+ }
+
+ if (!referrer_target_url_.empty()) {
+ return NPERR_NO_ERROR;
+ }
+
+ unsigned long stream_id =
+ reinterpret_cast<unsigned long>(stream->notifyData);
+ switch (stream_id) {
+ case SELF_URL_STREAM_ID:
+ // don't care
+ break;
+ case FETCHED_URL_STREAM_ID:
+ {
+ char read_buffer[STREAM_CHUNK];
+ size_t bytes = fread(read_buffer, 1, sizeof(read_buffer), test_file_);
+ if (bytes != 0)
+ SetError("Data and source mismatch on length");
+ file_util::CloseFile(test_file_);
+ }
+ break;
+ default:
+ SetError("Unexpected NewStream callback");
+ break;
+ }
+ return NPERR_NO_ERROR;
+}
+
+void PluginGetURLTest::StreamAsFile(NPStream* stream, const char* fname) {
+ if (stream == NULL) {
+ SetError("NewStream got null stream");
+ return;
+ }
+
+ COMPILE_ASSERT(sizeof(unsigned long) <= sizeof(stream->notifyData),
+ cast_validity_check);
+ unsigned long stream_id =
+ reinterpret_cast<unsigned long>(stream->notifyData);
+ switch (stream_id) {
+ case SELF_URL_STREAM_ID:
+ // don't care
+ break;
+ default:
+ SetError("Unexpected NewStream callback");
+ break;
+ }
+}
+
+void PluginGetURLTest::URLNotify(const char* url, NPReason reason, void* data) {
+ if (!tests_in_progress_) {
+ SetError("URLNotify received after tests completed");
+ return;
+ }
+
+ if (!url) {
+ SetError("URLNotify received NULL url");
+ return;
+ }
+
+ COMPILE_ASSERT(sizeof(unsigned long) <= sizeof(data), cast_validity_check);
+ unsigned long stream_id = reinterpret_cast<unsigned long>(data);
+ switch (stream_id) {
+ case SELF_URL_STREAM_ID:
+ if (strcmp(url, SELF_URL) != 0)
+ SetError("URLNotify reported incorrect url for SELF_URL");
+
+ // We have our stream url. Go fetch it.
+ HostFunctions()->geturlnotify(id(), self_url_.c_str(), NULL,
+ reinterpret_cast<void*>(FETCHED_URL_STREAM_ID));
+ break;
+ case FETCHED_URL_STREAM_ID:
+ if (!url || strcmp(url, self_url_.c_str()) != 0)
+ SetError("URLNotify reported incorrect url for FETCHED_URL");
+ tests_in_progress_--;
+ break;
+ case BOGUS_URL_STREAM_ID:
+ if (reason != NPRES_NETWORK_ERR) {
+ std::string err = "BOGUS_URL received unexpected URLNotify status: ";
+ err.append(IntToString(reason));
+ SetError(err);
+ }
+ tests_in_progress_--;
+ break;
+ default:
+ SetError("Unexpected NewStream callback");
+ break;
+ }
+
+ if (tests_in_progress_ == 0)
+ SignalTestCompleted();
+}
+
+} // namespace NPAPIClient
diff --git a/webkit/glue/plugins/test/plugin_geturl_test.h b/webkit/glue/plugins/test/plugin_geturl_test.h
new file mode 100644
index 0000000..9d5b826
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_geturl_test.h
@@ -0,0 +1,55 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_PORT_PLUGINS_TEST_PLUGIN_GETURL_TEST_H__
+#define WEBKIT_PORT_PLUGINS_TEST_PLUGIN_GETURL_TEST_H__
+
+#include <stdio.h>
+
+#include "webkit/glue/plugins/test/plugin_test.h"
+
+namespace NPAPIClient {
+
+// The PluginGetURLTest test functionality of the NPN_GetURL
+// and NPN_GetURLNotify methods.
+//
+// This test first discovers it's URL by sending a GetURL request
+// for 'javascript:top.location'. After receiving that, the
+// test will request the url itself (again via GetURL).
+class PluginGetURLTest : public PluginTest {
+ public:
+ // Constructor.
+ PluginGetURLTest(NPP id, NPNetscapeFuncs *host_functions);
+
+ //
+ // NPAPI functions
+ //
+ virtual NPError New(uint16 mode, int16 argc, const char* argn[],
+ const char* argv[], NPSavedData* saved);
+ virtual NPError SetWindow(NPWindow* pNPWindow);
+ virtual NPError NewStream(NPMIMEType type, NPStream* stream,
+ NPBool seekable, uint16* stype);
+ virtual int32 WriteReady(NPStream *stream);
+ virtual int32 Write(NPStream *stream, int32 offset, int32 len,
+ void *buffer);
+ virtual NPError DestroyStream(NPStream *stream, NPError reason);
+ virtual void StreamAsFile(NPStream* stream, const char* fname);
+ virtual void URLNotify(const char* url, NPReason reason, void* data);
+
+ private:
+ bool tests_started_;
+ int tests_in_progress_;
+ std::string self_url_;
+ FILE* test_file_;
+ bool expect_404_response_;
+ // This flag is set to true in the context of the NPN_Evaluate call.
+ bool npn_evaluate_context_;
+ std::string page_not_found_url_;
+ std::string fail_write_url_;
+ std::string referrer_target_url_;
+};
+
+} // namespace NPAPIClient
+
+#endif // WEBKIT_PORT_PLUGINS_TEST_PLUGIN_GETURL_TEST_H__
diff --git a/webkit/glue/plugins/test/plugin_javascript_open_popup.cc b/webkit/glue/plugins/test/plugin_javascript_open_popup.cc
new file mode 100644
index 0000000..0f93bf4
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_javascript_open_popup.cc
@@ -0,0 +1,103 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "build/build_config.h"
+#include "webkit/glue/plugins/test/plugin_javascript_open_popup.h"
+
+#if defined(USE_X11)
+#include "third_party/npapi/bindings/npapi_x11.h"
+#endif
+#include "webkit/glue/plugins/test/plugin_client.h"
+
+namespace NPAPIClient {
+
+ExecuteJavascriptOpenPopupWithPluginTest::
+ ExecuteJavascriptOpenPopupWithPluginTest(NPP id,
+ NPNetscapeFuncs *host_functions)
+ : PluginTest(id, host_functions),
+ popup_window_test_started_(false) {
+}
+
+int16 ExecuteJavascriptOpenPopupWithPluginTest::SetWindow(
+ NPWindow* window) {
+ if (window->window == NULL)
+ return NPERR_NO_ERROR;
+
+ if (!popup_window_test_started_) {
+ popup_window_test_started_ = true;
+ HostFunctions()->geturl(
+ id(), "popup_window_with_target_plugin.html", "_blank");
+ }
+ return NPERR_NO_ERROR;
+}
+
+// ExecuteJavascriptPopupWindowTargetPluginTest member defines.
+ExecuteJavascriptPopupWindowTargetPluginTest::
+ ExecuteJavascriptPopupWindowTargetPluginTest(
+ NPP id, NPNetscapeFuncs* host_functions)
+ : PluginTest(id, host_functions),
+ test_completed_(false) {
+}
+
+int16 ExecuteJavascriptPopupWindowTargetPluginTest::SetWindow(
+ NPWindow* window) {
+ if (window->window == NULL)
+ return NPERR_NO_ERROR;
+
+ if (!test_completed_) {
+ if (CheckWindow(window)) {
+ SignalTestCompleted();
+ test_completed_ = true;
+ }
+ }
+ return PluginTest::SetWindow(window);
+}
+
+#if defined(OS_WIN)
+bool ExecuteJavascriptPopupWindowTargetPluginTest::CheckWindow(
+ NPWindow* window) {
+ HWND window_handle = reinterpret_cast<HWND>(window->window);
+
+ if (IsWindow(window_handle)) {
+ HWND parent_window = GetParent(window_handle);
+ if (!IsWindow(parent_window) || parent_window == GetDesktopWindow())
+ SetError("Windowed plugin instantiated with NULL parent");
+ return true;
+ }
+
+ return false;
+}
+
+#elif defined(USE_X11)
+// This code blindly follows the same sorts of verifications done on
+// the Windows side. Does it make sense on X? Maybe not really, but
+// it can't hurt to do extra validations.
+bool ExecuteJavascriptPopupWindowTargetPluginTest::CheckWindow(
+ NPWindow* window) {
+ Window xwindow = reinterpret_cast<Window>(window->window);
+ // Grab a pointer to the extra SetWindow data so we can grab the display out.
+ NPSetWindowCallbackStruct* extra =
+ static_cast<NPSetWindowCallbackStruct*>(window->ws_info);
+
+ if (xwindow) {
+ Window root, parent;
+ Status status = XQueryTree(extra->display, xwindow, &root, &parent,
+ NULL, NULL); // NULL children info.
+ DCHECK(status != 0);
+ if (!parent || parent == root)
+ SetError("Windowed plugin instantiated with NULL parent");
+ return true;
+ }
+
+ return false;
+}
+#elif defined(OS_MACOSX)
+bool ExecuteJavascriptPopupWindowTargetPluginTest::CheckWindow(
+ NPWindow* window) {
+ // TODO(port) scaffolding--replace with a real test once NPWindow is done.
+ return false;
+}
+#endif
+
+} // namespace NPAPIClient
diff --git a/webkit/glue/plugins/test/plugin_javascript_open_popup.h b/webkit/glue/plugins/test/plugin_javascript_open_popup.h
new file mode 100644
index 0000000..552397a
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_javascript_open_popup.h
@@ -0,0 +1,47 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_JAVASCRIPT_OPEN_POPUP_H
+#define WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_JAVASCRIPT_OPEN_POPUP_H
+
+#include "webkit/glue/plugins/test/plugin_test.h"
+
+namespace NPAPIClient {
+
+// This class tests the case where a windowed plugin instance is
+// instantiated in a popup window. The plugin instance needs to
+// have a valid parent window.
+class ExecuteJavascriptOpenPopupWithPluginTest : public PluginTest {
+ public:
+ // Constructor.
+ ExecuteJavascriptOpenPopupWithPluginTest(
+ NPP id, NPNetscapeFuncs *host_functions);
+ // NPAPI SetWindow handler.
+ virtual NPError SetWindow(NPWindow* window);
+
+ private:
+ bool popup_window_test_started_;
+};
+
+// This class represents a windowed plugin instance instantiated within a
+// popup window. It verifies that the plugin instance has a valid parent.
+class ExecuteJavascriptPopupWindowTargetPluginTest : public PluginTest {
+ public:
+ ExecuteJavascriptPopupWindowTargetPluginTest(
+ NPP id, NPNetscapeFuncs *host_functions);
+ // NPAPI SetWindow handler.
+ virtual NPError SetWindow(NPWindow* window);
+
+ private:
+ // Do a platform-specific validation of the passed-in |window|.
+ // E.g. on Windows, verifies window->window is a reasonable HWND.
+ // Returns true if the test should be marked complete.
+ bool CheckWindow(NPWindow* window);
+
+ bool test_completed_;
+};
+
+} // namespace NPAPIClient
+
+#endif // WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_JAVASCRIPT_OPEN_POPUP_H
diff --git a/webkit/glue/plugins/test/plugin_new_fails_test.cc b/webkit/glue/plugins/test/plugin_new_fails_test.cc
new file mode 100644
index 0000000..2feeec6
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_new_fails_test.cc
@@ -0,0 +1,18 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/test/plugin_new_fails_test.h"
+
+namespace NPAPIClient {
+
+NewFailsTest::NewFailsTest(NPP id, NPNetscapeFuncs *host_functions)
+ : PluginTest(id, host_functions) {
+}
+
+NPError NewFailsTest::New(uint16 mode, int16 argc, const char* argn[],
+ const char* argv[], NPSavedData* saved) {
+ return NPERR_GENERIC_ERROR;
+}
+
+} // namespace NPAPIClient
diff --git a/webkit/glue/plugins/test/plugin_new_fails_test.h b/webkit/glue/plugins/test/plugin_new_fails_test.h
new file mode 100644
index 0000000..1acf9e5
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_new_fails_test.h
@@ -0,0 +1,21 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_PLUGIN_NEW_FAILS_TEST_H__
+#define WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_PLUGIN_NEW_FAILS_TEST_H__
+
+#include "webkit/glue/plugins/test/plugin_test.h"
+
+namespace NPAPIClient {
+
+class NewFailsTest : public PluginTest {
+ public:
+ NewFailsTest(NPP id, NPNetscapeFuncs *host_functions);
+ virtual NPError New(uint16 mode, int16 argc, const char* argn[],
+ const char* argv[], NPSavedData* saved);
+};
+
+} // namespace NPAPIClient
+
+#endif // WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_PLUGIN_NPP_NEW_FAILS_TEST_H__
diff --git a/webkit/glue/plugins/test/plugin_npobject_lifetime_test.cc b/webkit/glue/plugins/test/plugin_npobject_lifetime_test.cc
new file mode 100644
index 0000000..b62a764
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_npobject_lifetime_test.cc
@@ -0,0 +1,170 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/test/plugin_npobject_lifetime_test.h"
+
+namespace NPAPIClient {
+
+const int kNPObjectLifetimeTimer = 100;
+const int kNPObjectLifetimeTimerElapse = 2000;
+
+NPObject* NPObjectLifetimeTestInstance2::plugin_instance_object_ = NULL;
+
+NPObjectDeletePluginInNPN_Evaluate*
+ NPObjectDeletePluginInNPN_Evaluate::g_npn_evaluate_test_instance_ = NULL;
+
+NPObjectLifetimeTest::NPObjectLifetimeTest(NPP id,
+ NPNetscapeFuncs *host_functions)
+ : PluginTest(id, host_functions),
+ other_plugin_instance_object_(NULL) {
+}
+
+NPError NPObjectLifetimeTest::SetWindow(NPWindow* pNPWindow) {
+ if (pNPWindow->window == NULL)
+ return NPERR_NO_ERROR;
+
+ HWND window_handle = reinterpret_cast<HWND>(pNPWindow->window);
+ if (!::GetProp(window_handle, L"Plugin_Instance")) {
+ ::SetProp(window_handle, L"Plugin_Instance", this);
+ // We attempt to retreive the NPObject for the plugin instance identified
+ // by the NPObjectLifetimeTestInstance2 class as it may not have been
+ // instantiated yet.
+ SetTimer(window_handle, kNPObjectLifetimeTimer, kNPObjectLifetimeTimerElapse,
+ TimerProc);
+ }
+ return NPERR_NO_ERROR;
+}
+
+void CALLBACK NPObjectLifetimeTest::TimerProc(
+ HWND window, UINT message, UINT timer_id,
+ unsigned long elapsed_milli_seconds) {
+
+ KillTimer(window, kNPObjectLifetimeTimer);
+ NPObjectLifetimeTest* this_instance =
+ reinterpret_cast<NPObjectLifetimeTest*>
+ (::GetProp(window, L"Plugin_Instance"));
+
+ this_instance->other_plugin_instance_object_ =
+ NPObjectLifetimeTestInstance2::plugin_instance_object_;
+ this_instance->HostFunctions()->retainobject(
+ this_instance->other_plugin_instance_object_);
+
+ NPString script;
+ script.UTF8Characters = "javascript:DeleteSecondPluginInstance()";
+ script.UTF8Length = static_cast<uint32_t>(strlen(script.UTF8Characters));
+
+ this_instance->HostFunctions()->geturlnotify(
+ this_instance->id(), "javascript:DeleteSecondPluginInstance()", NULL,
+ reinterpret_cast<void*>(1));
+}
+
+void NPObjectLifetimeTest::URLNotify(const char* url, NPReason reason,
+ void* data) {
+ // Create a "location" identifier.
+ NPIdentifier identifier = HostFunctions()->getstringidentifier("location");
+ // Declare a local variant value.
+ NPVariant variantValue;
+ // Get the location property from the window object (which is another object).
+ bool b1 = HostFunctions()->getproperty(id(), other_plugin_instance_object_,
+ identifier, &variantValue );
+ HostFunctions()->releaseobject(other_plugin_instance_object_);
+ other_plugin_instance_object_ = NULL;
+ // If this test failed, then we'd have crashed by now.
+ SignalTestCompleted();
+}
+
+NPObjectLifetimeTestInstance2::NPObjectLifetimeTestInstance2(
+ NPP id, NPNetscapeFuncs *host_functions)
+ : PluginTest(id, host_functions) {
+}
+
+NPObjectLifetimeTestInstance2::~NPObjectLifetimeTestInstance2() {
+ if (plugin_instance_object_) {
+ HostFunctions()->releaseobject(plugin_instance_object_);
+ plugin_instance_object_ = NULL;
+ }
+}
+
+NPError NPObjectLifetimeTestInstance2::SetWindow(NPWindow* pNPWindow) {
+ if (pNPWindow->window == NULL)
+ return NPERR_NO_ERROR;
+
+ if (!plugin_instance_object_) {
+ if (!HostFunctions()->getvalue(id(), NPNVWindowNPObject,
+ &plugin_instance_object_)) {
+ SetError("Failed to get NPObject for plugin instance2");
+ SignalTestCompleted();
+ return NPERR_GENERIC_ERROR;
+ }
+ }
+
+ return NPERR_NO_ERROR;
+}
+
+
+NPObjectDeletePluginInNPN_Evaluate::NPObjectDeletePluginInNPN_Evaluate(
+ NPP id, NPNetscapeFuncs *host_functions)
+ : PluginTest(id, host_functions),
+ plugin_instance_object_(NULL),
+ npn_evaluate_timer_proc_set_(false) {
+ g_npn_evaluate_test_instance_ = this;
+}
+
+NPObjectDeletePluginInNPN_Evaluate::~NPObjectDeletePluginInNPN_Evaluate() {
+ if (plugin_instance_object_) {
+ HostFunctions()->releaseobject(plugin_instance_object_);
+ plugin_instance_object_ = NULL;
+ }
+}
+
+NPError NPObjectDeletePluginInNPN_Evaluate::SetWindow(NPWindow* np_window) {
+ if (np_window->window == NULL)
+ return NPERR_NO_ERROR;
+
+ HWND window_handle = reinterpret_cast<HWND>(np_window->window);
+ // We setup a timerproc to invoke NPN_Evaluate to destroy this plugin
+ // instance. This is to ensure that we don't destroy the plugin instance
+ // while it is being used in webkit as this leads to crashes and is a
+ // more accurate representation of the renderer crash as described in
+ // http://b/issue?id=1134683.
+ if (!npn_evaluate_timer_proc_set_) {
+ npn_evaluate_timer_proc_set_ = true;
+ SetTimer(window_handle, kNPObjectLifetimeTimer, kNPObjectLifetimeTimerElapse,
+ TimerProc);
+ }
+
+ return NPERR_NO_ERROR;
+}
+
+void CALLBACK NPObjectDeletePluginInNPN_Evaluate::TimerProc(
+ HWND window, UINT message, UINT timer_id,
+ unsigned long elapsed_milli_seconds) {
+
+ KillTimer(window, kNPObjectLifetimeTimer);
+ NPObject *window_obj = NULL;
+ g_npn_evaluate_test_instance_->HostFunctions()->getvalue(
+ g_npn_evaluate_test_instance_->id(), NPNVWindowNPObject,
+ &window_obj);
+
+ if (!window_obj) {
+ g_npn_evaluate_test_instance_->SetError(
+ "Failed to get NPObject for plugin instance2");
+ g_npn_evaluate_test_instance_->SignalTestCompleted();
+ return;
+ }
+
+ std::string script = "javascript:DeletePluginWithinScript()";
+ NPString script_string;
+ script_string.UTF8Characters = script.c_str();
+ script_string.UTF8Length =
+ static_cast<unsigned int>(script.length());
+
+ NPVariant result_var;
+ bool result = g_npn_evaluate_test_instance_->HostFunctions()->evaluate(
+ g_npn_evaluate_test_instance_->id(), window_obj,
+ &script_string, &result_var);
+ // If this test failed we would have crashed by now.
+}
+
+} // namespace NPAPIClient
diff --git a/webkit/glue/plugins/test/plugin_npobject_lifetime_test.h b/webkit/glue/plugins/test/plugin_npobject_lifetime_test.h
new file mode 100644
index 0000000..4303d99
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_npobject_lifetime_test.h
@@ -0,0 +1,81 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_NPOBJECT_LIFETIME_TEST_H__
+#define WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_NPOBJECT_LIFETIME_TEST_H__
+
+#include "build/build_config.h"
+#include "webkit/glue/plugins/test/plugin_test.h"
+
+namespace NPAPIClient {
+
+// The NPObjectLifeTime class tests the case where a plugin has an NPObject
+// which points to a different plugin instance on a different frame in the
+// page and whether refcounts on this npobject are valid when the source frame
+// is destroyed.
+class NPObjectLifetimeTest : public PluginTest {
+ public:
+ // Constructor.
+ NPObjectLifetimeTest(NPP id, NPNetscapeFuncs *host_functions);
+
+ // NPAPI SetWindow handler.
+ virtual NPError SetWindow(NPWindow* pNPWindow);
+
+ virtual void URLNotify(const char* url, NPReason reason, void* data);
+
+ protected:
+ NPObject* other_plugin_instance_object_;
+
+#if defined(OS_WIN)
+ static void CALLBACK TimerProc(HWND window, UINT message, UINT timer_id,
+ unsigned long elapsed_milli_seconds);
+#endif
+ DISALLOW_IMPLICIT_CONSTRUCTORS(NPObjectLifetimeTest);
+};
+
+// The NPObjectLifetimeTestInstance2 class represents the plugin instance
+// which is deleted by the NPObjectLifeTime class via a javascript function.
+class NPObjectLifetimeTestInstance2 : public PluginTest {
+ public:
+ // Constructor.
+ NPObjectLifetimeTestInstance2(NPP id, NPNetscapeFuncs *host_functions);
+ ~NPObjectLifetimeTestInstance2();
+
+ // NPAPI SetWindow handler.
+ virtual NPError SetWindow(NPWindow* pNPWindow);
+ protected:
+ static NPObject* plugin_instance_object_;
+ friend class NPObjectLifetimeTest;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(NPObjectLifetimeTestInstance2);
+};
+
+// The NPObjectLifeTime class tests the case where a plugin instance is
+// destroyed in NPN_Evaluate
+class NPObjectDeletePluginInNPN_Evaluate : public PluginTest {
+ public:
+ // Constructor.
+ NPObjectDeletePluginInNPN_Evaluate(NPP id, NPNetscapeFuncs *host_functions);
+ ~NPObjectDeletePluginInNPN_Evaluate();
+
+ // NPAPI SetWindow handler.
+ virtual NPError SetWindow(NPWindow* pNPWindow);
+
+ protected:
+ NPObject* plugin_instance_object_;
+#if defined(OS_WIN)
+ static void CALLBACK TimerProc(HWND window, UINT message, UINT timer_id,
+ unsigned long elapsed_milli_seconds);
+#endif
+
+ private:
+ bool npn_evaluate_timer_proc_set_;
+ static NPObjectDeletePluginInNPN_Evaluate* g_npn_evaluate_test_instance_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(NPObjectDeletePluginInNPN_Evaluate);
+};
+
+} // namespace NPAPIClient
+
+#endif // WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_NPOBJECT_LIFETIME_TEST_H__
diff --git a/webkit/glue/plugins/test/plugin_npobject_proxy_test.cc b/webkit/glue/plugins/test/plugin_npobject_proxy_test.cc
new file mode 100644
index 0000000..5b3a2ca
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_npobject_proxy_test.cc
@@ -0,0 +1,51 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+
+#if defined(OS_WIN)
+#define STRSAFE_NO_DEPRECATE
+#include <strsafe.h>
+#endif
+#include "webkit/glue/plugins/test/plugin_npobject_proxy_test.h"
+
+namespace NPAPIClient {
+
+NPObjectProxyTest::NPObjectProxyTest(NPP id, NPNetscapeFuncs *host_functions)
+ : PluginTest(id, host_functions) {
+}
+
+NPError NPObjectProxyTest::SetWindow(NPWindow* pNPWindow) {
+ if (pNPWindow->window == NULL)
+ return NPERR_NO_ERROR;
+
+ NPIdentifier document_id = HostFunctions()->getstringidentifier("document");
+ NPIdentifier create_text_node_id = HostFunctions()->getstringidentifier("createTextNode");
+ NPIdentifier append_child_id = HostFunctions()->getstringidentifier("appendChild");
+
+ NPVariant docv;
+ NPObject *window_obj = NULL;
+ HostFunctions()->getvalue(id(), NPNVWindowNPObject, &window_obj);
+
+ HostFunctions()->getproperty(id(), window_obj, document_id, &docv);
+ NPObject *doc = NPVARIANT_TO_OBJECT(docv);
+
+ NPVariant strv;
+ MSVC_SUPPRESS_WARNING(4267);
+ STRINGZ_TO_NPVARIANT("div", strv);
+
+ NPVariant textv;
+ HostFunctions()->invoke(id(), doc, create_text_node_id, &strv, 1, &textv);
+
+ NPVariant v;
+ HostFunctions()->invoke(id(), doc, append_child_id, &textv, 1, &v);
+
+ // If this test failed, then we'd have crashed by now.
+ SignalTestCompleted();
+
+ return NPERR_NO_ERROR;
+}
+
+} // namespace NPAPIClient
diff --git a/webkit/glue/plugins/test/plugin_npobject_proxy_test.h b/webkit/glue/plugins/test/plugin_npobject_proxy_test.h
new file mode 100644
index 0000000..3d14ddb
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_npobject_proxy_test.h
@@ -0,0 +1,27 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_NPOBJECT_PROXY_TEST_H__
+#define WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_NPOBJECT_PROXY_TEST_H__
+
+#include "webkit/glue/plugins/test/plugin_test.h"
+
+namespace NPAPIClient {
+
+// The NPObjectProxyTest tests that when we proxy an NPObject that is itself
+// a proxy, we don't create a new proxy but instead just use the original
+// pointer.
+
+class NPObjectProxyTest : public PluginTest {
+ public:
+ // Constructor.
+ NPObjectProxyTest(NPP id, NPNetscapeFuncs *host_functions);
+
+ // NPAPI SetWindow handler.
+ virtual NPError SetWindow(NPWindow* pNPWindow);
+};
+
+} // namespace NPAPIClient
+
+#endif // WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_NPOBJECT_PROXY_TEST_H__
diff --git a/webkit/glue/plugins/test/plugin_private_test.cc b/webkit/glue/plugins/test/plugin_private_test.cc
new file mode 100644
index 0000000..cdab7ce
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_private_test.cc
@@ -0,0 +1,57 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/test/plugin_private_test.h"
+
+#include "base/basictypes.h"
+#include "base/string_util.h"
+#include "webkit/glue/plugins/test/plugin_client.h"
+
+namespace NPAPIClient {
+
+PrivateTest::PrivateTest(NPP id, NPNetscapeFuncs *host_functions)
+ : PluginTest(id, host_functions) {
+}
+
+NPError PrivateTest::New(uint16 mode, int16 argc,
+ const char* argn[], const char* argv[],
+ NPSavedData* saved) {
+ PluginTest::New(mode, argc, argn, argv, saved);
+
+ NPBool private_mode = 0;
+ NPNetscapeFuncs* browser = NPAPIClient::PluginClient::HostFunctions();
+ NPError result = browser->getvalue(
+ id(), NPNVprivateModeBool, &private_mode);
+ if (result != NPERR_NO_ERROR) {
+ SetError("Failed to read NPNVprivateModeBool value.");
+ } else {
+ NPIdentifier location = HostFunctions()->getstringidentifier("location");
+ NPIdentifier href = HostFunctions()->getstringidentifier("href");
+
+ NPObject *window_obj = NULL;
+ HostFunctions()->getvalue(id(), NPNVWindowNPObject, &window_obj);
+
+ NPVariant location_var;
+ HostFunctions()->getproperty(id(), window_obj, location, &location_var);
+
+ NPVariant href_var;
+ HostFunctions()->getproperty(id(), NPVARIANT_TO_OBJECT(location_var), href,
+ &href_var);
+ std::string href_str(href_var.value.stringValue.UTF8Characters,
+ href_var.value.stringValue.UTF8Length);
+ bool private_expected = href_str.find("?private") != href_str.npos;
+ if (private_mode != static_cast<NPBool>(private_expected))
+ SetError("NPNVprivateModeBool returned incorrect value.");
+
+ HostFunctions()->releasevariantvalue(&href_var);
+ HostFunctions()->releasevariantvalue(&location_var);
+ HostFunctions()->releaseobject(window_obj);
+ }
+
+ SignalTestCompleted();
+
+ return NPERR_NO_ERROR;
+}
+
+} // namespace NPAPIClient
diff --git a/webkit/glue/plugins/test/plugin_private_test.h b/webkit/glue/plugins/test/plugin_private_test.h
new file mode 100644
index 0000000..db6b5d1
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_private_test.h
@@ -0,0 +1,25 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_PORT_PLUGINS_TEST_PLUGIN_PRIVATE_TEST_H_
+#define WEBKIT_PORT_PLUGINS_TEST_PLUGIN_PRIVATE_TEST_H_
+
+#include "webkit/glue/plugins/test/plugin_test.h"
+
+namespace NPAPIClient {
+
+// The PluginPrivateTest tests that a plugin can query if the browser is in
+// private browsing mode.
+class PrivateTest : public PluginTest {
+ public:
+ PrivateTest(NPP id, NPNetscapeFuncs *host_functions);
+
+ // Initialize this PluginTest based on the arguments from NPP_New.
+ virtual NPError New(uint16 mode, int16 argc, const char* argn[],
+ const char* argv[], NPSavedData* saved);
+};
+
+} // namespace NPAPIClient
+
+#endif // WEBKIT_PORT_PLUGINS_TEST_PLUGIN_PRIVATE_TEST_H_
diff --git a/webkit/glue/plugins/test/plugin_schedule_timer_test.cc b/webkit/glue/plugins/test/plugin_schedule_timer_test.cc
new file mode 100644
index 0000000..fbfce34
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_schedule_timer_test.cc
@@ -0,0 +1,116 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/test/plugin_schedule_timer_test.h"
+#include "webkit/glue/plugins/test/plugin_client.h"
+
+using base::Time;
+
+namespace NPAPIClient {
+
+// The times below are accurate but they are not tested against because it
+// might make the test flakey.
+ScheduleTimerTest::Event
+ ScheduleTimerTest::schedule_[ScheduleTimerTest::kNumEvents] = {
+ { 0, -1, 0, 100, false, -1 }, // schedule 0 100ms no-repeat
+ { 100, 0, 0, 200, false, -1 }, // schedule 0 200ms no-repeat
+ { 300, 0, 0, 100, true, -1 }, // schedule 0 100ms repeat
+ { 400, 0, 1, 50, true, -1 }, // schedule 1 50ms repeat
+ { 450, 1, -1, 0, true, -1 }, // receive 1 repeating
+ { 500, 0, -1, 0, true, -1 }, // receive 0 repeating
+ { 500, 1, -1, 0, true, -1 }, // receive 1 repeating
+ { 550, 1, -1, 0, true, -1 }, // receive 1 repeating
+ { 600, 0, -1, 0, true, 0 }, // receive 0 repeating and unschedule
+ { 600, 1, 2, 400, true, 1 }, // receive 1 repeating and unschedule
+ { 1000, 2, -1, 0, true, 2 }, // receive final and unschedule
+};
+
+ScheduleTimerTest::ScheduleTimerTest(
+ NPP id, NPNetscapeFuncs *host_functions)
+ : PluginTest(id, host_functions),
+ num_received_events_(0) {
+ for (int i = 0; i < kNumTimers; ++i) {
+ timer_ids_[i] = 0;
+ }
+ for (int i = 0; i < kNumEvents; ++i) {
+ received_events_[i] = false;
+ }
+}
+
+NPError ScheduleTimerTest::New(
+ uint16 mode, int16 argc, const char* argn[], const char* argv[],
+ NPSavedData* saved) {
+ NPError error = PluginTest::New(mode, argc, argn, argv, saved);
+ if (error != NPERR_NO_ERROR)
+ return error;
+
+ start_time_ = Time::Now();
+ HandleEvent(0);
+
+ return NPERR_NO_ERROR;
+}
+
+void ScheduleTimerTest::OnTimer(uint32 timer_id) {
+ Time current_time = Time::Now();
+ int relative_time = static_cast<int>(
+ (current_time - start_time_).InMilliseconds());
+
+ // See if there is a matching unreceived event.
+ int event_index = FindUnreceivedEvent(relative_time, timer_id);
+ if (event_index < 0) {
+ SetError("Received unexpected timer event");
+ SignalTestCompleted();
+ return;
+ }
+
+ HandleEvent(event_index);
+
+ // Finish test if all events have happened.
+ if (num_received_events_ == kNumEvents)
+ SignalTestCompleted();
+}
+
+int ScheduleTimerTest::FindUnreceivedEvent(int time, uint32 timer_id) {
+ for (int i = 0; i < kNumEvents; ++i) {
+ const Event& event = schedule_[i];
+ if (!received_events_[i] &&
+ timer_ids_[event.received_index] == timer_id) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+namespace {
+void OnTimerHelper(NPP id, uint32 timer_id) {
+ ScheduleTimerTest* plugin_object =
+ static_cast<ScheduleTimerTest*>(id->pdata);
+ if (plugin_object) {
+ plugin_object->OnTimer(timer_id);
+ }
+}
+}
+
+void ScheduleTimerTest::HandleEvent(int event_index) {
+ const Event& event = schedule_[event_index];
+
+ // Mark event as received.
+ DCHECK(!received_events_[event_index]);
+ received_events_[event_index] = true;
+ ++num_received_events_;
+
+ // Unschedule timer if present.
+ if (event.unscheduled_index >= 0) {
+ HostFunctions()->unscheduletimer(
+ id(), timer_ids_[event.unscheduled_index]);
+ }
+
+ // Schedule timer if present.
+ if (event.scheduled_index >= 0) {
+ timer_ids_[event.scheduled_index] = HostFunctions()->scheduletimer(
+ id(), event.scheduled_interval, event.schedule_repeated, OnTimerHelper);
+ }
+}
+
+} // namespace NPAPIClient
diff --git a/webkit/glue/plugins/test/plugin_schedule_timer_test.h b/webkit/glue/plugins/test/plugin_schedule_timer_test.h
new file mode 100644
index 0000000..9ca947f
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_schedule_timer_test.h
@@ -0,0 +1,70 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_SCHEDULE_TIMER_TEST_H
+#define WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_SCHEDULE_TIMER_TEST_H
+
+#include <vector>
+
+#include "base/at_exit.h"
+#include "base/time.h"
+#include "webkit/glue/plugins/test/plugin_test.h"
+
+namespace NPAPIClient {
+
+// This class tests scheduling and unscheduling of timers using
+// NPN_ScheduleTimer and NPN_UnscheduleTimer.
+class ScheduleTimerTest : public PluginTest {
+ public:
+ ScheduleTimerTest(NPP id, NPNetscapeFuncs *host_functions);
+
+ virtual NPError New(uint16 mode, int16 argc, const char* argn[],
+ const char* argv[], NPSavedData* saved);
+
+ void OnTimer(uint32 timer_id);
+
+ private:
+ // base::Time needs one of these.
+ base::AtExitManager at_exit_manager_;
+
+ // Table mapping timer index (as used in event schedule) to timer id.
+ static const int kNumTimers = 3;
+ uint32 timer_ids_[kNumTimers];
+
+ // Schedule of events for test.
+ static const int kNumEvents = 11;
+ struct Event {
+ int time;
+
+ // The index of the timer that triggered the event or -1 for the first
+ // event.
+ int received_index;
+
+ // The index of the timer to schedule on this event or -1.
+ int scheduled_index;
+
+ // Info about the timer to be scheduled (if any).
+ uint32 scheduled_interval;
+ bool schedule_repeated;
+
+ // The index of the timer to unschedule on this event or -1.
+ int unscheduled_index;
+ };
+ static Event schedule_[kNumEvents];
+ int num_received_events_;
+
+ // Set of events that have been received (by index).
+ bool received_events_[kNumEvents];
+
+ // Time of initial event.
+ base::Time start_time_;
+
+ // Returns index of matching unreceived event or -1 if not found.
+ int FindUnreceivedEvent(int time, uint32 timer_id);
+ void HandleEvent(int event_index);
+};
+
+} // namespace NPAPIClient
+
+#endif // WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_SCHEDULE_TIMER_TEST_H
diff --git a/webkit/glue/plugins/test/plugin_test.cc b/webkit/glue/plugins/test/plugin_test.cc
new file mode 100644
index 0000000..141c91a
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_test.cc
@@ -0,0 +1,150 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/test/plugin_test.h"
+
+#include "base/string_util.h"
+#include "webkit/glue/plugins/test/npapi_constants.h"
+
+namespace NPAPIClient {
+
+PluginTest::PluginTest(NPP id, NPNetscapeFuncs *host_functions) {
+ id_ = id;
+ id_->pdata = this;
+ host_functions_ = host_functions;
+ test_completed_ = false;
+}
+
+NPError PluginTest::New(uint16 mode, int16 argc, const char* argn[],
+ const char* argv[], NPSavedData* saved) {
+ test_name_ = this->GetArgValue("name", argc, argn, argv);
+ test_id_ = this->GetArgValue("id", argc, argn, argv);
+ return NPERR_NO_ERROR;
+}
+
+NPError PluginTest::Destroy() {
+ return NPERR_NO_ERROR;
+}
+
+NPError PluginTest::SetWindow(NPWindow* pNPWindow) {
+ return NPERR_NO_ERROR;
+}
+
+// It's a shame I have to implement URLEncode. But, using webkit's
+// or using chrome's means a ball of string of dlls and dependencies that
+// is very very long. After spending far too much time on it,
+// I'll just encode it myself. Too bad Microsoft doesn't implement
+// this in a reusable way either. Both webkit and chrome will
+// end up using libicu, which is a string of dependencies we don't
+// want.
+
+inline unsigned char toHex(const unsigned char x) {
+ return x > 9 ? (x + 'A' - 10) : (x + '0');
+}
+
+std::string URLEncode(const std::string &sIn) {
+ std::string sOut;
+
+ const size_t length = sIn.length();
+ for (size_t idx = 0; idx < length;) {
+ const char ch = sIn.at(idx);
+ if (isalnum(ch)) {
+ sOut.append(1, ch);
+ } else if (isspace(ch) && ((ch != '\n') && (ch != '\r'))) {
+ sOut.append(1, '+');
+ } else {
+ sOut.append(1, '%');
+ sOut.append(1, toHex(ch>>4));
+ sOut.append(1, toHex(ch%16));
+ }
+ idx++;
+ }
+ return sOut;
+}
+
+void PluginTest::SignalTestCompleted() {
+ NPObject *window_obj = NULL;
+ host_functions_->getvalue(id_, NPNVWindowNPObject, &window_obj);
+ if (!window_obj)
+ return;
+
+ test_completed_ = true;
+ // To signal test completion, we expect a couple of
+ // javascript functions to be defined in the webpage
+ // which hosts this plugin:
+ // onSuccess(test_name, test_id)
+ // onFailure(test_name, test_id, error_message)
+ std::string script("javascript:");
+ if (Succeeded()) {
+ script.append("onSuccess(\"");
+ script.append(test_name_);
+ script.append("\",\"");
+ script.append(test_id_);
+ script.append("\");");
+ } else {
+ script.append("onFailure(\"");
+ script.append(test_name_);
+ script.append("\",\"");
+ script.append(test_id_);
+ script.append("\",\"");
+ script.append(test_status_);
+ script.append("\");");
+ }
+
+ NPString script_string;
+ script_string.UTF8Characters = script.c_str();
+ script_string.UTF8Length = static_cast<unsigned int>(script.length());
+
+ NPVariant result_var;
+ host_functions_->evaluate(id_, window_obj, &script_string, &result_var);
+}
+
+const char *PluginTest::GetArgValue(const char *name, const int16 argc,
+ const char *argn[], const char *argv[]) {
+ for (int idx = 0; idx < argc; idx++)
+ if (base::strcasecmp(argn[idx], name) == 0)
+ return argv[idx];
+ return NULL;
+}
+
+void PluginTest::SetError(const std::string &msg) {
+ test_status_.append(msg);
+}
+
+NPError PluginTest::NewStream(NPMIMEType type, NPStream* stream,
+ NPBool seekable, uint16* stype) {
+ // There is no default action here.
+ return NPERR_NO_ERROR;
+}
+
+int32 PluginTest::WriteReady(NPStream *stream) {
+ // Take data in small chunks
+ return 4096;
+}
+
+int32 PluginTest::Write(NPStream *stream, int32 offset, int32 len,
+ void *buffer) {
+ // Pretend that we took all the data.
+ return len;
+}
+
+NPError PluginTest::DestroyStream(NPStream *stream, NPError reason) {
+ // There is no default action.
+ return NPERR_NO_ERROR;
+}
+
+void PluginTest::StreamAsFile(NPStream* stream, const char* fname) {
+ // There is no default action.
+}
+
+void PluginTest::URLNotify(const char* url, NPReason reason, void* data) {
+ // There is no default action
+}
+
+int16 PluginTest::HandleEvent(void* event) {
+ // There is no default action
+ return 0;
+}
+
+} // namespace NPAPIClient
diff --git a/webkit/glue/plugins/test/plugin_test.h b/webkit/glue/plugins/test/plugin_test.h
new file mode 100644
index 0000000..f06307e
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_test.h
@@ -0,0 +1,131 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_PORT_PLUGINS_TEST_PLUGIN_TEST_H__
+#define WEBKIT_PORT_PLUGINS_TEST_PLUGIN_TEST_H__
+
+#include <string>
+
+#include "base/string_util.h"
+#include "third_party/npapi/bindings/npapi.h"
+#include "third_party/npapi/bindings/nphostapi.h"
+
+namespace NPAPIClient {
+
+// A PluginTest represents an instance of the plugin, which in
+// our case is a test case.
+class PluginTest {
+ public:
+ // Constructor.
+ PluginTest(NPP id, NPNetscapeFuncs *host_functions);
+
+ // Destructor
+ virtual ~PluginTest() {}
+
+ // Returns true if the test runs in windowless plugin mode.
+ virtual bool IsWindowless() const { return false; }
+
+ //
+ // NPAPI Functions
+ //
+ virtual NPError New(uint16 mode, int16 argc, const char* argn[],
+ const char* argv[], NPSavedData* saved);
+ virtual NPError Destroy();
+ virtual NPError SetWindow(NPWindow* pNPWindow);
+ virtual NPError NewStream(NPMIMEType type, NPStream* stream,
+ NPBool seekable, uint16* stype);
+ virtual int32 WriteReady(NPStream *stream);
+ virtual int32 Write(NPStream *stream, int32 offset, int32 len,
+ void *buffer);
+ virtual NPError DestroyStream(NPStream *stream, NPError reason);
+ virtual void StreamAsFile(NPStream* stream, const char* fname);
+ virtual void URLNotify(const char* url, NPReason reason, void* data);
+ virtual int16 HandleEvent(void* event);
+ // Returns true if the test has not had any errors.
+ bool Succeeded() { return test_status_.length() == 0; }
+
+ // Sets an error for the test case. Appends the msg to the
+ // error that will be returned from the test.
+ void SetError(const std::string &msg);
+
+ // Expect two string values are equal, and if not, logs an
+ // appropriate error about it.
+ void ExpectStringLowerCaseEqual(const std::string &val1, const std::string &val2) {
+ if (!LowerCaseEqualsASCII(val1, val2.c_str())) {
+ std::string err;
+ err = "Expected Equal for '";
+ err.append(val1);
+ err.append("' and '");
+ err.append(val2);
+ err.append("'");
+ SetError(err);
+ }
+ };
+
+ // Expect two values to not be equal, and if they are
+ // logs an appropriate error about it.
+ void ExpectAsciiStringNotEqual(const char *val1, const char *val2) {
+ if (val1 == val2) {
+ std::string err;
+ err = "Expected Not Equal for '";
+ err.append(val1);
+ err.append("' and '");
+ err.append(val2);
+ err.append("'");
+ SetError(err);
+ }
+ }
+ // Expect two integer values are equal, and if not, logs an
+ // appropriate error about it.
+ void ExpectIntegerEqual(int val1, int val2) {
+ if (val1 != val2) {
+ std::string err;
+ err = "Expected Equal for '";
+ err.append(IntToString(val1));
+ err.append("' and '");
+ err.append(IntToString(val2));
+ err.append("'");
+ SetError(err);
+ }
+ }
+
+
+ protected:
+ // Signals to the Test that invoked us that the test is
+ // completed. This is done by forcing the plugin to
+ // set a cookie in the browser window, which the test program
+ // is waiting for. Note - because this is done by
+ // using javascript, the browser must have the frame
+ // setup before the plugin calls this function. So plugin
+ // tests MUST NOT call this function prior to having
+ // received the SetWindow() callback from the browser.
+ void SignalTestCompleted();
+
+ // Helper function to lookup names in the input array.
+ // If the name is found, returns the value, otherwise
+ // returns NULL.
+ const char *GetArgValue(const char *name, const int16 argc,
+ const char *argn[], const char *argv[]);
+
+ // Access to the list of functions provided
+ // by the NPAPI host.
+ NPNetscapeFuncs *HostFunctions() { return host_functions_; }
+
+ // The NPP Identifier for this plugin instance.
+ NPP id() { return id_; }
+ std::string test_id() const { return test_id_; }
+ std::string test_name() const { return test_name_; }
+ bool test_completed() const { return test_completed_; }
+ private:
+ NPP id_;
+ NPNetscapeFuncs * host_functions_;
+ std::string test_name_;
+ std::string test_id_;
+ std::string test_status_;
+ bool test_completed_;
+};
+
+} // namespace NPAPIClient
+
+#endif // WEBKIT_PORT_PLUGINS_TEST_PLUGIN_TEST_H__
diff --git a/webkit/glue/plugins/test/plugin_test_factory.cc b/webkit/glue/plugins/test/plugin_test_factory.cc
new file mode 100644
index 0000000..e2b42b3
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_test_factory.cc
@@ -0,0 +1,97 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/test/plugin_test_factory.h"
+
+#include "webkit/glue/plugins/test/plugin_arguments_test.h"
+#include "webkit/glue/plugins/test/plugin_delete_plugin_in_stream_test.h"
+#include "webkit/glue/plugins/test/plugin_get_javascript_url_test.h"
+#include "webkit/glue/plugins/test/plugin_get_javascript_url2_test.h"
+#include "webkit/glue/plugins/test/plugin_geturl_test.h"
+#include "webkit/glue/plugins/test/plugin_javascript_open_popup.h"
+#include "webkit/glue/plugins/test/plugin_new_fails_test.h"
+#include "webkit/glue/plugins/test/plugin_npobject_lifetime_test.h"
+#include "webkit/glue/plugins/test/plugin_npobject_proxy_test.h"
+#include "webkit/glue/plugins/test/plugin_private_test.h"
+#include "webkit/glue/plugins/test/plugin_schedule_timer_test.h"
+#include "webkit/glue/plugins/test/plugin_thread_async_call_test.h"
+#include "webkit/glue/plugins/test/plugin_window_size_test.h"
+#if defined(OS_WIN)
+#include "webkit/glue/plugins/test/plugin_windowed_test.h"
+#endif
+#include "webkit/glue/plugins/test/plugin_windowless_test.h"
+
+namespace NPAPIClient {
+
+PluginTest* CreatePluginTest(const std::string& test_name,
+ NPP instance,
+ NPNetscapeFuncs* host_functions) {
+ PluginTest* new_test = NULL;
+
+ if (test_name == "arguments") {
+ new_test = new PluginArgumentsTest(instance, host_functions);
+ } else if (test_name == "geturl" || test_name == "geturl_404_response" ||
+ test_name == "geturl_fail_write" ||
+ test_name == "plugin_referrer_test") {
+ new_test = new PluginGetURLTest(instance, host_functions);
+ } else if (test_name == "npobject_proxy") {
+ new_test = new NPObjectProxyTest(instance, host_functions);
+#if defined(OS_WIN) || defined(OS_MACOSX)
+ // TODO(port): plugin_windowless_test.*.
+ } else if (test_name == "execute_script_delete_in_paint" ||
+ test_name == "execute_script_delete_in_mouse_move" ||
+ test_name == "delete_frame_test" ||
+ test_name == "multiple_instances_sync_calls" ||
+ test_name == "no_hang_if_init_crashes" ||
+ test_name == "convert_point") {
+ new_test = new WindowlessPluginTest(instance, host_functions);
+#endif
+ } else if (test_name == "getjavascripturl") {
+ new_test = new ExecuteGetJavascriptUrlTest(instance, host_functions);
+ } else if (test_name == "getjavascripturl2") {
+ new_test = new ExecuteGetJavascriptUrl2Test(instance, host_functions);
+#if defined(OS_WIN)
+ // TODO(port): plugin_window_size_test.*.
+ } else if (test_name == "checkwindowrect") {
+ new_test = new PluginWindowSizeTest(instance, host_functions);
+#endif
+ } else if (test_name == "self_delete_plugin_stream") {
+ new_test = new DeletePluginInStreamTest(instance, host_functions);
+#if defined(OS_WIN)
+ // TODO(port): plugin_npobject_lifetime_test.*.
+ } else if (test_name == "npobject_lifetime_test") {
+ new_test = new NPObjectLifetimeTest(instance, host_functions);
+ } else if (test_name == "npobject_lifetime_test_second_instance") {
+ new_test = new NPObjectLifetimeTestInstance2(instance, host_functions);
+ } else if (test_name == "new_fails") {
+ new_test = new NewFailsTest(instance, host_functions);
+ } else if (test_name == "npobject_delete_plugin_in_evaluate") {
+ new_test = new NPObjectDeletePluginInNPN_Evaluate(instance, host_functions);
+#endif
+ } else if (test_name == "plugin_javascript_open_popup_with_plugin") {
+ new_test = new ExecuteJavascriptOpenPopupWithPluginTest(
+ instance, host_functions);
+ } else if (test_name == "plugin_popup_with_plugin_target") {
+ new_test = new ExecuteJavascriptPopupWindowTargetPluginTest(
+ instance, host_functions);
+ } else if (test_name == "plugin_thread_async_call") {
+ new_test = new PluginThreadAsyncCallTest(instance, host_functions);
+ } else if (test_name == "private") {
+ new_test = new PrivateTest(instance, host_functions);
+ } else if (test_name == "schedule_timer") {
+ new_test = new ScheduleTimerTest(instance, host_functions);
+#if defined(OS_WIN)
+ // TODO(port): plugin_windowed_test.*.
+ } else if (test_name == "hidden_plugin" ||
+ test_name == "create_instance_in_paint" ||
+ test_name == "alert_in_window_message" ||
+ test_name == "ensure_scripting_works_in_destroy") {
+ new_test = new WindowedPluginTest(instance, host_functions);
+#endif
+ }
+
+ return new_test;
+}
+
+} // namespace NPAPIClient
diff --git a/webkit/glue/plugins/test/plugin_test_factory.h b/webkit/glue/plugins/test/plugin_test_factory.h
new file mode 100644
index 0000000..3fd38d5
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_test_factory.h
@@ -0,0 +1,22 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_PORT_PLUGINS_TEST_PLUGIN_TEST_FACTROY_H__
+#define WEBKIT_PORT_PLUGINS_TEST_PLUGIN_TEST_FACTROY_H__
+
+#include <string>
+
+#include "third_party/npapi/bindings/nphostapi.h"
+
+namespace NPAPIClient {
+
+class PluginTest;
+
+extern PluginTest* CreatePluginTest(const std::string& test_name,
+ NPP instance,
+ NPNetscapeFuncs* host_functions);
+
+} // namespace NPAPIClient
+
+#endif // WEBKIT_PORT_PLUGINS_TEST_PLUGIN_TEST_FACTROY_H__
diff --git a/webkit/glue/plugins/test/plugin_thread_async_call_test.cc b/webkit/glue/plugins/test/plugin_thread_async_call_test.cc
new file mode 100644
index 0000000..2e9f9e9
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_thread_async_call_test.cc
@@ -0,0 +1,116 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/test/plugin_thread_async_call_test.h"
+
+#include "base/message_loop.h"
+#include "base/thread.h"
+#include "webkit/glue/plugins/test/plugin_client.h"
+
+namespace NPAPIClient {
+
+namespace {
+
+// There are two plugin instances in this test. The long lived instance is used
+// for reporting errors and signalling test completion. The short lived one is
+// used to verify that async callbacks are not invoked after NPP_Destroy.
+PluginThreadAsyncCallTest* g_short_lived_instance;
+PluginThreadAsyncCallTest* g_long_lived_instance;
+
+void OnCallSucceededHelper(void* data) {
+ static_cast<PluginThreadAsyncCallTest*>(data)->OnCallSucceeded();
+}
+
+class AsyncCallTask : public Task {
+ public:
+ AsyncCallTask(PluginThreadAsyncCallTest* test_class)
+ : test_class_(test_class) {}
+
+ void Run() {
+ test_class_->AsyncCall();
+ }
+
+ private:
+ PluginThreadAsyncCallTest* test_class_;
+};
+
+void OnCallFailed(void* data) {
+ g_long_lived_instance->SetError("Async callback invoked after NPP_Destroy");
+}
+
+void OnCallCompletedHelper(void* data) {
+ static_cast<PluginThreadAsyncCallTest*>(data)->OnCallCompleted();
+}
+}
+
+PluginThreadAsyncCallTest::PluginThreadAsyncCallTest(
+ NPP id, NPNetscapeFuncs *host_functions)
+ : PluginTest(id, host_functions) {
+}
+
+NPError PluginThreadAsyncCallTest::New(
+ uint16 mode, int16 argc, const char* argn[], const char* argv[],
+ NPSavedData* saved) {
+ NPError error = PluginTest::New(mode, argc, argn, argv, saved);
+ if (error != NPERR_NO_ERROR)
+ return error;
+
+ // Determine whether this is the short lived instance.
+ for (int i = 0; i < argc; ++i) {
+ if (base::strcasecmp(argn[i], "short_lived") == 0) {
+ if (base::strcasecmp(argv[i], "true") == 0) {
+ g_short_lived_instance = this;
+ } else {
+ g_long_lived_instance = this;
+ }
+ }
+ }
+
+ // Schedule an async call that will succeed. Make sure to call that API from
+ // a different thread to fully test it.
+ if (this == g_short_lived_instance) {
+ at_exit_manager_.reset(new base::AtExitManager());
+ base::Thread random_thread("random_thread");
+ random_thread.Start();
+ random_thread.message_loop()->PostTask(FROM_HERE, new AsyncCallTask(this));
+ }
+
+ return NPERR_NO_ERROR;
+}
+
+void PluginThreadAsyncCallTest::AsyncCall() {
+ HostFunctions()->pluginthreadasynccall(id(), OnCallSucceededHelper, this);
+}
+
+void PluginThreadAsyncCallTest::OnCallSucceeded() {
+ // Delete the short lived instance.
+ NPIdentifier delete_id = HostFunctions()->getstringidentifier(
+ "deleteShortLivedInstance");
+
+ NPObject *window_obj = NULL;
+ HostFunctions()->getvalue(id(), NPNVWindowNPObject, &window_obj);
+
+ NPVariant result;
+ HostFunctions()->invoke(id(), window_obj, delete_id, NULL, 0, &result);
+}
+
+NPError PluginThreadAsyncCallTest::Destroy() {
+ if (this == g_short_lived_instance) {
+ // Schedule an async call that should not be called.
+ HostFunctions()->pluginthreadasynccall(id(), OnCallFailed, NULL);
+
+ // Schedule an async call to end the test using the long lived instance.
+ HostFunctions()->pluginthreadasynccall(g_long_lived_instance->id(),
+ OnCallCompletedHelper,
+ g_long_lived_instance);
+ }
+
+ return NPERR_NO_ERROR;
+}
+
+void PluginThreadAsyncCallTest::OnCallCompleted() {
+ SignalTestCompleted();
+}
+
+} // namespace NPAPIClient
diff --git a/webkit/glue/plugins/test/plugin_thread_async_call_test.h b/webkit/glue/plugins/test/plugin_thread_async_call_test.h
new file mode 100644
index 0000000..020e5e1
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_thread_async_call_test.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_THREAD_ASYNC_CALL_TEST_H
+#define WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_THREAD_ASYNC_CALL_TEST_H
+
+#include <vector>
+
+#include "base/at_exit.h"
+#include "base/scoped_ptr.h"
+#include "webkit/glue/plugins/test/plugin_test.h"
+
+namespace NPAPIClient {
+
+// This class tests scheduling and unscheduling of async callbacks using
+// NPN_PluginThreadAsyncCall.
+class PluginThreadAsyncCallTest : public PluginTest {
+ public:
+ PluginThreadAsyncCallTest(NPP id, NPNetscapeFuncs *host_functions);
+
+ virtual NPError New(uint16 mode, int16 argc, const char* argn[],
+ const char* argv[], NPSavedData* saved);
+
+ virtual NPError Destroy();
+
+ void AsyncCall();
+ void OnCallSucceeded();
+ void OnCallCompleted();
+
+ private:
+ // base::Thread needs one of these.
+ scoped_ptr<base::AtExitManager> at_exit_manager_;
+};
+
+} // namespace NPAPIClient
+
+#endif // WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_THREAD_ASYNC_CALL_TEST_H
diff --git a/webkit/glue/plugins/test/plugin_window_size_test.cc b/webkit/glue/plugins/test/plugin_window_size_test.cc
new file mode 100644
index 0000000..9bfabca
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_window_size_test.cc
@@ -0,0 +1,55 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/test/plugin_window_size_test.h"
+#include "webkit/glue/plugins/test/plugin_client.h"
+
+namespace NPAPIClient {
+
+PluginWindowSizeTest::PluginWindowSizeTest(NPP id,
+ NPNetscapeFuncs *host_functions)
+ : PluginTest(id, host_functions) {
+}
+
+NPError PluginWindowSizeTest::SetWindow(NPWindow* pNPWindow) {
+ if (pNPWindow->window == NULL)
+ return NPERR_NO_ERROR;
+
+ HWND window = reinterpret_cast<HWND>(pNPWindow->window);
+ if (!pNPWindow || !::IsWindow(window)) {
+ SetError("Invalid arguments passed in");
+ return NPERR_INVALID_PARAM;
+ }
+
+ RECT window_rect = {0};
+ window_rect.left = pNPWindow->x;
+ window_rect.top = pNPWindow->y;
+ window_rect.right = pNPWindow->width;
+ window_rect.bottom = pNPWindow->height;
+
+ if (!::IsRectEmpty(&window_rect)) {
+ RECT client_rect = {0};
+ ::GetClientRect(window, &client_rect);
+ if (::IsRectEmpty(&client_rect)) {
+ SetError("The client rect of the plugin window is empty. Test failed");
+ }
+
+ // Bug 6742: ensure that the coordinates passed in are relative to the
+ // parent HWND.
+ POINT origin_from_os;
+ RECT window_rect_from_os;
+ ::GetWindowRect(window, &window_rect_from_os);
+ origin_from_os.x = window_rect_from_os.left;
+ origin_from_os.y = window_rect_from_os.top;
+ ::ScreenToClient(GetParent(window), &origin_from_os);
+ if (origin_from_os.x != pNPWindow->x || origin_from_os.y != pNPWindow->y)
+ SetError("Wrong position passed in to SetWindow! Test failed");
+
+ SignalTestCompleted();
+ }
+
+ return NPERR_NO_ERROR;
+}
+
+} // namespace NPAPIClient
diff --git a/webkit/glue/plugins/test/plugin_window_size_test.h b/webkit/glue/plugins/test/plugin_window_size_test.h
new file mode 100644
index 0000000..3650671
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_window_size_test.h
@@ -0,0 +1,24 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_WINDOW_SIZE_TEST_H
+#define WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_WINDOW_SIZE_TEST_H
+
+#include "webkit/glue/plugins/test/plugin_test.h"
+
+namespace NPAPIClient {
+
+// This class tests whether the plugin window has a non zero rect
+// on the second SetWindow call.
+class PluginWindowSizeTest : public PluginTest {
+ public:
+ // Constructor.
+ PluginWindowSizeTest(NPP id, NPNetscapeFuncs *host_functions);
+ // NPAPI SetWindow handler
+ virtual NPError SetWindow(NPWindow* pNPWindow);
+};
+
+} // namespace NPAPIClient
+
+#endif // WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_WINDOW_SIZE_TEST_H
diff --git a/webkit/glue/plugins/test/plugin_windowed_test.cc b/webkit/glue/plugins/test/plugin_windowed_test.cc
new file mode 100644
index 0000000..5ae8e30
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_windowed_test.cc
@@ -0,0 +1,139 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/test/plugin_windowed_test.h"
+#include "webkit/glue/plugins/test/plugin_client.h"
+
+namespace NPAPIClient {
+
+WindowedPluginTest::WindowedPluginTest(NPP id, NPNetscapeFuncs *host_functions)
+ : PluginTest(id, host_functions),
+ window_(NULL), done_(false) {
+}
+
+WindowedPluginTest::~WindowedPluginTest() {
+ if (window_)
+ DestroyWindow(window_);
+}
+
+NPError WindowedPluginTest::SetWindow(NPWindow* pNPWindow) {
+ if (pNPWindow->window == NULL)
+ return NPERR_NO_ERROR;
+
+ if (test_name() == "create_instance_in_paint" && test_id() == "2") {
+ SignalTestCompleted();
+ return NPERR_NO_ERROR;
+ }
+
+ if (window_)
+ return NPERR_NO_ERROR;
+
+ HWND parent = reinterpret_cast<HWND>(pNPWindow->window);
+ if (!pNPWindow || !::IsWindow(parent)) {
+ SetError("Invalid arguments passed in");
+ return NPERR_INVALID_PARAM;
+ }
+
+ if ((test_name() == "create_instance_in_paint" && test_id() == "1") ||
+ test_name() == "alert_in_window_message") {
+ static ATOM window_class = 0;
+ if (!window_class) {
+ WNDCLASSEX wcex;
+ wcex.cbSize = sizeof(WNDCLASSEX);
+ wcex.style = CS_DBLCLKS;
+ wcex.lpfnWndProc = &NPAPIClient::WindowedPluginTest::WindowProc;
+ wcex.cbClsExtra = 0;
+ wcex.cbWndExtra = 0;
+ wcex.hInstance = GetModuleHandle(NULL);
+ wcex.hIcon = 0;
+ wcex.hCursor = 0;
+ wcex.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_WINDOW+1);
+ wcex.lpszMenuName = 0;
+ wcex.lpszClassName = L"CreateInstanceInPaintTestWindowClass";
+ wcex.hIconSm = 0;
+ window_class = RegisterClassEx(&wcex);
+ }
+
+ window_ = CreateWindowEx(
+ WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR,
+ MAKEINTATOM(window_class), 0,
+ WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_VISIBLE ,
+ 0, 0, 100, 100, parent, 0, GetModuleHandle(NULL), 0);
+ DCHECK(window_);
+ ::SetProp(window_, L"Plugin_Instance", this);
+ }
+
+ return NPERR_NO_ERROR;
+}
+
+NPError WindowedPluginTest::Destroy() {
+ if (test_name() != "ensure_scripting_works_in_destroy")
+ return NPERR_NO_ERROR;
+
+ // Bug 23706: ensure that scripting works with no asserts.
+ NPObject *window_obj = NULL;
+ HostFunctions()->getvalue(id(), NPNVWindowNPObject,&window_obj);
+
+ if (!window_obj) {
+ SetError("Failed to get NPObject for plugin instance");
+ } else {
+ std::string script = "javascript:GetMagicNumber()";
+ NPString script_string;
+ script_string.UTF8Characters = script.c_str();
+ script_string.UTF8Length =
+ static_cast<unsigned int>(script.length());
+
+ NPVariant result_var;
+ bool result = HostFunctions()->evaluate(
+ id(), window_obj, &script_string, &result_var);
+ if (!result ||
+ result_var.type != NPVariantType_Int32 ||
+ result_var.value.intValue != 42) {
+ SetError("Failed to script during NPP_Destroy");
+ }
+ }
+
+ SignalTestCompleted();
+ return NPERR_NO_ERROR;
+}
+
+void WindowedPluginTest::CallJSFunction(
+ WindowedPluginTest* this_ptr, const char* function) {
+ NPIdentifier function_id = this_ptr->HostFunctions()->getstringidentifier(
+ function);
+
+ NPObject *window_obj = NULL;
+ this_ptr->HostFunctions()->getvalue(
+ this_ptr->id(), NPNVWindowNPObject, &window_obj);
+
+ NPVariant rv;
+ this_ptr->HostFunctions()->invoke(
+ this_ptr->id(), window_obj, function_id, NULL, 0, &rv);
+}
+
+LRESULT CALLBACK WindowedPluginTest::WindowProc(
+ HWND window, UINT message, WPARAM wparam, LPARAM lparam) {
+ WindowedPluginTest* this_ptr =
+ reinterpret_cast<WindowedPluginTest*>
+ (::GetProp(window, L"Plugin_Instance"));
+
+ if (this_ptr && !this_ptr->done_) {
+ if (this_ptr->test_name() == "create_instance_in_paint" &&
+ message == WM_PAINT) {
+ this_ptr->done_ = true;
+ CallJSFunction(this_ptr, "CreateNewInstance");
+ } else if (this_ptr->test_name() == "alert_in_window_message" &&
+ message == WM_PAINT) {
+ this_ptr->done_ = true;
+ // We call this function twice as we want to display two alerts
+ // and verify that we don't hang the browser.
+ CallJSFunction(this_ptr, "CallAlert");
+ CallJSFunction(this_ptr, "CallAlert");
+ }
+ }
+
+ return DefWindowProc(window, message, wparam, lparam);
+}
+
+} // namespace NPAPIClient
diff --git a/webkit/glue/plugins/test/plugin_windowed_test.h b/webkit/glue/plugins/test/plugin_windowed_test.h
new file mode 100644
index 0000000..949ea86
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_windowed_test.h
@@ -0,0 +1,33 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_WINDOWED_TEST_H
+#define WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_WINDOWED_TEST_H
+
+#include "webkit/glue/plugins/test/plugin_test.h"
+
+namespace NPAPIClient {
+
+// This class contains a list of windowed plugin tests. Please add additional
+// tests to this class.
+class WindowedPluginTest : public PluginTest {
+ public:
+ WindowedPluginTest(NPP id, NPNetscapeFuncs *host_functions);
+ ~WindowedPluginTest();
+
+ private:
+ static LRESULT CALLBACK WindowProc(
+ HWND window, UINT message, WPARAM wparam, LPARAM lparam);
+ static void CallJSFunction(WindowedPluginTest*, const char*);
+
+ virtual NPError SetWindow(NPWindow* pNPWindow);
+ virtual NPError Destroy();
+
+ HWND window_;
+ bool done_;
+};
+
+} // namespace NPAPIClient
+
+#endif // WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_WINDOWED_TEST_H
diff --git a/webkit/glue/plugins/test/plugin_windowless_test.cc b/webkit/glue/plugins/test/plugin_windowless_test.cc
new file mode 100644
index 0000000..c47c1d7
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_windowless_test.cc
@@ -0,0 +1,260 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#define STRSAFE_NO_DEPRECATE
+#include "base/string_util.h"
+#include "webkit/glue/plugins/test/plugin_windowless_test.h"
+#include "webkit/glue/plugins/test/plugin_client.h"
+
+#if defined(OS_MACOSX)
+#include <ApplicationServices/ApplicationServices.h>
+#include <Carbon/Carbon.h>
+#endif
+
+namespace NPAPIClient {
+
+// Remember the first plugin instance for tests involving multiple instances
+WindowlessPluginTest* g_other_instance = NULL;
+
+WindowlessPluginTest::WindowlessPluginTest(NPP id,
+ NPNetscapeFuncs *host_functions)
+ : PluginTest(id, host_functions) {
+ if (!g_other_instance)
+ g_other_instance = this;
+}
+
+static bool IsPaintEvent(NPEvent* np_event) {
+#if defined(OS_WIN)
+ return WM_PAINT == np_event->event;
+#elif defined(OS_MACOSX)
+ return np_event->what == updateEvt;
+#endif
+}
+
+static bool IsMouseMoveEvent(NPEvent* np_event) {
+#if defined(OS_WIN)
+ return WM_MOUSEMOVE == np_event->event;
+#elif defined(OS_MACOSX)
+ return np_event->what == nullEvent;
+#endif
+}
+
+static bool IsMouseUpEvent(NPEvent* np_event) {
+#if defined(OS_WIN)
+ return WM_LBUTTONUP == np_event->event;
+#elif defined(OS_MACOSX)
+ return np_event->what == mouseUp;
+#endif
+}
+
+static bool IsWindowActivationEvent(NPEvent* np_event) {
+#if defined(OS_WIN)
+ NOTIMPLEMENTED();
+ return false;
+#elif defined(OS_MACOSX)
+ return np_event->what == activateEvt;
+#endif
+}
+
+int16 WindowlessPluginTest::HandleEvent(void* event) {
+
+ NPNetscapeFuncs* browser = NPAPIClient::PluginClient::HostFunctions();
+
+ NPBool supports_windowless = 0;
+ NPError result = browser->getvalue(id(), NPNVSupportsWindowless,
+ &supports_windowless);
+ if ((result != NPERR_NO_ERROR) || (supports_windowless != TRUE)) {
+ SetError("Failed to read NPNVSupportsWindowless value");
+ SignalTestCompleted();
+ return PluginTest::HandleEvent(event);
+ }
+
+ NPEvent* np_event = reinterpret_cast<NPEvent*>(event);
+ if (IsPaintEvent(np_event)) {
+#if defined(OS_WIN)
+ HDC paint_dc = reinterpret_cast<HDC>(np_event->wParam);
+ if (paint_dc == NULL) {
+ SetError("Invalid Window DC passed to HandleEvent for WM_PAINT");
+ SignalTestCompleted();
+ return NPERR_GENERIC_ERROR;
+ }
+
+ HRGN clipping_region = CreateRectRgn(0, 0, 0, 0);
+ if (!GetClipRgn(paint_dc, clipping_region)) {
+ SetError("No clipping region set in window DC");
+ DeleteObject(clipping_region);
+ SignalTestCompleted();
+ return NPERR_GENERIC_ERROR;
+ }
+
+ DeleteObject(clipping_region);
+#endif
+
+ if (test_name() == "execute_script_delete_in_paint") {
+ ExecuteScriptDeleteInPaint(browser);
+ } else if (test_name() == "multiple_instances_sync_calls") {
+ MultipleInstanceSyncCalls(browser);
+ }
+#if OS_MACOSX
+ } else if (IsWindowActivationEvent(np_event) &&
+ test_name() == "convert_point") {
+ ConvertPoint(browser);
+#endif
+ } else if (IsMouseMoveEvent(np_event) &&
+ test_name() == "execute_script_delete_in_mouse_move") {
+ ExecuteScript(browser, id(), "DeletePluginWithinScript();", NULL);
+ SignalTestCompleted();
+ } else if (IsMouseUpEvent(np_event) &&
+ test_name() == "delete_frame_test") {
+ ExecuteScript(
+ browser, id(),
+ "parent.document.getElementById('frame').outerHTML = ''", NULL);
+ }
+ // If this test failed, then we'd have crashed by now.
+ return PluginTest::HandleEvent(event);
+}
+
+NPError WindowlessPluginTest::ExecuteScript(NPNetscapeFuncs* browser, NPP id,
+ const std::string& script, NPVariant* result) {
+ std::string script_url = "javascript:";
+ script_url += script;
+
+ NPString script_string = { script_url.c_str(), script_url.length() };
+ NPObject *window_obj = NULL;
+ browser->getvalue(id, NPNVWindowNPObject, &window_obj);
+
+ NPVariant unused_result;
+ if (!result)
+ result = &unused_result;
+
+ return browser->evaluate(id, window_obj, &script_string, result);
+}
+
+void WindowlessPluginTest::ExecuteScriptDeleteInPaint(
+ NPNetscapeFuncs* browser) {
+ const NPUTF8* urlString = "javascript:DeletePluginWithinScript()";
+ const NPUTF8* targetString = NULL;
+ browser->geturl(id(), urlString, targetString);
+ SignalTestCompleted();
+}
+
+void WindowlessPluginTest::MultipleInstanceSyncCalls(NPNetscapeFuncs* browser) {
+ if (this == g_other_instance)
+ return;
+
+ DCHECK(g_other_instance);
+ ExecuteScript(browser, g_other_instance->id(), "TestCallback();", NULL);
+ SignalTestCompleted();
+}
+
+#if defined(OS_MACOSX)
+std::string StringForPoint(int x, int y) {
+ std::string point_string("(");
+ point_string.append(IntToString(x));
+ point_string.append(", ");
+ point_string.append(IntToString(y));
+ point_string.append(")");
+ return point_string;
+}
+#endif
+
+void WindowlessPluginTest::ConvertPoint(NPNetscapeFuncs* browser) {
+#if defined(OS_MACOSX)
+ // First, just sanity-test that round trips work.
+ NPCoordinateSpace spaces[] = { NPCoordinateSpacePlugin,
+ NPCoordinateSpaceWindow,
+ NPCoordinateSpaceFlippedWindow,
+ NPCoordinateSpaceScreen,
+ NPCoordinateSpaceFlippedScreen };
+ for (unsigned int i = 0; i < arraysize(spaces); ++i) {
+ for (unsigned int j = 0; j < arraysize(spaces); ++j) {
+ double x, y, round_trip_x, round_trip_y;
+ if (!(browser->convertpoint(id(), 0, 0, spaces[i], &x, &y, spaces[j])) ||
+ !(browser->convertpoint(id(), x, y, spaces[j], &round_trip_x,
+ &round_trip_y, spaces[i]))) {
+ SetError("Conversion failed");
+ SignalTestCompleted();
+ return;
+ }
+ if (i != j && x == 0 && y == 0) {
+ SetError("Converting a coordinate should change it");
+ SignalTestCompleted();
+ return;
+ }
+ if (round_trip_x != 0 || round_trip_y != 0) {
+ SetError("Round-trip conversion should return the original point");
+ SignalTestCompleted();
+ return;
+ }
+ }
+ }
+
+ // Now, more extensive testing on a single point.
+ double screen_x, screen_y;
+ browser->convertpoint(id(), 0, 0, NPCoordinateSpacePlugin,
+ &screen_x, &screen_y, NPCoordinateSpaceScreen);
+ double flipped_screen_x, flipped_screen_y;
+ browser->convertpoint(id(), 0, 0, NPCoordinateSpacePlugin,
+ &flipped_screen_x, &flipped_screen_y,
+ NPCoordinateSpaceFlippedScreen);
+ double window_x, window_y;
+ browser->convertpoint(id(), 0, 0, NPCoordinateSpacePlugin,
+ &window_x, &window_y, NPCoordinateSpaceWindow);
+ double flipped_window_x, flipped_window_y;
+ browser->convertpoint(id(), 0, 0, NPCoordinateSpacePlugin,
+ &flipped_window_x, &flipped_window_y,
+ NPCoordinateSpaceFlippedWindow);
+
+ CGRect main_display_bounds = CGDisplayBounds(CGMainDisplayID());
+
+ // Check that all the coordinates are right. The constants below are based on
+ // the window frame set in the UI test and the content offset in the test
+ // html. Y-coordinates are not checked exactly so that the test is robust
+ // against toolbar changes, info and bookmark bar visibility, etc.
+ const int kWindowHeight = 400;
+ const int kWindowXOrigin = 50;
+ const int kWindowYOrigin = 50;
+ const int kPluginXContentOffset = 50;
+ const int kPluginYContentOffset = 50;
+ const int kChromeYTolerance = 200;
+
+ std::string error_string;
+ if (screen_x != flipped_screen_x)
+ error_string = "Flipping screen coordinates shouldn't change x";
+ else if (flipped_screen_y != main_display_bounds.size.height - screen_y)
+ error_string = "Flipped screen coordinates should be flipped vertically";
+ else if (screen_x != kWindowXOrigin + kPluginXContentOffset)
+ error_string = "Screen x location is wrong";
+ else if (flipped_screen_y < kWindowYOrigin + kPluginYContentOffset ||
+ flipped_screen_y > kWindowYOrigin + kPluginYContentOffset +
+ kChromeYTolerance)
+ error_string = "Screen y location is wrong";
+ else if (window_x != flipped_window_x)
+ error_string = "Flipping window coordinates shouldn't change x";
+ else if (flipped_window_y != kWindowHeight - window_y)
+ error_string = "Flipped window coordinates should be flipped vertically";
+ else if (window_x != kPluginXContentOffset)
+ error_string = "Window x location is wrong";
+ else if (flipped_window_y < kPluginYContentOffset ||
+ flipped_window_y > kPluginYContentOffset + kChromeYTolerance)
+ error_string = "Window y location is wrong";
+
+ if (!error_string.empty()) {
+ error_string.append(" - ");
+ error_string.append(StringForPoint(screen_x, screen_y));
+ error_string.append(" - ");
+ error_string.append(StringForPoint(flipped_screen_x, flipped_screen_y));
+ error_string.append(" - ");
+ error_string.append(StringForPoint(window_x, window_y));
+ error_string.append(" - ");
+ error_string.append(StringForPoint(flipped_window_x, flipped_window_y));
+ SetError(error_string);
+ }
+#else
+ SetError("Unimplemented");
+#endif
+ SignalTestCompleted();
+}
+
+} // namespace NPAPIClient
diff --git a/webkit/glue/plugins/test/plugin_windowless_test.h b/webkit/glue/plugins/test/plugin_windowless_test.h
new file mode 100644
index 0000000..f336653
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_windowless_test.h
@@ -0,0 +1,35 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_EXECUTE_SCRIPT_DELETE_TEST_H
+#define WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_EXECUTE_SCRIPT_DELETE_TEST_H
+
+#include "webkit/glue/plugins/test/plugin_test.h"
+
+namespace NPAPIClient {
+
+// This class contains a list of windowless plugin tests. Please add additional
+// tests to this class.
+class WindowlessPluginTest : public PluginTest {
+ public:
+ // Constructor.
+ WindowlessPluginTest(NPP id, NPNetscapeFuncs *host_functions);
+
+ // These tests run in windowless plugin mode.
+ virtual bool IsWindowless() const { return true; }
+
+ // NPAPI HandleEvent handler
+ virtual int16 HandleEvent(void* event);
+
+ protected:
+ NPError ExecuteScript(NPNetscapeFuncs* browser, NPP id,
+ const std::string& script, NPVariant* result);
+ void ExecuteScriptDeleteInPaint(NPNetscapeFuncs* browser);
+ void MultipleInstanceSyncCalls(NPNetscapeFuncs* browser);
+ void ConvertPoint(NPNetscapeFuncs* browser);
+};
+
+} // namespace NPAPIClient
+
+#endif // WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_EXECUTE_SCRIPT_DELETE_TEST_H
diff --git a/webkit/glue/plugins/test/resource.h b/webkit/glue/plugins/test/resource.h
new file mode 100644
index 0000000..c52fa82
--- /dev/null
+++ b/webkit/glue/plugins/test/resource.h
@@ -0,0 +1,15 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by npapi_test.rc
+//
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 101
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1001
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/webkit/glue/plugins/webplugin.cc b/webkit/glue/plugins/webplugin.cc
new file mode 100644
index 0000000..6443318
--- /dev/null
+++ b/webkit/glue/plugins/webplugin.cc
@@ -0,0 +1,23 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/webplugin.h"
+
+namespace webkit_glue {
+
+WebPluginGeometry::WebPluginGeometry()
+ : window(gfx::kNullPluginWindow),
+ rects_valid(false),
+ visible(false) {
+}
+
+bool WebPluginGeometry::Equals(const WebPluginGeometry& rhs) const {
+ return window == rhs.window &&
+ window_rect == rhs.window_rect &&
+ clip_rect == rhs.clip_rect &&
+ cutout_rects == rhs.cutout_rects &&
+ rects_valid == rhs.rects_valid &&
+ visible == rhs.visible;
+}
+} // namespace webkit_glue
diff --git a/webkit/glue/plugins/webplugin.h b/webkit/glue/plugins/webplugin.h
new file mode 100644
index 0000000..1813842
--- /dev/null
+++ b/webkit/glue/plugins/webplugin.h
@@ -0,0 +1,195 @@
+// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_WEBPLUGIN_H_
+#define WEBKIT_GLUE_WEBPLUGIN_H_
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "gfx/native_widget_types.h"
+#include "gfx/rect.h"
+
+// TODO(port): this typedef is obviously incorrect on non-Windows
+// platforms, but now a lot of code now accidentally depends on them
+// existing. #ifdef out these declarations and fix all the users.
+typedef void* HANDLE;
+
+class GURL;
+struct NPObject;
+
+namespace WebKit {
+class WebFrame;
+}
+
+namespace webkit_glue {
+
+class WebPluginDelegate;
+class WebPluginParentView;
+class WebPluginResourceClient;
+
+// Describes the new location for a plugin window.
+struct WebPluginGeometry {
+ WebPluginGeometry();
+
+ bool Equals(const WebPluginGeometry& rhs) const;
+
+ // On Windows, this is the plugin window in the plugin process.
+ // On X11, this is the XID of the plugin-side GtkPlug containing the
+ // GtkSocket hosting the actual plugin window.
+ //
+ // On Mac OS X, all of the plugin types are currently "windowless"
+ // (window == 0) except for the special case of the GPU plugin,
+ // which currently performs rendering on behalf of the Pepper 3D API
+ // and WebGL. The GPU plugin uses a simple integer for the
+ // PluginWindowHandle which is used to map to a side data structure
+ // containing information about the plugin. Soon this plugin will be
+ // generalized, at which point this mechanism will be rethought or
+ // removed.
+ gfx::PluginWindowHandle window;
+ gfx::Rect window_rect;
+ // Clip rect (include) and cutouts (excludes), relative to
+ // window_rect origin.
+ gfx::Rect clip_rect;
+ std::vector<gfx::Rect> cutout_rects;
+ bool rects_valid;
+ bool visible;
+};
+
+// The WebKit side of a plugin implementation. It provides wrappers around
+// operations that need to interact with the frame and other WebCore objects.
+class WebPlugin {
+ public:
+ virtual ~WebPlugin() {}
+
+ // Called by the plugin delegate to let the WebPlugin know if the plugin is
+ // windowed (i.e. handle is not NULL) or windowless (handle is NULL). This
+ // tells the WebPlugin to send mouse/keyboard events to the plugin delegate,
+ // as well as the information about the HDC for paint operations.
+ virtual void SetWindow(gfx::PluginWindowHandle window) = 0;
+
+ // Whether input events should be sent to the delegate.
+ virtual void SetAcceptsInputEvents(bool accepts) = 0;
+
+ // Called by the plugin delegate to let it know that the window is being
+ // destroyed.
+ virtual void WillDestroyWindow(gfx::PluginWindowHandle window) = 0;
+#if defined(OS_WIN)
+ // The pump_messages_event is a event handle which is valid only for
+ // windowless plugins and is used in NPP_HandleEvent calls to pump messages
+ // if the plugin enters a modal loop.
+ // Cancels a pending request.
+ virtual void SetWindowlessPumpEvent(HANDLE pump_messages_event) = 0;
+#endif
+ virtual void CancelResource(unsigned long id) = 0;
+ virtual void Invalidate() = 0;
+ virtual void InvalidateRect(const gfx::Rect& rect) = 0;
+
+ // Returns the NPObject for the browser's window object.
+ virtual NPObject* GetWindowScriptNPObject() = 0;
+
+ // Returns the DOM element that loaded the plugin.
+ virtual NPObject* GetPluginElement() = 0;
+
+ // Cookies
+ virtual void SetCookie(const GURL& url,
+ const GURL& first_party_for_cookies,
+ const std::string& cookie) = 0;
+ virtual std::string GetCookies(const GURL& url,
+ const GURL& first_party_for_cookies) = 0;
+
+ // Shows a modal HTML dialog containing the given URL. json_arguments are
+ // passed to the dialog via the DOM 'window.chrome.dialogArguments', and the
+ // retval is the string returned by 'window.chrome.send("DialogClose",
+ // retval)'.
+ virtual void ShowModalHTMLDialog(const GURL& url, int width, int height,
+ const std::string& json_arguments,
+ std::string* json_retval) = 0;
+
+ // When a default plugin has downloaded the plugin list and finds it is
+ // available, it calls this method to notify the renderer. Also it will update
+ // the status when user clicks on the plugin to install.
+ virtual void OnMissingPluginStatus(int status) = 0;
+
+ // Handles GetURL/GetURLNotify/PostURL/PostURLNotify requests initiated
+ // by plugins. If the plugin wants notification of the result, notify_id will
+ // be non-zero.
+ virtual void HandleURLRequest(const char* url,
+ const char* method,
+ const char* target,
+ const char* buf,
+ unsigned int len,
+ int notify_id,
+ bool popups_allowed) = 0;
+
+ // Cancels document load.
+ virtual void CancelDocumentLoad() = 0;
+
+ // Initiates a HTTP range request for an existing stream.
+ virtual void InitiateHTTPRangeRequest(const char* url,
+ const char* range_info,
+ int range_request_id) = 0;
+
+ // Returns true iff in off the record (Incognito) mode.
+ virtual bool IsOffTheRecord() = 0;
+
+ // Called when the WebPluginResourceClient instance is deleted.
+ virtual void ResourceClientDeleted(
+ WebPluginResourceClient* resource_client) {}
+
+ // Defers the loading of the resource identified by resource_id. This is
+ // controlled by the defer parameter.
+ virtual void SetDeferResourceLoading(unsigned long resource_id,
+ bool defer) = 0;
+
+#if defined(OS_MACOSX)
+ // Synthesize a fake window handle for the plug-in to identify the instance
+ // to the browser, allowing mapping to a surface for hardware accelleration
+ // of plug-in content. The browser generates the handle which is then set on
+ // the plug-in. |opaque| indicates whether the content should be treated as
+ // opaque or translucent.
+ virtual void BindFakePluginWindowHandle(bool opaque) {}
+
+ // Tell the browser (via the renderer) to invalidate because the
+ // accelerated buffers have changed.
+ virtual void AcceleratedFrameBuffersDidSwap(gfx::PluginWindowHandle window) {}
+
+ // Tell the renderer and browser to associate the given plugin handle with
+ // |accelerated_surface_identifier|. The geometry is used to resize any
+ // native "window" (which on the Mac is a CALayer).
+ virtual void SetAcceleratedSurface(gfx::PluginWindowHandle window,
+ int32 width,
+ int32 height,
+ uint64 accelerated_surface_identifier) {}
+#endif
+
+ // Gets the WebPluginDelegate that implements the interface.
+ // This API is only for use with Pepper, and is only overridden
+ // by in-renderer implementations.
+ virtual WebPluginDelegate* delegate() { return NULL; }
+};
+
+// Simpler version of ResourceHandleClient that lends itself to proxying.
+class WebPluginResourceClient {
+ public:
+ virtual ~WebPluginResourceClient() {}
+ virtual void WillSendRequest(const GURL& url) = 0;
+ // The request_is_seekable parameter indicates whether byte range requests
+ // can be issued for the underlying stream.
+ virtual void DidReceiveResponse(const std::string& mime_type,
+ const std::string& headers,
+ uint32 expected_length,
+ uint32 last_modified,
+ bool request_is_seekable) = 0;
+ virtual void DidReceiveData(const char* buffer, int length,
+ int data_offset) = 0;
+ virtual void DidFinishLoading() = 0;
+ virtual void DidFail() = 0;
+ virtual bool IsMultiByteResponseExpected() = 0;
+};
+
+} // namespace webkit_glue
+
+#endif // #ifndef WEBKIT_GLUE_WEBPLUGIN_H_
diff --git a/webkit/glue/plugins/webplugin_2d_device_delegate.h b/webkit/glue/plugins/webplugin_2d_device_delegate.h
new file mode 100644
index 0000000..69bd53a
--- /dev/null
+++ b/webkit/glue/plugins/webplugin_2d_device_delegate.h
@@ -0,0 +1,57 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_WEBPLUGIN_2D_DEVICE_DELEGATE_H_
+#define WEBKIT_GLUE_PLUGINS_WEBPLUGIN_2D_DEVICE_DELEGATE_H_
+
+#include "base/basictypes.h"
+#include "third_party/npapi/bindings/npapi_extensions.h"
+
+namespace webkit_glue {
+
+// Interface for the NPAPI 2D device extension. This class implements "NOP"
+// versions of all these functions so it can be used seamlessly by the
+// "regular" plugin delegate while being overridden by the "pepper" one.
+class WebPlugin2DDeviceDelegate {
+ public:
+ virtual NPError Device2DQueryCapability(int32 capability, int32* value) {
+ return NPERR_GENERIC_ERROR;
+ }
+ virtual NPError Device2DQueryConfig(const NPDeviceContext2DConfig* request,
+ NPDeviceContext2DConfig* obtain) {
+ return NPERR_GENERIC_ERROR;
+ }
+ virtual NPError Device2DInitializeContext(
+ const NPDeviceContext2DConfig* config,
+ NPDeviceContext2D* context) {
+ return NPERR_GENERIC_ERROR;
+ }
+ virtual NPError Device2DSetStateContext(NPDeviceContext2D* context,
+ int32 state,
+ intptr_t value) {
+ return NPERR_GENERIC_ERROR;
+ }
+ virtual NPError Device2DGetStateContext(NPDeviceContext2D* context,
+ int32 state,
+ intptr_t* value) {
+ return NPERR_GENERIC_ERROR;
+ }
+ virtual NPError Device2DFlushContext(NPP id,
+ NPDeviceContext2D* context,
+ NPDeviceFlushContextCallbackPtr callback,
+ void* user_data) {
+ return NPERR_GENERIC_ERROR;
+ }
+ virtual NPError Device2DDestroyContext(NPDeviceContext2D* context) {
+ return NPERR_GENERIC_ERROR;
+ }
+
+ protected:
+ WebPlugin2DDeviceDelegate() {}
+ virtual ~WebPlugin2DDeviceDelegate() {}
+};
+
+} // namespace webkit_glue
+
+#endif // WEBKIT_GLUE_PLUGINS_WEBPLUGIN_2D_DEVICE_DELEGATE_H_
diff --git a/webkit/glue/plugins/webplugin_3d_device_delegate.h b/webkit/glue/plugins/webplugin_3d_device_delegate.h
new file mode 100644
index 0000000..fbb46eb
--- /dev/null
+++ b/webkit/glue/plugins/webplugin_3d_device_delegate.h
@@ -0,0 +1,101 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_WEBPLUGIN_3D_DEVICE_DELEGATE_H_
+#define WEBKIT_GLUE_PLUGINS_WEBPLUGIN_3D_DEVICE_DELEGATE_H_
+
+#include "base/basictypes.h"
+#include "third_party/npapi/bindings/npapi_extensions.h"
+
+namespace webkit_glue {
+
+// Interface for the NPAPI 3D device extension. This class implements "NOP"
+// versions of all these functions so it can be used seamlessly by the
+// "regular" plugin delegate while being overridden by the "pepper" one.
+class WebPlugin3DDeviceDelegate {
+ public:
+ virtual NPError Device3DQueryCapability(int32 capability, int32* value) {
+ return NPERR_GENERIC_ERROR;
+ }
+ virtual NPError Device3DQueryConfig(const NPDeviceContext3DConfig* request,
+ NPDeviceContext3DConfig* obtain) {
+ return NPERR_GENERIC_ERROR;
+ }
+ virtual NPError Device3DInitializeContext(
+ const NPDeviceContext3DConfig* config,
+ NPDeviceContext3D* context) {
+ return NPERR_GENERIC_ERROR;
+ }
+ virtual NPError Device3DSetStateContext(NPDeviceContext3D* context,
+ int32 state,
+ intptr_t value) {
+ return NPERR_GENERIC_ERROR;
+ }
+ virtual NPError Device3DGetStateContext(NPDeviceContext3D* context,
+ int32 state,
+ intptr_t* value) {
+ return NPERR_GENERIC_ERROR;
+ }
+ virtual NPError Device3DFlushContext(NPP id,
+ NPDeviceContext3D* context,
+ NPDeviceFlushContextCallbackPtr callback,
+ void* user_data) {
+ return NPERR_GENERIC_ERROR;
+ }
+ virtual NPError Device3DDestroyContext(NPDeviceContext3D* context) {
+ return NPERR_GENERIC_ERROR;
+ }
+ virtual NPError Device3DCreateBuffer(NPDeviceContext3D* context,
+ size_t size,
+ int32* id) {
+ return NPERR_GENERIC_ERROR;
+ }
+ virtual NPError Device3DDestroyBuffer(NPDeviceContext3D* context,
+ int32 id) {
+ return NPERR_GENERIC_ERROR;
+ }
+ virtual NPError Device3DMapBuffer(NPDeviceContext3D* context,
+ int32 id,
+ NPDeviceBuffer* buffer) {
+ return NPERR_GENERIC_ERROR;
+ }
+ virtual NPError Device3DGetNumConfigs(int32* num_configs) {
+ return NPERR_GENERIC_ERROR;
+ }
+ virtual NPError Device3DGetConfigAttribs(int32 config,
+ int32* attrib_list) {
+ return NPERR_GENERIC_ERROR;
+ }
+ virtual NPError Device3DCreateContext(int32 config,
+ const int32* attrib_list,
+ NPDeviceContext3D** context) {
+ return NPERR_GENERIC_ERROR;
+ }
+ virtual NPError Device3DRegisterCallback(
+ NPP id,
+ NPDeviceContext* context,
+ int32 callback_type,
+ NPDeviceGenericCallbackPtr callback,
+ void* callback_data) {
+ return NPERR_GENERIC_ERROR;
+ }
+ virtual NPError Device3DSynchronizeContext(
+ NPP id,
+ NPDeviceContext3D* context,
+ NPDeviceSynchronizationMode mode,
+ const int32* input_attrib_list,
+ int32* output_attrib_list,
+ NPDeviceSynchronizeContextCallbackPtr callback,
+ void* callback_data) {
+ return NPERR_GENERIC_ERROR;
+ }
+
+ protected:
+ WebPlugin3DDeviceDelegate() {}
+ virtual ~WebPlugin3DDeviceDelegate() {}
+};
+
+} // namespace webkit_glue
+
+#endif // WEBKIT_GLUE_PLUGINS_WEBPLUGIN_3D_DEVICE_DELEGATE_H_
diff --git a/webkit/glue/plugins/webplugin_audio_device_delegate.h b/webkit/glue/plugins/webplugin_audio_device_delegate.h
new file mode 100644
index 0000000..3f37246
--- /dev/null
+++ b/webkit/glue/plugins/webplugin_audio_device_delegate.h
@@ -0,0 +1,56 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_WEBPLUGIN_AUDIO_DEVICE_DELEGATE_H_
+#define WEBKIT_GLUE_PLUGINS_WEBPLUGIN_AUDIO_DEVICE_DELEGATE_H_
+
+#include "base/basictypes.h"
+#include "third_party/npapi/bindings/npapi_extensions.h"
+
+namespace webkit_glue {
+
+// Interface for the NPAPI audio device extension. This class implements "NOP"
+// versions of all these functions so it can be used seamlessly by the
+// "regular" plugin delegate while being overridden by the "pepper" one.
+class WebPluginAudioDeviceDelegate {
+ public:
+ virtual NPError DeviceAudioQueryCapability(int32 capability, int32* value) {
+ return NPERR_GENERIC_ERROR;
+ }
+ virtual NPError DeviceAudioQueryConfig(
+ const NPDeviceContextAudioConfig* request,
+ NPDeviceContextAudioConfig* obtain) {
+ return NPERR_GENERIC_ERROR;
+ }
+ virtual NPError DeviceAudioInitializeContext(
+ const NPDeviceContextAudioConfig* config,
+ NPDeviceContextAudio* context) {
+ return NPERR_GENERIC_ERROR;
+ }
+ virtual NPError DeviceAudioSetStateContext(NPDeviceContextAudio* context,
+ int32 state, intptr_t value) {
+ return NPERR_GENERIC_ERROR;
+ }
+ virtual NPError DeviceAudioGetStateContext(NPDeviceContextAudio* context,
+ int32 state, intptr_t* value) {
+ return NPERR_GENERIC_ERROR;
+ }
+ virtual NPError DeviceAudioFlushContext(
+ NPP id, NPDeviceContextAudio* context,
+ NPDeviceFlushContextCallbackPtr callback, void* user_data) {
+ return NPERR_GENERIC_ERROR;
+ }
+ virtual NPError DeviceAudioDestroyContext(NPDeviceContextAudio* context) {
+ return NPERR_GENERIC_ERROR;
+ }
+
+ protected:
+ WebPluginAudioDeviceDelegate() {}
+ virtual ~WebPluginAudioDeviceDelegate() {}
+};
+
+} // namespace webkit_glue
+
+#endif // WEBKIT_GLUE_PLUGINS_WEBPLUGIN_AUDIO_DEVICE_DELEGATE_H_
+
diff --git a/webkit/glue/plugins/webplugin_delegate.h b/webkit/glue/plugins/webplugin_delegate.h
new file mode 100644
index 0000000..901cdea
--- /dev/null
+++ b/webkit/glue/plugins/webplugin_delegate.h
@@ -0,0 +1,166 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_WEBPLUGIN_DELEGATE_H_
+#define WEBKIT_GLUE_WEBPLUGIN_DELEGATE_H_
+
+#include <string>
+#include <vector>
+
+#include "base/string16.h"
+#include "build/build_config.h"
+#include "gfx/native_widget_types.h"
+#include "third_party/npapi/bindings/npapi.h"
+#include "third_party/npapi/bindings/npapi_extensions.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebCanvas.h"
+#include "webkit/glue/plugins/webplugin_2d_device_delegate.h"
+#include "webkit/glue/plugins/webplugin_3d_device_delegate.h"
+#include "webkit/glue/plugins/webplugin_audio_device_delegate.h"
+#include "webkit/glue/plugins/webplugin_file_delegate.h"
+#include "webkit/glue/plugins/webplugin_print_delegate.h"
+
+class FilePath;
+class GURL;
+struct NPObject;
+
+namespace WebKit {
+class WebInputEvent;
+struct WebCursorInfo;
+}
+
+namespace gfx {
+class Rect;
+}
+
+namespace webkit_glue {
+
+class WebPlugin;
+class WebPluginResourceClient;
+
+// This is the interface that a plugin implementation needs to provide.
+class WebPluginDelegate : public WebPlugin2DDeviceDelegate,
+ public WebPlugin3DDeviceDelegate,
+ public WebPluginAudioDeviceDelegate,
+ public WebPluginPrintDelegate,
+ public WebPluginFileDelegate {
+ public:
+ virtual ~WebPluginDelegate() {}
+
+ // Initializes the plugin implementation with the given (UTF8) arguments.
+ // Note that the lifetime of WebPlugin must be longer than this delegate.
+ // If this function returns false the plugin isn't started and shouldn't be
+ // called again. If this method succeeds, then the WebPlugin is valid until
+ // PluginDestroyed is called.
+ // The load_manually parameter if true indicates that the plugin data would
+ // be passed from webkit. if false indicates that the plugin should download
+ // the data. This also controls whether the plugin is instantiated as a full
+ // page plugin (NP_FULL) or embedded (NP_EMBED).
+ virtual bool Initialize(const GURL& url,
+ const std::vector<std::string>& arg_names,
+ const std::vector<std::string>& arg_values,
+ WebPlugin* plugin,
+ bool load_manually) = 0;
+
+ // Called when the WebPlugin is being destroyed. This is a signal to the
+ // delegate that it should tear-down the plugin implementation and not call
+ // methods on the WebPlugin again.
+ virtual void PluginDestroyed() = 0;
+
+ // Update the geometry of the plugin. This is a request to move the
+ // plugin, relative to its containing window, to the coords given by
+ // window_rect. Its contents should be clipped to the coords given
+ // by clip_rect, which are relative to the origin of the plugin
+ // window. The clip_rect is in plugin-relative coordinates.
+ virtual void UpdateGeometry(const gfx::Rect& window_rect,
+ const gfx::Rect& clip_rect) = 0;
+
+ // Tells the plugin to paint the damaged rect. |canvas| is only used for
+ // windowless plugins.
+ virtual void Paint(WebKit::WebCanvas* canvas, const gfx::Rect& rect) = 0;
+
+ // Tells the plugin to print itself.
+ virtual void Print(gfx::NativeDrawingContext hdc) = 0;
+
+ // Informs the plugin that it has gained or lost focus. This is only called in
+ // windowless mode.
+ virtual void SetFocus(bool focused) = 0;
+
+ // For windowless plugins, gives them a user event like mouse/keyboard.
+ // Returns whether the event was handled. This is only called in windowsless
+ // mode. See NPAPI NPP_HandleEvent for more information.
+ virtual bool HandleInputEvent(const WebKit::WebInputEvent& event,
+ WebKit::WebCursorInfo* cursor) = 0;
+
+ // Gets the NPObject associated with the plugin for scripting.
+ virtual NPObject* GetPluginScriptableObject() = 0;
+
+ // Receives notification about a resource load that the plugin initiated
+ // for a frame.
+ virtual void DidFinishLoadWithReason(const GURL& url, NPReason reason,
+ int notify_id) = 0;
+
+ // Returns the process id of the process that is running the plugin.
+ virtual int GetProcessId() = 0;
+
+ // The result, UTF-8 encoded, of the script execution is returned via this
+ // function.
+ virtual void SendJavaScriptStream(const GURL& url,
+ const std::string& result,
+ bool success,
+ int notify_id) = 0;
+
+ // Receives notification about data being available.
+ virtual void DidReceiveManualResponse(const GURL& url,
+ const std::string& mime_type,
+ const std::string& headers,
+ uint32 expected_length,
+ uint32 last_modified) = 0;
+
+ // Receives the data.
+ virtual void DidReceiveManualData(const char* buffer, int length) = 0;
+
+ // Indicates end of data load.
+ virtual void DidFinishManualLoading() = 0;
+
+ // Indicates a failure in data receipt.
+ virtual void DidManualLoadFail() = 0;
+
+ // Only supported when the plugin is the default plugin.
+ virtual void InstallMissingPlugin() = 0;
+
+ // Creates a WebPluginResourceClient instance and returns the same.
+ virtual WebPluginResourceClient* CreateResourceClient(
+ unsigned long resource_id,
+ const GURL& url,
+ int notify_id) = 0;
+
+ // Creates a WebPluginResourceClient instance for an existing stream that is
+ // has become seekable.
+ virtual WebPluginResourceClient* CreateSeekableResourceClient(
+ unsigned long resource_id, int range_request_id) = 0;
+
+ // See WebPluginContainerImpl's description of the interface.
+ virtual bool StartFind(const string16& search_text,
+ bool case_sensitive,
+ int identifier) { return false; }
+ virtual void SelectFindResult(bool forward) {}
+ virtual void StopFind() {}
+ virtual void NumberOfFindResultsChanged(int total, bool final_result) {}
+ virtual void SelectedFindResultChanged(int index) {}
+ virtual NPWidgetExtensions* GetWidgetExtensions() { return NULL; }
+ virtual bool SetCursor(NPCursorType type) { return false; }
+ virtual NPFontExtensions* GetFontExtensions() { return NULL; }
+
+ // Used for zooming of full page plugins. 0 means reset, while -1 means zoom
+ // out and +1 means zoom in.
+ virtual void SetZoomFactor(float scale, bool text_only) {}
+ // Gets the selected text, if any.
+ virtual bool HasSelection() const { return false; }
+ virtual string16 GetSelectionAsText() const { return string16(); }
+ virtual string16 GetSelectionAsMarkup() const { return string16(); }
+};
+
+} // namespace webkit_glue
+
+#endif // WEBKIT_GLUE_WEBPLUGIN_DELEGATE_H_
diff --git a/webkit/glue/plugins/webplugin_delegate_impl.cc b/webkit/glue/plugins/webplugin_delegate_impl.cc
new file mode 100644
index 0000000..b73b5ae
--- /dev/null
+++ b/webkit/glue/plugins/webplugin_delegate_impl.cc
@@ -0,0 +1,268 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/webplugin_delegate_impl.h"
+
+#include <string>
+#include <vector>
+
+#include "base/file_util.h"
+#include "base/message_loop.h"
+#include "base/process_util.h"
+#include "base/scoped_ptr.h"
+#include "base/stats_counters.h"
+#include "base/string_util.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebInputEvent.h"
+#include "webkit/glue/plugins/plugin_constants_win.h"
+#include "webkit/glue/plugins/plugin_instance.h"
+#include "webkit/glue/plugins/plugin_lib.h"
+#include "webkit/glue/plugins/plugin_list.h"
+#include "webkit/glue/plugins/plugin_stream_url.h"
+#include "webkit/glue/webkit_glue.h"
+
+using webkit_glue::WebPlugin;
+using webkit_glue::WebPluginDelegate;
+using webkit_glue::WebPluginResourceClient;
+using WebKit::WebCursorInfo;
+using WebKit::WebKeyboardEvent;
+using WebKit::WebInputEvent;
+using WebKit::WebMouseEvent;
+
+WebPluginDelegateImpl* WebPluginDelegateImpl::Create(
+ const FilePath& filename,
+ const std::string& mime_type,
+ gfx::PluginWindowHandle containing_view) {
+ scoped_refptr<NPAPI::PluginLib> plugin_lib =
+ NPAPI::PluginLib::CreatePluginLib(filename);
+ if (plugin_lib.get() == NULL)
+ return NULL;
+
+ NPError err = plugin_lib->NP_Initialize();
+ if (err != NPERR_NO_ERROR)
+ return NULL;
+
+ scoped_refptr<NPAPI::PluginInstance> instance =
+ plugin_lib->CreateInstance(mime_type);
+ return new WebPluginDelegateImpl(containing_view, instance.get());
+}
+
+void WebPluginDelegateImpl::PluginDestroyed() {
+ if (handle_event_depth_) {
+ MessageLoop::current()->DeleteSoon(FROM_HERE, this);
+ } else {
+ delete this;
+ }
+}
+
+bool WebPluginDelegateImpl::Initialize(
+ const GURL& url,
+ const std::vector<std::string>& arg_names,
+ const std::vector<std::string>& arg_values,
+ WebPlugin* plugin,
+ bool load_manually) {
+ plugin_ = plugin;
+
+ instance_->set_web_plugin(plugin_);
+ if (quirks_ & PLUGIN_QUIRK_DONT_ALLOW_MULTIPLE_INSTANCES) {
+ NPAPI::PluginLib* plugin_lib = instance()->plugin_lib();
+ if (plugin_lib->instance_count() > 1) {
+ return false;
+ }
+ }
+
+ if (quirks_ & PLUGIN_QUIRK_DIE_AFTER_UNLOAD)
+ webkit_glue::SetForcefullyTerminatePluginProcess(true);
+
+ int argc = 0;
+ scoped_array<char*> argn(new char*[arg_names.size()]);
+ scoped_array<char*> argv(new char*[arg_names.size()]);
+ for (size_t i = 0; i < arg_names.size(); ++i) {
+ if (quirks_ & PLUGIN_QUIRK_NO_WINDOWLESS &&
+ LowerCaseEqualsASCII(arg_names[i], "windowlessvideo")) {
+ continue;
+ }
+ argn[argc] = const_cast<char*>(arg_names[i].c_str());
+ argv[argc] = const_cast<char*>(arg_values[i].c_str());
+ argc++;
+ }
+
+ bool start_result = instance_->Start(
+ url, argn.get(), argv.get(), argc, load_manually);
+ if (!start_result)
+ return false;
+
+ windowless_ = instance_->windowless();
+ if (!windowless_) {
+ if (!WindowedCreatePlugin())
+ return false;
+ } else {
+ // For windowless plugins we should set the containing window handle
+ // as the instance window handle. This is what Safari does. Not having
+ // a valid window handle causes subtle bugs with plugins which retrieve
+ // the window handle and validate the same. The window handle can be
+ // retrieved via NPN_GetValue of NPNVnetscapeWindow.
+ instance_->set_window_handle(parent_);
+ }
+
+ bool should_load = PlatformInitialize();
+
+ plugin_url_ = url.spec();
+
+ return should_load;
+}
+
+void WebPluginDelegateImpl::DestroyInstance() {
+ if (instance_ && (instance_->npp()->ndata != NULL)) {
+ // Shutdown all streams before destroying so that
+ // no streams are left "in progress". Need to do
+ // this before calling set_web_plugin(NULL) because the
+ // instance uses the helper to do the download.
+ instance_->CloseStreams();
+
+ window_.window = NULL;
+ if (!(quirks_ & PLUGIN_QUIRK_DONT_SET_NULL_WINDOW_HANDLE_ON_DESTROY)) {
+ instance_->NPP_SetWindow(&window_);
+ }
+
+ instance_->NPP_Destroy();
+
+ instance_->set_web_plugin(NULL);
+
+ PlatformDestroyInstance();
+
+ instance_ = 0;
+ }
+}
+
+void WebPluginDelegateImpl::UpdateGeometry(
+ const gfx::Rect& window_rect,
+ const gfx::Rect& clip_rect) {
+
+ if (first_set_window_call_) {
+ first_set_window_call_ = false;
+ // Plugins like media player on Windows have a bug where in they handle the
+ // first geometry update and ignore the rest resulting in painting issues.
+ // This quirk basically ignores the first set window call sequence for
+ // these plugins and has been tested for Windows plugins only.
+ if (quirks_ & PLUGIN_QUIRK_IGNORE_FIRST_SETWINDOW_CALL)
+ return;
+ }
+
+ if (windowless_) {
+ WindowlessUpdateGeometry(window_rect, clip_rect);
+ } else {
+ WindowedUpdateGeometry(window_rect, clip_rect);
+ }
+}
+
+NPObject* WebPluginDelegateImpl::GetPluginScriptableObject() {
+ return instance_->GetPluginScriptableObject();
+}
+
+void WebPluginDelegateImpl::DidFinishLoadWithReason(const GURL& url,
+ NPReason reason,
+ int notify_id) {
+ if (quirks_ & PLUGIN_QUIRK_ALWAYS_NOTIFY_SUCCESS &&
+ reason == NPRES_NETWORK_ERR) {
+ // Flash needs this or otherwise it unloads the launching swf object.
+ reason = NPRES_DONE;
+ }
+
+ instance()->DidFinishLoadWithReason(url, reason, notify_id);
+}
+
+int WebPluginDelegateImpl::GetProcessId() {
+ // We are in process, so the plugin pid is this current process pid.
+ return base::GetCurrentProcId();
+}
+
+void WebPluginDelegateImpl::SendJavaScriptStream(const GURL& url,
+ const std::string& result,
+ bool success,
+ int notify_id) {
+ instance()->SendJavaScriptStream(url, result, success, notify_id);
+}
+
+void WebPluginDelegateImpl::DidReceiveManualResponse(
+ const GURL& url, const std::string& mime_type,
+ const std::string& headers, uint32 expected_length, uint32 last_modified) {
+ if (!windowless_) {
+ // Calling NPP_WriteReady before NPP_SetWindow causes movies to not load in
+ // Flash. See http://b/issue?id=892174.
+ DCHECK(windowed_did_set_window_);
+ }
+
+ instance()->DidReceiveManualResponse(url, mime_type, headers,
+ expected_length, last_modified);
+}
+
+void WebPluginDelegateImpl::DidReceiveManualData(const char* buffer,
+ int length) {
+ instance()->DidReceiveManualData(buffer, length);
+}
+
+void WebPluginDelegateImpl::DidFinishManualLoading() {
+ instance()->DidFinishManualLoading();
+}
+
+void WebPluginDelegateImpl::DidManualLoadFail() {
+ instance()->DidManualLoadFail();
+}
+
+FilePath WebPluginDelegateImpl::GetPluginPath() {
+ return instance()->plugin_lib()->plugin_info().path;
+}
+
+void WebPluginDelegateImpl::WindowedUpdateGeometry(
+ const gfx::Rect& window_rect,
+ const gfx::Rect& clip_rect) {
+ if (WindowedReposition(window_rect, clip_rect) ||
+ !windowed_did_set_window_) {
+ // Let the plugin know that it has been moved
+ WindowedSetWindow();
+ }
+}
+
+bool WebPluginDelegateImpl::HandleInputEvent(const WebInputEvent& event,
+ WebCursorInfo* cursor_info) {
+ DCHECK(windowless_) << "events should only be received in windowless mode";
+
+ bool pop_user_gesture = false;
+ if (IsUserGesture(event)) {
+ pop_user_gesture = true;
+ instance()->PushPopupsEnabledState(true);
+ }
+
+ bool handled = PlatformHandleInputEvent(event, cursor_info);
+
+ if (pop_user_gesture) {
+ instance()->PopPopupsEnabledState();
+ }
+
+ return handled;
+}
+
+bool WebPluginDelegateImpl::IsUserGesture(const WebInputEvent& event) {
+ switch (event.type) {
+ case WebInputEvent::MouseDown:
+ case WebInputEvent::MouseUp:
+ case WebInputEvent::KeyDown:
+ case WebInputEvent::KeyUp:
+ return true;
+ default:
+ return false;
+ }
+ return false;
+}
+
+WebPluginResourceClient* WebPluginDelegateImpl::CreateResourceClient(
+ unsigned long resource_id, const GURL& url, int notify_id) {
+ return instance()->CreateStream(
+ resource_id, url, std::string(), notify_id);
+}
+
+WebPluginResourceClient* WebPluginDelegateImpl::CreateSeekableResourceClient(
+ unsigned long resource_id, int range_request_id) {
+ return instance()->GetRangeRequest(range_request_id);
+}
diff --git a/webkit/glue/plugins/webplugin_delegate_impl.h b/webkit/glue/plugins/webplugin_delegate_impl.h
new file mode 100644
index 0000000..ebf5d3e
--- /dev/null
+++ b/webkit/glue/plugins/webplugin_delegate_impl.h
@@ -0,0 +1,490 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGIN_WEBPLUGIN_DELEGATE_IMPL_H_
+#define WEBKIT_GLUE_PLUGIN_WEBPLUGIN_DELEGATE_IMPL_H_
+
+#include "build/build_config.h"
+
+#include <string>
+#include <list>
+#include <set>
+
+#include "base/file_path.h"
+#include "base/ref_counted.h"
+#include "base/task.h"
+#include "base/time.h"
+#include "base/timer.h"
+#include "gfx/native_widget_types.h"
+#include "gfx/rect.h"
+#include "third_party/npapi/bindings/npapi.h"
+#include "webkit/glue/plugins/webplugin_delegate.h"
+#include "webkit/glue/webcursor.h"
+
+#if defined(OS_MACOSX)
+#include "app/surface/accelerated_surface_mac.h"
+#endif
+
+#if defined(USE_X11)
+typedef struct _GdkDrawable GdkPixmap;
+#endif
+
+namespace NPAPI {
+class PluginInstance;
+}
+
+namespace WebKit {
+class WebMouseEvent;
+}
+
+#if defined(OS_MACOSX)
+class ExternalDragTracker;
+#ifndef NP_NO_QUICKDRAW
+class QuickDrawDrawingManager;
+#endif
+#ifdef __OBJC__
+@class CALayer;
+@class CARenderer;
+#else
+class CALayer;
+class CARenderer;
+#endif
+#endif
+
+// An implementation of WebPluginDelegate that runs in the plugin process,
+// proxied from the renderer by WebPluginDelegateProxy.
+class WebPluginDelegateImpl : public webkit_glue::WebPluginDelegate {
+ public:
+ enum PluginQuirks {
+ PLUGIN_QUIRK_SETWINDOW_TWICE = 1, // Win32
+ PLUGIN_QUIRK_THROTTLE_WM_USER_PLUS_ONE = 2, // Win32
+ PLUGIN_QUIRK_DONT_CALL_WND_PROC_RECURSIVELY = 4, // Win32
+ PLUGIN_QUIRK_DONT_SET_NULL_WINDOW_HANDLE_ON_DESTROY = 8, // Win32
+ PLUGIN_QUIRK_DONT_ALLOW_MULTIPLE_INSTANCES = 16, // Win32
+ PLUGIN_QUIRK_DIE_AFTER_UNLOAD = 32, // Win32
+ PLUGIN_QUIRK_PATCH_SETCURSOR = 64, // Win32
+ PLUGIN_QUIRK_BLOCK_NONSTANDARD_GETURL_REQUESTS = 128, // Win32
+ PLUGIN_QUIRK_WINDOWLESS_OFFSET_WINDOW_TO_DRAW = 256, // Linux
+ PLUGIN_QUIRK_WINDOWLESS_INVALIDATE_AFTER_SET_WINDOW = 512, // Linux
+ PLUGIN_QUIRK_NO_WINDOWLESS = 1024, // Windows
+ PLUGIN_QUIRK_PATCH_REGENUMKEYEXW = 2048, // Windows
+ PLUGIN_QUIRK_ALWAYS_NOTIFY_SUCCESS = 4096, // Windows
+ PLUGIN_QUIRK_ALLOW_FASTER_QUICKDRAW_PATH = 8192, // Mac
+ PLUGIN_QUIRK_HANDLE_MOUSE_CAPTURE = 16384, // Windows
+ PLUGIN_QUIRK_WINDOWLESS_NO_RIGHT_CLICK = 32768, // Linux
+ PLUGIN_QUIRK_IGNORE_FIRST_SETWINDOW_CALL = 65536, // Windows.
+ };
+
+ static WebPluginDelegateImpl* Create(const FilePath& filename,
+ const std::string& mime_type,
+ gfx::PluginWindowHandle containing_view);
+
+ static bool IsPluginDelegateWindow(gfx::NativeWindow window);
+ static bool GetPluginNameFromWindow(gfx::NativeWindow window,
+ std::wstring *plugin_name);
+
+ // Returns true if the window handle passed in is that of the dummy
+ // activation window for windowless plugins.
+ static bool IsDummyActivationWindow(gfx::NativeWindow window);
+
+ // WebPluginDelegate implementation
+ virtual bool Initialize(const GURL& url,
+ const std::vector<std::string>& arg_names,
+ const std::vector<std::string>& arg_values,
+ webkit_glue::WebPlugin* plugin,
+ bool load_manually);
+ virtual void PluginDestroyed();
+ virtual void UpdateGeometry(const gfx::Rect& window_rect,
+ const gfx::Rect& clip_rect);
+ virtual void Paint(WebKit::WebCanvas* canvas, const gfx::Rect& rect);
+ virtual void Print(gfx::NativeDrawingContext context);
+ virtual void SetFocus(bool focused);
+ virtual bool HandleInputEvent(const WebKit::WebInputEvent& event,
+ WebKit::WebCursorInfo* cursor_info);
+ virtual NPObject* GetPluginScriptableObject();
+ virtual void DidFinishLoadWithReason(
+ const GURL& url, NPReason reason, int notify_id);
+ virtual int GetProcessId();
+ virtual void SendJavaScriptStream(const GURL& url,
+ const std::string& result,
+ bool success,
+ int notify_id);
+ virtual void DidReceiveManualResponse(const GURL& url,
+ const std::string& mime_type,
+ const std::string& headers,
+ uint32 expected_length,
+ uint32 last_modified);
+ virtual void DidReceiveManualData(const char* buffer, int length);
+ virtual void DidFinishManualLoading();
+ virtual void DidManualLoadFail();
+ virtual void InstallMissingPlugin();
+ virtual webkit_glue::WebPluginResourceClient* CreateResourceClient(
+ unsigned long resource_id, const GURL& url, int notify_id);
+ virtual webkit_glue::WebPluginResourceClient* CreateSeekableResourceClient(
+ unsigned long resource_id, int range_request_id);
+ // End of WebPluginDelegate implementation.
+
+ bool IsWindowless() const { return windowless_ ; }
+ gfx::Rect GetRect() const { return window_rect_; }
+ gfx::Rect GetClipRect() const { return clip_rect_; }
+
+ // Returns the path for the library implementing this plugin.
+ FilePath GetPluginPath();
+
+ // Returns a combination of PluginQuirks.
+ int GetQuirks() const { return quirks_; }
+
+#if defined(OS_MACOSX)
+ // Informs the plugin that the geometry has changed, as with UpdateGeometry,
+ // but also includes the new buffer context for that new geometry.
+ void UpdateGeometryAndContext(const gfx::Rect& window_rect,
+ const gfx::Rect& clip_rect,
+ gfx::NativeDrawingContext context);
+ // Informs the delegate that the plugin called NPN_Invalidate*. Used as a
+ // trigger for Core Animation drawing.
+ void PluginDidInvalidate();
+ // Returns the delegate currently processing events.
+ static WebPluginDelegateImpl* GetActiveDelegate();
+ // Informs the plugin that the window it is in has gained or lost focus.
+ void SetWindowHasFocus(bool has_focus);
+ // Informs the plugin that the view it is in has gained or lost first
+ // responder status.
+ void SetContentAreaHasFocus(bool has_focus);
+ // Returns whether or not the window the plugin is in has focus.
+ bool GetWindowHasFocus() const { return containing_window_has_focus_; }
+ // Informs the plugin that its tab or window has been hidden or shown.
+ void SetContainerVisibility(bool is_visible);
+ // Informs the plugin that its containing window's frame has changed.
+ // Frames are in screen coordinates.
+ void WindowFrameChanged(gfx::Rect window_frame, gfx::Rect view_frame);
+ // Informs the delegate that the plugin set a Carbon ThemeCursor.
+ void SetThemeCursor(ThemeCursor cursor);
+ // Informs the delegate that the plugin set a Carbon Cursor.
+ void SetCursor(const Cursor* cursor);
+ // Informs the delegate that the plugin set a Cocoa NSCursor.
+ void SetNSCursor(NSCursor* cursor);
+
+#ifndef NP_NO_CARBON
+ // Indicates that it's time to send the plugin a null event.
+ void FireIdleEvent();
+#endif
+#endif // OS_MACOSX
+
+ gfx::PluginWindowHandle windowed_handle() const {
+ return windowed_handle_;
+ }
+
+#if defined(OS_MACOSX)
+ // Allow setting a "fake" window handle to associate this plug-in with
+ // an IOSurface in the browser. Used for accelerated drawing surfaces.
+ void set_windowed_handle(gfx::PluginWindowHandle handle);
+#endif
+
+ private:
+ friend class DeleteTask<WebPluginDelegateImpl>;
+ friend class webkit_glue::WebPluginDelegate;
+
+ WebPluginDelegateImpl(gfx::PluginWindowHandle containing_view,
+ NPAPI::PluginInstance *instance);
+ ~WebPluginDelegateImpl();
+
+ // Called by Initialize() for platform-specific initialization.
+ // If this returns false, the plugin shouldn't be started--see Initialize().
+ bool PlatformInitialize();
+
+ // Called by DestroyInstance(), used for platform-specific destruction.
+ void PlatformDestroyInstance();
+
+ //--------------------------
+ // used for windowed plugins
+ void WindowedUpdateGeometry(const gfx::Rect& window_rect,
+ const gfx::Rect& clip_rect);
+ // Create the native window.
+ // Returns true if the window is created (or already exists).
+ // Returns false if unable to create the window.
+ bool WindowedCreatePlugin();
+
+ // Destroy the native window.
+ void WindowedDestroyWindow();
+
+ // Reposition the native window to be in sync with the given geometry.
+ // Returns true if the native window has moved or been clipped differently.
+ bool WindowedReposition(const gfx::Rect& window_rect,
+ const gfx::Rect& clip_rect);
+
+ // Tells the plugin about the current state of the window.
+ // See NPAPI NPP_SetWindow for more information.
+ void WindowedSetWindow();
+
+#if defined(OS_WIN)
+ // Registers the window class for our window
+ ATOM RegisterNativeWindowClass();
+
+ // Our WndProc functions.
+ static LRESULT CALLBACK DummyWindowProc(
+ HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
+ static LRESULT CALLBACK NativeWndProc(
+ HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam);
+ static LRESULT CALLBACK FlashWindowlessWndProc(
+ HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam);
+
+ // Used for throttling Flash messages.
+ static void ClearThrottleQueueForWindow(HWND window);
+ static void OnThrottleMessage();
+ static void ThrottleMessage(WNDPROC proc, HWND hwnd, UINT message,
+ WPARAM wParam, LPARAM lParam);
+#endif
+
+ //----------------------------
+ // used for windowless plugins
+ void WindowlessUpdateGeometry(const gfx::Rect& window_rect,
+ const gfx::Rect& clip_rect);
+ void WindowlessPaint(gfx::NativeDrawingContext hdc, const gfx::Rect& rect);
+
+ // Tells the plugin about the current state of the window.
+ // See NPAPI NPP_SetWindow for more information.
+ void WindowlessSetWindow();
+
+ //-----------------------------------------
+ // used for windowed and windowless plugins
+
+ // Does platform-specific event handling. Arguments and return are identical
+ // to HandleInputEvent.
+ bool PlatformHandleInputEvent(const WebKit::WebInputEvent& event,
+ WebKit::WebCursorInfo* cursor_info);
+
+ NPAPI::PluginInstance* instance() { return instance_.get(); }
+
+ // Closes down and destroys our plugin instance.
+ void DestroyInstance();
+
+
+ // used for windowed plugins
+ // Note: on Mac OS X, the only time the windowed handle is non-zero
+ // is the case of accelerated rendering, which uses a fake window handle to
+ // identify itself back to the browser. It still performs all of its
+ // work offscreen.
+ gfx::PluginWindowHandle windowed_handle_;
+ gfx::Rect windowed_last_pos_;
+
+ bool windowed_did_set_window_;
+
+ // used by windowed and windowless plugins
+ bool windowless_;
+
+ webkit_glue::WebPlugin* plugin_;
+ scoped_refptr<NPAPI::PluginInstance> instance_;
+
+#if defined(OS_WIN)
+ // Original wndproc before we subclassed.
+ WNDPROC plugin_wnd_proc_;
+
+ // Used to throttle WM_USER+1 messages in Flash.
+ uint32 last_message_;
+ bool is_calling_wndproc;
+
+ // The current keyboard layout of this process and the main thread ID of the
+ // browser process. These variables are used for synchronizing the keyboard
+ // layout of this process with the one of the browser process.
+ HKL keyboard_layout_;
+ int parent_thread_id_;
+#endif // OS_WIN
+
+#if defined(USE_X11)
+ // The pixmap we're drawing into, for a windowless plugin.
+ GdkPixmap* pixmap_;
+ double first_event_time_;
+
+ // On Linux some plugins assume that the GtkSocket container is in the same
+ // process. So we create a GtkPlug to plug into the browser's container, and
+ // a GtkSocket to hold the plugin. We then send the GtkPlug to the browser
+ // process.
+ GtkWidget* plug_;
+ GtkWidget* socket_;
+
+ // Ensure pixmap_ exists and is at least width by height pixels.
+ void EnsurePixmapAtLeastSize(int width, int height);
+#endif
+
+ gfx::PluginWindowHandle parent_;
+ NPWindow window_;
+ gfx::Rect window_rect_;
+ gfx::Rect clip_rect_;
+ int quirks_;
+
+#if defined(OS_WIN)
+ // Windowless plugins don't have keyboard focus causing issues with the
+ // plugin not receiving keyboard events if the plugin enters a modal
+ // loop like TrackPopupMenuEx or MessageBox, etc.
+ // This is a basic issue with windows activation and focus arising due to
+ // the fact that these windows are created by different threads. Activation
+ // and focus are thread specific states, and if the browser has focus,
+ // the plugin may not have focus.
+ // To fix a majority of these activation issues we create a dummy visible
+ // child window to which we set focus whenever the windowless plugin
+ // receives a WM_LBUTTONDOWN/WM_RBUTTONDOWN message via NPP_HandleEvent.
+
+ HWND dummy_window_for_activation_;
+ bool CreateDummyWindowForActivation();
+
+ // Returns true if the event passed in needs to be tracked for a potential
+ // modal loop.
+ static bool ShouldTrackEventForModalLoops(NPEvent* event);
+
+ // The message filter hook procedure, which tracks modal loops entered by
+ // a plugin in the course of a NPP_HandleEvent call.
+ static LRESULT CALLBACK HandleEventMessageFilterHook(int code, WPARAM wParam,
+ LPARAM lParam);
+
+ // TrackPopupMenu interceptor. Parameters are the same as the Win32 function
+ // TrackPopupMenu.
+ static BOOL WINAPI TrackPopupMenuPatch(HMENU menu, unsigned int flags, int x,
+ int y, int reserved, HWND window,
+ const RECT* rect);
+
+ // SetCursor interceptor for windowless plugins.
+ static HCURSOR WINAPI SetCursorPatch(HCURSOR cursor);
+
+ // RegEnumKeyExW interceptor.
+ static LONG WINAPI RegEnumKeyExWPatch(
+ HKEY key, DWORD index, LPWSTR name, LPDWORD name_size, LPDWORD reserved,
+ LPWSTR class_name, LPDWORD class_size, PFILETIME last_write_time);
+
+ // The mouse hook proc which handles mouse capture in windowed plugins.
+ static LRESULT CALLBACK MouseHookProc(int code, WPARAM wParam,
+ LPARAM lParam);
+
+ // Calls SetCapture/ReleaseCapture based on the message type.
+ static void HandleCaptureForMessage(HWND window, UINT message);
+
+#elif defined(OS_MACOSX)
+ // Sets window_rect_ to |rect|
+ void SetPluginRect(const gfx::Rect& rect);
+ // Sets content_area_origin to |origin|
+ void SetContentAreaOrigin(const gfx::Point& origin);
+ // Updates everything that depends on the plugin's absolute screen location.
+ void PluginScreenLocationChanged();
+
+ // Informs the plugin that it has gained or lost keyboard focus (i.e., window
+ // first responder status).
+ void SetPluginHasFocus(bool has_focus);
+
+ // Returns the apparent zoom ratio for the given event, as inferred from our
+ // current knowledge about about where on screen the plugin is.
+ // This is a temporary workaround for <http://crbug.com/9996>; once that is
+ // fixed we should have correct event coordinates (or an explicit
+ // notification of zoom level).
+ float ApparentEventZoomLevel(const WebKit::WebMouseEvent& event);
+
+ // Informs the browser about the updated accelerated drawing surface.
+ void UpdateAcceleratedSurface();
+
+ // Updates anything that depends on plugin visibility.
+ void PluginVisibilityChanged();
+
+ // Uses a CARenderer to draw the plug-in's layer in our OpenGL surface.
+ void DrawLayerInSurface();
+
+#ifndef NP_NO_CARBON
+ // Moves our dummy window to match the current screen location of the plugin.
+ void UpdateDummyWindowBounds(const gfx::Point& plugin_origin);
+
+#ifndef NP_NO_QUICKDRAW
+ // Sets the mode used for QuickDraw plugin drawing. If enabled is true the
+ // plugin draws into a GWorld that's not connected to a window (the faster
+ // path), otherwise the plugin draws into our invisible dummy window (which is
+ // slower, since the call we use to scrape the window contents is much more
+ // expensive than copying between GWorlds).
+ void SetQuickDrawFastPathEnabled(bool enabled);
+#endif
+
+ // Adjusts the idle event rate for a Carbon plugin based on its current
+ // visibility.
+ void UpdateIdleEventRate();
+#endif // !NP_NO_CARBON
+
+ CGContextRef buffer_context_; // Weak ref.
+
+#ifndef NP_NO_CARBON
+ NP_CGContext np_cg_context_;
+#endif
+#ifndef NP_NO_QUICKDRAW
+ NP_Port qd_port_;
+ scoped_ptr<QuickDrawDrawingManager> qd_manager_;
+ base::TimeTicks fast_path_enable_tick_;
+#endif
+
+ CALayer* layer_; // Used for CA drawing mode. Weak, retained by plug-in.
+ AcceleratedSurface* surface_;
+ CARenderer* renderer_; // Renders layer_ to surface_.
+ scoped_ptr<base::RepeatingTimer<WebPluginDelegateImpl> > redraw_timer_;
+
+ // The upper-left corner of the web content area in screen coordinates,
+ // relative to an upper-left (0,0).
+ gfx::Point content_area_origin_;
+
+ // True if the plugin thinks it has keyboard focus
+ bool plugin_has_focus_;
+ // True if the plugin element has focus within the page, regardless of whether
+ // its containing view is currently the first responder for the window.
+ bool has_webkit_focus_;
+ // True if the containing view is the window's first responder.
+ bool containing_view_has_focus_;
+
+ bool containing_window_has_focus_;
+ bool initial_window_focus_;
+ bool container_is_visible_;
+ bool have_called_set_window_;
+
+ gfx::Rect cached_clip_rect_;
+
+ scoped_ptr<ExternalDragTracker> external_drag_tracker_;
+#endif // OS_MACOSX
+
+ // Called by the message filter hook when the plugin enters a modal loop.
+ void OnModalLoopEntered();
+
+ // Returns true if the message passed in corresponds to a user gesture.
+ static bool IsUserGesture(const WebKit::WebInputEvent& event);
+
+ // The url with which the plugin was instantiated.
+ std::string plugin_url_;
+
+#if defined(OS_WIN)
+ // Indicates the end of a user gesture period.
+ void OnUserGestureEnd();
+
+ // Handle to the message filter hook
+ HHOOK handle_event_message_filter_hook_;
+
+ // Event which is set when the plugin enters a modal loop in the course
+ // of a NPP_HandleEvent call.
+ HANDLE handle_event_pump_messages_event_;
+
+ // This flag indicates whether we started tracking a user gesture message.
+ bool user_gesture_message_posted_;
+
+ // Runnable Method Factory used to invoke the OnUserGestureEnd method
+ // asynchronously.
+ ScopedRunnableMethodFactory<WebPluginDelegateImpl> user_gesture_msg_factory_;
+
+ // Handle to the mouse hook installed for certain windowed plugins like
+ // flash.
+ HHOOK mouse_hook_;
+#endif
+
+ // Holds the depth of the HandleEvent callstack.
+ int handle_event_depth_;
+
+ // Holds the current cursor set by the windowless plugin.
+ WebCursor current_windowless_cursor_;
+
+ // Set to true initially and indicates if this is the first npp_setwindow
+ // call received by the plugin.
+ bool first_set_window_call_;
+
+ DISALLOW_COPY_AND_ASSIGN(WebPluginDelegateImpl);
+};
+
+#endif // WEBKIT_GLUE_PLUGIN_WEBPLUGIN_DELEGATE_IMPL_H_
diff --git a/webkit/glue/plugins/webplugin_delegate_impl_gtk.cc b/webkit/glue/plugins/webplugin_delegate_impl_gtk.cc
new file mode 100644
index 0000000..18b1504
--- /dev/null
+++ b/webkit/glue/plugins/webplugin_delegate_impl_gtk.cc
@@ -0,0 +1,709 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/webplugin_delegate_impl.h"
+
+#include <string>
+#include <vector>
+
+#include <gtk/gtk.h>
+#include <gdk/gdkx.h>
+
+#include "base/basictypes.h"
+#include "base/file_util.h"
+#include "base/message_loop.h"
+#include "base/process_util.h"
+#include "base/stats_counters.h"
+#include "base/string_util.h"
+#include "gfx/blit.h"
+#include "skia/ext/platform_canvas.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebCursorInfo.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebInputEvent.h"
+#include "webkit/glue/plugins/gtk_plugin_container.h"
+#include "webkit/glue/plugins/plugin_constants_win.h"
+#include "webkit/glue/plugins/plugin_instance.h"
+#include "webkit/glue/plugins/plugin_lib.h"
+#include "webkit/glue/plugins/plugin_list.h"
+#include "webkit/glue/plugins/plugin_stream_url.h"
+#include "webkit/glue/plugins/webplugin.h"
+#include "webkit/glue/webkit_glue.h"
+
+#include "third_party/npapi/bindings/npapi_x11.h"
+
+using WebKit::WebCursorInfo;
+using WebKit::WebKeyboardEvent;
+using WebKit::WebInputEvent;
+using WebKit::WebMouseEvent;
+
+WebPluginDelegateImpl::WebPluginDelegateImpl(
+ gfx::PluginWindowHandle containing_view,
+ NPAPI::PluginInstance *instance)
+ : windowed_handle_(0),
+ windowed_did_set_window_(false),
+ windowless_(false),
+ plugin_(NULL),
+ instance_(instance),
+ pixmap_(NULL),
+ first_event_time_(-1.0),
+ plug_(NULL),
+ socket_(NULL),
+ parent_(containing_view),
+ quirks_(0),
+ handle_event_depth_(0),
+ first_set_window_call_(true) {
+ memset(&window_, 0, sizeof(window_));
+ if (instance_->mime_type() == "application/x-shockwave-flash") {
+ // Flash is tied to Firefox's whacky behavior with windowless plugins. See
+ // comments in WindowlessPaint.
+ // TODO(viettrungluu): PLUGIN_QUIRK_WINDOWLESS_NO_RIGHT_CLICK: Don't allow
+ // right-clicks in windowless content since Flash 10.1 (initial release, at
+ // least) hangs in that case. Remove this once Flash is fixed.
+ quirks_ |= PLUGIN_QUIRK_WINDOWLESS_OFFSET_WINDOW_TO_DRAW
+ | PLUGIN_QUIRK_WINDOWLESS_INVALIDATE_AFTER_SET_WINDOW
+ | PLUGIN_QUIRK_WINDOWLESS_NO_RIGHT_CLICK;
+ }
+
+ // TODO(evanm): I played with this for quite a while but couldn't
+ // figure out a way to make Flash not crash unless I didn't call
+ // NPP_SetWindow.
+ // However, after piman's grand refactor of windowed plugins, maybe
+ // this is no longer necessary.
+ quirks_ |= PLUGIN_QUIRK_DONT_SET_NULL_WINDOW_HANDLE_ON_DESTROY;
+}
+
+WebPluginDelegateImpl::~WebPluginDelegateImpl() {
+ DestroyInstance();
+
+ if (!windowless_)
+ WindowedDestroyWindow();
+
+ if (window_.ws_info) {
+ // We only ever use ws_info as an NPSetWindowCallbackStruct.
+ delete static_cast<NPSetWindowCallbackStruct*>(window_.ws_info);
+ }
+
+ if (pixmap_) {
+ g_object_unref(pixmap_);
+ pixmap_ = NULL;
+ }
+}
+
+bool WebPluginDelegateImpl::PlatformInitialize() {
+ gfx::PluginWindowHandle handle =
+ windowless_ ? 0 : gtk_plug_get_id(GTK_PLUG(plug_));
+ plugin_->SetWindow(handle);
+ return true;
+}
+
+void WebPluginDelegateImpl::PlatformDestroyInstance() {
+ // Nothing to do here.
+}
+
+void WebPluginDelegateImpl::Paint(WebKit::WebCanvas* canvas,
+ const gfx::Rect& rect) {
+ if (!windowless_)
+ return;
+ cairo_t* context = canvas->beginPlatformPaint();
+ WindowlessPaint(context, rect);
+ canvas->endPlatformPaint();
+}
+
+void WebPluginDelegateImpl::Print(cairo_t* context) {
+ NOTIMPLEMENTED();
+}
+
+void WebPluginDelegateImpl::InstallMissingPlugin() {
+ NOTIMPLEMENTED();
+}
+
+bool WebPluginDelegateImpl::WindowedCreatePlugin() {
+ DCHECK(!windowed_handle_);
+ DCHECK(!plug_);
+
+ // NPP_GetValue() might write 4 bytes of data to this variable. Don't use a
+ // single byte bool, use an int instead and make sure it is initialized.
+ int xembed = 0;
+ NPError err = instance_->NPP_GetValue(NPPVpluginNeedsXEmbed, &xembed);
+ if (err != NPERR_NO_ERROR || !xembed) {
+ NOTIMPLEMENTED() << " windowed plugin but without xembed. "
+ "See http://code.google.com/p/chromium/issues/detail?id=38229";
+ return false;
+ }
+
+ // Passing 0 as the socket XID creates a plug without plugging it in a socket
+ // yet, so that it can be latter added with gtk_socket_add_id().
+ plug_ = gtk_plug_new(0);
+ gtk_widget_show(plug_);
+ socket_ = gtk_socket_new();
+ gtk_widget_show(socket_);
+ gtk_container_add(GTK_CONTAINER(plug_), socket_);
+ gtk_widget_show_all(plug_);
+
+ // Prevent the plug from being destroyed if the browser kills the container
+ // window.
+ g_signal_connect(plug_, "delete-event", G_CALLBACK(gtk_true), NULL);
+ // Prevent the socket from being destroyed when the plugin removes itself.
+ g_signal_connect(socket_, "plug_removed", G_CALLBACK(gtk_true), NULL);
+
+ windowed_handle_ = gtk_socket_get_id(GTK_SOCKET(socket_));
+
+ window_.window = reinterpret_cast<void*>(windowed_handle_);
+
+ if (!window_.ws_info)
+ window_.ws_info = new NPSetWindowCallbackStruct;
+ NPSetWindowCallbackStruct* extra =
+ static_cast<NPSetWindowCallbackStruct*>(window_.ws_info);
+ extra->display = GDK_DISPLAY();
+ extra->visual = DefaultVisual(GDK_DISPLAY(), 0);
+ extra->depth = DefaultDepth(GDK_DISPLAY(), 0);
+ extra->colormap = DefaultColormap(GDK_DISPLAY(), 0);
+
+ return true;
+}
+
+void WebPluginDelegateImpl::WindowedDestroyWindow() {
+ if (plug_) {
+ plugin_->WillDestroyWindow(gtk_plug_get_id(GTK_PLUG(plug_)));
+
+ gtk_widget_destroy(plug_);
+ plug_ = NULL;
+ socket_ = NULL;
+ windowed_handle_ = 0;
+ }
+}
+
+bool WebPluginDelegateImpl::WindowedReposition(
+ const gfx::Rect& window_rect,
+ const gfx::Rect& clip_rect) {
+ if (window_rect == window_rect_ && clip_rect == clip_rect_)
+ return false;
+
+ window_rect_ = window_rect;
+ clip_rect_ = clip_rect;
+
+ return true;
+}
+
+void WebPluginDelegateImpl::WindowedSetWindow() {
+ if (!instance_)
+ return;
+
+ if (!windowed_handle_) {
+ NOTREACHED();
+ return;
+ }
+
+ // See https://bugzilla.mozilla.org/show_bug.cgi?id=108347
+ // If we call NPP_SetWindow with a <= 0 width or height, problems arise in
+ // Flash (and possibly other plugins).
+ // TODO(piman): the Mozilla code suggests that for the Java plugin, we should
+ // still call NPP_SetWindow in that case. We need to verify that.
+ if (window_rect_.width() <= 0 || window_rect_.height() <= 0) {
+ return;
+ }
+
+ instance()->set_window_handle(windowed_handle_);
+
+ DCHECK(!instance()->windowless());
+
+ window_.clipRect.top = clip_rect_.y();
+ window_.clipRect.left = clip_rect_.x();
+ window_.clipRect.bottom = clip_rect_.y() + clip_rect_.height();
+ window_.clipRect.right = clip_rect_.x() + clip_rect_.width();
+ window_.height = window_rect_.height();
+ window_.width = window_rect_.width();
+ window_.x = window_rect_.x();
+ window_.y = window_rect_.y();
+
+ //window_.window = windowed_handle_;
+ window_.type = NPWindowTypeWindow;
+
+ // Reset this flag before entering the instance in case of side-effects.
+ windowed_did_set_window_ = true;
+
+ NPError err = instance()->NPP_SetWindow(&window_);
+ DCHECK(err == NPERR_NO_ERROR);
+}
+
+void WebPluginDelegateImpl::WindowlessUpdateGeometry(
+ const gfx::Rect& window_rect,
+ const gfx::Rect& clip_rect) {
+ // Only resend to the instance if the geometry has changed.
+ if (window_rect == window_rect_ && clip_rect == clip_rect_)
+ return;
+
+ clip_rect_ = clip_rect;
+ window_rect_ = window_rect;
+ WindowlessSetWindow();
+}
+
+void WebPluginDelegateImpl::EnsurePixmapAtLeastSize(int width, int height) {
+ if (pixmap_) {
+ gint cur_width, cur_height;
+ gdk_drawable_get_size(pixmap_, &cur_width, &cur_height);
+ if (cur_width >= width && cur_height >= height)
+ return; // We are already the appropriate size.
+
+ // Otherwise, we need to recreate ourselves.
+ g_object_unref(pixmap_);
+ pixmap_ = NULL;
+ }
+
+ // |sys_visual| is owned by gdk; we shouldn't free it.
+ GdkVisual* sys_visual = gdk_visual_get_system();
+ pixmap_ = gdk_pixmap_new(NULL, // use width/height/depth params
+ std::max(1, width), std::max(1, height),
+ sys_visual->depth);
+ GdkColormap* colormap = gdk_colormap_new(gdk_visual_get_system(),
+ FALSE);
+ gdk_drawable_set_colormap(GDK_DRAWABLE(pixmap_), colormap);
+ // The GdkDrawable now owns the GdkColormap.
+ g_object_unref(colormap);
+}
+
+#ifdef DEBUG_RECTANGLES
+namespace {
+
+// Draw a rectangle on a Cairo context.
+// Useful for debugging various rectangles involved in drawing plugins.
+void DrawDebugRectangle(cairo_t* cairo,
+ const gfx::Rect& rect,
+ float r, float g, float b) {
+ cairo_set_source_rgba(cairo, r, g, b, 0.5);
+ cairo_rectangle(cairo, rect.x(), rect.y(),
+ rect.width(), rect.height());
+ cairo_stroke(cairo);
+}
+
+} // namespace
+#endif
+
+void WebPluginDelegateImpl::WindowlessPaint(cairo_t* context,
+ const gfx::Rect& damage_rect) {
+ // Compare to:
+ // http://mxr.mozilla.org/firefox/source/layout/generic/nsObjectFrame.cpp:
+ // nsPluginInstanceOwner::Renderer::NativeDraw().
+
+ DCHECK(context);
+
+ // TODO(darin): we should avoid calling NPP_SetWindow here since it may
+ // cause page layout to be invalidated.
+
+ // The actual dirty region is just the intersection of the plugin window and
+ // the clip window with the damage region. However, the plugin wants to draw
+ // relative to the containing window's origin, so our pixmap must be from the
+ // window's origin down to the bottom-right edge of the dirty region.
+ //
+ // Typical case:
+ // X-----------------------------------+-----------------------------+
+ // | | |
+ // | pixmap +-------------------+ |
+ // | | damage | window |
+ // | | | |
+ // | +---+-------------------+-------------+ |
+ // | | | | clip | |
+ // | +---+---+-------------------+----------+ | |
+ // | | | | | | | |
+ // | | | | draw | | | |
+ // | | | | | | | |
+ // +-------+---+---+-------------------+----------+--+ |
+ // | | | | | |
+ // | | +-------------------+ | |
+ // | | | |
+ // | | plugin | |
+ // | +--------------------------------------+ |
+ // | |
+ // | |
+ // +-----------------------------------------------------------------+
+ // X = origin
+ //
+ // NPAPI doesn't properly define which coordinates each of
+ // - window.clipRect, window.x and window.y in the SetWindow call
+ // - x and y in GraphicsExpose HandleEvent call
+ // are relative to, nor does it define what the pixmap is relative to.
+ //
+ // Any sane values for them just don't work with the flash plugin. Firefox
+ // has some interesting behavior. Experiments showed that:
+ // - window.clipRect is always in the same space as window.x and window.y
+ // - in the first SetWindow call, or when scrolling, window.x and window.y are
+ // the coordinates of the plugin relative to the window.
+ // - whenever only a part of the plugin is drawn, Firefox issues a SetWindow
+ // call before each GraphicsExpose event, that sets the drawing origin to
+ // (0, 0) as if the plugin was scrolled to be partially out of the view. The
+ // GraphicsExpose event has coordinates relative to the "window" (assuming
+ // that virtual scroll). The pixmap is also relative to the window. It always
+ // sets the clip rect to the draw rect.
+ //
+ // Attempts to deviate from that makes Flash render at the wrong place in the
+ // pixmap, or render the wrong pixels.
+ //
+ // Flash plugin:
+ // X-----------------------------------------------------------------+
+ // | |
+ // | +-------------------+ "real" window |
+ // | | damage | |
+ // | | | |
+ // | +---+-------------------+-------------+ |
+ // | | | | "real" clip | |
+ // | +---+---O===================#==========#==#===============#
+ // | | | H draw | | | H
+ // | | | H = pixmap | | | H
+ // | | | H = "apparent" clip | | | H
+ // | + +---#-------------------+----------+--+ H
+ // | | H | | H
+ // | | H-------------------+ | H
+ // | | H | H
+ // | | H plugin | H
+ // | +-------#------------------------------+ H
+ // | H H
+ // | H "apparent" window H
+ // +---------------#=================================================#
+ // X = "real" origin
+ // O = "apparent" origin
+ // "real" means as seen by Chrome
+ // "apparent" means as seen by the plugin.
+
+ gfx::Rect draw_rect = window_rect_.Intersect(damage_rect);
+
+ // clip_rect_ is relative to the plugin
+ gfx::Rect clip_rect_window = clip_rect_;
+ clip_rect_window.Offset(window_rect_.x(), window_rect_.y());
+ draw_rect = draw_rect.Intersect(clip_rect_window);
+
+ // These offsets represent by how much the view is shifted to accomodate
+ // Flash (the coordinates of X relative to O in the diagram above).
+ int offset_x = 0;
+ int offset_y = 0;
+ if (quirks_ & PLUGIN_QUIRK_WINDOWLESS_OFFSET_WINDOW_TO_DRAW) {
+ offset_x = -draw_rect.x();
+ offset_y = -draw_rect.y();
+ window_.clipRect.top = 0;
+ window_.clipRect.left = 0;
+ window_.clipRect.bottom = draw_rect.height();
+ window_.clipRect.right = draw_rect.width();
+ window_.height = window_rect_.height();
+ window_.width = window_rect_.width();
+ window_.x = window_rect_.x() - draw_rect.x();
+ window_.y = window_rect_.y() - draw_rect.y();
+ window_.type = NPWindowTypeDrawable;
+ DCHECK(window_.ws_info);
+ NPError err = instance()->NPP_SetWindow(&window_);
+ DCHECK_EQ(err, NPERR_NO_ERROR);
+ }
+
+ gfx::Rect pixmap_draw_rect = draw_rect;
+ pixmap_draw_rect.Offset(offset_x, offset_y);
+
+ gfx::Rect pixmap_rect(0, 0,
+ pixmap_draw_rect.right(),
+ pixmap_draw_rect.bottom());
+
+ EnsurePixmapAtLeastSize(pixmap_rect.width(), pixmap_rect.height());
+
+ // Copy the current image into the pixmap, so the plugin can draw over
+ // this background.
+ cairo_t* cairo = gdk_cairo_create(pixmap_);
+ BlitContextToContext(cairo, pixmap_draw_rect, context, draw_rect.origin());
+ cairo_destroy(cairo);
+
+ // Construct the paint message, targeting the pixmap.
+ NPEvent np_event = {0};
+ XGraphicsExposeEvent &event = np_event.xgraphicsexpose;
+ event.type = GraphicsExpose;
+ event.display = GDK_DISPLAY();
+ event.drawable = GDK_PIXMAP_XID(pixmap_);
+ event.x = pixmap_draw_rect.x();
+ event.y = pixmap_draw_rect.y();
+ event.width = pixmap_draw_rect.width();
+ event.height = pixmap_draw_rect.height();
+
+ // Tell the plugin to paint into the pixmap.
+ static StatsRate plugin_paint("Plugin.Paint");
+ StatsScope<StatsRate> scope(plugin_paint);
+ NPError err = instance()->NPP_HandleEvent(&np_event);
+ DCHECK_EQ(err, NPERR_NO_ERROR);
+
+ cairo_save(context);
+ // Now copy the rendered image pixmap back into the drawing buffer.
+ gdk_cairo_set_source_pixmap(context, pixmap_, -offset_x, -offset_y);
+ cairo_rectangle(context, draw_rect.x(), draw_rect.y(),
+ draw_rect.width(), draw_rect.height());
+ cairo_clip(context);
+ cairo_paint(context);
+
+#ifdef DEBUG_RECTANGLES
+ // Draw some debugging rectangles.
+ // Pixmap rect = blue.
+ DrawDebugRectangle(context, pixmap_rect, 0, 0, 1);
+ // Drawing rect = red.
+ DrawDebugRectangle(context, draw_rect, 1, 0, 0);
+#endif
+ cairo_restore(context);
+}
+
+void WebPluginDelegateImpl::WindowlessSetWindow() {
+ if (!instance())
+ return;
+
+ if (window_rect_.IsEmpty()) // wait for geometry to be set.
+ return;
+
+ DCHECK(instance()->windowless());
+ // Mozilla docs say that this window param is not used for windowless
+ // plugins; rather, the window is passed during the GraphicsExpose event.
+ DCHECK(window_.window == 0);
+
+ window_.clipRect.top = clip_rect_.y() + window_rect_.y();
+ window_.clipRect.left = clip_rect_.x() + window_rect_.x();
+ window_.clipRect.bottom =
+ clip_rect_.y() + clip_rect_.height() + window_rect_.y();
+ window_.clipRect.right =
+ clip_rect_.x() + clip_rect_.width() + window_rect_.x();
+ window_.height = window_rect_.height();
+ window_.width = window_rect_.width();
+ window_.x = window_rect_.x();
+ window_.y = window_rect_.y();
+ window_.type = NPWindowTypeDrawable;
+
+ if (!window_.ws_info)
+ window_.ws_info = new NPSetWindowCallbackStruct;
+ NPSetWindowCallbackStruct* extra =
+ static_cast<NPSetWindowCallbackStruct*>(window_.ws_info);
+ extra->display = GDK_DISPLAY();
+ extra->visual = DefaultVisual(GDK_DISPLAY(), 0);
+ extra->depth = DefaultDepth(GDK_DISPLAY(), 0);
+ extra->colormap = DefaultColormap(GDK_DISPLAY(), 0);
+
+ NPError err = instance()->NPP_SetWindow(&window_);
+ DCHECK(err == NPERR_NO_ERROR);
+ if (quirks_ & PLUGIN_QUIRK_WINDOWLESS_INVALIDATE_AFTER_SET_WINDOW) {
+ // After a NPP_SetWindow, Flash cancels its timer that generates the
+ // invalidates until it gets a paint event, but doesn't explicitly call
+ // NPP_InvalidateRect.
+ plugin_->InvalidateRect(clip_rect_);
+ }
+}
+
+void WebPluginDelegateImpl::SetFocus(bool focused) {
+ DCHECK(instance()->windowless());
+
+ NPEvent np_event = {0};
+ XFocusChangeEvent &event = np_event.xfocus;
+ event.type = focused ? FocusIn : FocusOut;
+ event.display = GDK_DISPLAY();
+ // Same values as Firefox. .serial and .window stay 0.
+ event.mode = -1;
+ event.detail = NotifyDetailNone;
+ instance()->NPP_HandleEvent(&np_event);
+}
+
+// Converts a WebInputEvent::Modifiers bitfield into a
+// corresponding X modifier state.
+static int GetXModifierState(int modifiers) {
+ int x_state = 0;
+ if (modifiers & WebInputEvent::ControlKey)
+ x_state |= ControlMask;
+ if (modifiers & WebInputEvent::ShiftKey)
+ x_state |= ShiftMask;
+ if (modifiers & WebInputEvent::AltKey)
+ x_state |= Mod1Mask;
+ if (modifiers & WebInputEvent::MetaKey)
+ x_state |= Mod2Mask;
+ if (modifiers & WebInputEvent::LeftButtonDown)
+ x_state |= Button1Mask;
+ if (modifiers & WebInputEvent::MiddleButtonDown)
+ x_state |= Button2Mask;
+ if (modifiers & WebInputEvent::RightButtonDown)
+ x_state |= Button3Mask;
+ // TODO(piman@google.com): There are other modifiers, e.g. Num Lock, that
+ // should be set (and Firefox does), but we didn't keep the information in
+ // the WebKit event.
+ return x_state;
+}
+
+static bool NPEventFromWebMouseEvent(const WebMouseEvent& event,
+ Time timestamp,
+ NPEvent *np_event) {
+ np_event->xany.display = GDK_DISPLAY();
+ // NOTE: Firefox keeps xany.serial and xany.window as 0.
+
+ int modifier_state = GetXModifierState(event.modifiers);
+
+ Window root = GDK_ROOT_WINDOW();
+ switch (event.type) {
+ case WebInputEvent::MouseMove: {
+ np_event->type = MotionNotify;
+ XMotionEvent &motion_event = np_event->xmotion;
+ motion_event.root = root;
+ motion_event.time = timestamp;
+ motion_event.x = event.x;
+ motion_event.y = event.y;
+ motion_event.x_root = event.globalX;
+ motion_event.y_root = event.globalY;
+ motion_event.state = modifier_state;
+ motion_event.is_hint = NotifyNormal;
+ motion_event.same_screen = True;
+ break;
+ }
+ case WebInputEvent::MouseLeave:
+ case WebInputEvent::MouseEnter: {
+ if (event.type == WebInputEvent::MouseEnter) {
+ np_event->type = EnterNotify;
+ } else {
+ np_event->type = LeaveNotify;
+ }
+ XCrossingEvent &crossing_event = np_event->xcrossing;
+ crossing_event.root = root;
+ crossing_event.time = timestamp;
+ crossing_event.x = event.x;
+ crossing_event.y = event.y;
+ crossing_event.x_root = event.globalX;
+ crossing_event.y_root = event.globalY;
+ crossing_event.mode = -1; // This is what Firefox sets it to.
+ crossing_event.detail = NotifyDetailNone;
+ crossing_event.same_screen = True;
+ // TODO(piman@google.com): set this to the correct value. Firefox does. I
+ // don't know where to get the information though, we get focus
+ // notifications, but no unfocus.
+ crossing_event.focus = 0;
+ crossing_event.state = modifier_state;
+ break;
+ }
+ case WebInputEvent::MouseUp:
+ case WebInputEvent::MouseDown: {
+ if (event.type == WebInputEvent::MouseDown) {
+ np_event->type = ButtonPress;
+ } else {
+ np_event->type = ButtonRelease;
+ }
+ XButtonEvent &button_event = np_event->xbutton;
+ button_event.root = root;
+ button_event.time = timestamp;
+ button_event.x = event.x;
+ button_event.y = event.y;
+ button_event.x_root = event.globalX;
+ button_event.y_root = event.globalY;
+ button_event.state = modifier_state;
+ switch (event.button) {
+ case WebMouseEvent::ButtonLeft:
+ button_event.button = Button1;
+ break;
+ case WebMouseEvent::ButtonMiddle:
+ button_event.button = Button2;
+ break;
+ case WebMouseEvent::ButtonRight:
+ button_event.button = Button3;
+ break;
+ default:
+ NOTREACHED();
+ }
+ button_event.same_screen = True;
+ break;
+ }
+ default:
+ NOTREACHED();
+ return false;
+ }
+ return true;
+}
+
+static bool NPEventFromWebKeyboardEvent(const WebKeyboardEvent& event,
+ Time timestamp,
+ NPEvent *np_event) {
+ np_event->xany.display = GDK_DISPLAY();
+ // NOTE: Firefox keeps xany.serial and xany.window as 0.
+
+ switch (event.type) {
+ case WebKeyboardEvent::KeyDown:
+ np_event->type = KeyPress;
+ break;
+ case WebKeyboardEvent::KeyUp:
+ np_event->type = KeyRelease;
+ break;
+ default:
+ NOTREACHED();
+ return false;
+ }
+ XKeyEvent &key_event = np_event->xkey;
+ key_event.send_event = False;
+ key_event.display = GDK_DISPLAY();
+ // NOTE: Firefox keeps xany.serial and xany.window as 0.
+ // TODO(piman@google.com): is this right for multiple screens ?
+ key_event.root = DefaultRootWindow(key_event.display);
+ key_event.time = timestamp;
+ // NOTE: We don't have the correct information for x/y/x_root/y_root. Firefox
+ // doesn't have it either, so we pass the same values.
+ key_event.x = 0;
+ key_event.y = 0;
+ key_event.x_root = -1;
+ key_event.y_root = -1;
+ key_event.state = GetXModifierState(event.modifiers);
+ key_event.keycode = event.nativeKeyCode;
+ key_event.same_screen = True;
+ return true;
+}
+
+static bool NPEventFromWebInputEvent(const WebInputEvent& event,
+ Time timestamp,
+ NPEvent* np_event) {
+ switch (event.type) {
+ case WebInputEvent::MouseMove:
+ case WebInputEvent::MouseLeave:
+ case WebInputEvent::MouseEnter:
+ case WebInputEvent::MouseDown:
+ case WebInputEvent::MouseUp:
+ if (event.size < sizeof(WebMouseEvent)) {
+ NOTREACHED();
+ return false;
+ }
+ return NPEventFromWebMouseEvent(
+ *static_cast<const WebMouseEvent*>(&event), timestamp, np_event);
+ case WebInputEvent::KeyDown:
+ case WebInputEvent::KeyUp:
+ if (event.size < sizeof(WebKeyboardEvent)) {
+ NOTREACHED();
+ return false;
+ }
+ return NPEventFromWebKeyboardEvent(
+ *static_cast<const WebKeyboardEvent*>(&event), timestamp, np_event);
+ default:
+ return false;
+ }
+}
+
+bool WebPluginDelegateImpl::PlatformHandleInputEvent(
+ const WebInputEvent& event, WebCursorInfo* cursor_info) {
+
+ if (first_event_time_ < 0.0)
+ first_event_time_ = event.timeStampSeconds;
+ Time timestamp = static_cast<Time>(
+ (event.timeStampSeconds - first_event_time_) * 1.0e3);
+ NPEvent np_event = {0};
+ if (!NPEventFromWebInputEvent(event, timestamp, &np_event)) {
+ return false;
+ }
+ // See comment about PLUGIN_QUIRK_WINDOWLESS_NO_RIGHT_CLICK in constructor.
+ if (windowless_ &&
+ (quirks_ & PLUGIN_QUIRK_WINDOWLESS_NO_RIGHT_CLICK) &&
+ (np_event.type == ButtonPress || np_event.type == ButtonRelease) &&
+ (np_event.xbutton.button == Button3)) {
+ return false;
+ }
+
+ bool ret = instance()->NPP_HandleEvent(&np_event) != 0;
+
+ // Flash always returns false, even when the event is handled.
+ ret = true;
+
+#if 0
+ if (event->event == WM_MOUSEMOVE) {
+ // Snag a reference to the current cursor ASAP in case the plugin modified
+ // it. There is a nasty race condition here with the multiprocess browser
+ // as someone might be setting the cursor in the main process as well.
+ *cursor = current_windowless_cursor_;
+ }
+#endif
+
+ return ret;
+}
diff --git a/webkit/glue/plugins/webplugin_delegate_impl_mac.mm b/webkit/glue/plugins/webplugin_delegate_impl_mac.mm
new file mode 100644
index 0000000..efa6bdd
--- /dev/null
+++ b/webkit/glue/plugins/webplugin_delegate_impl_mac.mm
@@ -0,0 +1,1160 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import <Cocoa/Cocoa.h>
+#import <QuartzCore/QuartzCore.h>
+
+#include "webkit/glue/plugins/webplugin_delegate_impl.h"
+
+#include <string>
+#include <unistd.h>
+#include <set>
+
+#include "base/file_util.h"
+#include "base/lazy_instance.h"
+#include "base/message_loop.h"
+#include "base/scoped_ptr.h"
+#include "base/stats_counters.h"
+#include "base/string_util.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebInputEvent.h"
+#include "webkit/glue/plugins/plugin_instance.h"
+#include "webkit/glue/plugins/plugin_lib.h"
+#include "webkit/glue/plugins/plugin_list.h"
+#include "webkit/glue/plugins/plugin_stream_url.h"
+#include "webkit/glue/plugins/plugin_web_event_converter_mac.h"
+#include "webkit/glue/plugins/webplugin.h"
+#include "webkit/glue/webkit_glue.h"
+
+#ifndef NP_NO_CARBON
+#include "webkit/glue/plugins/carbon_plugin_window_tracker_mac.h"
+#endif
+
+#ifndef NP_NO_QUICKDRAW
+#include "webkit/glue/plugins/quickdraw_drawing_manager_mac.h"
+#endif
+
+using webkit_glue::WebPlugin;
+using webkit_glue::WebPluginDelegate;
+using webkit_glue::WebPluginResourceClient;
+using WebKit::WebCursorInfo;
+using WebKit::WebKeyboardEvent;
+using WebKit::WebInputEvent;
+using WebKit::WebMouseEvent;
+using WebKit::WebMouseWheelEvent;
+
+const int kCoreAnimationRedrawPeriodMs = 10; // 100 Hz
+
+// Important implementation notes: The Mac definition of NPAPI, particularly
+// the distinction between windowed and windowless modes, differs from the
+// Windows and Linux definitions. Most of those differences are
+// accomodated by the WebPluginDelegate class.
+
+namespace {
+
+WebPluginDelegateImpl* g_active_delegate;
+
+// Helper to simplify correct usage of g_active_delegate. Instantiating will
+// set the active delegate to |delegate| for the lifetime of the object, then
+// NULL when it goes out of scope.
+class ScopedActiveDelegate {
+public:
+ explicit ScopedActiveDelegate(WebPluginDelegateImpl* delegate) {
+ g_active_delegate = delegate;
+ }
+ ~ScopedActiveDelegate() {
+ g_active_delegate = NULL;
+ }
+private:
+ DISALLOW_COPY_AND_ASSIGN(ScopedActiveDelegate);
+};
+
+#ifndef NP_NO_CARBON
+// Timer periods for sending idle events to Carbon plugins. The visible value
+// (50Hz) matches both Safari and Firefox. The hidden value (8Hz) matches
+// Firefox; according to https://bugzilla.mozilla.org/show_bug.cgi?id=525533
+// going lower than that causes issues.
+const int kVisibleIdlePeriodMs = 20; // (50Hz)
+const int kHiddenIdlePeriodMs = 125; // (8Hz)
+
+class CarbonIdleEventSource {
+ public:
+ // Returns the shared Carbon idle event source.
+ static CarbonIdleEventSource* SharedInstance() {
+ DCHECK(MessageLoop::current()->type() == MessageLoop::TYPE_UI);
+ static CarbonIdleEventSource* event_source = new CarbonIdleEventSource();
+ return event_source;
+ }
+
+ // Registers the plugin delegate as interested in receiving idle events at
+ // a rate appropriate for the given visibility. A delegate can safely be
+ // re-registered any number of times, with the latest registration winning.
+ void RegisterDelegate(WebPluginDelegateImpl* delegate, bool visible) {
+ if (visible) {
+ visible_delegates_->RegisterDelegate(delegate);
+ hidden_delegates_->UnregisterDelegate(delegate);
+ } else {
+ hidden_delegates_->RegisterDelegate(delegate);
+ visible_delegates_->UnregisterDelegate(delegate);
+ }
+ }
+
+ // Removes the plugin delegate from the list of plugins receiving idle events.
+ void UnregisterDelegate(WebPluginDelegateImpl* delegate) {
+ visible_delegates_->UnregisterDelegate(delegate);
+ hidden_delegates_->UnregisterDelegate(delegate);
+ }
+
+ private:
+ class VisibilityGroup {
+ public:
+ explicit VisibilityGroup(int timer_period)
+ : timer_period_(timer_period), iterator_(delegates_.end()) {}
+
+ // Adds |delegate| to this visibility group.
+ void RegisterDelegate(WebPluginDelegateImpl* delegate) {
+ if (delegates_.empty()) {
+ timer_.Start(base::TimeDelta::FromMilliseconds(timer_period_),
+ this, &VisibilityGroup::SendIdleEvents);
+ }
+ delegates_.insert(delegate);
+ }
+
+ // Removes |delegate| from this visibility group.
+ void UnregisterDelegate(WebPluginDelegateImpl* delegate) {
+ // If a plugin changes visibility during idle event handling, it
+ // may be removed from this set while SendIdleEvents is still iterating;
+ // if that happens and it's next on the list, increment the iterator
+ // before erasing so that the iteration won't be corrupted.
+ if ((iterator_ != delegates_.end()) && (*iterator_ == delegate))
+ ++iterator_;
+ size_t removed = delegates_.erase(delegate);
+ if (removed > 0 && delegates_.empty())
+ timer_.Stop();
+ }
+
+ private:
+ // Fires off idle events for each delegate in the group.
+ void SendIdleEvents() {
+ for (iterator_ = delegates_.begin(); iterator_ != delegates_.end();) {
+ // Pre-increment so that the skip logic in UnregisterDelegates works.
+ WebPluginDelegateImpl* delegate = *(iterator_++);
+ delegate->FireIdleEvent();
+ }
+ }
+
+ int timer_period_;
+ base::RepeatingTimer<VisibilityGroup> timer_;
+ std::set<WebPluginDelegateImpl*> delegates_;
+ std::set<WebPluginDelegateImpl*>::iterator iterator_;
+ };
+
+ CarbonIdleEventSource()
+ : visible_delegates_(new VisibilityGroup(kVisibleIdlePeriodMs)),
+ hidden_delegates_(new VisibilityGroup(kHiddenIdlePeriodMs)) {}
+
+ scoped_ptr<VisibilityGroup> visible_delegates_;
+ scoped_ptr<VisibilityGroup> hidden_delegates_;
+
+ DISALLOW_COPY_AND_ASSIGN(CarbonIdleEventSource);
+};
+#endif // !NP_NO_CARBON
+
+} // namespace
+
+// Helper to build and maintain a model of a drag entering the plugin but not
+// starting there. See explanation in PlatformHandleInputEvent.
+class ExternalDragTracker {
+ public:
+ ExternalDragTracker() : pressed_buttons_(0) {}
+
+ // Returns true if an external drag is in progress.
+ bool IsDragInProgress() { return pressed_buttons_ != 0; };
+
+ // Returns true if the given event appears to be related to an external drag.
+ bool EventIsRelatedToDrag(const WebInputEvent& event);
+
+ // Updates the tracking of whether an external drag is in progress--and if
+ // so what buttons it involves--based on the given event.
+ void UpdateDragStateFromEvent(const WebInputEvent& event);
+
+ private:
+ // Returns the mask for just the button state in a WebInputEvent's modifiers.
+ static int WebEventButtonModifierMask();
+
+ // The WebInputEvent modifier flags for any buttons that were down when an
+ // external drag entered the plugin, and which and are still down now.
+ int pressed_buttons_;
+
+ DISALLOW_COPY_AND_ASSIGN(ExternalDragTracker);
+};
+
+void ExternalDragTracker::UpdateDragStateFromEvent(const WebInputEvent& event) {
+ switch (event.type) {
+ case WebInputEvent::MouseEnter:
+ pressed_buttons_ = event.modifiers & WebEventButtonModifierMask();
+ break;
+ case WebInputEvent::MouseUp: {
+ const WebMouseEvent* mouse_event =
+ static_cast<const WebMouseEvent*>(&event);
+ if (mouse_event->button == WebMouseEvent::ButtonLeft)
+ pressed_buttons_ &= ~WebInputEvent::LeftButtonDown;
+ if (mouse_event->button == WebMouseEvent::ButtonMiddle)
+ pressed_buttons_ &= ~WebInputEvent::MiddleButtonDown;
+ if (mouse_event->button == WebMouseEvent::ButtonRight)
+ pressed_buttons_ &= ~WebInputEvent::RightButtonDown;
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+bool ExternalDragTracker::EventIsRelatedToDrag(const WebInputEvent& event) {
+ const WebMouseEvent* mouse_event = static_cast<const WebMouseEvent*>(&event);
+ switch (event.type) {
+ case WebInputEvent::MouseUp:
+ // We only care about release of buttons that were part of the drag.
+ return ((mouse_event->button == WebMouseEvent::ButtonLeft &&
+ (pressed_buttons_ & WebInputEvent::LeftButtonDown)) ||
+ (mouse_event->button == WebMouseEvent::ButtonMiddle &&
+ (pressed_buttons_ & WebInputEvent::MiddleButtonDown)) ||
+ (mouse_event->button == WebMouseEvent::ButtonRight &&
+ (pressed_buttons_ & WebInputEvent::RightButtonDown)));
+ case WebInputEvent::MouseEnter:
+ return (event.modifiers & WebEventButtonModifierMask()) != 0;
+ case WebInputEvent::MouseLeave:
+ case WebInputEvent::MouseMove: {
+ int event_buttons = (event.modifiers & WebEventButtonModifierMask());
+ return (pressed_buttons_ &&
+ pressed_buttons_ == event_buttons);
+ }
+ default:
+ return false;
+ }
+ return false;
+}
+
+int ExternalDragTracker::WebEventButtonModifierMask() {
+ return WebInputEvent::LeftButtonDown |
+ WebInputEvent::RightButtonDown |
+ WebInputEvent::MiddleButtonDown;
+}
+
+#pragma mark -
+#pragma mark Core WebPluginDelegate implementation
+
+WebPluginDelegateImpl::WebPluginDelegateImpl(
+ gfx::PluginWindowHandle containing_view,
+ NPAPI::PluginInstance *instance)
+ : windowed_handle_(NULL),
+ // all Mac plugins are "windowless" in the Windows/X11 sense
+ windowless_(true),
+ plugin_(NULL),
+ instance_(instance),
+ parent_(containing_view),
+ quirks_(0),
+ buffer_context_(NULL),
+ layer_(nil),
+ surface_(NULL),
+ renderer_(nil),
+ plugin_has_focus_(false),
+ has_webkit_focus_(false),
+ containing_view_has_focus_(false),
+ containing_window_has_focus_(false),
+ initial_window_focus_(false),
+ container_is_visible_(false),
+ have_called_set_window_(false),
+ external_drag_tracker_(new ExternalDragTracker()),
+ handle_event_depth_(0),
+ first_set_window_call_(true) {
+ memset(&window_, 0, sizeof(window_));
+#ifndef NP_NO_CARBON
+ memset(&np_cg_context_, 0, sizeof(np_cg_context_));
+#endif
+#ifndef NP_NO_QUICKDRAW
+ memset(&qd_port_, 0, sizeof(qd_port_));
+#endif
+ instance->set_windowless(true);
+}
+
+WebPluginDelegateImpl::~WebPluginDelegateImpl() {
+ DestroyInstance();
+
+#ifndef NP_NO_CARBON
+ if (np_cg_context_.window) {
+ CarbonPluginWindowTracker::SharedInstance()->DestroyDummyWindowForDelegate(
+ this, reinterpret_cast<WindowRef>(np_cg_context_.window));
+ }
+#endif
+}
+
+bool WebPluginDelegateImpl::PlatformInitialize() {
+ // Don't set a NULL window handle on destroy for Mac plugins. This matches
+ // Safari and other Mac browsers (see PluginView::stop() in PluginView.cpp,
+ // where code to do so is surrounded by an #ifdef that excludes Mac OS X, or
+ // destroyPlugin in WebNetscapePluginView.mm, for examples).
+ quirks_ |= PLUGIN_QUIRK_DONT_SET_NULL_WINDOW_HANDLE_ON_DESTROY;
+
+ // Mac plugins don't expect to be unloaded, and they don't always do so
+ // cleanly, so don't unload them at shutdown.
+ instance()->plugin_lib()->PreventLibraryUnload();
+
+#ifndef NP_NO_QUICKDRAW
+ if (instance()->drawing_model() == NPDrawingModelQuickDraw) {
+ // For some QuickDraw plugins, we can sometimes get away with giving them
+ // a port pointing to a pixel buffer instead of a our actual dummy window.
+ // This gives us much better frame rates, because the window scraping we
+ // normally use is very slow.
+ // This breaks down if the plugin does anything complicated with the port
+ // (as QuickTime seems to during event handling, and sometimes when painting
+ // its controls), so we switch on the fly as necessary. (It might be
+ // possible to interpose sufficiently that we wouldn't have to switch back
+ // and forth, but the current approach gets us most of the benefit.)
+ // We can't do this at all with plugins that bypass the port entirely and
+ // attaches their own surface to the window.
+ // TODO(stuartmorgan): Test other QuickDraw plugins that we support and
+ // see if any others can use the fast path.
+ const WebPluginInfo& plugin_info = instance_->plugin_lib()->plugin_info();
+ if (plugin_info.name.find(ASCIIToUTF16("QuickTime")) != string16::npos)
+ quirks_ |= PLUGIN_QUIRK_ALLOW_FASTER_QUICKDRAW_PATH;
+ }
+#endif
+
+#ifndef NP_NO_CARBON
+ if (instance()->event_model() == NPEventModelCarbon) {
+ // Create a stand-in for the browser window so that the plugin will have
+ // a non-NULL WindowRef to which it can refer.
+ CarbonPluginWindowTracker* window_tracker =
+ CarbonPluginWindowTracker::SharedInstance();
+ np_cg_context_.window = window_tracker->CreateDummyWindowForDelegate(this);
+ np_cg_context_.context = NULL;
+ UpdateDummyWindowBounds(gfx::Point(0, 0));
+ }
+#endif
+
+ NPDrawingModel drawing_model = instance()->drawing_model();
+ switch (drawing_model) {
+#ifndef NP_NO_QUICKDRAW
+ case NPDrawingModelQuickDraw:
+ if (instance()->event_model() != NPEventModelCarbon)
+ return false;
+ qd_manager_.reset(new QuickDrawDrawingManager());
+ qd_manager_->SetPluginWindow(
+ reinterpret_cast<WindowRef>(np_cg_context_.window));
+ qd_port_.port = qd_manager_->port();
+ window_.window = &qd_port_;
+ window_.type = NPWindowTypeDrawable;
+ break;
+#endif
+ case NPDrawingModelCoreGraphics:
+#ifndef NP_NO_CARBON
+ if (instance()->event_model() == NPEventModelCarbon)
+ window_.window = &np_cg_context_;
+#endif
+ window_.type = NPWindowTypeDrawable;
+ break;
+ case NPDrawingModelCoreAnimation:
+ case NPDrawingModelInvalidatingCoreAnimation: {
+ if (instance()->event_model() != NPEventModelCocoa)
+ return false;
+ window_.type = NPWindowTypeDrawable;
+ // Ask the plug-in for the CALayer it created for rendering content. Have
+ // the renderer tell the browser to create a "windowed plugin" to host
+ // the IOSurface.
+ CALayer* layer = nil;
+ NPError err = instance()->NPP_GetValue(NPPVpluginCoreAnimationLayer,
+ reinterpret_cast<void*>(&layer));
+ if (!err) {
+ if (drawing_model == NPDrawingModelCoreAnimation) {
+ // Create the timer; it will be started when we get a window handle.
+ redraw_timer_.reset(new base::RepeatingTimer<WebPluginDelegateImpl>);
+ }
+ layer_ = layer;
+ surface_ = new AcceleratedSurface;
+ surface_->Initialize(NULL, true);
+ renderer_ = [[CARenderer rendererWithCGLContext:surface_->context()
+ options:NULL] retain];
+ [renderer_ setLayer:layer_];
+ plugin_->BindFakePluginWindowHandle(false);
+ }
+ break;
+ }
+ default:
+ NOTREACHED();
+ break;
+ }
+
+ // TODO(stuartmorgan): We need real plugin container visibility information
+ // when the plugin is initialized; for now, assume it's visible.
+ // None of the calls SetContainerVisibility would make are useful at this
+ // point, so we just set the initial state directly.
+ container_is_visible_ = true;
+
+ // Let the WebPlugin know that we are windowless (unless this is a
+ // Core Animation plugin, in which case BindFakePluginWindowHandle will take
+ // care of setting up the appropriate window handle).
+ if (!layer_)
+ plugin_->SetWindow(NULL);
+
+#ifndef NP_NO_CARBON
+ // If the plugin wants Carbon events, hook up to the source of idle events.
+ if (instance()->event_model() == NPEventModelCarbon)
+ UpdateIdleEventRate();
+#endif
+
+ // QuickTime (in QD mode only) can crash if it gets other calls (e.g.,
+ // NPP_Write) before it gets a SetWindow call, so call SetWindow (with a 0x0
+ // rect) immediately.
+#ifndef NP_NO_QUICKDRAW
+ if (instance()->drawing_model() == NPDrawingModelQuickDraw) {
+ const WebPluginInfo& plugin_info = instance_->plugin_lib()->plugin_info();
+ if (plugin_info.name.find(ASCIIToUTF16("QuickTime")) != string16::npos)
+ WindowlessSetWindow();
+ }
+#endif
+
+ return true;
+}
+
+void WebPluginDelegateImpl::PlatformDestroyInstance() {
+#ifndef NP_NO_CARBON
+ if (instance()->event_model() == NPEventModelCarbon)
+ CarbonIdleEventSource::SharedInstance()->UnregisterDelegate(this);
+#endif
+ if (redraw_timer_.get())
+ redraw_timer_->Stop();
+ [renderer_ release];
+ renderer_ = nil;
+ layer_ = nil;
+ if (surface_) {
+ surface_->Destroy();
+ delete surface_;
+ surface_ = NULL;
+ }
+}
+
+void WebPluginDelegateImpl::UpdateGeometryAndContext(
+ const gfx::Rect& window_rect, const gfx::Rect& clip_rect,
+ CGContextRef context) {
+ buffer_context_ = context;
+#ifndef NP_NO_CARBON
+ if (instance()->event_model() == NPEventModelCarbon) {
+ // Update the structure that is passed to Carbon+CoreGraphics plugins in
+ // NPP_SetWindow before calling UpdateGeometry, since that will trigger an
+ // NPP_SetWindow call if the geometry changes (which is the only time the
+ // context would be different), and some plugins (e.g., Flash) have an
+ // internal cache of the context that they only update when NPP_SetWindow
+ // is called.
+ np_cg_context_.context = context;
+ }
+#endif
+#ifndef NP_NO_QUICKDRAW
+ if (instance()->drawing_model() == NPDrawingModelQuickDraw)
+ qd_manager_->SetTargetContext(context, window_rect.size());
+#endif
+ UpdateGeometry(window_rect, clip_rect);
+}
+
+void WebPluginDelegateImpl::Paint(CGContextRef context, const gfx::Rect& rect) {
+ WindowlessPaint(context, rect);
+
+#ifndef NP_NO_QUICKDRAW
+ // Paint events are our cue to dump the current plugin bits into the buffer
+ // context if we are dealing with a QuickDraw plugin.
+ if (instance()->drawing_model() == NPDrawingModelQuickDraw) {
+ qd_manager_->UpdateContext();
+ }
+#endif
+}
+
+void WebPluginDelegateImpl::Print(CGContextRef context) {
+ NOTIMPLEMENTED();
+}
+
+void WebPluginDelegateImpl::SetFocus(bool focused) {
+ // This is called when internal WebKit focus (the focused element on the page)
+ // changes, but plugins need to know about actual first responder status, so
+ // we have an extra layer of focus tracking.
+ has_webkit_focus_ = focused;
+ if (containing_view_has_focus_)
+ SetPluginHasFocus(focused);
+}
+
+bool WebPluginDelegateImpl::PlatformHandleInputEvent(
+ const WebInputEvent& event, WebCursorInfo* cursor_info) {
+ DCHECK(cursor_info != NULL);
+
+ // If we get an event before we've set up the plugin, bail.
+ if (!have_called_set_window_)
+ return false;
+#ifndef NP_NO_CARBON
+ if (instance()->event_model() == NPEventModelCarbon &&
+ !np_cg_context_.context) {
+ return false;
+ }
+#endif
+
+ if (WebInputEvent::isMouseEventType(event.type) ||
+ event.type == WebInputEvent::MouseWheel) {
+ // Ideally we would compute the content origin from the web event using the
+ // code below as a safety net for missed content area location changes.
+ // Because of <http://crbug.com/9996>, however, only globalX/Y are right if
+ // the page has been zoomed, so for now the coordinates we get aren't
+ // trustworthy enough to use for corrections.
+#if PLUGIN_SCALING_FIXED
+ // Check our plugin location before we send the event to the plugin, just
+ // in case we somehow missed a plugin frame change.
+ const WebMouseEvent* mouse_event =
+ static_cast<const WebMouseEvent*>(&event);
+ gfx::Point content_origin(
+ mouse_event->globalX - mouse_event->x - window_rect_.x(),
+ mouse_event->globalY - mouse_event->y - window_rect_.y());
+ if (content_origin.x() != content_area_origin_.x() ||
+ content_origin.y() != content_area_origin_.y()) {
+ DLOG(WARNING) << "Stale plugin content area location: "
+ << content_area_origin_ << " instead of "
+ << content_origin;
+ SetContentAreaOrigin(content_origin);
+ }
+#endif
+
+ current_windowless_cursor_.GetCursorInfo(cursor_info);
+ }
+
+#ifndef NP_NO_CARBON
+ if (instance()->event_model() == NPEventModelCarbon) {
+#ifndef NP_NO_QUICKDRAW
+ if (instance()->drawing_model() == NPDrawingModelQuickDraw) {
+ if (quirks_ & PLUGIN_QUIRK_ALLOW_FASTER_QUICKDRAW_PATH) {
+ // Mouse event handling doesn't work correctly in the fast path mode,
+ // so any time we get a mouse event turn the fast path off, but set a
+ // time to switch it on again (we don't rely just on MouseLeave because
+ // we don't want poor performance in the case of clicking the play
+ // button and then leaving the mouse there).
+ // This isn't perfect (specifically, click-and-hold doesn't seem to work
+ // if the fast path is on), but the slight regression is worthwhile
+ // for the improved framerates.
+ if (WebInputEvent::isMouseEventType(event.type)) {
+ if (event.type == WebInputEvent::MouseLeave) {
+ SetQuickDrawFastPathEnabled(true);
+ } else {
+ SetQuickDrawFastPathEnabled(false);
+ }
+ // Make sure the plugin wasn't destroyed during the switch.
+ if (!instance())
+ return false;
+ }
+ }
+
+ qd_manager_->MakePortCurrent();
+ }
+#endif
+
+ if (event.type == WebInputEvent::MouseMove) {
+ return true; // The recurring FireIdleEvent will send null events.
+ }
+ }
+#endif
+
+ ScopedActiveDelegate active_delegate(this);
+
+ // Create the plugin event structure.
+ NPEventModel event_model = instance()->event_model();
+ scoped_ptr<PluginWebEventConverter> event_converter(
+ PluginWebEventConverterFactory::CreateConverterForModel(event_model));
+ if (!(event_converter.get() && event_converter->InitWithEvent(event))) {
+ // Silently consume any keyboard event types that we don't handle, so that
+ // they don't fall through to the page.
+ if (WebInputEvent::isKeyboardEventType(event.type))
+ return true;
+ return false;
+ }
+ void* plugin_event = event_converter->plugin_event();
+
+ if (instance()->event_model() == NPEventModelCocoa) {
+ // We recieve events related to drags starting outside the plugin, but the
+ // NPAPI Cocoa event model spec says plugins shouldn't receive them, so
+ // filter them out.
+ // If we add a page capture mode at the WebKit layer (like the plugin
+ // capture mode that handles drags starting inside) this can be removed.
+ bool drag_related = external_drag_tracker_->EventIsRelatedToDrag(event);
+ external_drag_tracker_->UpdateDragStateFromEvent(event);
+ if (drag_related) {
+ if (event.type == WebInputEvent::MouseUp &&
+ !external_drag_tracker_->IsDragInProgress()) {
+ // When an external drag ends, we need to synthesize a MouseEntered.
+ NPCocoaEvent enter_event = *(static_cast<NPCocoaEvent*>(plugin_event));
+ enter_event.type = NPCocoaEventMouseEntered;
+ NPAPI::ScopedCurrentPluginEvent event_scope(instance(), &enter_event);
+ instance()->NPP_HandleEvent(&enter_event);
+ }
+ return false;
+ }
+ }
+
+#ifndef PLUGIN_SCALING_FIXED
+ // Because of <http://crbug.com/9996>, the non-global coordinates we get for
+ // zoomed pages are wrong. As a temporary hack around that bug, override the
+ // coordinates we are given with ones computed based on our knowledge of where
+ // the plugin is on screen. We only need to do this for Cocoa, since Carbon
+ // only uses the global coordinates.
+ if (instance()->event_model() == NPEventModelCocoa &&
+ (WebInputEvent::isMouseEventType(event.type) ||
+ event.type == WebInputEvent::MouseWheel)) {
+ const WebMouseEvent* mouse_event =
+ static_cast<const WebMouseEvent*>(&event);
+ NPCocoaEvent* cocoa_event = static_cast<NPCocoaEvent*>(plugin_event);
+ cocoa_event->data.mouse.pluginX =
+ mouse_event->globalX - content_area_origin_.x() - window_rect_.x();
+ cocoa_event->data.mouse.pluginY =
+ mouse_event->globalY - content_area_origin_.y() - window_rect_.y();
+ }
+#endif
+
+ // Send the plugin the event.
+ scoped_ptr<NPAPI::ScopedCurrentPluginEvent> event_scope(NULL);
+ if (instance()->event_model() == NPEventModelCocoa) {
+ event_scope.reset(new NPAPI::ScopedCurrentPluginEvent(
+ instance(), static_cast<NPCocoaEvent*>(plugin_event)));
+ }
+ bool handled = instance()->NPP_HandleEvent(plugin_event) != 0;
+
+ // Plugins don't give accurate information about whether or not they handled
+ // events, so browsers on the Mac ignore the return value.
+ // Scroll events are the exception, since the Cocoa spec defines a meaning
+ // for the return value.
+ if (WebInputEvent::isMouseEventType(event.type)) {
+ handled = true;
+ } else if (WebInputEvent::isKeyboardEventType(event.type)) {
+ // For Command-key events, trust the return value since eating all menu
+ // shortcuts is not ideal.
+ // TODO(stuartmorgan): Implement the advanced key handling spec, and trust
+ // trust the key event return value from plugins that implement it.
+ if (!(event.modifiers & WebInputEvent::MetaKey))
+ handled = true;
+ }
+
+ return handled;
+}
+
+void WebPluginDelegateImpl::InstallMissingPlugin() {
+ NOTIMPLEMENTED();
+}
+
+#pragma mark -
+
+void WebPluginDelegateImpl::WindowlessUpdateGeometry(
+ const gfx::Rect& window_rect,
+ const gfx::Rect& clip_rect) {
+ gfx::Rect old_clip_rect = clip_rect_;
+ cached_clip_rect_ = clip_rect;
+ if (container_is_visible_) // Remove check when cached_clip_rect_ is removed.
+ clip_rect_ = clip_rect;
+ bool clip_rect_changed = (clip_rect_ != old_clip_rect);
+ bool window_size_changed = (window_rect.size() != window_rect_.size());
+
+ bool force_set_window = false;
+#ifndef NP_NO_QUICKDRAW
+ // In a QuickDraw plugin, a geometry update might have caused a port change;
+ // if so, we need to call SetWindow even if nothing else changed.
+ if (qd_manager_.get() && (qd_port_.port != qd_manager_->port())) {
+ qd_port_.port = qd_manager_->port();
+ force_set_window = true;
+ }
+#endif
+
+ if (window_rect == window_rect_ && !clip_rect_changed && !force_set_window)
+ return;
+
+ if (old_clip_rect.IsEmpty() != clip_rect_.IsEmpty()) {
+ PluginVisibilityChanged();
+ }
+
+ SetPluginRect(window_rect);
+
+#ifndef NP_NO_QUICKDRAW
+ if (window_size_changed && qd_manager_.get() &&
+ qd_manager_->IsFastPathEnabled()) {
+ // If the window size has changed, we need to turn off the fast path so that
+ // the full redraw goes to the window and we get a correct baseline paint.
+ SetQuickDrawFastPathEnabled(false);
+ return; // SetQuickDrawFastPathEnabled will call SetWindow for us.
+ }
+#endif
+
+ if (window_size_changed || clip_rect_changed || force_set_window)
+ WindowlessSetWindow();
+}
+
+void WebPluginDelegateImpl::WindowlessPaint(gfx::NativeDrawingContext context,
+ const gfx::Rect& damage_rect) {
+ // If we get a paint event before we are completely set up (e.g., a nested
+ // call while the plugin is still in NPP_SetWindow), bail.
+ if (!have_called_set_window_ || !buffer_context_)
+ return;
+ DCHECK(buffer_context_ == context);
+
+ static StatsRate plugin_paint("Plugin.Paint");
+ StatsScope<StatsRate> scope(plugin_paint);
+
+ // Plugin invalidates trigger asynchronous paints with the original
+ // invalidation rect; the plugin may be resized before the paint is handled,
+ // so we need to ensure that the damage rect is still sane.
+ const gfx::Rect paint_rect(damage_rect.Intersect(
+ gfx::Rect(0, 0, window_rect_.width(), window_rect_.height())));
+
+ ScopedActiveDelegate active_delegate(this);
+
+#ifndef NP_NO_QUICKDRAW
+ if (instance()->drawing_model() == NPDrawingModelQuickDraw)
+ qd_manager_->MakePortCurrent();
+#endif
+
+ CGContextSaveGState(context);
+
+ switch (instance()->event_model()) {
+#ifndef NP_NO_CARBON
+ case NPEventModelCarbon: {
+ NPEvent paint_event = { 0 };
+ paint_event.what = updateEvt;
+ paint_event.message = reinterpret_cast<uint32>(np_cg_context_.window);
+ paint_event.when = TickCount();
+ instance()->NPP_HandleEvent(&paint_event);
+ break;
+ }
+#endif
+ case NPEventModelCocoa: {
+ NPCocoaEvent paint_event;
+ memset(&paint_event, 0, sizeof(NPCocoaEvent));
+ paint_event.type = NPCocoaEventDrawRect;
+ paint_event.data.draw.context = context;
+ paint_event.data.draw.x = paint_rect.x();
+ paint_event.data.draw.y = paint_rect.y();
+ paint_event.data.draw.width = paint_rect.width();
+ paint_event.data.draw.height = paint_rect.height();
+ instance()->NPP_HandleEvent(&paint_event);
+ break;
+ }
+ }
+
+ // The backing buffer can change during the call to NPP_HandleEvent, in which
+ // case the old context is (or is about to be) invalid.
+ if (context == buffer_context_)
+ CGContextRestoreGState(context);
+}
+
+void WebPluginDelegateImpl::WindowlessSetWindow() {
+ if (!instance())
+ return;
+
+ window_.x = 0;
+ window_.y = 0;
+ window_.height = window_rect_.height();
+ window_.width = window_rect_.width();
+ window_.clipRect.left = clip_rect_.x();
+ window_.clipRect.top = clip_rect_.y();
+ window_.clipRect.right = clip_rect_.x() + clip_rect_.width();
+ window_.clipRect.bottom = clip_rect_.y() + clip_rect_.height();
+
+ NPError err = instance()->NPP_SetWindow(&window_);
+
+ // Send an appropriate window focus event after the first SetWindow.
+ if (!have_called_set_window_) {
+ have_called_set_window_ = true;
+ SetWindowHasFocus(initial_window_focus_);
+ }
+
+#ifndef NP_NO_QUICKDRAW
+ if ((quirks_ & PLUGIN_QUIRK_ALLOW_FASTER_QUICKDRAW_PATH) &&
+ !qd_manager_->IsFastPathEnabled() && !clip_rect_.IsEmpty()) {
+ // Give the plugin a few seconds to stabilize so we get a good initial paint
+ // to use as a baseline, then switch to the fast path.
+ fast_path_enable_tick_ = base::TimeTicks::Now() +
+ base::TimeDelta::FromSeconds(3);
+ }
+#endif
+
+ DCHECK(err == NPERR_NO_ERROR);
+}
+
+#pragma mark -
+
+bool WebPluginDelegateImpl::WindowedCreatePlugin() {
+ NOTREACHED();
+ return false;
+}
+
+void WebPluginDelegateImpl::WindowedDestroyWindow() {
+ NOTREACHED();
+}
+
+bool WebPluginDelegateImpl::WindowedReposition(const gfx::Rect& window_rect,
+ const gfx::Rect& clip_rect) {
+ NOTREACHED();
+ return false;
+}
+
+void WebPluginDelegateImpl::WindowedSetWindow() {
+ NOTREACHED();
+}
+
+#pragma mark -
+#pragma mark Mac Extensions
+
+void WebPluginDelegateImpl::PluginDidInvalidate() {
+ if (instance()->drawing_model() == NPDrawingModelInvalidatingCoreAnimation)
+ DrawLayerInSurface();
+}
+
+WebPluginDelegateImpl* WebPluginDelegateImpl::GetActiveDelegate() {
+ return g_active_delegate;
+}
+
+void WebPluginDelegateImpl::SetWindowHasFocus(bool has_focus) {
+ // If we get a window focus event before calling SetWindow, just remember the
+ // states (WindowlessSetWindow will then send it on the first call).
+ if (!have_called_set_window_) {
+ initial_window_focus_ = has_focus;
+ return;
+ }
+
+ if (has_focus == containing_window_has_focus_)
+ return;
+ containing_window_has_focus_ = has_focus;
+
+#ifndef NP_NO_QUICKDRAW
+ // Make sure controls repaint with the correct look.
+ if (quirks_ & PLUGIN_QUIRK_ALLOW_FASTER_QUICKDRAW_PATH)
+ SetQuickDrawFastPathEnabled(false);
+#endif
+
+ ScopedActiveDelegate active_delegate(this);
+ switch (instance()->event_model()) {
+#ifndef NP_NO_CARBON
+ case NPEventModelCarbon: {
+ NPEvent focus_event = { 0 };
+ focus_event.what = activateEvt;
+ if (has_focus)
+ focus_event.modifiers |= activeFlag;
+ focus_event.message =
+ reinterpret_cast<unsigned long>(np_cg_context_.window);
+ focus_event.when = TickCount();
+ instance()->NPP_HandleEvent(&focus_event);
+ break;
+ }
+#endif
+ case NPEventModelCocoa: {
+ NPCocoaEvent focus_event;
+ memset(&focus_event, 0, sizeof(focus_event));
+ focus_event.type = NPCocoaEventWindowFocusChanged;
+ focus_event.data.focus.hasFocus = has_focus;
+ instance()->NPP_HandleEvent(&focus_event);
+ break;
+ }
+ }
+}
+
+void WebPluginDelegateImpl::SetPluginHasFocus(bool has_focus) {
+ if (!have_called_set_window_)
+ return;
+
+ if (has_focus == plugin_has_focus_)
+ return;
+ plugin_has_focus_ = has_focus;
+
+ ScopedActiveDelegate active_delegate(this);
+
+ switch (instance()->event_model()) {
+#ifndef NP_NO_CARBON
+ case NPEventModelCarbon: {
+ NPEvent focus_event = { 0 };
+ if (plugin_has_focus_)
+ focus_event.what = NPEventType_GetFocusEvent;
+ else
+ focus_event.what = NPEventType_LoseFocusEvent;
+ focus_event.when = TickCount();
+ instance()->NPP_HandleEvent(&focus_event);
+ break;
+ }
+#endif
+ case NPEventModelCocoa: {
+ NPCocoaEvent focus_event;
+ memset(&focus_event, 0, sizeof(focus_event));
+ focus_event.type = NPCocoaEventFocusChanged;
+ focus_event.data.focus.hasFocus = plugin_has_focus_;
+ instance()->NPP_HandleEvent(&focus_event);
+ break;
+ }
+ }
+}
+
+void WebPluginDelegateImpl::SetContentAreaHasFocus(bool has_focus) {
+ containing_view_has_focus_ = has_focus;
+ SetPluginHasFocus(containing_view_has_focus_ && has_webkit_focus_);
+}
+
+void WebPluginDelegateImpl::SetContainerVisibility(bool is_visible) {
+ if (is_visible == container_is_visible_)
+ return;
+ container_is_visible_ = is_visible;
+
+ // TODO(stuartmorgan): This is a temporary workarond for
+ // <http://crbug.com/34266>. When that is fixed, the cached_clip_rect_ code
+ // should all be removed.
+ if (is_visible) {
+ clip_rect_ = cached_clip_rect_;
+ } else {
+ clip_rect_.set_width(0);
+ clip_rect_.set_height(0);
+ }
+
+ // If the plugin is changing visibility, let the plugin know. If it's scrolled
+ // off screen (i.e., cached_clip_rect_ is empty), then container visibility
+ // doesn't change anything.
+ if (!cached_clip_rect_.IsEmpty()) {
+ PluginVisibilityChanged();
+ WindowlessSetWindow();
+ }
+
+ // When the plugin become visible, send an empty invalidate. If there were any
+ // pending invalidations this will trigger a paint event for the damaged
+ // region, and if not it's a no-op. This is necessary since higher levels
+ // that would normally do this weren't responsible for the clip_rect_ change).
+ if (!clip_rect_.IsEmpty())
+ instance()->webplugin()->InvalidateRect(gfx::Rect());
+}
+
+void WebPluginDelegateImpl::WindowFrameChanged(gfx::Rect window_frame,
+ gfx::Rect view_frame) {
+ instance()->set_window_frame(window_frame);
+ SetContentAreaOrigin(gfx::Point(view_frame.x(), view_frame.y()));
+}
+
+void WebPluginDelegateImpl::SetThemeCursor(ThemeCursor cursor) {
+ current_windowless_cursor_.InitFromThemeCursor(cursor);
+}
+
+void WebPluginDelegateImpl::SetCursor(const Cursor* cursor) {
+ current_windowless_cursor_.InitFromCursor(cursor);
+}
+
+void WebPluginDelegateImpl::SetNSCursor(NSCursor* cursor) {
+ current_windowless_cursor_.InitFromNSCursor(cursor);
+}
+
+#pragma mark -
+#pragma mark Internal Tracking
+
+void WebPluginDelegateImpl::SetPluginRect(const gfx::Rect& rect) {
+ bool plugin_size_changed = rect.width() != window_rect_.width() ||
+ rect.height() != window_rect_.height();
+ window_rect_ = rect;
+ PluginScreenLocationChanged();
+ if (plugin_size_changed)
+ UpdateAcceleratedSurface();
+}
+
+void WebPluginDelegateImpl::SetContentAreaOrigin(const gfx::Point& origin) {
+ content_area_origin_ = origin;
+ PluginScreenLocationChanged();
+}
+
+void WebPluginDelegateImpl::PluginScreenLocationChanged() {
+ gfx::Point plugin_origin(content_area_origin_.x() + window_rect_.x(),
+ content_area_origin_.y() + window_rect_.y());
+ instance()->set_plugin_origin(plugin_origin);
+
+#ifndef NP_NO_CARBON
+ if (instance()->event_model() == NPEventModelCarbon) {
+ UpdateDummyWindowBounds(plugin_origin);
+ }
+#endif
+}
+
+void WebPluginDelegateImpl::PluginVisibilityChanged() {
+#ifndef NP_NO_CARBON
+ if (instance()->event_model() == NPEventModelCarbon)
+ UpdateIdleEventRate();
+#endif
+ if (instance()->drawing_model() == NPDrawingModelCoreAnimation) {
+ bool plugin_visible = container_is_visible_ && !clip_rect_.IsEmpty();
+ if (plugin_visible && !redraw_timer_->IsRunning() && windowed_handle()) {
+ redraw_timer_->Start(
+ base::TimeDelta::FromMilliseconds(kCoreAnimationRedrawPeriodMs),
+ this, &WebPluginDelegateImpl::DrawLayerInSurface);
+ } else if (!plugin_visible) {
+ redraw_timer_->Stop();
+ }
+ }
+}
+
+#pragma mark -
+#pragma mark Core Animation Support
+
+void WebPluginDelegateImpl::DrawLayerInSurface() {
+ // If we haven't plumbed up the surface yet, don't try to draw.
+ if (!windowed_handle())
+ return;
+
+ surface_->MakeCurrent();
+
+ surface_->Clear(window_rect_);
+
+ [renderer_ beginFrameAtTime:CACurrentMediaTime() timeStamp:NULL];
+ if (CGRectIsEmpty([renderer_ updateBounds])) {
+ // If nothing has changed, we are done.
+ [renderer_ endFrame];
+ return;
+ }
+ CGRect layerRect = [layer_ bounds];
+ [renderer_ addUpdateRect:layerRect];
+ [renderer_ render];
+ [renderer_ endFrame];
+
+ surface_->SwapBuffers();
+ plugin_->AcceleratedFrameBuffersDidSwap(windowed_handle());
+}
+
+// Update the size of the IOSurface to match the current size of the plug-in,
+// then tell the browser host view so it can adjust its bookkeeping and CALayer
+// appropriately.
+void WebPluginDelegateImpl::UpdateAcceleratedSurface() {
+ // Will only have a window handle when using a Core Animation drawing model.
+ if (!windowed_handle() || !layer_)
+ return;
+
+ [layer_ setFrame:CGRectMake(0, 0,
+ window_rect_.width(), window_rect_.height())];
+ [renderer_ setBounds:[layer_ bounds]];
+
+ uint64 io_surface_id = surface_->SetSurfaceSize(window_rect_.size());
+ if (io_surface_id) {
+ plugin_->SetAcceleratedSurface(windowed_handle(),
+ window_rect_.width(),
+ window_rect_.height(),
+ io_surface_id);
+ }
+}
+
+void WebPluginDelegateImpl::set_windowed_handle(
+ gfx::PluginWindowHandle handle) {
+ windowed_handle_ = handle;
+ UpdateAcceleratedSurface();
+ // Kick off the drawing timer, if necessary.
+ PluginVisibilityChanged();
+}
+
+#pragma mark -
+#pragma mark Carbon Event support
+
+#ifndef NP_NO_CARBON
+void WebPluginDelegateImpl::UpdateDummyWindowBounds(
+ const gfx::Point& plugin_origin) {
+ WindowRef window = reinterpret_cast<WindowRef>(np_cg_context_.window);
+ Rect current_bounds;
+ GetWindowBounds(window, kWindowContentRgn, &current_bounds);
+
+ Rect new_bounds;
+ // We never want to resize the window to 0x0, so if the plugin is 0x0 just
+ // move the window without resizing it.
+ if (window_rect_.width() > 0 && window_rect_.height() > 0) {
+ SetRect(&new_bounds, 0, 0, window_rect_.width(), window_rect_.height());
+ OffsetRect(&new_bounds, plugin_origin.x(), plugin_origin.y());
+ } else {
+ new_bounds = current_bounds;
+ OffsetRect(&new_bounds, plugin_origin.x() - current_bounds.left,
+ plugin_origin.y() - current_bounds.top);
+ }
+
+ if (new_bounds.left != current_bounds.left ||
+ new_bounds.top != current_bounds.top ||
+ new_bounds.right != current_bounds.right ||
+ new_bounds.bottom != current_bounds.bottom)
+ SetWindowBounds(window, kWindowContentRgn, &new_bounds);
+}
+
+void WebPluginDelegateImpl::UpdateIdleEventRate() {
+ bool plugin_visible = container_is_visible_ && !clip_rect_.IsEmpty();
+ CarbonIdleEventSource::SharedInstance()->RegisterDelegate(this,
+ plugin_visible);
+}
+
+void WebPluginDelegateImpl::FireIdleEvent() {
+ // Avoid a race condition between IO and UI threads during plugin shutdown
+ if (!instance())
+ return;
+ // Don't send idle events until we've called SetWindow.
+ if (!have_called_set_window_)
+ return;
+
+#ifndef NP_NO_QUICKDRAW
+ // Check whether it's time to turn the QuickDraw fast path back on.
+ if (!fast_path_enable_tick_.is_null() &&
+ (base::TimeTicks::Now() > fast_path_enable_tick_)) {
+ SetQuickDrawFastPathEnabled(true);
+ fast_path_enable_tick_ = base::TimeTicks();
+ }
+#endif
+
+ ScopedActiveDelegate active_delegate(this);
+
+#ifndef NP_NO_QUICKDRAW
+ if (instance()->drawing_model() == NPDrawingModelQuickDraw)
+ qd_manager_->MakePortCurrent();
+#endif
+
+ // Send an idle event so that the plugin can do background work
+ NPEvent np_event = {0};
+ np_event.what = nullEvent;
+ np_event.when = TickCount();
+ np_event.modifiers = GetCurrentKeyModifiers();
+ if (!Button())
+ np_event.modifiers |= btnState;
+ HIPoint mouse_location;
+ HIGetMousePosition(kHICoordSpaceScreenPixel, NULL, &mouse_location);
+ np_event.where.h = mouse_location.x;
+ np_event.where.v = mouse_location.y;
+ instance()->NPP_HandleEvent(&np_event);
+
+#ifndef NP_NO_QUICKDRAW
+ // Quickdraw-based plugins can draw at any time, so tell the renderer to
+ // repaint.
+ if (instance() && instance()->drawing_model() == NPDrawingModelQuickDraw)
+ instance()->webplugin()->Invalidate();
+#endif
+}
+#endif // !NP_NO_CARBON
+
+#pragma mark -
+#pragma mark QuickDraw Support
+
+#ifndef NP_NO_QUICKDRAW
+void WebPluginDelegateImpl::SetQuickDrawFastPathEnabled(bool enabled) {
+ if (!enabled) {
+ // Wait a couple of seconds, then turn the fast path back on. If we're
+ // turning it off for event handling, that ensures that the common case of
+ // move-mouse-then-click works (as well as making it likely that a second
+ // click attempt will work if the first one fails). If we're turning it
+ // off to force a new baseline image, this leaves plenty of time for the
+ // plugin to draw.
+ fast_path_enable_tick_ = base::TimeTicks::Now() +
+ base::TimeDelta::FromSeconds(2);
+ }
+
+ if (enabled == qd_manager_->IsFastPathEnabled())
+ return;
+ if (enabled && clip_rect_.IsEmpty()) {
+ // Don't switch to the fast path while the plugin is completely clipped;
+ // we can only switch when the window has an up-to-date image for us to
+ // scrape. We'll automatically switch after we become visible again.
+ return;
+ }
+
+ qd_manager_->SetFastPathEnabled(enabled);
+ qd_port_.port = qd_manager_->port();
+ WindowlessSetWindow();
+ // Send a paint event so that the new buffer gets updated immediately.
+ WindowlessPaint(buffer_context_, clip_rect_);
+}
+#endif // !NP_NO_QUICKDRAW
diff --git a/webkit/glue/plugins/webplugin_delegate_impl_win.cc b/webkit/glue/plugins/webplugin_delegate_impl_win.cc
new file mode 100644
index 0000000..09184ab
--- /dev/null
+++ b/webkit/glue/plugins/webplugin_delegate_impl_win.cc
@@ -0,0 +1,1397 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/webplugin_delegate_impl.h"
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/file_util.h"
+#include "base/iat_patch.h"
+#include "base/lazy_instance.h"
+#include "base/message_loop.h"
+#include "base/registry.h"
+#include "base/scoped_ptr.h"
+#include "base/stats_counters.h"
+#include "base/string_util.h"
+#include "base/win_util.h"
+#include "skia/ext/platform_canvas.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebInputEvent.h"
+#include "webkit/glue/plugins/default_plugin_shared.h"
+#include "webkit/glue/plugins/plugin_constants_win.h"
+#include "webkit/glue/plugins/plugin_instance.h"
+#include "webkit/glue/plugins/plugin_lib.h"
+#include "webkit/glue/plugins/plugin_list.h"
+#include "webkit/glue/plugins/plugin_stream_url.h"
+#include "webkit/glue/plugins/webplugin.h"
+#include "webkit/glue/webkit_glue.h"
+
+using WebKit::WebCursorInfo;
+using WebKit::WebKeyboardEvent;
+using WebKit::WebInputEvent;
+using WebKit::WebMouseEvent;
+
+namespace {
+
+const wchar_t kWebPluginDelegateProperty[] = L"WebPluginDelegateProperty";
+const wchar_t kPluginNameAtomProperty[] = L"PluginNameAtom";
+const wchar_t kDummyActivationWindowName[] = L"DummyWindowForActivation";
+const wchar_t kPluginFlashThrottle[] = L"FlashThrottle";
+
+// The fastest we are willing to process WM_USER+1 events for Flash.
+// Flash can easily exceed the limits of our CPU if we don't throttle it.
+// The throttle has been chosen by testing various delays and compromising
+// on acceptable Flash performance and reasonable CPU consumption.
+//
+// I'd like to make the throttle delay variable, based on the amount of
+// time currently required to paint Flash plugins. There isn't a good
+// way to count the time spent in aggregate plugin painting, however, so
+// this seems to work well enough.
+const int kFlashWMUSERMessageThrottleDelayMs = 5;
+
+// Flash displays popups in response to user clicks by posting a WM_USER
+// message to the plugin window. The handler for this message displays
+// the popup. To ensure that the popups allowed state is sent correctly
+// to the renderer we reset the popups allowed state in a timer.
+const int kWindowedPluginPopupTimerMs = 50;
+
+// The current instance of the plugin which entered the modal loop.
+WebPluginDelegateImpl* g_current_plugin_instance = NULL;
+
+typedef std::deque<MSG> ThrottleQueue;
+base::LazyInstance<ThrottleQueue> g_throttle_queue(base::LINKER_INITIALIZED);
+base::LazyInstance<std::map<HWND, WNDPROC> > g_window_handle_proc_map(
+ base::LINKER_INITIALIZED);
+
+
+// Helper object for patching the TrackPopupMenu API.
+base::LazyInstance<iat_patch::IATPatchFunction> g_iat_patch_track_popup_menu(
+ base::LINKER_INITIALIZED);
+
+// Helper object for patching the SetCursor API.
+base::LazyInstance<iat_patch::IATPatchFunction> g_iat_patch_set_cursor(
+ base::LINKER_INITIALIZED);
+
+// Helper object for patching the RegEnumKeyExW API.
+base::LazyInstance<iat_patch::IATPatchFunction> g_iat_patch_reg_enum_key_ex_w(
+ base::LINKER_INITIALIZED);
+
+// http://crbug.com/16114
+// Enforces providing a valid device context in NPWindow, so that NPP_SetWindow
+// is never called with NPNWindoTypeDrawable and NPWindow set to NULL.
+// Doing so allows removing NPP_SetWindow call during painting a windowless
+// plugin, which otherwise could trigger layout change while painting by
+// invoking NPN_Evaluate. Which would cause bad, bad crashes. Bad crashes.
+// TODO(dglazkov): If this approach doesn't produce regressions, move class to
+// webplugin_delegate_impl.h and implement for other platforms.
+class DrawableContextEnforcer {
+ public:
+ explicit DrawableContextEnforcer(NPWindow* window)
+ : window_(window),
+ disposable_dc_(window && !window->window) {
+ // If NPWindow is NULL, create a device context with monochrome 1x1 surface
+ // and stuff it to NPWindow.
+ if (disposable_dc_)
+ window_->window = CreateCompatibleDC(NULL);
+ }
+
+ ~DrawableContextEnforcer() {
+ if (!disposable_dc_)
+ return;
+
+ DeleteDC(static_cast<HDC>(window_->window));
+ window_->window = NULL;
+ }
+
+ private:
+ NPWindow* window_;
+ bool disposable_dc_;
+};
+
+// These are from ntddk.h
+typedef LONG NTSTATUS;
+
+#ifndef STATUS_SUCCESS
+#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
+#endif
+
+#ifndef STATUS_BUFFER_TOO_SMALL
+#define STATUS_BUFFER_TOO_SMALL ((NTSTATUS)0xC0000023L)
+#endif
+
+typedef enum _KEY_INFORMATION_CLASS {
+ KeyBasicInformation,
+ KeyNodeInformation,
+ KeyFullInformation,
+ KeyNameInformation,
+ KeyCachedInformation,
+ KeyVirtualizationInformation
+} KEY_INFORMATION_CLASS;
+
+typedef struct _KEY_NAME_INFORMATION {
+ ULONG NameLength;
+ WCHAR Name[1];
+} KEY_NAME_INFORMATION, *PKEY_NAME_INFORMATION;
+
+typedef DWORD (__stdcall *ZwQueryKeyType)(
+ HANDLE key_handle,
+ int key_information_class,
+ PVOID key_information,
+ ULONG length,
+ PULONG result_length);
+
+// Returns a key's full path.
+std::wstring GetKeyPath(HKEY key) {
+ if (key == NULL)
+ return L"";
+
+ HMODULE dll = GetModuleHandle(L"ntdll.dll");
+ if (dll == NULL)
+ return L"";
+
+ ZwQueryKeyType func = reinterpret_cast<ZwQueryKeyType>(
+ ::GetProcAddress(dll, "ZwQueryKey"));
+ if (func == NULL)
+ return L"";
+
+ DWORD size = 0;
+ DWORD result = 0;
+ result = func(key, KeyNameInformation, 0, 0, &size);
+ if (result != STATUS_BUFFER_TOO_SMALL)
+ return L"";
+
+ scoped_array<char> buffer(new char[size]);
+ if (buffer.get() == NULL)
+ return L"";
+
+ result = func(key, KeyNameInformation, buffer.get(), size, &size);
+ if (result != STATUS_SUCCESS)
+ return L"";
+
+ KEY_NAME_INFORMATION* info =
+ reinterpret_cast<KEY_NAME_INFORMATION*>(buffer.get());
+ return std::wstring(info->Name, info->NameLength / sizeof(wchar_t));
+}
+
+} // namespace
+
+bool WebPluginDelegateImpl::IsPluginDelegateWindow(HWND window) {
+ // We use a buffer that is one char longer than we need to detect cases where
+ // kNativeWindowClassName is a prefix of the given window's class name. It
+ // happens that GetClassNameW will just silently truncate the class name to
+ // fit into the given buffer.
+ wchar_t class_name[arraysize(kNativeWindowClassName) + 1];
+ if (!GetClassNameW(window, class_name, arraysize(class_name)))
+ return false;
+ return wcscmp(class_name, kNativeWindowClassName) == 0;
+}
+
+bool WebPluginDelegateImpl::GetPluginNameFromWindow(
+ HWND window, std::wstring *plugin_name) {
+ if (NULL == plugin_name) {
+ return false;
+ }
+ if (!IsPluginDelegateWindow(window)) {
+ return false;
+ }
+ ATOM plugin_name_atom = reinterpret_cast<ATOM>(
+ GetPropW(window, kPluginNameAtomProperty));
+ if (plugin_name_atom != 0) {
+ WCHAR plugin_name_local[MAX_PATH] = {0};
+ GlobalGetAtomNameW(plugin_name_atom,
+ plugin_name_local,
+ ARRAYSIZE(plugin_name_local));
+ *plugin_name = plugin_name_local;
+ return true;
+ }
+ return false;
+}
+
+bool WebPluginDelegateImpl::IsDummyActivationWindow(HWND window) {
+ if (!IsWindow(window))
+ return false;
+
+ wchar_t window_title[MAX_PATH + 1] = {0};
+ if (GetWindowText(window, window_title, arraysize(window_title))) {
+ return (0 == lstrcmpiW(window_title, kDummyActivationWindowName));
+ }
+ return false;
+}
+
+LRESULT CALLBACK WebPluginDelegateImpl::HandleEventMessageFilterHook(
+ int code, WPARAM wParam, LPARAM lParam) {
+ if (g_current_plugin_instance) {
+ g_current_plugin_instance->OnModalLoopEntered();
+ } else {
+ NOTREACHED();
+ }
+ return CallNextHookEx(NULL, code, wParam, lParam);
+}
+
+LRESULT CALLBACK WebPluginDelegateImpl::MouseHookProc(
+ int code, WPARAM wParam, LPARAM lParam) {
+ if (code == HC_ACTION) {
+ MOUSEHOOKSTRUCT* hook_struct = reinterpret_cast<MOUSEHOOKSTRUCT*>(lParam);
+ if (hook_struct)
+ HandleCaptureForMessage(hook_struct->hwnd, wParam);
+ }
+
+ return CallNextHookEx(NULL, code, wParam, lParam);
+}
+
+WebPluginDelegateImpl::WebPluginDelegateImpl(
+ gfx::PluginWindowHandle containing_view,
+ NPAPI::PluginInstance *instance)
+ : parent_(containing_view),
+ instance_(instance),
+ quirks_(0),
+ plugin_(NULL),
+ windowless_(false),
+ windowed_handle_(NULL),
+ windowed_did_set_window_(false),
+ plugin_wnd_proc_(NULL),
+ last_message_(0),
+ is_calling_wndproc(false),
+ keyboard_layout_(NULL),
+ parent_thread_id_(0),
+ dummy_window_for_activation_(NULL),
+ handle_event_message_filter_hook_(NULL),
+ handle_event_pump_messages_event_(NULL),
+ user_gesture_message_posted_(false),
+#pragma warning(suppress: 4355) // can use this
+ user_gesture_msg_factory_(this),
+ handle_event_depth_(0),
+ mouse_hook_(NULL),
+ first_set_window_call_(true) {
+ memset(&window_, 0, sizeof(window_));
+
+ const WebPluginInfo& plugin_info = instance_->plugin_lib()->plugin_info();
+ std::wstring filename =
+ StringToLowerASCII(plugin_info.path.BaseName().value());
+
+ if (instance_->mime_type() == "application/x-shockwave-flash" ||
+ filename == kFlashPlugin) {
+ // Flash only requests windowless plugins if we return a Mozilla user
+ // agent.
+ instance_->set_use_mozilla_user_agent();
+ quirks_ |= PLUGIN_QUIRK_THROTTLE_WM_USER_PLUS_ONE;
+ quirks_ |= PLUGIN_QUIRK_PATCH_SETCURSOR;
+ quirks_ |= PLUGIN_QUIRK_ALWAYS_NOTIFY_SUCCESS;
+ quirks_ |= PLUGIN_QUIRK_HANDLE_MOUSE_CAPTURE;
+ } else if (filename == kAcrobatReaderPlugin) {
+ // Check for the version number above or equal 9.
+ std::vector<std::wstring> version;
+ SplitString(plugin_info.version, L'.', &version);
+ if (version.size() > 0) {
+ int major = static_cast<int>(StringToInt64(version[0]));
+ if (major >= 9) {
+ quirks_ |= PLUGIN_QUIRK_DIE_AFTER_UNLOAD;
+
+ // 9.2 needs this.
+ quirks_ |= PLUGIN_QUIRK_SETWINDOW_TWICE;
+ }
+ }
+ quirks_ |= PLUGIN_QUIRK_BLOCK_NONSTANDARD_GETURL_REQUESTS;
+ } else if (plugin_info.name.find(L"Windows Media Player") !=
+ std::wstring::npos) {
+ // Windows Media Player needs two NPP_SetWindow calls.
+ quirks_ |= PLUGIN_QUIRK_SETWINDOW_TWICE;
+
+ // Windowless mode doesn't work in the WMP NPAPI plugin.
+ quirks_ |= PLUGIN_QUIRK_NO_WINDOWLESS;
+
+ // The media player plugin sets its size on the first NPP_SetWindow call
+ // and never updates its size. We should call the underlying NPP_SetWindow
+ // only when we have the correct size.
+ quirks_ |= PLUGIN_QUIRK_IGNORE_FIRST_SETWINDOW_CALL;
+
+ if (filename == kOldWMPPlugin) {
+ // Non-admin users on XP couldn't modify the key to force the new UI.
+ quirks_ |= PLUGIN_QUIRK_PATCH_REGENUMKEYEXW;
+ }
+ } else if (instance_->mime_type() == "audio/x-pn-realaudio-plugin" ||
+ filename == kRealPlayerPlugin) {
+ quirks_ |= PLUGIN_QUIRK_DONT_CALL_WND_PROC_RECURSIVELY;
+ } else if (plugin_info.name.find(L"VLC Multimedia Plugin") !=
+ std::wstring::npos ||
+ plugin_info.name.find(L"VLC Multimedia Plug-in") !=
+ std::wstring::npos) {
+ // VLC hangs on NPP_Destroy if we call NPP_SetWindow with a null window
+ // handle
+ quirks_ |= PLUGIN_QUIRK_DONT_SET_NULL_WINDOW_HANDLE_ON_DESTROY;
+ // VLC 0.8.6d and 0.8.6e crash if multiple instances are created.
+ quirks_ |= PLUGIN_QUIRK_DONT_ALLOW_MULTIPLE_INSTANCES;
+ } else if (filename == kSilverlightPlugin) {
+ // Explanation for this quirk can be found in
+ // WebPluginDelegateImpl::Initialize.
+ quirks_ |= PLUGIN_QUIRK_PATCH_SETCURSOR;
+ } else if (plugin_info.name.find(L"DivX Web Player") !=
+ std::wstring::npos) {
+ // The divx plugin sets its size on the first NPP_SetWindow call and never
+ // updates its size. We should call the underlying NPP_SetWindow only when
+ // we have the correct size.
+ quirks_ |= PLUGIN_QUIRK_IGNORE_FIRST_SETWINDOW_CALL;
+ }
+}
+
+WebPluginDelegateImpl::~WebPluginDelegateImpl() {
+ if (::IsWindow(dummy_window_for_activation_)) {
+ ::DestroyWindow(dummy_window_for_activation_);
+ }
+
+ DestroyInstance();
+
+ if (!windowless_)
+ WindowedDestroyWindow();
+
+ if (handle_event_pump_messages_event_) {
+ CloseHandle(handle_event_pump_messages_event_);
+ }
+}
+
+bool WebPluginDelegateImpl::PlatformInitialize() {
+ plugin_->SetWindow(windowed_handle_);
+
+ if (windowless_ && !instance_->plugin_lib()->internal()) {
+ CreateDummyWindowForActivation();
+ handle_event_pump_messages_event_ = CreateEvent(NULL, TRUE, FALSE, NULL);
+ plugin_->SetWindowlessPumpEvent(handle_event_pump_messages_event_);
+ }
+
+ // We cannot patch internal plugins as they are not shared libraries.
+ if (!instance_->plugin_lib()->internal()) {
+ // Windowless plugins call the WindowFromPoint API and passes the result of
+ // that to the TrackPopupMenu API call as the owner window. This causes the
+ // API to fail as the API expects the window handle to live on the same
+ // thread as the caller. It works in the other browsers as the plugin lives
+ // on the browser thread. Our workaround is to intercept the TrackPopupMenu
+ // API and replace the window handle with the dummy activation window.
+ if (windowless_ && !g_iat_patch_track_popup_menu.Pointer()->is_patched()) {
+ g_iat_patch_track_popup_menu.Pointer()->Patch(
+ GetPluginPath().value().c_str(), "user32.dll", "TrackPopupMenu",
+ WebPluginDelegateImpl::TrackPopupMenuPatch);
+ }
+
+ // Windowless plugins can set cursors by calling the SetCursor API. This
+ // works because the thread inputs of the browser UI thread and the plugin
+ // thread are attached. We intercept the SetCursor API for windowless
+ // plugins and remember the cursor being set. This is shipped over to the
+ // browser in the HandleEvent call, which ensures that the cursor does not
+ // change when a windowless plugin instance changes the cursor
+ // in a background tab.
+ if (windowless_ && !g_iat_patch_set_cursor.Pointer()->is_patched() &&
+ (quirks_ & PLUGIN_QUIRK_PATCH_SETCURSOR)) {
+ g_iat_patch_set_cursor.Pointer()->Patch(
+ GetPluginPath().value().c_str(), "user32.dll", "SetCursor",
+ WebPluginDelegateImpl::SetCursorPatch);
+ }
+
+ // The windowed flash plugin has a bug which occurs when the plugin enters
+ // fullscreen mode. It basically captures the mouse on WM_LBUTTONDOWN and
+ // does not release capture correctly causing it to stop receiving
+ // subsequent mouse events. This problem is also seen in Safari where there
+ // is code to handle this in the wndproc. However the plugin subclasses the
+ // window again in WM_LBUTTONDOWN before entering full screen. As a result
+ // Safari does not receive the WM_LBUTTONUP message. To workaround this
+ // issue we use a per thread mouse hook. This bug does not occur in Firefox
+ // and opera. Firefox has code similar to Safari. It could well be a bug in
+ // the flash plugin, which only occurs in webkit based browsers.
+ if (quirks_ & PLUGIN_QUIRK_HANDLE_MOUSE_CAPTURE) {
+ mouse_hook_ = SetWindowsHookEx(WH_MOUSE, MouseHookProc, NULL,
+ GetCurrentThreadId());
+ }
+ }
+
+ // On XP, WMP will use its old UI unless a registry key under HKLM has the
+ // name of the current process. We do it in the installer for admin users,
+ // for the rest patch this function.
+ if ((quirks_ & PLUGIN_QUIRK_PATCH_REGENUMKEYEXW) &&
+ win_util::GetWinVersion() == win_util::WINVERSION_XP &&
+ !RegKey().Open(HKEY_LOCAL_MACHINE,
+ L"SOFTWARE\\Microsoft\\MediaPlayer\\ShimInclusionList\\chrome.exe") &&
+ !g_iat_patch_reg_enum_key_ex_w.Pointer()->is_patched()) {
+ g_iat_patch_reg_enum_key_ex_w.Pointer()->Patch(
+ L"wmpdxm.dll", "advapi32.dll", "RegEnumKeyExW",
+ WebPluginDelegateImpl::RegEnumKeyExWPatch);
+ }
+
+ return true;
+}
+
+void WebPluginDelegateImpl::PlatformDestroyInstance() {
+ if (!instance_->plugin_lib())
+ return;
+
+ // Unpatch if this is the last plugin instance.
+ if (instance_->plugin_lib()->instance_count() != 1)
+ return;
+
+ if (g_iat_patch_set_cursor.Pointer()->is_patched())
+ g_iat_patch_set_cursor.Pointer()->Unpatch();
+
+ if (g_iat_patch_track_popup_menu.Pointer()->is_patched())
+ g_iat_patch_track_popup_menu.Pointer()->Unpatch();
+
+ if (g_iat_patch_reg_enum_key_ex_w.Pointer()->is_patched())
+ g_iat_patch_reg_enum_key_ex_w.Pointer()->Unpatch();
+
+ if (mouse_hook_) {
+ UnhookWindowsHookEx(mouse_hook_);
+ mouse_hook_ = NULL;
+ }
+}
+
+void WebPluginDelegateImpl::Paint(skia::PlatformCanvas* canvas,
+ const gfx::Rect& rect) {
+ if (windowless_) {
+ HDC hdc = canvas->beginPlatformPaint();
+ WindowlessPaint(hdc, rect);
+ canvas->endPlatformPaint();
+ }
+}
+
+void WebPluginDelegateImpl::Print(HDC hdc) {
+ // Disabling the call to NPP_Print as it causes a crash in
+ // flash in some cases. In any case this does not work as expected
+ // as the EMF meta file dc passed in needs to be created with the
+ // the plugin window dc as its sibling dc and the window rect
+ // in .01 mm units.
+#if 0
+ NPPrint npprint;
+ npprint.mode = NP_EMBED;
+ npprint.print.embedPrint.platformPrint = reinterpret_cast<void*>(hdc);
+ npprint.print.embedPrint.window = window_;
+ instance()->NPP_Print(&npprint);
+#endif
+}
+
+void WebPluginDelegateImpl::InstallMissingPlugin() {
+ NPEvent evt;
+ evt.event = default_plugin::kInstallMissingPluginMessage;
+ evt.lParam = 0;
+ evt.wParam = 0;
+ instance()->NPP_HandleEvent(&evt);
+}
+
+bool WebPluginDelegateImpl::WindowedCreatePlugin() {
+ DCHECK(!windowed_handle_);
+
+ RegisterNativeWindowClass();
+
+ // The window will be sized and shown later.
+ windowed_handle_ = CreateWindowEx(
+ WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR,
+ kNativeWindowClassName,
+ 0,
+ WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
+ 0,
+ 0,
+ 0,
+ 0,
+ parent_,
+ 0,
+ GetModuleHandle(NULL),
+ 0);
+ if (windowed_handle_ == 0)
+ return false;
+
+ if (IsWindow(parent_)) {
+ // This is a tricky workaround for Issue 2673 in chromium "Flash: IME not
+ // available". To use IMEs in this window, we have to make Windows attach
+ // IMEs to this window (i.e. load IME DLLs, attach them to this process,
+ // and add their message hooks to this window). Windows attaches IMEs while
+ // this process creates a top-level window. On the other hand, to layout
+ // this window correctly in the given parent window (RenderWidgetHostHWND),
+ // this window should be a child window of the parent window.
+ // To satisfy both of the above conditions, this code once creates a
+ // top-level window and change it to a child window of the parent window.
+ SetWindowLongPtr(windowed_handle_, GWL_STYLE,
+ WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS);
+ SetParent(windowed_handle_, parent_);
+ }
+
+ BOOL result = SetProp(windowed_handle_, kWebPluginDelegateProperty, this);
+ DCHECK(result == TRUE) << "SetProp failed, last error = " << GetLastError();
+ // Get the name of the plugin, create an atom and set that in a window
+ // property. Use an atom so that other processes can access the name of
+ // the plugin that this window is hosting
+ if (instance_ != NULL) {
+ NPAPI::PluginLib* plugin_lib = instance()->plugin_lib();
+ if (plugin_lib != NULL) {
+ std::wstring plugin_name = plugin_lib->plugin_info().name;
+ if (!plugin_name.empty()) {
+ ATOM plugin_name_atom = GlobalAddAtomW(plugin_name.c_str());
+ DCHECK(0 != plugin_name_atom);
+ result = SetProp(windowed_handle_,
+ kPluginNameAtomProperty,
+ reinterpret_cast<HANDLE>(plugin_name_atom));
+ DCHECK(result == TRUE) << "SetProp failed, last error = " <<
+ GetLastError();
+ }
+ }
+ }
+
+ // Calling SetWindowLongPtrA here makes the window proc ASCII, which is
+ // required by at least the Shockwave Director plug-in.
+ SetWindowLongPtrA(
+ windowed_handle_, GWL_WNDPROC, reinterpret_cast<LONG>(DefWindowProcA));
+
+ return true;
+}
+
+void WebPluginDelegateImpl::WindowedDestroyWindow() {
+ if (windowed_handle_ != NULL) {
+ // Unsubclass the window.
+ WNDPROC current_wnd_proc = reinterpret_cast<WNDPROC>(
+ GetWindowLongPtr(windowed_handle_, GWLP_WNDPROC));
+ if (current_wnd_proc == NativeWndProc) {
+ SetWindowLongPtr(windowed_handle_,
+ GWLP_WNDPROC,
+ reinterpret_cast<LONG>(plugin_wnd_proc_));
+ }
+
+ plugin_->WillDestroyWindow(windowed_handle_);
+
+ DestroyWindow(windowed_handle_);
+ windowed_handle_ = 0;
+ }
+}
+
+// Erase all messages in the queue destined for a particular window.
+// When windows are closing, callers should use this function to clear
+// the queue.
+// static
+void WebPluginDelegateImpl::ClearThrottleQueueForWindow(HWND window) {
+ ThrottleQueue* throttle_queue = g_throttle_queue.Pointer();
+
+ ThrottleQueue::iterator it;
+ for (it = throttle_queue->begin(); it != throttle_queue->end(); ) {
+ if (it->hwnd == window) {
+ it = throttle_queue->erase(it);
+ } else {
+ it++;
+ }
+ }
+}
+
+// Delayed callback for processing throttled messages.
+// Throttled messages are aggregated globally across all plugins.
+// static
+void WebPluginDelegateImpl::OnThrottleMessage() {
+ // The current algorithm walks the list and processes the first
+ // message it finds for each plugin. It is important to service
+ // all active plugins with each pass through the throttle, otherwise
+ // we see video jankiness. Copy the set to notify before notifying
+ // since we may re-enter OnThrottleMessage from CallWindowProc!
+ ThrottleQueue* throttle_queue = g_throttle_queue.Pointer();
+ ThrottleQueue notify_queue;
+ std::set<HWND> processed;
+
+ ThrottleQueue::iterator it = throttle_queue->begin();
+ while (it != throttle_queue->end()) {
+ const MSG& msg = *it;
+ if (processed.find(msg.hwnd) == processed.end()) {
+ processed.insert(msg.hwnd);
+ notify_queue.push_back(msg);
+ it = throttle_queue->erase(it);
+ } else {
+ it++;
+ }
+ }
+
+ for (it = notify_queue.begin(); it != notify_queue.end(); ++it) {
+ const MSG& msg = *it;
+ WNDPROC proc = reinterpret_cast<WNDPROC>(msg.time);
+ // It is possible that the window was closed after we queued
+ // this message. This is a rare event; just verify the window
+ // is alive. (see also bug 1259488)
+ if (IsWindow(msg.hwnd))
+ CallWindowProc(proc, msg.hwnd, msg.message, msg.wParam, msg.lParam);
+ }
+
+ if (!throttle_queue->empty()) {
+ MessageLoop::current()->PostDelayedTask(FROM_HERE,
+ NewRunnableFunction(&WebPluginDelegateImpl::OnThrottleMessage),
+ kFlashWMUSERMessageThrottleDelayMs);
+ }
+}
+
+// Schedule a windows message for delivery later.
+// static
+void WebPluginDelegateImpl::ThrottleMessage(WNDPROC proc, HWND hwnd,
+ UINT message, WPARAM wParam,
+ LPARAM lParam) {
+ MSG msg;
+ msg.time = reinterpret_cast<DWORD>(proc);
+ msg.hwnd = hwnd;
+ msg.message = message;
+ msg.wParam = wParam;
+ msg.lParam = lParam;
+
+ ThrottleQueue* throttle_queue = g_throttle_queue.Pointer();
+
+ throttle_queue->push_back(msg);
+
+ if (throttle_queue->size() == 1) {
+ MessageLoop::current()->PostDelayedTask(FROM_HERE,
+ NewRunnableFunction(&WebPluginDelegateImpl::OnThrottleMessage),
+ kFlashWMUSERMessageThrottleDelayMs);
+ }
+}
+
+// We go out of our way to find the hidden windows created by Flash for
+// windowless plugins. We throttle the rate at which they deliver messages
+// so that they will not consume outrageous amounts of CPU.
+// static
+LRESULT CALLBACK WebPluginDelegateImpl::FlashWindowlessWndProc(HWND hwnd,
+ UINT message, WPARAM wparam, LPARAM lparam) {
+ std::map<HWND, WNDPROC>::iterator index =
+ g_window_handle_proc_map.Get().find(hwnd);
+
+ WNDPROC old_proc = (*index).second;
+ DCHECK(old_proc);
+
+ switch (message) {
+ case WM_NCDESTROY: {
+ WebPluginDelegateImpl::ClearThrottleQueueForWindow(hwnd);
+ g_window_handle_proc_map.Get().erase(index);
+ break;
+ }
+ // Flash may flood the message queue with WM_USER+1 message causing 100% CPU
+ // usage. See https://bugzilla.mozilla.org/show_bug.cgi?id=132759. We
+ // prevent this by throttling the messages.
+ case WM_USER + 1: {
+ WebPluginDelegateImpl::ThrottleMessage(old_proc, hwnd, message, wparam,
+ lparam);
+ return TRUE;
+ }
+ default: {
+ break;
+ }
+ }
+ return CallWindowProc(old_proc, hwnd, message, wparam, lparam);
+}
+
+// Callback for enumerating the Flash windows.
+BOOL CALLBACK EnumFlashWindows(HWND window, LPARAM arg) {
+ WNDPROC wnd_proc = reinterpret_cast<WNDPROC>(arg);
+ TCHAR class_name[1024];
+ if (!RealGetWindowClass(window, class_name,
+ sizeof(class_name)/sizeof(TCHAR))) {
+ LOG(ERROR) << "RealGetWindowClass failure: " << GetLastError();
+ return FALSE;
+ }
+
+ if (wcscmp(class_name, L"SWFlash_PlaceholderX"))
+ return TRUE;
+
+ WNDPROC current_wnd_proc = reinterpret_cast<WNDPROC>(
+ GetWindowLongPtr(window, GWLP_WNDPROC));
+ if (current_wnd_proc != wnd_proc) {
+ WNDPROC old_flash_proc = reinterpret_cast<WNDPROC>(SetWindowLongPtr(
+ window, GWLP_WNDPROC,
+ reinterpret_cast<LONG>(wnd_proc)));
+ DCHECK(old_flash_proc);
+ g_window_handle_proc_map.Get()[window] = old_flash_proc;
+ }
+
+ return TRUE;
+}
+
+bool WebPluginDelegateImpl::CreateDummyWindowForActivation() {
+ DCHECK(!dummy_window_for_activation_);
+ dummy_window_for_activation_ = CreateWindowEx(
+ 0,
+ L"Static",
+ kDummyActivationWindowName,
+ WS_CHILD,
+ 0,
+ 0,
+ 0,
+ 0,
+ parent_,
+ 0,
+ GetModuleHandle(NULL),
+ 0);
+
+ if (dummy_window_for_activation_ == 0)
+ return false;
+
+ // Flash creates background windows which use excessive CPU in our
+ // environment; we wrap these windows and throttle them so that they don't
+ // get out of hand.
+ if (!EnumThreadWindows(::GetCurrentThreadId(), EnumFlashWindows,
+ reinterpret_cast<LPARAM>(
+ &WebPluginDelegateImpl::FlashWindowlessWndProc))) {
+ // Log that this happened. Flash will still work; it just means the
+ // throttle isn't installed (and Flash will use more CPU).
+ NOTREACHED();
+ LOG(ERROR) << "Failed to wrap all windowless Flash windows";
+ }
+ return true;
+}
+
+bool WebPluginDelegateImpl::WindowedReposition(
+ const gfx::Rect& window_rect,
+ const gfx::Rect& clip_rect) {
+ if (!windowed_handle_) {
+ NOTREACHED();
+ return false;
+ }
+
+ if (window_rect_ == window_rect && clip_rect_ == clip_rect)
+ return false;
+
+ // We only set the plugin's size here. Its position is moved elsewhere, which
+ // allows the window moves/scrolling/clipping to be synchronized with the page
+ // and other windows.
+ // If the plugin window has no parent, then don't focus it because it isn't
+ // being displayed anywhere. See:
+ // http://code.google.com/p/chromium/issues/detail?id=32658
+ if (window_rect.size() != window_rect_.size()) {
+ UINT flags = SWP_NOMOVE | SWP_NOZORDER;
+ if (!GetParent(windowed_handle_))
+ flags |= SWP_NOACTIVATE;
+ ::SetWindowPos(windowed_handle_,
+ NULL,
+ 0,
+ 0,
+ window_rect.width(),
+ window_rect.height(),
+ flags);
+ }
+
+ window_rect_ = window_rect;
+ clip_rect_ = clip_rect;
+
+ // Ensure that the entire window gets repainted.
+ ::InvalidateRect(windowed_handle_, NULL, FALSE);
+
+ return true;
+}
+
+void WebPluginDelegateImpl::WindowedSetWindow() {
+ if (!instance_)
+ return;
+
+ if (!windowed_handle_) {
+ NOTREACHED();
+ return;
+ }
+
+ instance()->set_window_handle(windowed_handle_);
+
+ DCHECK(!instance()->windowless());
+
+ window_.clipRect.top = std::max(0, clip_rect_.y());
+ window_.clipRect.left = std::max(0, clip_rect_.x());
+ window_.clipRect.bottom = std::max(0, clip_rect_.y() + clip_rect_.height());
+ window_.clipRect.right = std::max(0, clip_rect_.x() + clip_rect_.width());
+ window_.height = window_rect_.height();
+ window_.width = window_rect_.width();
+ window_.x = 0;
+ window_.y = 0;
+
+ window_.window = windowed_handle_;
+ window_.type = NPWindowTypeWindow;
+
+ // Reset this flag before entering the instance in case of side-effects.
+ windowed_did_set_window_ = true;
+
+ NPError err = instance()->NPP_SetWindow(&window_);
+ if (quirks_ & PLUGIN_QUIRK_SETWINDOW_TWICE)
+ instance()->NPP_SetWindow(&window_);
+
+ WNDPROC current_wnd_proc = reinterpret_cast<WNDPROC>(
+ GetWindowLongPtr(windowed_handle_, GWLP_WNDPROC));
+ if (current_wnd_proc != NativeWndProc) {
+ plugin_wnd_proc_ = reinterpret_cast<WNDPROC>(SetWindowLongPtr(
+ windowed_handle_, GWLP_WNDPROC, reinterpret_cast<LONG>(NativeWndProc)));
+ }
+}
+
+ATOM WebPluginDelegateImpl::RegisterNativeWindowClass() {
+ static bool have_registered_window_class = false;
+ if (have_registered_window_class == true)
+ return true;
+
+ have_registered_window_class = true;
+
+ WNDCLASSEX wcex;
+ wcex.cbSize = sizeof(WNDCLASSEX);
+ wcex.style = CS_DBLCLKS;
+ wcex.lpfnWndProc = DummyWindowProc;
+ wcex.cbClsExtra = 0;
+ wcex.cbWndExtra = 0;
+ wcex.hInstance = GetModuleHandle(NULL);
+ wcex.hIcon = 0;
+ wcex.hCursor = 0;
+ // Some plugins like windows media player 11 create child windows parented
+ // by our plugin window, where the media content is rendered. These plugins
+ // dont implement WM_ERASEBKGND, which causes painting issues, when the
+ // window where the media is rendered is moved around. DefWindowProc does
+ // implement WM_ERASEBKGND correctly if we have a valid background brush.
+ wcex.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_WINDOW+1);
+ wcex.lpszMenuName = 0;
+ wcex.lpszClassName = kNativeWindowClassName;
+ wcex.hIconSm = 0;
+
+ return RegisterClassEx(&wcex);
+}
+
+LRESULT CALLBACK WebPluginDelegateImpl::DummyWindowProc(
+ HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
+ // This is another workaround for Issue 2673 in chromium "Flash: IME not
+ // available". Somehow, the CallWindowProc() function does not dispatch
+ // window messages when its first parameter is a handle representing the
+ // DefWindowProc() function. To avoid this problem, this code creates a
+ // wrapper function which just encapsulates the DefWindowProc() function
+ // and set it as the window procedure of a windowed plug-in.
+ return DefWindowProc(hWnd, message, wParam, lParam);
+}
+
+// Returns true if the message passed in corresponds to a user gesture.
+static bool IsUserGestureMessage(unsigned int message) {
+ switch (message) {
+ case WM_LBUTTONUP:
+ case WM_RBUTTONUP:
+ case WM_MBUTTONUP:
+ case WM_KEYUP:
+ return true;
+
+ default:
+ break;
+ }
+
+ return false;
+}
+
+LRESULT CALLBACK WebPluginDelegateImpl::NativeWndProc(
+ HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) {
+ WebPluginDelegateImpl* delegate = reinterpret_cast<WebPluginDelegateImpl*>(
+ GetProp(hwnd, kWebPluginDelegateProperty));
+ if (!delegate) {
+ NOTREACHED();
+ return 0;
+ }
+
+ if (message == delegate->last_message_ &&
+ delegate->GetQuirks() & PLUGIN_QUIRK_DONT_CALL_WND_PROC_RECURSIVELY &&
+ delegate->is_calling_wndproc) {
+ // Real may go into a state where it recursively dispatches the same event
+ // when subclassed. See https://bugzilla.mozilla.org/show_bug.cgi?id=192914
+ // We only do the recursive check for Real because it's possible and valid
+ // for a plugin to synchronously dispatch a message to itself such that it
+ // looks like it's in recursion.
+ return TRUE;
+ }
+
+ // Flash may flood the message queue with WM_USER+1 message causing 100% CPU
+ // usage. See https://bugzilla.mozilla.org/show_bug.cgi?id=132759. We
+ // prevent this by throttling the messages.
+ if (message == WM_USER + 1 &&
+ delegate->GetQuirks() & PLUGIN_QUIRK_THROTTLE_WM_USER_PLUS_ONE) {
+ WebPluginDelegateImpl::ThrottleMessage(delegate->plugin_wnd_proc_, hwnd,
+ message, wparam, lparam);
+ return FALSE;
+ }
+
+ LRESULT result;
+ uint32 old_message = delegate->last_message_;
+ delegate->last_message_ = message;
+
+ static UINT custom_msg = RegisterWindowMessage(kPaintMessageName);
+ if (message == custom_msg) {
+ // Get the invalid rect which is in screen coordinates and convert to
+ // window coordinates.
+ gfx::Rect invalid_rect;
+ invalid_rect.set_x(wparam >> 16);
+ invalid_rect.set_y(wparam & 0xFFFF);
+ invalid_rect.set_width(lparam >> 16);
+ invalid_rect.set_height(lparam & 0xFFFF);
+
+ RECT window_rect;
+ GetWindowRect(hwnd, &window_rect);
+ invalid_rect.Offset(-window_rect.left, -window_rect.top);
+
+ // The plugin window might have non-client area. If we don't pass in
+ // RDW_FRAME then the children don't receive WM_NCPAINT messages while
+ // scrolling, which causes painting problems (http://b/issue?id=923945).
+ uint32 flags = RDW_INVALIDATE | RDW_ALLCHILDREN | RDW_FRAME;
+
+ // If a plugin (like Google Earth or Java) has child windows that are hosted
+ // in a different process, then RedrawWindow with UPDATENOW will
+ // synchronously wait for this call to complete. Some messages are pumped
+ // but not others, which could lead to a deadlock. So avoid reentrancy by
+ // only synchronously calling RedrawWindow once at a time.
+ if (old_message != custom_msg)
+ flags |= RDW_UPDATENOW;
+
+ RedrawWindow(hwnd, &invalid_rect.ToRECT(), NULL, flags);
+ result = FALSE;
+ } else {
+ delegate->is_calling_wndproc = true;
+
+ if (!delegate->user_gesture_message_posted_ &&
+ IsUserGestureMessage(message)) {
+ delegate->user_gesture_message_posted_ = true;
+
+ delegate->instance()->PushPopupsEnabledState(true);
+
+ MessageLoop::current()->PostDelayedTask(FROM_HERE,
+ delegate->user_gesture_msg_factory_.NewRunnableMethod(
+ &WebPluginDelegateImpl::OnUserGestureEnd),
+ kWindowedPluginPopupTimerMs);
+ }
+
+ HandleCaptureForMessage(hwnd, message);
+
+ // Maintain a local/global stack for the g_current_plugin_instance variable
+ // as this may be a nested invocation.
+ WebPluginDelegateImpl* last_plugin_instance = g_current_plugin_instance;
+
+ g_current_plugin_instance = delegate;
+
+ result = CallWindowProc(
+ delegate->plugin_wnd_proc_, hwnd, message, wparam, lparam);
+
+ delegate->is_calling_wndproc = false;
+ g_current_plugin_instance = last_plugin_instance;
+
+ if (message == WM_NCDESTROY) {
+ RemoveProp(hwnd, kWebPluginDelegateProperty);
+ ATOM plugin_name_atom = reinterpret_cast<ATOM>(
+ RemoveProp(hwnd, kPluginNameAtomProperty));
+ if (plugin_name_atom != 0)
+ GlobalDeleteAtom(plugin_name_atom);
+ ClearThrottleQueueForWindow(hwnd);
+ }
+ }
+ delegate->last_message_ = old_message;
+ return result;
+}
+
+void WebPluginDelegateImpl::WindowlessUpdateGeometry(
+ const gfx::Rect& window_rect,
+ const gfx::Rect& clip_rect) {
+ bool window_rect_changed = (window_rect_ != window_rect);
+ // Only resend to the instance if the geometry has changed.
+ if (!window_rect_changed && clip_rect == clip_rect_)
+ return;
+
+ clip_rect_ = clip_rect;
+ window_rect_ = window_rect;
+
+ WindowlessSetWindow();
+
+ if (window_rect_changed) {
+ WINDOWPOS win_pos = {0};
+ win_pos.x = window_rect_.x();
+ win_pos.y = window_rect_.y();
+ win_pos.cx = window_rect_.width();
+ win_pos.cy = window_rect_.height();
+
+ NPEvent pos_changed_event;
+ pos_changed_event.event = WM_WINDOWPOSCHANGED;
+ pos_changed_event.wParam = 0;
+ pos_changed_event.lParam = PtrToUlong(&win_pos);
+
+ instance()->NPP_HandleEvent(&pos_changed_event);
+ }
+}
+
+void WebPluginDelegateImpl::WindowlessPaint(HDC hdc,
+ const gfx::Rect& damage_rect) {
+ DCHECK(hdc);
+
+ RECT damage_rect_win;
+ damage_rect_win.left = damage_rect.x(); // + window_rect_.x();
+ damage_rect_win.top = damage_rect.y(); // + window_rect_.y();
+ damage_rect_win.right = damage_rect_win.left + damage_rect.width();
+ damage_rect_win.bottom = damage_rect_win.top + damage_rect.height();
+
+ window_.window = hdc;
+
+ NPEvent paint_event;
+ paint_event.event = WM_PAINT;
+ // NOTE: NPAPI is not 64bit safe. It puts pointers into 32bit values.
+ paint_event.wParam = PtrToUlong(hdc);
+ paint_event.lParam = PtrToUlong(&damage_rect_win);
+ static StatsRate plugin_paint("Plugin.Paint");
+ StatsScope<StatsRate> scope(plugin_paint);
+ instance()->NPP_HandleEvent(&paint_event);
+}
+
+void WebPluginDelegateImpl::WindowlessSetWindow() {
+ if (!instance())
+ return;
+
+ if (window_rect_.IsEmpty()) // wait for geometry to be set.
+ return;
+
+ DCHECK(instance()->windowless());
+
+ window_.clipRect.top = clip_rect_.y();
+ window_.clipRect.left = clip_rect_.x();
+ window_.clipRect.bottom = clip_rect_.y() + clip_rect_.height();
+ window_.clipRect.right = clip_rect_.x() + clip_rect_.width();
+ window_.height = window_rect_.height();
+ window_.width = window_rect_.width();
+ window_.x = window_rect_.x();
+ window_.y = window_rect_.y();
+ window_.type = NPWindowTypeDrawable;
+ DrawableContextEnforcer enforcer(&window_);
+
+ NPError err = instance()->NPP_SetWindow(&window_);
+ DCHECK(err == NPERR_NO_ERROR);
+}
+
+void WebPluginDelegateImpl::SetFocus(bool focused) {
+ DCHECK(instance()->windowless());
+
+ NPEvent focus_event;
+ focus_event.event = focused ? WM_SETFOCUS : WM_KILLFOCUS;
+ focus_event.wParam = 0;
+ focus_event.lParam = 0;
+
+ instance()->NPP_HandleEvent(&focus_event);
+}
+
+static bool NPEventFromWebMouseEvent(const WebMouseEvent& event,
+ NPEvent *np_event) {
+ np_event->lParam = static_cast<uint32>(MAKELPARAM(event.windowX,
+ event.windowY));
+ np_event->wParam = 0;
+
+ if (event.modifiers & WebInputEvent::ControlKey)
+ np_event->wParam |= MK_CONTROL;
+ if (event.modifiers & WebInputEvent::ShiftKey)
+ np_event->wParam |= MK_SHIFT;
+ if (event.modifiers & WebInputEvent::LeftButtonDown)
+ np_event->wParam |= MK_LBUTTON;
+ if (event.modifiers & WebInputEvent::MiddleButtonDown)
+ np_event->wParam |= MK_MBUTTON;
+ if (event.modifiers & WebInputEvent::RightButtonDown)
+ np_event->wParam |= MK_RBUTTON;
+
+ switch (event.type) {
+ case WebInputEvent::MouseMove:
+ case WebInputEvent::MouseLeave:
+ case WebInputEvent::MouseEnter:
+ np_event->event = WM_MOUSEMOVE;
+ return true;
+ case WebInputEvent::MouseDown:
+ switch (event.button) {
+ case WebMouseEvent::ButtonLeft:
+ np_event->event = WM_LBUTTONDOWN;
+ break;
+ case WebMouseEvent::ButtonMiddle:
+ np_event->event = WM_MBUTTONDOWN;
+ break;
+ case WebMouseEvent::ButtonRight:
+ np_event->event = WM_RBUTTONDOWN;
+ break;
+ }
+ return true;
+ case WebInputEvent::MouseUp:
+ switch (event.button) {
+ case WebMouseEvent::ButtonLeft:
+ np_event->event = WM_LBUTTONUP;
+ break;
+ case WebMouseEvent::ButtonMiddle:
+ np_event->event = WM_MBUTTONUP;
+ break;
+ case WebMouseEvent::ButtonRight:
+ np_event->event = WM_RBUTTONUP;
+ break;
+ }
+ return true;
+ default:
+ NOTREACHED();
+ return false;
+ }
+}
+
+static bool NPEventFromWebKeyboardEvent(const WebKeyboardEvent& event,
+ NPEvent *np_event) {
+ np_event->wParam = event.windowsKeyCode;
+
+ switch (event.type) {
+ case WebInputEvent::KeyDown:
+ np_event->event = WM_KEYDOWN;
+ np_event->lParam = 0;
+ return true;
+ case WebInputEvent::Char:
+ np_event->event = WM_CHAR;
+ np_event->lParam = 0;
+ return true;
+ case WebInputEvent::KeyUp:
+ np_event->event = WM_KEYUP;
+ np_event->lParam = 0x8000;
+ return true;
+ default:
+ NOTREACHED();
+ return false;
+ }
+}
+
+static bool NPEventFromWebInputEvent(const WebInputEvent& event,
+ NPEvent* np_event) {
+ switch (event.type) {
+ case WebInputEvent::MouseMove:
+ case WebInputEvent::MouseLeave:
+ case WebInputEvent::MouseEnter:
+ case WebInputEvent::MouseDown:
+ case WebInputEvent::MouseUp:
+ if (event.size < sizeof(WebMouseEvent)) {
+ NOTREACHED();
+ return false;
+ }
+ return NPEventFromWebMouseEvent(
+ *static_cast<const WebMouseEvent*>(&event), np_event);
+ case WebInputEvent::KeyDown:
+ case WebInputEvent::Char:
+ case WebInputEvent::KeyUp:
+ if (event.size < sizeof(WebKeyboardEvent)) {
+ NOTREACHED();
+ return false;
+ }
+ return NPEventFromWebKeyboardEvent(
+ *static_cast<const WebKeyboardEvent*>(&event), np_event);
+ default:
+ return false;
+ }
+}
+
+bool WebPluginDelegateImpl::PlatformHandleInputEvent(
+ const WebInputEvent& event, WebCursorInfo* cursor_info) {
+ DCHECK(cursor_info != NULL);
+
+ NPEvent np_event;
+ if (!NPEventFromWebInputEvent(event, &np_event)) {
+ return false;
+ }
+
+ // Synchronize the keyboard layout with the one of the browser process. Flash
+ // uses the keyboard layout of this window to verify a WM_CHAR message is
+ // valid. That is, Flash discards a WM_CHAR message unless its character is
+ // the one translated with ToUnicode(). (Since a plug-in is running on a
+ // separate process from the browser process, we need to syncronize it
+ // manually.)
+ if (np_event.event == WM_CHAR) {
+ if (!keyboard_layout_)
+ keyboard_layout_ = GetKeyboardLayout(GetCurrentThreadId());
+ if (!parent_thread_id_)
+ parent_thread_id_ = GetWindowThreadProcessId(parent_, NULL);
+ HKL parent_layout = GetKeyboardLayout(parent_thread_id_);
+ if (keyboard_layout_ != parent_layout) {
+ std::wstring layout_name(StringPrintf(L"%08x", parent_layout));
+ LoadKeyboardLayout(layout_name.c_str(), KLF_ACTIVATE);
+ keyboard_layout_ = parent_layout;
+ }
+ }
+
+ if (ShouldTrackEventForModalLoops(&np_event)) {
+ // A windowless plugin can enter a modal loop in a NPP_HandleEvent call.
+ // For e.g. Flash puts up a context menu when we right click on the
+ // windowless plugin area. We detect this by setting up a message filter
+ // hook pror to calling NPP_HandleEvent on the plugin and unhook on
+ // return from NPP_HandleEvent. If the plugin does enter a modal loop
+ // in that context we unhook on receiving the first notification in
+ // the message filter hook.
+ handle_event_message_filter_hook_ =
+ SetWindowsHookEx(WH_MSGFILTER, HandleEventMessageFilterHook, NULL,
+ GetCurrentThreadId());
+ }
+
+ bool old_task_reentrancy_state =
+ MessageLoop::current()->NestableTasksAllowed();
+
+
+ // Maintain a local/global stack for the g_current_plugin_instance variable
+ // as this may be a nested invocation.
+ WebPluginDelegateImpl* last_plugin_instance = g_current_plugin_instance;
+
+ g_current_plugin_instance = this;
+
+ handle_event_depth_++;
+
+ bool ret = instance()->NPP_HandleEvent(&np_event) != 0;
+
+ // Flash and SilverLight always return false, even when they swallow the
+ // event. Flash does this because it passes the event to its window proc,
+ // which is supposed to return 0 if an event was handled. There are few
+ // exceptions, such as IME, where it sometimes returns true.
+ ret = true;
+
+ if (np_event.event == WM_MOUSEMOVE) {
+ // Snag a reference to the current cursor ASAP in case the plugin modified
+ // it. There is a nasty race condition here with the multiprocess browser
+ // as someone might be setting the cursor in the main process as well.
+ current_windowless_cursor_.GetCursorInfo(cursor_info);
+ }
+
+ handle_event_depth_--;
+
+ g_current_plugin_instance = last_plugin_instance;
+
+ MessageLoop::current()->SetNestableTasksAllowed(old_task_reentrancy_state);
+
+ // We could have multiple NPP_HandleEvent calls nested together in case
+ // the plugin enters a modal loop. Reset the pump messages event when
+ // the outermost NPP_HandleEvent call unwinds.
+ if (handle_event_depth_ == 0) {
+ ResetEvent(handle_event_pump_messages_event_);
+ }
+
+ return ret;
+}
+
+
+void WebPluginDelegateImpl::OnModalLoopEntered() {
+ DCHECK(handle_event_pump_messages_event_ != NULL);
+ SetEvent(handle_event_pump_messages_event_);
+
+ MessageLoop::current()->SetNestableTasksAllowed(true);
+
+ UnhookWindowsHookEx(handle_event_message_filter_hook_);
+ handle_event_message_filter_hook_ = NULL;
+}
+
+bool WebPluginDelegateImpl::ShouldTrackEventForModalLoops(NPEvent* event) {
+ if (event->event == WM_RBUTTONDOWN)
+ return true;
+ return false;
+}
+
+void WebPluginDelegateImpl::OnUserGestureEnd() {
+ user_gesture_message_posted_ = false;
+ instance()->PopPopupsEnabledState();
+}
+
+BOOL WINAPI WebPluginDelegateImpl::TrackPopupMenuPatch(
+ HMENU menu, unsigned int flags, int x, int y, int reserved,
+ HWND window, const RECT* rect) {
+
+ HWND last_focus_window = NULL;
+
+ if (g_current_plugin_instance) {
+ unsigned long window_process_id = 0;
+ unsigned long window_thread_id =
+ GetWindowThreadProcessId(window, &window_process_id);
+ // TrackPopupMenu fails if the window passed in belongs to a different
+ // thread.
+ if (::GetCurrentThreadId() != window_thread_id) {
+ window = g_current_plugin_instance->dummy_window_for_activation_;
+ }
+
+ // To ensure that the plugin receives keyboard events we set focus to the
+ // dummy window.
+ // TODO(iyengar) We need a framework in the renderer to identify which
+ // windowless plugin is under the mouse and to handle this. This would
+ // also require some changes in RenderWidgetHost to detect this in the
+ // WM_MOUSEACTIVATE handler and inform the renderer accordingly.
+ if (g_current_plugin_instance->dummy_window_for_activation_) {
+ last_focus_window =
+ ::SetFocus(g_current_plugin_instance->dummy_window_for_activation_);
+ }
+ }
+
+ BOOL result = TrackPopupMenu(menu, flags, x, y, reserved, window, rect);
+
+ if (IsWindow(last_focus_window)) {
+ // The Flash plugin at times sets focus to its hidden top level window
+ // with class name SWFlash_PlaceholderX. This causes the chrome browser
+ // window to receive a WM_ACTIVATEAPP message as a top level window from
+ // another thread is now active. We end up in a state where the chrome
+ // browser window is not active even though the user clicked on it.
+ // Our workaround for this is to send over a raw
+ // WM_LBUTTONDOWN/WM_LBUTTONUP combination to the last focus window, which
+ // does the trick.
+ if (g_current_plugin_instance->dummy_window_for_activation_ !=
+ ::GetFocus()) {
+ INPUT input_info = {0};
+ input_info.type = INPUT_MOUSE;
+ input_info.mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
+ ::SendInput(1, &input_info, sizeof(INPUT));
+
+ input_info.type = INPUT_MOUSE;
+ input_info.mi.dwFlags = MOUSEEVENTF_LEFTUP;
+ ::SendInput(1, &input_info, sizeof(INPUT));
+ } else {
+ ::SetFocus(last_focus_window);
+ }
+ }
+
+ return result;
+}
+
+HCURSOR WINAPI WebPluginDelegateImpl::SetCursorPatch(HCURSOR cursor) {
+ // The windowless flash plugin periodically calls SetCursor in a wndproc
+ // instantiated on the plugin thread. This causes annoying cursor flicker
+ // when the mouse is moved on a foreground tab, with a windowless plugin
+ // instance in a background tab. We just ignore the call here.
+ if (!g_current_plugin_instance) {
+ HCURSOR current_cursor = GetCursor();
+ if (current_cursor != cursor) {
+ ::SetCursor(cursor);
+ }
+ return current_cursor;
+ }
+
+ if (!g_current_plugin_instance->IsWindowless()) {
+ return ::SetCursor(cursor);
+ }
+
+ // It is ok to pass NULL here to GetCursor as we are not looking for cursor
+ // types defined by Webkit.
+ HCURSOR previous_cursor =
+ g_current_plugin_instance->current_windowless_cursor_.GetCursor(NULL);
+
+ g_current_plugin_instance->current_windowless_cursor_.InitFromExternalCursor(
+ cursor);
+ return previous_cursor;
+}
+
+LONG WINAPI WebPluginDelegateImpl::RegEnumKeyExWPatch(
+ HKEY key, DWORD index, LPWSTR name, LPDWORD name_size, LPDWORD reserved,
+ LPWSTR class_name, LPDWORD class_size, PFILETIME last_write_time) {
+ DWORD orig_size = *name_size;
+ LONG rv = RegEnumKeyExW(key, index, name, name_size, reserved, class_name,
+ class_size, last_write_time);
+ if (rv == ERROR_SUCCESS &&
+ GetKeyPath(key).find(L"Microsoft\\MediaPlayer\\ShimInclusionList") !=
+ std::wstring::npos) {
+ static const wchar_t kChromeExeName[] = L"chrome.exe";
+ wcsncpy_s(name, orig_size, kChromeExeName, arraysize(kChromeExeName));
+ *name_size =
+ std::min(orig_size, static_cast<DWORD>(arraysize(kChromeExeName)));
+ }
+
+ return rv;
+}
+
+void WebPluginDelegateImpl::HandleCaptureForMessage(HWND window,
+ UINT message) {
+ if (!WebPluginDelegateImpl::IsPluginDelegateWindow(window))
+ return;
+
+ switch (message) {
+ case WM_LBUTTONDOWN:
+ case WM_MBUTTONDOWN:
+ case WM_RBUTTONDOWN:
+ ::SetCapture(window);
+ break;
+
+ case WM_LBUTTONUP:
+ case WM_MBUTTONUP:
+ case WM_RBUTTONUP:
+ ::ReleaseCapture();
+ break;
+
+ default:
+ break;
+ }
+}
diff --git a/webkit/glue/plugins/webplugin_file_delegate.h b/webkit/glue/plugins/webplugin_file_delegate.h
new file mode 100644
index 0000000..162516c
--- /dev/null
+++ b/webkit/glue/plugins/webplugin_file_delegate.h
@@ -0,0 +1,35 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_WEBPLUGIN_FILE_DELEGATE_H_
+#define WEBKIT_GLUE_PLUGINS_WEBPLUGIN_FILE_DELEGATE_H_
+
+#include "base/basictypes.h"
+#include "third_party/npapi/bindings/npapi_extensions.h"
+
+namespace webkit_glue {
+
+// Interface for the NPAPI file extensions. This class implements "NOP"
+// versions of all these functions so it can be used seamlessly by the
+// "regular" plugin delegate while being overridden by the "pepper" one.
+class WebPluginFileDelegate {
+ public:
+ // See NPChooseFilePtr in npapi_extensions.h. Returns true on success, on
+ // cancel, returns true but *filename will be filled with an empty FilePath
+ // and *handle will be 0.
+ virtual bool ChooseFile(const char* mime_types,
+ int mode,
+ NPChooseFileCallback callback,
+ void* user_data) {
+ return false;
+ }
+
+ protected:
+ WebPluginFileDelegate() {}
+ virtual ~WebPluginFileDelegate() {}
+};
+
+} // namespace webkit_glue
+
+#endif // WEBKIT_GLUE_PLUGINS_WEBPLUGIN_FILE_DELEGATE_H_
diff --git a/webkit/glue/plugins/webplugin_impl.cc b/webkit/glue/plugins/webplugin_impl.cc
new file mode 100644
index 0000000..1660ede
--- /dev/null
+++ b/webkit/glue/plugins/webplugin_impl.cc
@@ -0,0 +1,1305 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/webplugin_impl.h"
+
+#include "base/logging.h"
+#include "base/message_loop.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "gfx/rect.h"
+#include "googleurl/src/gurl.h"
+#include "net/base/escape.h"
+#include "net/base/net_errors.h"
+#include "skia/ext/platform_canvas.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebConsoleMessage.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebCookieJar.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebCString.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebCursorInfo.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebDevToolsAgent.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebData.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebDocument.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebFrame.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebHTTPBody.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebHTTPHeaderVisitor.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebInputEvent.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebKit.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebKitClient.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebPluginContainer.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebPluginParams.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURL.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURLError.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURLLoader.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURLLoaderClient.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURLResponse.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebView.h"
+#include "webkit/glue/multipart_response_delegate.h"
+#include "webkit/glue/plugins/plugin_host.h"
+#include "webkit/glue/plugins/plugin_instance.h"
+#include "webkit/glue/plugins/webplugin_delegate.h"
+#include "webkit/glue/plugins/webplugin_page_delegate.h"
+
+using WebKit::WebCanvas;
+using WebKit::WebConsoleMessage;
+using WebKit::WebCookieJar;
+using WebKit::WebCString;
+using WebKit::WebCursorInfo;
+using WebKit::WebData;
+using WebKit::WebDataSource;
+using WebKit::WebDevToolsAgent;
+using WebKit::WebFrame;
+using WebKit::WebHTTPBody;
+using WebKit::WebHTTPHeaderVisitor;
+using WebKit::WebInputEvent;
+using WebKit::WebKeyboardEvent;
+using WebKit::WebMouseEvent;
+using WebKit::WebPluginContainer;
+using WebKit::WebPluginParams;
+using WebKit::WebRect;
+using WebKit::WebString;
+using WebKit::WebURL;
+using WebKit::WebURLError;
+using WebKit::WebURLLoader;
+using WebKit::WebURLLoaderClient;
+using WebKit::WebURLRequest;
+using WebKit::WebURLResponse;
+using WebKit::WebVector;
+using WebKit::WebView;
+using webkit_glue::MultipartResponseDelegate;
+
+namespace webkit_glue {
+namespace {
+
+// This class handles individual multipart responses. It is instantiated when
+// we receive HTTP status code 206 in the HTTP response. This indicates
+// that the response could have multiple parts each separated by a boundary
+// specified in the response header.
+class MultiPartResponseClient : public WebURLLoaderClient {
+ public:
+ explicit MultiPartResponseClient(WebPluginResourceClient* resource_client)
+ : resource_client_(resource_client) {
+ Clear();
+ }
+
+ virtual void willSendRequest(
+ WebURLLoader*, WebURLRequest&, const WebURLResponse&) {}
+ virtual void didSendData(
+ WebURLLoader*, unsigned long long, unsigned long long) {}
+
+ // Called when the multipart parser encounters an embedded multipart
+ // response.
+ virtual void didReceiveResponse(
+ WebURLLoader*, const WebURLResponse& response) {
+ if (!MultipartResponseDelegate::ReadContentRanges(
+ response,
+ &byte_range_lower_bound_,
+ &byte_range_upper_bound_)) {
+ NOTREACHED();
+ return;
+ }
+
+ resource_response_ = response;
+ }
+
+ // Receives individual part data from a multipart response.
+ virtual void didReceiveData(
+ WebURLLoader*, const char* data, int data_size) {
+ // TODO(ananta)
+ // We should defer further loads on multipart resources on the same lines
+ // as regular resources requested by plugins to prevent reentrancy.
+ resource_client_->DidReceiveData(
+ data, data_size, byte_range_lower_bound_);
+ byte_range_lower_bound_ += data_size;
+ }
+
+ virtual void didFinishLoading(WebURLLoader*) {}
+ virtual void didFail(WebURLLoader*, const WebURLError&) {}
+
+ void Clear() {
+ resource_response_.reset();
+ byte_range_lower_bound_ = 0;
+ byte_range_upper_bound_ = 0;
+ }
+
+ private:
+ WebURLResponse resource_response_;
+ // The lower bound of the byte range.
+ int byte_range_lower_bound_;
+ // The upper bound of the byte range.
+ int byte_range_upper_bound_;
+ // The handler for the data.
+ WebPluginResourceClient* resource_client_;
+};
+
+class HeaderFlattener : public WebHTTPHeaderVisitor {
+ public:
+ HeaderFlattener(std::string* buf) : buf_(buf) {
+ }
+
+ virtual void visitHeader(const WebString& name, const WebString& value) {
+ // TODO(darin): Should we really exclude headers with an empty value?
+ if (!name.isEmpty() && !value.isEmpty()) {
+ buf_->append(name.utf8());
+ buf_->append(": ");
+ buf_->append(value.utf8());
+ buf_->append("\n");
+ }
+ }
+
+ private:
+ std::string* buf_;
+};
+
+std::string GetAllHeaders(const WebURLResponse& response) {
+ // TODO(darin): It is possible for httpStatusText to be empty and still have
+ // an interesting response, so this check seems wrong.
+ std::string result;
+ const WebString& status = response.httpStatusText();
+ if (status.isEmpty())
+ return result;
+
+ // TODO(darin): Shouldn't we also report HTTP version numbers?
+ result = StringPrintf("HTTP %d ", response.httpStatusCode());
+ result.append(status.utf8());
+ result.append("\n");
+
+ HeaderFlattener flattener(&result);
+ response.visitHTTPHeaderFields(&flattener);
+
+ return result;
+}
+
+struct ResponseInfo {
+ GURL url;
+ std::string mime_type;
+ uint32 last_modified;
+ uint32 expected_length;
+};
+
+void GetResponseInfo(const WebURLResponse& response,
+ ResponseInfo* response_info) {
+ response_info->url = response.url();
+ response_info->mime_type = response.mimeType().utf8();
+
+ // Measured in seconds since 12:00 midnight GMT, January 1, 1970.
+ response_info->last_modified =
+ static_cast<uint32>(response.lastModifiedDate());
+
+ // If the length comes in as -1, then it indicates that it was not
+ // read off the HTTP headers. We replicate Safari webkit behavior here,
+ // which is to set it to 0.
+ response_info->expected_length =
+ static_cast<uint32>(std::max(response.expectedContentLength(), 0LL));
+
+ WebString content_encoding =
+ response.httpHeaderField(WebString::fromUTF8("Content-Encoding"));
+ if (!content_encoding.isNull() &&
+ !EqualsASCII(content_encoding, "identity")) {
+ // Don't send the compressed content length to the plugin, which only
+ // cares about the decoded length.
+ response_info->expected_length = 0;
+ }
+}
+
+} // namespace
+
+// WebKit::WebPlugin ----------------------------------------------------------
+
+bool WebPluginImpl::initialize(WebPluginContainer* container) {
+ if (!page_delegate_)
+ return false;
+
+ WebPluginDelegate* plugin_delegate = page_delegate_->CreatePluginDelegate(
+ file_path_, mime_type_);
+ if (!plugin_delegate)
+ return false;
+
+ // Set the container before Initialize because the plugin may
+ // synchronously call NPN_GetValue to get its container during its
+ // initialization.
+ SetContainer(container);
+ bool ok = plugin_delegate->Initialize(
+ plugin_url_, arg_names_, arg_values_, this, load_manually_);
+ if (!ok) {
+ plugin_delegate->PluginDestroyed();
+ return false;
+ }
+
+ delegate_ = plugin_delegate;
+
+ return true;
+}
+
+void WebPluginImpl::destroy() {
+ SetContainer(NULL);
+ MessageLoop::current()->DeleteSoon(FROM_HERE, this);
+}
+
+NPObject* WebPluginImpl::scriptableObject() {
+ return delegate_->GetPluginScriptableObject();
+}
+
+void WebPluginImpl::paint(WebCanvas* canvas, const WebRect& paint_rect) {
+ if (!delegate_ || !container_)
+ return;
+
+#if defined(OS_WIN)
+ // Force a geometry update if needed to allow plugins like media player
+ // which defer the initial geometry update to work.
+ container_->reportGeometry();
+#endif // OS_WIN
+
+ // Note that |canvas| is only used when in windowless mode.
+ delegate_->Paint(canvas, paint_rect);
+}
+
+void WebPluginImpl::updateGeometry(
+ const WebRect& window_rect, const WebRect& clip_rect,
+ const WebVector<WebRect>& cutout_rects, bool is_visible) {
+ WebPluginGeometry new_geometry;
+ new_geometry.window = window_;
+ new_geometry.window_rect = window_rect;
+ new_geometry.clip_rect = clip_rect;
+ new_geometry.visible = is_visible;
+ new_geometry.rects_valid = true;
+ for (size_t i = 0; i < cutout_rects.size(); ++i)
+ new_geometry.cutout_rects.push_back(cutout_rects[i]);
+
+ // Only send DidMovePlugin if the geometry changed in some way.
+ if (window_ &&
+ page_delegate_ &&
+ (first_geometry_update_ || !new_geometry.Equals(geometry_))) {
+ page_delegate_->DidMovePlugin(new_geometry);
+ }
+
+ // Only UpdateGeometry if either the window or clip rects have changed.
+ if (first_geometry_update_ ||
+ new_geometry.window_rect != geometry_.window_rect ||
+ new_geometry.clip_rect != geometry_.clip_rect) {
+ // Notify the plugin that its parameters have changed.
+ delegate_->UpdateGeometry(new_geometry.window_rect, new_geometry.clip_rect);
+ }
+
+ // Initiate a download on the plugin url. This should be done for the
+ // first update geometry sequence. We need to ensure that the plugin
+ // receives the geometry update before it starts receiving data.
+ if (first_geometry_update_) {
+ // An empty url corresponds to an EMBED tag with no src attribute.
+ if (!load_manually_ && plugin_url_.is_valid()) {
+ // The Flash plugin hangs for a while if it receives data before
+ // receiving valid plugin geometry. By valid geometry we mean the
+ // geometry received by a call to setFrameRect in the Webkit
+ // layout code path. To workaround this issue we download the
+ // plugin source url on a timer.
+ MessageLoop::current()->PostDelayedTask(
+ FROM_HERE, method_factory_.NewRunnableMethod(
+ &WebPluginImpl::OnDownloadPluginSrcUrl), 0);
+ }
+ }
+
+#if defined(OS_WIN)
+ // Don't cache the geometry during the first geometry update. The first
+ // geometry update sequence is received when Widget::setParent is called.
+ // For plugins like media player which have a bug where they only honor
+ // the first geometry update, we have a quirk which ignores the first
+ // geometry update. To ensure that these plugins work correctly in cases
+ // where we receive only one geometry update from webkit, we also force
+ // a geometry update during paint which should go out correctly as the
+ // initial geometry update was not cached.
+ if (!first_geometry_update_)
+ geometry_ = new_geometry;
+#else // OS_WIN
+ geometry_ = new_geometry;
+#endif // OS_WIN
+ first_geometry_update_ = false;
+}
+
+void WebPluginImpl::updateFocus(bool focused) {
+ if (accepts_input_events_)
+ delegate_->SetFocus(focused);
+}
+
+void WebPluginImpl::updateVisibility(bool visible) {
+ if (!window_ || !page_delegate_)
+ return;
+
+ WebPluginGeometry move;
+ move.window = window_;
+ move.window_rect = gfx::Rect();
+ move.clip_rect = gfx::Rect();
+ move.rects_valid = false;
+ move.visible = visible;
+
+ page_delegate_->DidMovePlugin(move);
+}
+
+bool WebPluginImpl::acceptsInputEvents() {
+ return accepts_input_events_;
+}
+
+bool WebPluginImpl::handleInputEvent(
+ const WebInputEvent& event, WebCursorInfo& cursor_info) {
+ return delegate_->HandleInputEvent(event, &cursor_info);
+}
+
+void WebPluginImpl::didReceiveResponse(const WebURLResponse& response) {
+ ignore_response_error_ = false;
+
+ ResponseInfo response_info;
+ GetResponseInfo(response, &response_info);
+
+ delegate_->DidReceiveManualResponse(
+ response_info.url,
+ response_info.mime_type,
+ GetAllHeaders(response),
+ response_info.expected_length,
+ response_info.last_modified);
+}
+
+void WebPluginImpl::didReceiveData(const char* data, int data_length) {
+ delegate_->DidReceiveManualData(data, data_length);
+}
+
+void WebPluginImpl::didFinishLoading() {
+ delegate_->DidFinishManualLoading();
+}
+
+void WebPluginImpl::didFailLoading(const WebURLError& error) {
+ if (!ignore_response_error_)
+ delegate_->DidManualLoadFail();
+}
+
+void WebPluginImpl::didFinishLoadingFrameRequest(
+ const WebURL& url, void* notify_data) {
+ if (delegate_) {
+ // We're converting a void* into an arbitrary int id. Though
+ // these types are the same size on all the platforms we support,
+ // the compiler may complain as though they are different, so to
+ // make the casting gods happy go through an intptr_t (the union
+ // of void* and int) rather than converting straight across.
+ delegate_->DidFinishLoadWithReason(
+ url, NPRES_DONE, reinterpret_cast<intptr_t>(notify_data));
+ }
+}
+
+void WebPluginImpl::didFailLoadingFrameRequest(
+ const WebURL& url, void* notify_data, const WebURLError& error) {
+ if (!delegate_)
+ return;
+
+ NPReason reason =
+ error.reason == net::ERR_ABORTED ? NPRES_USER_BREAK : NPRES_NETWORK_ERR;
+ // See comment in didFinishLoadingFrameRequest about the cast here.
+ delegate_->DidFinishLoadWithReason(
+ url, reason, reinterpret_cast<intptr_t>(notify_data));
+}
+
+bool WebPluginImpl::supportsPaginatedPrint() {
+ if (!delegate_)
+ return false;
+ return delegate_->PrintSupportsPrintExtension();
+}
+
+int WebPluginImpl::printBegin(const WebRect& printable_area, int printer_dpi) {
+ if (!delegate_)
+ return 0;
+
+ if (!supportsPaginatedPrint())
+ return 0;
+
+ return delegate_->PrintBegin(printable_area, printer_dpi);
+}
+
+bool WebPluginImpl::printPage(int page_number, WebCanvas* canvas) {
+ if (!delegate_)
+ return false;
+
+ return delegate_->PrintPage(page_number, canvas);
+}
+
+void WebPluginImpl::printEnd() {
+ if (delegate_)
+ delegate_->PrintEnd();
+}
+
+bool WebPluginImpl::hasSelection() const {
+ if (!delegate_)
+ return false;
+
+ return delegate_->HasSelection();
+}
+
+WebKit::WebString WebPluginImpl::selectionAsText() const {
+ if (!delegate_)
+ return WebString();
+
+ return delegate_->GetSelectionAsText();
+}
+
+WebKit::WebString WebPluginImpl::selectionAsMarkup() const {
+ if (!delegate_)
+ return WebString();
+
+ return delegate_->GetSelectionAsMarkup();
+}
+
+void WebPluginImpl::setZoomFactor(float scale, bool text_only) {
+ if (delegate_)
+ delegate_->SetZoomFactor(scale, text_only);
+}
+
+bool WebPluginImpl::startFind(const WebKit::WebString& search_text,
+ bool case_sensitive,
+ int identifier) {
+ if (!delegate_)
+ return false;
+ return delegate_->StartFind(search_text, case_sensitive, identifier);
+}
+
+void WebPluginImpl::selectFindResult(bool forward) {
+ if (delegate_)
+ delegate_->SelectFindResult(forward);
+}
+
+void WebPluginImpl::stopFind() {
+ if (delegate_)
+ delegate_->StopFind();
+}
+
+
+// -----------------------------------------------------------------------------
+
+WebPluginImpl::WebPluginImpl(
+ WebFrame* webframe,
+ const WebPluginParams& params,
+ const FilePath& file_path,
+ const std::string& mime_type,
+ const base::WeakPtr<WebPluginPageDelegate>& page_delegate)
+ : windowless_(false),
+ window_(gfx::kNullPluginWindow),
+ accepts_input_events_(false),
+ page_delegate_(page_delegate),
+ webframe_(webframe),
+ delegate_(NULL),
+ container_(NULL),
+ plugin_url_(params.url),
+ load_manually_(params.loadManually),
+ first_geometry_update_(true),
+ ignore_response_error_(false),
+ file_path_(file_path),
+ mime_type_(mime_type),
+ ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) {
+ DCHECK_EQ(params.attributeNames.size(), params.attributeValues.size());
+ StringToLowerASCII(&mime_type_);
+
+ for (size_t i = 0; i < params.attributeNames.size(); ++i) {
+ arg_names_.push_back(params.attributeNames[i].utf8());
+ arg_values_.push_back(params.attributeValues[i].utf8());
+ }
+}
+
+WebPluginImpl::~WebPluginImpl() {
+}
+
+void WebPluginImpl::SetWindow(gfx::PluginWindowHandle window) {
+#if defined(OS_MACOSX)
+ // The only time this is called twice, and the second time with a
+ // non-zero PluginWindowHandle, is the case when this WebPluginImpl
+ // is created on behalf of the GPU plugin. This entire code path
+ // will go away soon, as soon as the GPU plugin becomes the GPU
+ // process, so it is being separated out for easy deletion.
+
+ // The logic we want here is: if (window) DCHECK(!window_);
+ DCHECK(!(window_ && window));
+ window_ = window;
+ // Lie to ourselves about being windowless even if we got a fake
+ // plugin window handle, so we continue to get input events.
+ windowless_ = true;
+ accepts_input_events_ = true;
+ // We do not really need to notify the page delegate that a plugin
+ // window was created -- so don't.
+#else
+ if (window) {
+ DCHECK(!windowless_);
+ window_ = window;
+ accepts_input_events_ = false;
+ if (page_delegate_) {
+ // Tell the view delegate that the plugin window was created, so that it
+ // can create necessary container widgets.
+ page_delegate_->CreatedPluginWindow(window);
+ }
+ } else {
+ DCHECK(!window_); // Make sure not called twice.
+ windowless_ = true;
+ accepts_input_events_ = true;
+ }
+#endif
+}
+
+void WebPluginImpl::WillDestroyWindow(gfx::PluginWindowHandle window) {
+ DCHECK_EQ(window, window_);
+ window_ = gfx::kNullPluginWindow;
+ if (page_delegate_)
+ page_delegate_->WillDestroyPluginWindow(window);
+}
+
+GURL WebPluginImpl::CompleteURL(const char* url) {
+ if (!webframe_) {
+ NOTREACHED();
+ return GURL();
+ }
+ // TODO(darin): Is conversion from UTF8 correct here?
+ return webframe_->document().completeURL(WebString::fromUTF8(url));
+}
+
+void WebPluginImpl::CancelResource(unsigned long id) {
+ for (size_t i = 0; i < clients_.size(); ++i) {
+ if (clients_[i].id == id) {
+ if (clients_[i].loader.get()) {
+ clients_[i].loader->setDefersLoading(false);
+ clients_[i].loader->cancel();
+ RemoveClient(i);
+ }
+ return;
+ }
+ }
+}
+
+bool WebPluginImpl::SetPostData(WebURLRequest* request,
+ const char *buf,
+ uint32 length) {
+ std::vector<std::string> names;
+ std::vector<std::string> values;
+ std::vector<char> body;
+ bool rv = NPAPI::PluginHost::SetPostData(buf, length, &names, &values, &body);
+
+ for (size_t i = 0; i < names.size(); ++i) {
+ request->addHTTPHeaderField(WebString::fromUTF8(names[i]),
+ WebString::fromUTF8(values[i]));
+ }
+
+ WebString content_type_header = WebString::fromUTF8("Content-Type");
+ const WebString& content_type =
+ request->httpHeaderField(content_type_header);
+ if (content_type.isEmpty()) {
+ request->setHTTPHeaderField(
+ content_type_header,
+ WebString::fromUTF8("application/x-www-form-urlencoded"));
+ }
+
+ WebHTTPBody http_body;
+ if (body.size()) {
+ http_body.initialize();
+ http_body.appendData(WebData(&body[0], body.size()));
+ }
+ request->setHTTPBody(http_body);
+
+ return rv;
+}
+
+bool WebPluginImpl::IsValidUrl(const GURL& url, Referrer referrer_flag) {
+ if (referrer_flag == PLUGIN_SRC &&
+ mime_type_ == "application/x-shockwave-flash" &&
+ url.GetOrigin() != plugin_url_.GetOrigin()) {
+ // Do url check to make sure that there are no @, ;, \ chars in between url
+ // scheme and url path.
+ const char* url_to_check(url.spec().data());
+ url_parse::Parsed parsed;
+ url_parse::ParseStandardURL(url_to_check, strlen(url_to_check), &parsed);
+ if (parsed.path.begin <= parsed.scheme.end())
+ return true;
+ std::string string_to_search;
+ string_to_search.assign(url_to_check + parsed.scheme.end(),
+ parsed.path.begin - parsed.scheme.end());
+ if (string_to_search.find("@") != std::string::npos ||
+ string_to_search.find(";") != std::string::npos ||
+ string_to_search.find("\\") != std::string::npos)
+ return false;
+ }
+
+ return true;
+}
+
+WebPluginImpl::RoutingStatus WebPluginImpl::RouteToFrame(
+ const char* url,
+ bool is_javascript_url,
+ const char* method,
+ const char* target,
+ const char* buf,
+ unsigned int len,
+ int notify_id,
+ Referrer referrer_flag) {
+ // If there is no target, there is nothing to do
+ if (!target)
+ return NOT_ROUTED;
+
+ // This could happen if the WebPluginContainer was already deleted.
+ if (!webframe_)
+ return NOT_ROUTED;
+
+ WebString target_str = WebString::fromUTF8(target);
+
+ // Take special action for JavaScript URLs
+ if (is_javascript_url) {
+ WebFrame* target_frame =
+ webframe_->view()->findFrameByName(target_str, webframe_);
+ // For security reasons, do not allow JavaScript on frames
+ // other than this frame.
+ if (target_frame != webframe_) {
+ // TODO(darin): Localize this message.
+ const char kMessage[] =
+ "Ignoring cross-frame javascript URL load requested by plugin.";
+ webframe_->addMessageToConsole(
+ WebConsoleMessage(WebConsoleMessage::LevelError,
+ WebString::fromUTF8(kMessage)));
+ return ROUTED;
+ }
+
+ // Route javascript calls back to the plugin.
+ return NOT_ROUTED;
+ }
+
+ // If we got this far, we're routing content to a target frame.
+ // Go fetch the URL.
+
+ GURL complete_url = CompleteURL(url);
+ // Remove when flash bug is fixed. http://crbug.com/40016.
+ if (!WebPluginImpl::IsValidUrl(complete_url, referrer_flag))
+ return INVALID_URL;
+
+ if (strcmp(method, "GET") != 0) {
+ // We're only going to route HTTP/HTTPS requests
+ if (!(complete_url.SchemeIs("http") || complete_url.SchemeIs("https")))
+ return INVALID_URL;
+ }
+
+ WebURLRequest request(complete_url);
+ SetReferrer(&request, referrer_flag);
+
+ request.setHTTPMethod(WebString::fromUTF8(method));
+ request.setFirstPartyForCookies(
+ webframe_->document().firstPartyForCookies());
+ if (len > 0) {
+ if (!SetPostData(&request, buf, len)) {
+ // Uhoh - we're in trouble. There isn't a good way
+ // to recover at this point. Break out.
+ NOTREACHED();
+ return ROUTED;
+ }
+ }
+
+ container_->loadFrameRequest(
+ request, target_str, notify_id != 0, reinterpret_cast<void*>(notify_id));
+ return ROUTED;
+}
+
+NPObject* WebPluginImpl::GetWindowScriptNPObject() {
+ if (!webframe_) {
+ NOTREACHED();
+ return NULL;
+ }
+ return webframe_->windowObject();
+}
+
+NPObject* WebPluginImpl::GetPluginElement() {
+ return container_->scriptableObjectForElement();
+}
+
+void WebPluginImpl::SetCookie(const GURL& url,
+ const GURL& first_party_for_cookies,
+ const std::string& cookie) {
+ if (!page_delegate_)
+ return;
+
+ WebCookieJar* cookie_jar = page_delegate_->GetCookieJar();
+ if (!cookie_jar) {
+ DLOG(WARNING) << "No cookie jar!";
+ return;
+ }
+
+ cookie_jar->setCookie(
+ url, first_party_for_cookies, WebString::fromUTF8(cookie));
+}
+
+std::string WebPluginImpl::GetCookies(const GURL& url,
+ const GURL& first_party_for_cookies) {
+ if (!page_delegate_)
+ return std::string();
+
+ WebCookieJar* cookie_jar = page_delegate_->GetCookieJar();
+ if (!cookie_jar) {
+ DLOG(WARNING) << "No cookie jar!";
+ return std::string();
+ }
+
+ return UTF16ToUTF8(cookie_jar->cookies(url, first_party_for_cookies));
+}
+
+void WebPluginImpl::ShowModalHTMLDialog(const GURL& url, int width, int height,
+ const std::string& json_arguments,
+ std::string* json_retval) {
+ if (page_delegate_) {
+ page_delegate_->ShowModalHTMLDialogForPlugin(
+ url, gfx::Size(width, height), json_arguments, json_retval);
+ }
+}
+
+void WebPluginImpl::OnMissingPluginStatus(int status) {
+ NOTREACHED();
+}
+
+void WebPluginImpl::Invalidate() {
+ if (container_)
+ container_->invalidate();
+}
+
+void WebPluginImpl::InvalidateRect(const gfx::Rect& rect) {
+ if (container_)
+ container_->invalidateRect(rect);
+}
+
+void WebPluginImpl::OnDownloadPluginSrcUrl() {
+ HandleURLRequestInternal(
+ plugin_url_.spec().c_str(), "GET", NULL, NULL, 0, 0, false, DOCUMENT_URL);
+}
+
+WebPluginResourceClient* WebPluginImpl::GetClientFromLoader(
+ WebURLLoader* loader) {
+ ClientInfo* client_info = GetClientInfoFromLoader(loader);
+ if (client_info)
+ return client_info->client;
+ return NULL;
+}
+
+WebPluginImpl::ClientInfo* WebPluginImpl::GetClientInfoFromLoader(
+ WebURLLoader* loader) {
+ for (size_t i = 0; i < clients_.size(); ++i) {
+ if (clients_[i].loader.get() == loader)
+ return &clients_[i];
+ }
+
+ NOTREACHED();
+ return 0;
+}
+
+void WebPluginImpl::willSendRequest(WebURLLoader* loader,
+ WebURLRequest& request,
+ const WebURLResponse&) {
+ WebPluginResourceClient* client = GetClientFromLoader(loader);
+ if (client)
+ client->WillSendRequest(request.url());
+}
+
+void WebPluginImpl::didSendData(WebURLLoader* loader,
+ unsigned long long bytes_sent,
+ unsigned long long total_bytes_to_be_sent) {
+}
+
+void WebPluginImpl::didReceiveResponse(WebURLLoader* loader,
+ const WebURLResponse& response) {
+ static const int kHttpPartialResponseStatusCode = 206;
+ static const int kHttpResponseSuccessStatusCode = 200;
+
+ WebPluginResourceClient* client = GetClientFromLoader(loader);
+ if (!client)
+ return;
+
+ ResponseInfo response_info;
+ GetResponseInfo(response, &response_info);
+
+ bool request_is_seekable = true;
+ if (client->IsMultiByteResponseExpected()) {
+ if (response.httpStatusCode() == kHttpPartialResponseStatusCode) {
+ HandleHttpMultipartResponse(response, client);
+ return;
+ } else if (response.httpStatusCode() == kHttpResponseSuccessStatusCode) {
+ // If the client issued a byte range request and the server responds with
+ // HTTP 200 OK, it indicates that the server does not support byte range
+ // requests.
+ // We need to emulate Firefox behavior by doing the following:-
+ // 1. Destroy the plugin instance in the plugin process. Ensure that
+ // existing resource requests initiated for the plugin instance
+ // continue to remain valid.
+ // 2. Create a new plugin instance and notify it about the response
+ // received here.
+ if (!ReinitializePluginForResponse(loader)) {
+ NOTREACHED();
+ return;
+ }
+
+ // The server does not support byte range requests. No point in creating
+ // seekable streams.
+ request_is_seekable = false;
+
+ delete client;
+ client = NULL;
+
+ // Create a new resource client for this request.
+ for (size_t i = 0; i < clients_.size(); ++i) {
+ if (clients_[i].loader.get() == loader) {
+ WebPluginResourceClient* resource_client =
+ delegate_->CreateResourceClient(clients_[i].id, plugin_url_, 0);
+ clients_[i].client = resource_client;
+ client = resource_client;
+ break;
+ }
+ }
+
+ DCHECK(client != NULL);
+ }
+ }
+
+ // Calling into a plugin could result in reentrancy if the plugin yields
+ // control to the OS like entering a modal loop etc. Prevent this by
+ // stopping further loading until the plugin notifies us that it is ready to
+ // accept data
+ loader->setDefersLoading(true);
+
+ client->DidReceiveResponse(
+ response_info.mime_type,
+ GetAllHeaders(response),
+ response_info.expected_length,
+ response_info.last_modified,
+ request_is_seekable);
+
+ if (WebDevToolsAgent* devtools_agent = GetDevToolsAgent()) {
+ ClientInfo* client_info = GetClientInfoFromLoader(loader);
+ if (client_info)
+ devtools_agent->didReceiveResponse(client_info->id, response);
+ }
+
+ // Bug http://b/issue?id=925559. The flash plugin would not handle the HTTP
+ // error codes in the stream header and as a result, was unaware of the
+ // fate of the HTTP requests issued via NPN_GetURLNotify. Webkit and FF
+ // destroy the stream and invoke the NPP_DestroyStream function on the
+ // plugin if the HTTP request fails.
+ const GURL& url = response.url();
+ if (url.SchemeIs("http") || url.SchemeIs("https")) {
+ if (response.httpStatusCode() < 100 || response.httpStatusCode() >= 400) {
+ // The plugin instance could be in the process of deletion here.
+ // Verify if the WebPluginResourceClient instance still exists before
+ // use.
+ ClientInfo* client_info = GetClientInfoFromLoader(loader);
+ if (client_info) {
+ client_info->pending_failure_notification = true;
+ }
+ }
+ }
+}
+
+void WebPluginImpl::didReceiveData(WebURLLoader* loader,
+ const char *buffer,
+ int length) {
+ WebPluginResourceClient* client = GetClientFromLoader(loader);
+ if (!client)
+ return;
+
+ // ClientInfo can be removed from clients_ vector by next statements.
+ if (WebDevToolsAgent* devtools_agent = GetDevToolsAgent()) {
+ ClientInfo* client_info = GetClientInfoFromLoader(loader);
+ if (client_info)
+ devtools_agent->didReceiveData(client_info->id, length);
+ }
+ MultiPartResponseHandlerMap::iterator index =
+ multi_part_response_map_.find(client);
+ if (index != multi_part_response_map_.end()) {
+ MultipartResponseDelegate* multi_part_handler = (*index).second;
+ DCHECK(multi_part_handler != NULL);
+ multi_part_handler->OnReceivedData(buffer, length);
+ } else {
+ loader->setDefersLoading(true);
+ client->DidReceiveData(buffer, length, 0);
+ }
+}
+
+void WebPluginImpl::didFinishLoading(WebURLLoader* loader) {
+ ClientInfo* client_info = GetClientInfoFromLoader(loader);
+ if (client_info && client_info->client) {
+ MultiPartResponseHandlerMap::iterator index =
+ multi_part_response_map_.find(client_info->client);
+ if (index != multi_part_response_map_.end()) {
+ delete (*index).second;
+ multi_part_response_map_.erase(index);
+ if (page_delegate_)
+ page_delegate_->DidStopLoadingForPlugin();
+ }
+ loader->setDefersLoading(true);
+ WebPluginResourceClient* resource_client = client_info->client;
+ // The ClientInfo can get deleted in the call to DidFinishLoading below.
+ // It is not safe to access this structure after that.
+ client_info->client = NULL;
+ resource_client->DidFinishLoading();
+
+ if (WebDevToolsAgent* devtools_agent = GetDevToolsAgent())
+ devtools_agent->didFinishLoading(client_info->id);
+ }
+}
+
+void WebPluginImpl::didFail(WebURLLoader* loader,
+ const WebURLError& error) {
+ ClientInfo* client_info = GetClientInfoFromLoader(loader);
+ if (client_info && client_info->client) {
+ loader->setDefersLoading(true);
+ WebPluginResourceClient* resource_client = client_info->client;
+ // The ClientInfo can get deleted in the call to DidFail below.
+ // It is not safe to access this structure after that.
+ client_info->client = NULL;
+ resource_client->DidFail();
+
+ if (WebDevToolsAgent* devtools_agent = GetDevToolsAgent())
+ devtools_agent->didFailLoading(client_info->id, error);
+ }
+}
+
+void WebPluginImpl::RemoveClient(size_t i) {
+ clients_.erase(clients_.begin() + i);
+}
+
+void WebPluginImpl::RemoveClient(WebURLLoader* loader) {
+ for (size_t i = 0; i < clients_.size(); ++i) {
+ if (clients_[i].loader.get() == loader) {
+ RemoveClient(i);
+ return;
+ }
+ }
+}
+
+void WebPluginImpl::SetContainer(WebPluginContainer* container) {
+ if (!container)
+ TearDownPluginInstance(NULL);
+ container_ = container;
+}
+
+void WebPluginImpl::HandleURLRequest(const char* url,
+ const char* method,
+ const char* target,
+ const char* buf,
+ unsigned int len,
+ int notify_id,
+ bool popups_allowed) {
+ // GetURL/PostURL requests initiated explicitly by plugins should specify the
+ // plugin SRC url as the referrer if it is available.
+ HandleURLRequestInternal(
+ url, method, target, buf, len, notify_id, popups_allowed, PLUGIN_SRC);
+}
+
+void WebPluginImpl::HandleURLRequestInternal(const char* url,
+ const char* method,
+ const char* target,
+ const char* buf,
+ unsigned int len,
+ int notify_id,
+ bool popups_allowed,
+ Referrer referrer_flag) {
+ // For this request, we either route the output to a frame
+ // because a target has been specified, or we handle the request
+ // here, i.e. by executing the script if it is a javascript url
+ // or by initiating a download on the URL, etc. There is one special
+ // case in that the request is a javascript url and the target is "_self",
+ // in which case we route the output to the plugin rather than routing it
+ // to the plugin's frame.
+ bool is_javascript_url = StartsWithASCII(url, "javascript:", false);
+ RoutingStatus routing_status = RouteToFrame(
+ url, is_javascript_url, method, target, buf, len, notify_id,
+ referrer_flag);
+ if (routing_status == ROUTED)
+ return;
+
+ if (is_javascript_url) {
+ GURL gurl(url);
+ WebString result = container_->executeScriptURL(gurl, popups_allowed);
+
+ // delegate_ could be NULL because executeScript caused the container to
+ // be deleted.
+ if (delegate_) {
+ delegate_->SendJavaScriptStream(
+ gurl, result.utf8(), !result.isNull(), notify_id);
+ }
+
+ return;
+ }
+
+ unsigned long resource_id = GetNextResourceId();
+ if (!resource_id)
+ return;
+
+ GURL complete_url = CompleteURL(url);
+ // Remove when flash bug is fixed. http://crbug.com/40016.
+ if (!WebPluginImpl::IsValidUrl(complete_url, referrer_flag))
+ return;
+
+ WebPluginResourceClient* resource_client = delegate_->CreateResourceClient(
+ resource_id, complete_url, notify_id);
+ if (!resource_client)
+ return;
+
+ // If the RouteToFrame call returned a failure then inform the result
+ // back to the plugin asynchronously.
+ if ((routing_status == INVALID_URL) ||
+ (routing_status == GENERAL_FAILURE)) {
+ resource_client->DidFail();
+ return;
+ }
+
+ // CreateResourceClient() sends a synchronous IPC message so it's possible
+ // that TearDownPluginInstance() may have been called in the nested
+ // message loop. If so, don't start the request.
+ if (!delegate_)
+ return;
+
+ InitiateHTTPRequest(resource_id, resource_client, complete_url, method, buf,
+ len, NULL, referrer_flag);
+}
+
+unsigned long WebPluginImpl::GetNextResourceId() {
+ if (!webframe_)
+ return 0;
+ WebView* view = webframe_->view();
+ if (!view)
+ return 0;
+ return view->createUniqueIdentifierForRequest();
+}
+
+bool WebPluginImpl::InitiateHTTPRequest(unsigned long resource_id,
+ WebPluginResourceClient* client,
+ const GURL& url,
+ const char* method,
+ const char* buf,
+ int buf_len,
+ const char* range_info,
+ Referrer referrer_flag) {
+ if (!client) {
+ NOTREACHED();
+ return false;
+ }
+
+ ClientInfo info;
+ info.id = resource_id;
+ info.client = client;
+ info.request.initialize();
+ info.request.setURL(url);
+ info.request.setFirstPartyForCookies(
+ webframe_->document().firstPartyForCookies());
+ info.request.setRequestorProcessID(delegate_->GetProcessId());
+ info.request.setTargetType(WebURLRequest::TargetIsObject);
+ info.request.setHTTPMethod(WebString::fromUTF8(method));
+ info.pending_failure_notification = false;
+
+ if (range_info) {
+ info.request.addHTTPHeaderField(WebString::fromUTF8("Range"),
+ WebString::fromUTF8(range_info));
+ }
+
+ if (strcmp(method, "POST") == 0) {
+ // Adds headers or form data to a request. This must be called before
+ // we initiate the actual request.
+ SetPostData(&info.request, buf, buf_len);
+ }
+
+ SetReferrer(&info.request, referrer_flag);
+
+ // Sets the routing id to associate the ResourceRequest with the RenderView.
+ webframe_->dispatchWillSendRequest(info.request);
+ if (WebDevToolsAgent* devtools_agent = GetDevToolsAgent()) {
+ devtools_agent->identifierForInitialRequest(resource_id, webframe_,
+ info.request);
+ devtools_agent->willSendRequest(resource_id, info.request);
+ }
+
+ info.loader.reset(WebKit::webKitClient()->createURLLoader());
+ if (!info.loader.get())
+ return false;
+ info.loader->loadAsynchronously(info.request, this);
+
+ clients_.push_back(info);
+ return true;
+}
+
+void WebPluginImpl::CancelDocumentLoad() {
+ if (webframe_) {
+ ignore_response_error_ = true;
+ webframe_->stopLoading();
+ }
+}
+
+void WebPluginImpl::InitiateHTTPRangeRequest(
+ const char* url, const char* range_info, int range_request_id) {
+ unsigned long resource_id = GetNextResourceId();
+ if (!resource_id)
+ return;
+
+ GURL complete_url = CompleteURL(url);
+ // Remove when flash bug is fixed. http://crbug.com/40016.
+ if (!WebPluginImpl::IsValidUrl(complete_url,
+ load_manually_ ? NO_REFERRER : PLUGIN_SRC))
+ return;
+
+ WebPluginResourceClient* resource_client =
+ delegate_->CreateSeekableResourceClient(resource_id, range_request_id);
+ InitiateHTTPRequest(
+ resource_id, resource_client, complete_url, "GET", NULL, 0, range_info,
+ load_manually_ ? NO_REFERRER : PLUGIN_SRC);
+}
+
+void WebPluginImpl::SetDeferResourceLoading(unsigned long resource_id,
+ bool defer) {
+ std::vector<ClientInfo>::iterator client_index = clients_.begin();
+ while (client_index != clients_.end()) {
+ ClientInfo& client_info = *client_index;
+
+ if (client_info.id == resource_id) {
+ client_info.loader->setDefersLoading(defer);
+
+ // If we determined that the request had failed via the HTTP headers
+ // in the response then we send out a failure notification to the
+ // plugin process, as certain plugins don't handle HTTP failure codes
+ // correctly.
+ if (!defer && client_info.client &&
+ client_info.pending_failure_notification) {
+ // The ClientInfo and the iterator can become invalid due to the call
+ // to DidFail below.
+ WebPluginResourceClient* resource_client = client_info.client;
+ client_info.loader->cancel();
+ clients_.erase(client_index++);
+ resource_client->DidFail();
+
+ // Report that resource loading finished.
+ if (WebDevToolsAgent* devtools_agent = GetDevToolsAgent())
+ devtools_agent->didFinishLoading(resource_id);
+ }
+ break;
+ }
+ client_index++;
+ }
+}
+
+void WebPluginImpl::HandleHttpMultipartResponse(
+ const WebURLResponse& response, WebPluginResourceClient* client) {
+ std::string multipart_boundary;
+ if (!MultipartResponseDelegate::ReadMultipartBoundary(
+ response, &multipart_boundary)) {
+ NOTREACHED();
+ return;
+ }
+
+ if (page_delegate_)
+ page_delegate_->DidStartLoadingForPlugin();
+
+ MultiPartResponseClient* multi_part_response_client =
+ new MultiPartResponseClient(client);
+
+ MultipartResponseDelegate* multi_part_response_handler =
+ new MultipartResponseDelegate(multi_part_response_client, NULL,
+ response,
+ multipart_boundary);
+ multi_part_response_map_[client] = multi_part_response_handler;
+}
+
+bool WebPluginImpl::ReinitializePluginForResponse(
+ WebURLLoader* loader) {
+ WebFrame* webframe = webframe_;
+ if (!webframe)
+ return false;
+
+ WebView* webview = webframe->view();
+ if (!webview)
+ return false;
+
+ WebPluginContainer* container_widget = container_;
+
+ // Destroy the current plugin instance.
+ TearDownPluginInstance(loader);
+
+ container_ = container_widget;
+ webframe_ = webframe;
+
+ WebPluginDelegate* plugin_delegate = page_delegate_->CreatePluginDelegate(
+ file_path_, mime_type_);
+
+ bool ok = plugin_delegate && plugin_delegate->Initialize(
+ plugin_url_, arg_names_, arg_values_, this, load_manually_);
+
+ if (!ok) {
+ container_ = NULL;
+ // TODO(iyengar) Should we delete the current plugin instance here?
+ return false;
+ }
+
+ delegate_ = plugin_delegate;
+
+ // Force a geometry update to occur to ensure that the plugin becomes
+ // visible.
+ container_->reportGeometry();
+
+ // The plugin move sequences accumulated via DidMove are sent to the browser
+ // whenever the renderer paints. Force a paint here to ensure that changes
+ // to the plugin window are propagated to the browser.
+ container_->invalidate();
+ return true;
+}
+
+void WebPluginImpl::TearDownPluginInstance(
+ WebURLLoader* loader_to_ignore) {
+ // The container maintains a list of JSObjects which are related to this
+ // plugin. Tell the frame we're gone so that it can invalidate all of
+ // those sub JSObjects.
+ if (container_)
+ container_->clearScriptObjects();
+
+ if (delegate_) {
+ // Call PluginDestroyed() first to prevent the plugin from calling us back
+ // in the middle of tearing down the render tree.
+ delegate_->PluginDestroyed();
+ delegate_ = NULL;
+ }
+
+ // Cancel any pending requests because otherwise this deleted object will
+ // be called by the ResourceDispatcher.
+ std::vector<ClientInfo>::iterator client_index = clients_.begin();
+ while (client_index != clients_.end()) {
+ ClientInfo& client_info = *client_index;
+
+ if (loader_to_ignore == client_info.loader) {
+ client_index++;
+ continue;
+ }
+
+ if (client_info.loader.get())
+ client_info.loader->cancel();
+
+ client_index = clients_.erase(client_index);
+ }
+
+ // This needs to be called now and not in the destructor since the
+ // webframe_ might not be valid anymore.
+ webframe_ = NULL;
+ method_factory_.RevokeAll();
+}
+
+void WebPluginImpl::SetReferrer(WebKit::WebURLRequest* request,
+ Referrer referrer_flag) {
+ switch (referrer_flag) {
+ case DOCUMENT_URL:
+ webframe_->setReferrerForRequest(*request, GURL());
+ break;
+
+ case PLUGIN_SRC:
+ webframe_->setReferrerForRequest(*request, plugin_url_);
+ break;
+
+ default:
+ break;
+ }
+}
+
+WebDevToolsAgent* WebPluginImpl::GetDevToolsAgent() {
+ if (!webframe_)
+ return NULL;
+ WebView* view = webframe_->view();
+ if (!view)
+ return NULL;
+ return view->devToolsAgent();
+}
+
+} // namespace webkit_glue
diff --git a/webkit/glue/plugins/webplugin_impl.h b/webkit/glue/plugins/webplugin_impl.h
new file mode 100644
index 0000000..9b75e6e
--- /dev/null
+++ b/webkit/glue/plugins/webplugin_impl.h
@@ -0,0 +1,332 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_WEBPLUGIN_IMPL_H_
+#define WEBKIT_GLUE_WEBPLUGIN_IMPL_H_
+
+#include <string>
+#include <map>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/file_path.h"
+#include "base/linked_ptr.h"
+#include "base/task.h"
+#include "base/weak_ptr.h"
+#include "gfx/native_widget_types.h"
+#include "googleurl/src/gurl.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebPlugin.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebRect.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebString.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURLLoaderClient.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURLRequest.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebVector.h"
+#include "webkit/glue/plugins/webplugin.h"
+
+class WebViewDelegate;
+
+namespace WebKit {
+class WebDevToolsAgent;
+class WebFrame;
+class WebPluginContainer;
+class WebURLResponse;
+class WebURLLoader;
+class WebURLRequest;
+}
+
+namespace webkit_glue {
+
+class MultipartResponseDelegate;
+class WebPluginDelegate;
+class WebPluginPageDelegate;
+
+// This is the WebKit side of the plugin implementation that forwards calls,
+// after changing out of WebCore types, to a delegate. The delegate may
+// be in a different process.
+class WebPluginImpl : public WebPlugin,
+ public WebKit::WebPlugin,
+ public WebKit::WebURLLoaderClient {
+ public:
+ WebPluginImpl(
+ WebKit::WebFrame* frame,
+ const WebKit::WebPluginParams& params,
+ const FilePath& file_path,
+ const std::string& mime_type,
+ const base::WeakPtr<WebPluginPageDelegate>& page_delegate);
+ virtual ~WebPluginImpl();
+
+ // Helper function for sorting post data.
+ static bool SetPostData(WebKit::WebURLRequest* request,
+ const char* buf,
+ uint32 length);
+
+ virtual WebPluginDelegate* delegate() { return delegate_; }
+
+ private:
+ // WebKit::WebPlugin methods:
+ virtual bool initialize(
+ WebKit::WebPluginContainer* container);
+ virtual void destroy();
+ virtual NPObject* scriptableObject();
+ virtual void paint(
+ WebKit::WebCanvas* canvas, const WebKit::WebRect& paint_rect);
+ virtual void updateGeometry(
+ const WebKit::WebRect& frame_rect, const WebKit::WebRect& clip_rect,
+ const WebKit::WebVector<WebKit::WebRect>& cut_outs, bool is_visible);
+ virtual void updateFocus(bool focused);
+ virtual void updateVisibility(bool visible);
+ virtual bool acceptsInputEvents();
+ virtual bool handleInputEvent(
+ const WebKit::WebInputEvent& event, WebKit::WebCursorInfo& cursor_info);
+ virtual void didReceiveResponse(const WebKit::WebURLResponse& response);
+ virtual void didReceiveData(const char* data, int data_length);
+ virtual void didFinishLoading();
+ virtual void didFailLoading(const WebKit::WebURLError& error);
+ virtual void didFinishLoadingFrameRequest(
+ const WebKit::WebURL& url, void* notify_data);
+ virtual void didFailLoadingFrameRequest(
+ const WebKit::WebURL& url, void* notify_data,
+ const WebKit::WebURLError& error);
+ virtual bool supportsPaginatedPrint();
+ virtual int printBegin(const WebKit::WebRect& printable_area,
+ int printer_dpi);
+ virtual bool printPage(int page_number, WebKit::WebCanvas* canvas);
+ virtual void printEnd();
+ virtual bool hasSelection() const;
+ virtual WebKit::WebString selectionAsText() const;
+ virtual WebKit::WebString selectionAsMarkup() const;
+ virtual void setZoomFactor(float scale, bool text_only);
+ virtual bool startFind(const WebKit::WebString& search_text,
+ bool case_sensitive,
+ int identifier);
+ virtual void selectFindResult(bool forward);
+ virtual void stopFind();
+
+ // WebPlugin implementation:
+ void SetWindow(gfx::PluginWindowHandle window);
+ virtual void SetAcceptsInputEvents(bool accepts) {
+ accepts_input_events_ = accepts;
+ }
+ void WillDestroyWindow(gfx::PluginWindowHandle window);
+#if defined(OS_WIN)
+ void SetWindowlessPumpEvent(HANDLE pump_messages_event) { }
+#endif
+ virtual void CancelResource(unsigned long id);
+ virtual void Invalidate();
+ virtual void InvalidateRect(const gfx::Rect& rect);
+ virtual NPObject* GetWindowScriptNPObject();
+ virtual NPObject* GetPluginElement();
+ virtual void SetCookie(const GURL& url,
+ const GURL& first_party_for_cookies,
+ const std::string& cookie);
+ virtual std::string GetCookies(const GURL& url,
+ const GURL& first_party_for_cookies);
+ virtual void ShowModalHTMLDialog(const GURL& url, int width, int height,
+ const std::string& json_arguments,
+ std::string* json_retval);
+ virtual void OnMissingPluginStatus(int status);
+
+ // Given a (maybe partial) url, completes using the base url.
+ GURL CompleteURL(const char* url);
+
+ // Executes the script passed in. The notify_needed and notify_data arguments
+ // are passed in by the plugin process. These indicate whether the plugin
+ // expects a notification on script execution. We pass them back to the
+ // plugin as is. This avoids having to track the notification arguments in
+ // the plugin process.
+ bool ExecuteScript(const std::string& url, const std::wstring& script,
+ bool notify_needed, intptr_t notify_data,
+ bool popups_allowed);
+
+ enum RoutingStatus {
+ ROUTED,
+ NOT_ROUTED,
+ INVALID_URL,
+ GENERAL_FAILURE
+ };
+
+ // Determines the referrer value sent along with outgoing HTTP requests
+ // issued by plugins.
+ enum Referrer {
+ PLUGIN_SRC,
+ DOCUMENT_URL,
+ NO_REFERRER
+ };
+
+ // Given a download request, check if we need to route the output to a frame.
+ // Returns ROUTED if the load is done and routed to a frame, NOT_ROUTED or
+ // corresponding error codes otherwise.
+ RoutingStatus RouteToFrame(const char* url,
+ bool is_javascript_url,
+ const char* method,
+ const char* target,
+ const char* buf,
+ unsigned int len,
+ int notify_id,
+ Referrer referrer_flag);
+
+ // Returns the next avaiable resource id. Returns 0 if the operation fails.
+ // It may fail if the page has already been closed.
+ unsigned long GetNextResourceId();
+
+ // Initiates HTTP GET/POST requests.
+ // Returns true on success.
+ bool InitiateHTTPRequest(unsigned long resource_id,
+ WebPluginResourceClient* client,
+ const GURL& url,
+ const char* method,
+ const char* buf,
+ int len,
+ const char* range_info,
+ Referrer referrer_flag);
+
+ gfx::Rect GetWindowClipRect(const gfx::Rect& rect);
+
+ // Sets the actual Widget for the plugin.
+ void SetContainer(WebKit::WebPluginContainer* container);
+
+ // Destroys the plugin instance.
+ // The response_handle_to_ignore parameter if not NULL indicates the
+ // resource handle to be left valid during plugin shutdown.
+ void TearDownPluginInstance(WebKit::WebURLLoader* loader_to_ignore);
+
+ // WebURLLoaderClient implementation. We implement this interface in the
+ // renderer process, and then use the simple WebPluginResourceClient interface
+ // to relay the callbacks to the plugin.
+ virtual void willSendRequest(WebKit::WebURLLoader* loader,
+ WebKit::WebURLRequest& request,
+ const WebKit::WebURLResponse&);
+ virtual void didSendData(WebKit::WebURLLoader* loader,
+ unsigned long long bytes_sent,
+ unsigned long long total_bytes_to_be_sent);
+ virtual void didReceiveResponse(WebKit::WebURLLoader* loader,
+ const WebKit::WebURLResponse& response);
+ virtual void didReceiveData(WebKit::WebURLLoader* loader, const char *buffer,
+ int length);
+ virtual void didFinishLoading(WebKit::WebURLLoader* loader);
+ virtual void didFail(WebKit::WebURLLoader* loader,
+ const WebKit::WebURLError& error);
+
+ // Helper function to remove the stored information about a resource
+ // request given its index in m_clients.
+ void RemoveClient(size_t i);
+
+ // Helper function to remove the stored information about a resource
+ // request given a handle.
+ void RemoveClient(WebKit::WebURLLoader* loader);
+
+ void HandleURLRequest(const char* url,
+ const char *method,
+ const char* target,
+ const char* buf,
+ unsigned int len,
+ int notify_id,
+ bool popups_allowed);
+
+ void CancelDocumentLoad();
+
+ void InitiateHTTPRangeRequest(
+ const char* url, const char* range_info, int pending_request_id);
+
+ void SetDeferResourceLoading(unsigned long resource_id, bool defer);
+
+ // Ignore in-process plugins mode for this flag.
+ bool IsOffTheRecord() { return false; }
+
+ // Handles HTTP multipart responses, i.e. responses received with a HTTP
+ // status code of 206.
+ void HandleHttpMultipartResponse(const WebKit::WebURLResponse& response,
+ WebPluginResourceClient* client);
+
+ void HandleURLRequestInternal(const char* url,
+ const char* method,
+ const char* target,
+ const char* buf,
+ unsigned int len,
+ int notify_id,
+ bool popups_allowed,
+ Referrer referrer_flag);
+
+ // Tears down the existing plugin instance and creates a new plugin instance
+ // to handle the response identified by the loader parameter.
+ bool ReinitializePluginForResponse(WebKit::WebURLLoader* loader);
+
+ // Delayed task for downloading the plugin source URL.
+ void OnDownloadPluginSrcUrl();
+
+ struct ClientInfo {
+ unsigned long id;
+ WebPluginResourceClient* client;
+ WebKit::WebURLRequest request;
+ bool pending_failure_notification;
+ linked_ptr<WebKit::WebURLLoader> loader;
+ };
+
+ // Helper functions
+ WebPluginResourceClient* GetClientFromLoader(WebKit::WebURLLoader* loader);
+ ClientInfo* GetClientInfoFromLoader(WebKit::WebURLLoader* loader);
+
+ // Helper function to set the referrer on the request passed in.
+ void SetReferrer(WebKit::WebURLRequest* request, Referrer referrer_flag);
+
+ // Returns DevToolsAgent for the frame or 0.
+ WebKit::WebDevToolsAgent* GetDevToolsAgent();
+
+ // Check for invalid chars like @, ;, \ before the first / (in path).
+ bool IsValidUrl(const GURL& url, Referrer referrer_flag);
+
+ std::vector<ClientInfo> clients_;
+
+ bool windowless_;
+ gfx::PluginWindowHandle window_;
+ bool accepts_input_events_;
+ base::WeakPtr<WebPluginPageDelegate> page_delegate_;
+ WebKit::WebFrame* webframe_;
+
+ WebPluginDelegate* delegate_;
+
+ // This is just a weak reference.
+ WebKit::WebPluginContainer* container_;
+
+ typedef std::map<WebPluginResourceClient*,
+ webkit_glue::MultipartResponseDelegate*>
+ MultiPartResponseHandlerMap;
+ // Tracks HTTP multipart response handlers instantiated for
+ // a WebPluginResourceClient instance.
+ MultiPartResponseHandlerMap multi_part_response_map_;
+
+ // The plugin source URL.
+ GURL plugin_url_;
+
+ // Indicates if the download would be initiated by the plugin or us.
+ bool load_manually_;
+
+ // Indicates if this is the first geometry update received by the plugin.
+ bool first_geometry_update_;
+
+ // Set to true if the next response error should be ignored.
+ bool ignore_response_error_;
+
+ // The current plugin geometry and clip rectangle.
+ WebPluginGeometry geometry_;
+
+ // The location of the plugin on disk.
+ FilePath file_path_;
+
+ // The mime type of the plugin.
+ std::string mime_type_;
+
+ // Holds the list of argument names and values passed to the plugin. We keep
+ // these so that we can re-initialize the plugin if we need to.
+ std::vector<std::string> arg_names_;
+ std::vector<std::string> arg_values_;
+
+ ScopedRunnableMethodFactory<WebPluginImpl> method_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(WebPluginImpl);
+};
+
+} // namespace webkit_glue
+
+#endif // #ifndef WEBKIT_GLUE_WEBPLUGIN_IMPL_H_
diff --git a/webkit/glue/plugins/webplugin_impl_unittest.cc b/webkit/glue/plugins/webplugin_impl_unittest.cc
new file mode 100644
index 0000000..e70e39a
--- /dev/null
+++ b/webkit/glue/plugins/webplugin_impl_unittest.cc
@@ -0,0 +1,232 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/string_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebCString.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebString.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURLRequest.h"
+#include "webkit/glue/plugins/webplugin_impl.h"
+
+using WebKit::WebHTTPBody;
+using WebKit::WebString;
+using WebKit::WebURLRequest;
+using webkit_glue::WebPluginImpl;
+
+namespace {
+
+class WebPluginImplTest : public testing::Test {
+};
+
+}
+
+static std::string GetHeader(const WebURLRequest& request, const char* name) {
+ std::string result;
+ TrimWhitespace(
+ request.httpHeaderField(WebString::fromUTF8(name)).utf8(),
+ TRIM_ALL,
+ &result);
+ return result;
+}
+
+static std::string GetBodyText(const WebURLRequest& request) {
+ const WebHTTPBody& body = request.httpBody();
+ if (body.isNull())
+ return std::string();
+
+ std::string result;
+ size_t i = 0;
+ WebHTTPBody::Element element;
+ while (body.elementAt(i++, element)) {
+ if (element.type == WebHTTPBody::Element::TypeData) {
+ result.append(element.data.data(), element.data.size());
+ } else {
+ NOTREACHED() << "unexpected element type encountered!";
+ }
+ }
+ return result;
+}
+
+// The Host functions for NPN_PostURL and NPN_PostURLNotify
+// need to parse out some HTTP headers. Make sure it works
+// with the following tests
+
+TEST(WebPluginImplTest, PostParserSimple) {
+ // Test a simple case with headers & data
+ const char *ex1 = "foo: bar\nContent-length: 10\n\nabcdefghij";
+ WebURLRequest request;
+ request.initialize();
+ bool rv = WebPluginImpl::SetPostData(&request, ex1,
+ static_cast<uint32>(strlen(ex1)));
+ EXPECT_EQ(true, rv);
+ EXPECT_EQ("bar", GetHeader(request, "foo"));
+ EXPECT_EQ(0U, GetHeader(request, "bar").length());
+ EXPECT_EQ(0U, GetHeader(request, "Content-length").length());
+ EXPECT_EQ("abcdefghij", GetBodyText(request));
+}
+
+TEST(WebPluginImplTest, PostParserLongHeader) {
+ // Test a simple case with long headers
+ const char *ex1 = "foo: 0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789\n\nabcdefghij";
+ WebURLRequest request;
+ request.initialize();
+ bool rv = WebPluginImpl::SetPostData(&request, ex1,
+ static_cast<uint32>(strlen(ex1)));
+ EXPECT_EQ(true, rv);
+ EXPECT_EQ(100U, GetHeader(request, "foo").length());
+}
+
+TEST(WebPluginImplTest, PostParserManyHeaders) {
+ // Test a simple case with long headers
+ const char *ex1 = "h1:h1\nh2:h2\nh3:h3\nh4:h4\nh5:h5\nh6:h6\nh7:h7\nh8:h8\nh9:h9\nh10:h10\n\nbody";
+ WebURLRequest request;
+ request.initialize();
+ bool rv = WebPluginImpl::SetPostData(&request, ex1,
+ static_cast<uint32>(strlen(ex1)));
+ EXPECT_EQ(true, rv);
+ EXPECT_EQ("h1", GetHeader(request, "h1"));
+ EXPECT_EQ("h2", GetHeader(request, "h2"));
+ EXPECT_EQ("h3", GetHeader(request, "h3"));
+ EXPECT_EQ("h4", GetHeader(request, "h4"));
+ EXPECT_EQ("h5", GetHeader(request, "h5"));
+ EXPECT_EQ("h6", GetHeader(request, "h6"));
+ EXPECT_EQ("h7", GetHeader(request, "h7"));
+ EXPECT_EQ("h8", GetHeader(request, "h8"));
+ EXPECT_EQ("h9", GetHeader(request, "h9"));
+ EXPECT_EQ("h10", GetHeader(request, "h10"));
+ EXPECT_EQ("body", GetBodyText(request));
+}
+
+TEST(WebPluginImplTest, PostParserDuplicateHeaders) {
+ // Test a simple case with long headers
+ // What value gets returned doesn't really matter. It shouldn't error
+ // out.
+ const char *ex1 = "h1:h1\nh1:h2\n\nbody";
+ WebURLRequest request;
+ request.initialize();
+ bool rv = WebPluginImpl::SetPostData(&request, ex1,
+ static_cast<uint32>(strlen(ex1)));
+ EXPECT_EQ(true, rv);
+}
+
+TEST(WebPluginImplTest, PostParserNoHeaders) {
+ // Test a simple case with no headers but with data
+ const char *ex1 = "\nabcdefghij";
+ WebURLRequest request;
+ request.initialize();
+ bool rv = WebPluginImpl::SetPostData(&request, ex1,
+ static_cast<uint32>(strlen(ex1)));
+ EXPECT_EQ(true, rv);
+ EXPECT_EQ(0U, GetHeader(request, "foo").length());
+ EXPECT_EQ(0U, GetHeader(request, "bar").length());
+ EXPECT_EQ(0U, GetHeader(request, "Content-length").length());
+ EXPECT_EQ("abcdefghij", GetBodyText(request));
+}
+
+TEST(WebPluginImplTest, PostParserNoBody) {
+ // Test a simple case with headers and no body
+ const char *ex1 = "Foo:bar\n\n";
+ WebURLRequest request;
+ request.initialize();
+ bool rv = WebPluginImpl::SetPostData(&request, ex1,
+ static_cast<uint32>(strlen(ex1)));
+ EXPECT_EQ(true, rv);
+ EXPECT_EQ("bar", GetHeader(request, "foo"));
+ EXPECT_EQ(0U, GetHeader(request, "bar").length());
+ EXPECT_EQ(0U, GetHeader(request, "Content-length").length());
+ EXPECT_EQ(0U, GetBodyText(request).length());
+}
+
+TEST(WebPluginImplTest, PostParserBodyWithNewLines) {
+ // Test a simple case with headers and no body
+ const char *ex1 = "Foo:bar\n\n\n\nabcdefg\n\nabcdefg";
+ WebURLRequest request;
+ request.initialize();
+ bool rv = WebPluginImpl::SetPostData(&request, ex1,
+ static_cast<uint32>(strlen(ex1)));
+ EXPECT_EQ(true, rv);
+ EXPECT_EQ(GetBodyText(request), "\n\nabcdefg\n\nabcdefg");
+}
+
+TEST(WebPluginImplTest, PostParserErrorNoBody) {
+ // Test with headers and no body
+ const char *ex1 = "Foo:bar\n";
+ WebURLRequest request;
+ request.initialize();
+ bool rv = WebPluginImpl::SetPostData(&request, ex1,
+ static_cast<uint32>(strlen(ex1)));
+ EXPECT_EQ(true, rv);
+}
+
+TEST(WebPluginImplTest, PostParserErrorEmpty) {
+ // Test with an empty string
+ const char *ex1 = "";
+ WebURLRequest request;
+ request.initialize();
+ bool rv = WebPluginImpl::SetPostData(&request, ex1,
+ static_cast<uint32>(strlen(ex1)));
+ EXPECT_EQ(true, rv);
+}
+
+TEST(WebPluginImplTest, PostParserEmptyName) {
+ // Test an error case with an empty header name field
+ const char *ex1 = "foo:bar\n:blat\n\nbody";
+ WebURLRequest request;
+ request.initialize();
+ bool rv = WebPluginImpl::SetPostData(&request, ex1,
+ static_cast<uint32>(strlen(ex1)));
+ EXPECT_EQ(true, rv);
+ EXPECT_EQ("bar", GetHeader(request, "foo"));
+ EXPECT_EQ("body", GetBodyText(request));
+}
+
+TEST(WebPluginImplTest, PostParserEmptyValue) {
+ // Test an error case with an empty value field
+ const char *ex1 = "foo:bar\nbar:\n\nbody";
+ WebURLRequest request;
+ request.initialize();
+ bool rv = WebPluginImpl::SetPostData(&request, ex1,
+ static_cast<uint32>(strlen(ex1)));
+ EXPECT_EQ(true, rv);
+ EXPECT_EQ("bar", GetHeader(request, "foo"));
+ EXPECT_EQ(0U, GetHeader(request, "bar").length());
+ EXPECT_EQ("body", GetBodyText(request));
+}
+
+TEST(WebPluginImplTest, PostParserCRLF) {
+ // Test an error case with an empty value field
+ const char *ex1 = "foo: bar\r\nbar:\r\n\r\nbody\r\n\r\nbody2";
+ WebURLRequest request;
+ request.initialize();
+ bool rv = WebPluginImpl::SetPostData(&request, ex1,
+ static_cast<uint32>(strlen(ex1)));
+ EXPECT_EQ(true, rv);
+ EXPECT_EQ("bar", GetHeader(request, "foo"));
+ EXPECT_EQ(0U, GetHeader(request, "bar").length());
+ EXPECT_EQ("body\r\n\r\nbody2", GetBodyText(request));
+}
+
+TEST(WebPluginImplTest, PostParserBodyWithBinaryData) {
+ // Test a simple case with headers and binary data.
+ char ex1[33] = "foo: bar\nContent-length: 10\n\n";
+ unsigned int binary_data = 0xFFFFFFF0;
+ memcpy(ex1 + strlen("foo: bar\nContent-length: 10\n\n"), &binary_data,
+ sizeof(binary_data));
+
+ WebURLRequest request;
+ request.initialize();
+ bool rv = WebPluginImpl::SetPostData(&request, ex1,
+ sizeof(ex1)/sizeof(ex1[0]));
+ EXPECT_EQ(true, rv);
+ EXPECT_EQ("bar", GetHeader(request, "foo"));
+ EXPECT_EQ(0U, GetHeader(request, "bar").length());
+ EXPECT_EQ(0U, GetHeader(request, "Content-length").length());
+
+ std::string body = GetBodyText(request);
+
+ EXPECT_EQ(0xF0, (unsigned char)body[0]);
+ EXPECT_EQ(0xFF, (unsigned char)body[1]);
+ EXPECT_EQ(0xFF, (unsigned char)body[2]);
+ EXPECT_EQ(0xFF, (unsigned char)body[3]);
+}
diff --git a/webkit/glue/plugins/webplugin_page_delegate.h b/webkit/glue/plugins/webplugin_page_delegate.h
new file mode 100644
index 0000000..d915fdd
--- /dev/null
+++ b/webkit/glue/plugins/webplugin_page_delegate.h
@@ -0,0 +1,69 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_WEBPLUGIN_PAGE_DELEGATE_
+#define WEBKIT_GLUE_WEBPLUGIN_PAGE_DELEGATE_
+
+#include "gfx/native_widget_types.h"
+
+class FilePath;
+class GURL;
+
+namespace WebKit {
+class WebCookieJar;
+}
+
+namespace webkit_glue {
+
+class WebPluginDelegate;
+struct WebPluginGeometry;
+
+// Used by the WebPlugin to communicate back to the containing page.
+class WebPluginPageDelegate {
+ public:
+ // This method is called to create a WebPluginDelegate implementation when a
+ // new plugin is instanced. See webkit_glue::CreateWebPluginDelegateHelper
+ // for a default WebPluginDelegate implementation.
+ virtual WebPluginDelegate* CreatePluginDelegate(
+ const FilePath& file_path,
+ const std::string& mime_type) = 0;
+
+ // Called when a windowed plugin is created.
+ // Lets the view delegate create anything it is using to wrap the plugin.
+ virtual void CreatedPluginWindow(
+ gfx::PluginWindowHandle handle) = 0;
+
+ // Called when a windowed plugin is closing.
+ // Lets the view delegate shut down anything it is using to wrap the plugin.
+ virtual void WillDestroyPluginWindow(
+ gfx::PluginWindowHandle handle) = 0;
+
+ // Keeps track of the necessary window move for a plugin window that resulted
+ // from a scroll operation. That way, all plugin windows can be moved at the
+ // same time as each other and the page.
+ virtual void DidMovePlugin(
+ const WebPluginGeometry& move) = 0;
+
+ // Notifies the parent view that a load has begun.
+ virtual void DidStartLoadingForPlugin() = 0;
+
+ // Notifies the parent view that all loads are finished.
+ virtual void DidStopLoadingForPlugin() = 0;
+
+ // Asks the browser to show a modal HTML dialog. The dialog is passed the
+ // given arguments as a JSON string, and returns its result as a JSON string
+ // through json_retval.
+ virtual void ShowModalHTMLDialogForPlugin(
+ const GURL& url,
+ const gfx::Size& size,
+ const std::string& json_arguments,
+ std::string* json_retval) = 0;
+
+ // The WebCookieJar to use for this plugin.
+ virtual WebKit::WebCookieJar* GetCookieJar() = 0;
+};
+
+} // namespace webkit_glue
+
+#endif // WEBKIT_GLUE_WEBPLUGIN_PAGE_DELEGATE_H_
diff --git a/webkit/glue/plugins/webplugin_print_delegate.h b/webkit/glue/plugins/webplugin_print_delegate.h
new file mode 100644
index 0000000..040e58f
--- /dev/null
+++ b/webkit/glue/plugins/webplugin_print_delegate.h
@@ -0,0 +1,49 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_WEBPLUGIN_PRINT_DELEGATE_H_
+#define WEBKIT_GLUE_PLUGINS_WEBPLUGIN_PRINT_DELEGATE_H_
+
+#include "base/basictypes.h"
+#include "third_party/npapi/bindings/npapi_extensions.h"
+
+namespace gfx {
+class Rect;
+}
+
+namespace webkit_glue {
+
+// Interface for the NPAPI print extension. This class implements "NOP"
+// versions of all these functions so it can be used seamlessly by the
+// "regular" plugin delegate while being overridden by the "pepper" one.
+class WebPluginPrintDelegate {
+ public:
+ // If a plugin supports print extensions, then it gets to participate fully
+ // in the browser's print workflow by specifying the number of pages to be
+ // printed and providing a print output for specified pages.
+ virtual bool PrintSupportsPrintExtension() {
+ return false;
+ }
+
+ // Note: printable_area is in points (a point is 1/72 of an inch).
+ virtual int PrintBegin(const gfx::Rect& printable_area, int printer_dpi) {
+ return 0;
+ }
+
+ virtual bool PrintPage(int page_number, WebKit::WebCanvas* canvas) {
+ return false;
+ }
+
+ virtual void PrintEnd() {
+ }
+
+ protected:
+ WebPluginPrintDelegate() {}
+ virtual ~WebPluginPrintDelegate() {}
+};
+
+} // namespace webkit_glue
+
+#endif // WEBKIT_GLUE_PLUGINS_WEBPLUGIN_PRINT_DELEGATE_H_
+
diff --git a/webkit/glue/plugins/webplugininfo.h b/webkit/glue/plugins/webplugininfo.h
new file mode 100644
index 0000000..9d2ab6e
--- /dev/null
+++ b/webkit/glue/plugins/webplugininfo.h
@@ -0,0 +1,47 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_WEBPLUGININFO_H_
+#define WEBKIT_GLUE_WEBPLUGININFO_H_
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/file_path.h"
+
+// Describes a mime type entry for a plugin.
+struct WebPluginMimeType {
+ // The name of the mime type (e.g., "application/x-shockwave-flash").
+ std::string mime_type;
+
+ // A list of all the file extensions for this mime type.
+ std::vector<std::string> file_extensions;
+
+ // Description of the mime type.
+ string16 description;
+};
+
+// Describes an available NPAPI plugin.
+struct WebPluginInfo {
+ // The name of the plugin (i.e. Flash).
+ string16 name;
+
+ // The path to the plugin file (DLL/bundle/library).
+ FilePath path;
+
+ // The version number of the plugin file (may be OS-specific)
+ string16 version;
+
+ // A description of the plugin that we get from its version info.
+ string16 desc;
+
+ // A list of all the mime types that this plugin supports.
+ std::vector<WebPluginMimeType> mime_types;
+
+ // Whether the plugin is enabled.
+ bool enabled;
+};
+
+#endif // WEBKIT_GLUE_WEBPLUGININFO_H_
diff --git a/webkit/glue/plugins/webview_plugin.cc b/webkit/glue/plugins/webview_plugin.cc
new file mode 100644
index 0000000..413ae10
--- /dev/null
+++ b/webkit/glue/plugins/webview_plugin.cc
@@ -0,0 +1,142 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/webview_plugin.h"
+
+#include "base/message_loop.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebCursorInfo.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebFrame.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebPluginContainer.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebSettings.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebSize.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURL.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURLRequest.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebView.h"
+
+#if WEBKIT_USING_CG
+#include <CoreGraphics/CGContext.h>
+#elif WEBKIT_USING_SKIA
+#include "skia/ext/platform_canvas.h"
+#endif
+
+using WebKit::WebCanvas;
+using WebKit::WebCursorInfo;
+using WebKit::WebDragData;
+using WebKit::WebDragOperationsMask;
+using WebKit::WebFrame;
+using WebKit::WebImage;
+using WebKit::WebInputEvent;
+using WebKit::WebPluginContainer;
+using WebKit::WebPoint;
+using WebKit::WebRect;
+using WebKit::WebSize;
+using WebKit::WebURLError;
+using WebKit::WebURLRequest;
+using WebKit::WebVector;
+using WebKit::WebView;
+
+WebViewPlugin::WebViewPlugin(WebViewPlugin::Delegate* delegate)
+ : delegate_(delegate),
+ container_(NULL) {
+ web_view_ = WebView::create(this, NULL);
+ web_view_->initializeMainFrame(this);
+}
+
+WebViewPlugin::~WebViewPlugin() {
+ web_view_->close();
+}
+
+bool WebViewPlugin::initialize(WebPluginContainer* container) {
+ container_ = container;
+ return true;
+}
+
+void WebViewPlugin::destroy() {
+ delegate_->WillDestroyPlugin();
+ delegate_ = NULL;
+ MessageLoop::current()->DeleteSoon(FROM_HERE, this);
+}
+
+void WebViewPlugin::paint(WebCanvas* canvas, const WebRect& rect) {
+ gfx::Rect paintRect(rect_.Intersect(rect));
+ if (paintRect.IsEmpty())
+ return;
+
+ paintRect.Offset(-rect_.x(), -rect_.y());
+
+#if WEBKIT_USING_CG
+ CGContextRef context = canvas;
+ CGContextTranslateCTM(context, rect_.x(), rect_.y());
+ CGContextSaveGState(context);
+#elif WEBKIT_USING_SKIA
+ skia::PlatformCanvas* platform_canvas = canvas;
+ platform_canvas->translate(SkIntToScalar(rect_.x()),
+ SkIntToScalar(rect_.y()));
+ platform_canvas->save();
+#endif
+
+ web_view_->layout();
+ web_view_->paint(canvas, paintRect);
+
+#if WEBKIT_USING_SKIA
+ platform_canvas->restore();
+#elif WEBKIT_USING_CG
+ CGContextRestoreGState(context);
+#endif
+}
+
+// Coordinates are relative to the containing window.
+void WebViewPlugin::updateGeometry(
+ const WebRect& frame_rect, const WebRect& clip_rect,
+ const WebVector<WebRect>& cut_out_rects, bool is_visible) {
+ if (frame_rect != rect_) {
+ rect_ = frame_rect;
+ web_view_->resize(WebSize(frame_rect.width, frame_rect.height));
+ }
+}
+
+bool WebViewPlugin::handleInputEvent(const WebInputEvent& event,
+ WebCursorInfo& cursor) {
+ current_cursor_ = cursor;
+ bool handled = web_view_->handleInputEvent(event);
+ cursor = current_cursor_;
+ return handled;
+}
+
+void WebViewPlugin::startDragging(const WebDragData&,
+ WebDragOperationsMask,
+ const WebImage&,
+ const WebPoint&) {
+ // Immediately stop dragging.
+ web_view_->dragSourceSystemDragEnded();
+}
+
+void WebViewPlugin::didInvalidateRect(const WebRect& rect) {
+ if (container_)
+ container_->invalidateRect(WebRect(rect));
+}
+
+void WebViewPlugin::didChangeCursor(const WebCursorInfo& cursor) {
+ current_cursor_ = cursor;
+}
+
+void WebViewPlugin::didClearWindowObject(WebFrame* frame) {
+ delegate_->BindWebFrame(frame);
+}
+
+bool WebViewPlugin::canHandleRequest(WebFrame* frame,
+ const WebURLRequest& request) {
+ return GURL(request.url()).SchemeIs("chrome");
+}
+
+WebURLError WebViewPlugin::cancelledError(WebFrame* frame,
+ const WebURLRequest& request) {
+ // Return an error with a non-zero reason so isNull() on the corresponding
+ // ResourceError is false.
+ WebURLError error;
+ error.domain = "WebViewPlugin";
+ error.reason = -1;
+ error.unreachableURL = request.url();
+ return error;
+}
diff --git a/webkit/glue/plugins/webview_plugin.h b/webkit/glue/plugins/webview_plugin.h
new file mode 100644
index 0000000..2e41218
--- /dev/null
+++ b/webkit/glue/plugins/webview_plugin.h
@@ -0,0 +1,107 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_WEBVIEW_PLUGIN_H_
+#define WEBKIT_GLUE_PLUGINS_WEBVIEW_PLUGIN_H_
+
+#include "base/scoped_ptr.h"
+#include "base/task.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebCursorInfo.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebPlugin.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebFrameClient.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebViewClient.h"
+
+// This class implements the WebPlugin interface by forwarding drawing and
+// handling input events to a WebView.
+// It can be used as a placeholder for an actual plugin, using HTML for the UI.
+// To show HTML data inside the WebViewPlugin,
+// call web_view->mainFrame()->loadHTMLString() with the HTML data and a fake
+// chrome:// URL as origin.
+
+class WebViewPlugin: public WebKit::WebPlugin, public WebKit::WebViewClient,
+ public WebKit::WebFrameClient {
+ public:
+ class Delegate {
+ public:
+ // Bind |frame| to a Javascript object, enabling the delegate to receive
+ // callback methods from Javascript inside the WebFrame.
+ // This method is called from WebFrameClient::didClearWindowObject.
+ virtual void BindWebFrame(WebKit::WebFrame* frame) = 0;
+
+ // Called before the WebViewPlugin is destroyed. The delegate should delete
+ // itself here.
+ virtual void WillDestroyPlugin() = 0;
+ };
+
+ explicit WebViewPlugin(Delegate* delegate);
+
+ virtual WebKit::WebView* web_view() { return web_view_; }
+
+ virtual WebKit::WebPluginContainer* container() { return container_; }
+
+ // WebPlugin methods:
+ virtual bool initialize(WebKit::WebPluginContainer*);
+ virtual void destroy();
+
+ virtual NPObject* scriptableObject() { return NULL; }
+
+ virtual void paint(WebKit::WebCanvas* canvas, const WebKit::WebRect& rect);
+
+ // Coordinates are relative to the containing window.
+ virtual void updateGeometry(
+ const WebKit::WebRect& frame_rect, const WebKit::WebRect& clip_rect,
+ const WebKit::WebVector<WebKit::WebRect>& cut_out_rects, bool is_visible);
+
+ virtual void updateFocus(bool) { }
+ virtual void updateVisibility(bool) { }
+
+ virtual bool acceptsInputEvents() { return true; }
+ virtual bool handleInputEvent(const WebKit::WebInputEvent& event,
+ WebKit::WebCursorInfo& cursor_info);
+
+ virtual void didReceiveResponse(const WebKit::WebURLResponse& response) { }
+ virtual void didReceiveData(const char* data, int data_length) { }
+ virtual void didFinishLoading() { }
+ virtual void didFailLoading(const WebKit::WebURLError& error) { }
+
+ // Called in response to WebPluginContainer::loadFrameRequest
+ virtual void didFinishLoadingFrameRequest(
+ const WebKit::WebURL& url, void* notifyData) { }
+ virtual void didFailLoadingFrameRequest(const WebKit::WebURL& url,
+ void* notify_data,
+ const WebKit::WebURLError& error) { }
+
+ // WebViewClient methods:
+ virtual bool acceptsLoadDrops() { return false; }
+
+ virtual void startDragging(const WebKit::WebDragData& drag_data,
+ WebKit::WebDragOperationsMask mask,
+ const WebKit::WebImage& image,
+ const WebKit::WebPoint& point);
+
+ // WebWidgetClient methods:
+ virtual void didInvalidateRect(const WebKit::WebRect&);
+ virtual void didChangeCursor(const WebKit::WebCursorInfo& cursor);
+
+ // WebFrameClient methods:
+ virtual void didClearWindowObject(WebKit::WebFrame* frame);
+
+ virtual bool canHandleRequest(WebKit::WebFrame* frame,
+ const WebKit::WebURLRequest& request);
+
+ virtual WebKit::WebURLError cancelledError(
+ WebKit::WebFrame* frame, const WebKit::WebURLRequest& request);
+
+ private:
+ friend class DeleteTask<WebViewPlugin>;
+ virtual ~WebViewPlugin();
+
+ Delegate* delegate_;
+ WebKit::WebCursorInfo current_cursor_;
+ WebKit::WebPluginContainer* container_;
+ WebKit::WebView* web_view_;
+ gfx::Rect rect_;
+};
+
+#endif // WEBKIT_GLUE_PLUGINS_WEBVIEW_PLUGIN_H_
diff --git a/webkit/glue/regular_expression_unittest.cc b/webkit/glue/regular_expression_unittest.cc
new file mode 100644
index 0000000..39741f8
--- /dev/null
+++ b/webkit/glue/regular_expression_unittest.cc
@@ -0,0 +1,105 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebRegularExpression.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebString.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebTextCaseSensitivity.h"
+
+using namespace WebKit;
+
+namespace {
+
+class RegexTest : public testing::Test {
+};
+
+struct Match {
+ const WebUChar* text;
+ const int textLength;
+ const int matchPosition;
+ const int matchLength;
+};
+
+void testMatches(const WebRegularExpression& regex,
+ const Match* matches,
+ const size_t nMatches) {
+
+ for (size_t i = 0; i < nMatches; ++i) {
+ int matchedLength = matches[i].textLength;
+ EXPECT_EQ(matches[i].matchPosition, regex.match(
+ WebString(matches[i].text, matches[i].textLength), 0, &matchedLength));
+ if (matches[i].matchPosition != -1)
+ EXPECT_EQ(matches[i].matchLength, matchedLength);
+ }
+
+}
+
+} // namespace
+
+#define MATCH_DESC(webuchar, matchPosition, matchLength) \
+ { webuchar, arraysize(webuchar), matchPosition, matchLength }
+
+
+TEST(RegexTest, Basic) {
+ // Just make sure we're not completely broken.
+ WebRegularExpression regex("the quick brown fox", WebTextCaseSensitive);
+ EXPECT_EQ(0, regex.match("the quick brown fox"));
+ EXPECT_EQ(1, regex.match(" the quick brown fox"));
+ EXPECT_EQ(3, regex.match("foothe quick brown foxbar"));
+
+ EXPECT_EQ(-1, regex.match("The quick brown FOX"));
+ EXPECT_EQ(-1, regex.match("the quick brown fo"));
+}
+
+TEST(RegexTest, Unicode) {
+ // Make sure we get the right offsets for unicode strings.
+ WebUChar pattern[] = {L'\x6240', L'\x6709', L'\x7f51', L'\x9875'};
+ WebRegularExpression regex(WebString(pattern, arraysize(pattern)),
+ WebTextCaseInsensitive);
+
+ WebUChar text1[] = {L'\x6240', L'\x6709', L'\x7f51', L'\x9875'};
+ WebUChar text2[] = {L' ', L'\x6240', L'\x6709', L'\x7f51', L'\x9875'};
+ WebUChar text3[] = {L'f', L'o', L'o', L'\x6240', L'\x6709', L'\x7f51', L'\x9875', L'b', L'a', L'r'};
+ WebUChar text4[] = {L'\x4e2d', L'\x6587', L'\x7f51', L'\x9875', L'\x6240', L'\x6709', L'\x7f51', L'\x9875'};
+
+ const Match matches[] = {
+ MATCH_DESC(text1, 0, 4),
+ MATCH_DESC(text2, 1, 4),
+ MATCH_DESC(text3, 3, 4),
+ MATCH_DESC(text4, 4, 4),
+ };
+
+ testMatches(regex, matches, arraysize(matches));
+}
+
+TEST(RegexTest, UnicodeMixedLength) {
+ WebUChar pattern[] = {L':', L'[', L' ', L'\x2000', L']', L'+', L':'};
+ WebRegularExpression regex(WebString(pattern, arraysize(pattern)),
+ WebTextCaseInsensitive);
+
+ WebUChar text1[] = {L':', L' ', L' ', L':'};
+ WebUChar text2[] = {L' ', L' ', L':', L' ', L' ', L' ', L' ', L':', L' ', L' '};
+ WebUChar text3[] = {L' ', L':', L' ', L'\x2000', L' ', L':', L' '};
+ WebUChar text4[] = {L'\x6240', L'\x6709', L'\x7f51', L'\x9875', L' ', L':', L' ', L'\x2000', L' ', L'\x2000', L' ', L':', L' '};
+ WebUChar text5[] = {L' '};
+ WebUChar text6[] = {L':', L':'};
+
+ const Match matches[] = {
+ MATCH_DESC(text1, 0, 4),
+ MATCH_DESC(text2, 2, 6),
+ MATCH_DESC(text3, 1, 5),
+ MATCH_DESC(text4, 5, 7),
+ MATCH_DESC(text5, -1, -1),
+ MATCH_DESC(text6, -1, -1),
+ };
+
+ testMatches(regex, matches, arraysize(matches));
+}
+
+TEST(RegexTest, EmptyMatch) {
+ WebRegularExpression regex("|x", WebTextCaseInsensitive);
+ int matchedLength = 0;
+ EXPECT_EQ(0, regex.match("", 0, &matchedLength));
+ EXPECT_EQ(0, matchedLength);
+}
diff --git a/webkit/glue/resource_fetcher.cc b/webkit/glue/resource_fetcher.cc
new file mode 100644
index 0000000..186092b
--- /dev/null
+++ b/webkit/glue/resource_fetcher.cc
@@ -0,0 +1,129 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/resource_fetcher.h"
+
+#include "base/logging.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebFrame.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebKit.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebKitClient.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURLError.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURLLoader.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURLRequest.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURL.h"
+
+using base::TimeDelta;
+using WebKit::WebFrame;
+using WebKit::WebURLError;
+using WebKit::WebURLLoader;
+using WebKit::WebURLRequest;
+using WebKit::WebURLResponse;
+
+namespace webkit_glue {
+
+ResourceFetcher::ResourceFetcher(const GURL& url, WebFrame* frame,
+ Callback* c)
+ : url_(url),
+ callback_(c),
+ completed_(false) {
+ // Can't do anything without a frame. However, delegate can be NULL (so we
+ // can do a http request and ignore the results).
+ DCHECK(frame);
+ Start(frame);
+}
+
+ResourceFetcher::~ResourceFetcher() {
+ if (!completed_ && loader_.get())
+ loader_->cancel();
+}
+
+void ResourceFetcher::Cancel() {
+ if (!completed_) {
+ loader_->cancel();
+ completed_ = true;
+ }
+}
+
+void ResourceFetcher::Start(WebFrame* frame) {
+ WebURLRequest request(url_);
+ frame->dispatchWillSendRequest(request);
+
+ loader_.reset(WebKit::webKitClient()->createURLLoader());
+ loader_->loadAsynchronously(request, this);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// WebURLLoaderClient methods
+
+void ResourceFetcher::willSendRequest(
+ WebURLLoader* loader, WebURLRequest& new_request,
+ const WebURLResponse& redirect_response) {
+}
+
+void ResourceFetcher::didSendData(
+ WebURLLoader* loader, unsigned long long bytes_sent,
+ unsigned long long total_bytes_to_be_sent) {
+}
+
+void ResourceFetcher::didReceiveResponse(
+ WebURLLoader* loader, const WebURLResponse& response) {
+ DCHECK(!completed_);
+ response_ = response;
+}
+
+void ResourceFetcher::didReceiveData(
+ WebURLLoader* loader, const char* data, int data_length) {
+ DCHECK(!completed_);
+ DCHECK(data_length > 0);
+
+ data_.append(data, data_length);
+}
+
+void ResourceFetcher::didReceiveCachedMetadata(
+ WebURLLoader* loader, const char* data, int data_length) {
+ DCHECK(!completed_);
+ DCHECK(data_length > 0);
+
+ metadata_.assign(data, data_length);
+}
+
+void ResourceFetcher::didFinishLoading(WebURLLoader* loader) {
+ DCHECK(!completed_);
+ completed_ = true;
+
+ if (callback_.get()) {
+ callback_->Run(response_, data_);
+ callback_.reset();
+ }
+}
+
+void ResourceFetcher::didFail(WebURLLoader* loader, const WebURLError& error) {
+ DCHECK(!completed_);
+ completed_ = true;
+
+ // Go ahead and tell our delegate that we're done.
+ if (callback_.get()) {
+ callback_->Run(WebURLResponse(), std::string());
+ callback_.reset();
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// A resource fetcher with a timeout
+
+ResourceFetcherWithTimeout::ResourceFetcherWithTimeout(
+ const GURL& url, WebFrame* frame, int timeout_secs, Callback* c)
+ : ResourceFetcher(url, frame, c) {
+ timeout_timer_.Start(TimeDelta::FromSeconds(timeout_secs), this,
+ &ResourceFetcherWithTimeout::TimeoutFired);
+}
+
+void ResourceFetcherWithTimeout::TimeoutFired() {
+ if (!completed_) {
+ loader_->cancel();
+ didFail(NULL, WebURLError());
+ }
+}
+
+} // namespace webkit_glue
diff --git a/webkit/glue/resource_fetcher.h b/webkit/glue/resource_fetcher.h
new file mode 100644
index 0000000..7910fc1
--- /dev/null
+++ b/webkit/glue/resource_fetcher.h
@@ -0,0 +1,117 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// A wrapper around ResourceHandle and ResourceHandleClient that simplifies
+// the download of an HTTP object. The interface is modeled after URLFetcher
+// in the /chrome/browser.
+//
+// ResourceFetcher::Delegate::OnURLFetchComplete will be called async after
+// the ResourceFetcher object is created.
+
+#ifndef WEBKIT_GLUE_RESOURCE_FETCHER_H_
+#define WEBKIT_GLUE_RESOURCE_FETCHER_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/scoped_ptr.h"
+#include "base/timer.h"
+#include "googleurl/src/gurl.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURLLoaderClient.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURLResponse.h"
+
+class GURL;
+
+namespace WebKit {
+class WebFrame;
+class WebURLLoader;
+class WebURLRequest;
+struct WebURLError;
+}
+
+namespace webkit_glue {
+
+class ResourceFetcher : public WebKit::WebURLLoaderClient {
+ public:
+ // This will be called when the URL has been fetched, successfully or not.
+ // If there is a failure, response and data will both be empty. |response|
+ // and |data| are both valid until the URLFetcher instance is destroyed.
+ typedef Callback2<const WebKit::WebURLResponse&,
+ const std::string&>::Type Callback;
+
+ // We need a frame to make requests.
+ ResourceFetcher(
+ const GURL& url, WebKit::WebFrame* frame, Callback* callback);
+ ~ResourceFetcher();
+
+ // Stop the request and don't call the callback.
+ void Cancel();
+
+ bool completed() const { return completed_; }
+
+ protected:
+ // WebURLLoaderClient methods:
+ virtual void willSendRequest(
+ WebKit::WebURLLoader* loader, WebKit::WebURLRequest& new_request,
+ const WebKit::WebURLResponse& redirect_response);
+ virtual void didSendData(
+ WebKit::WebURLLoader* loader, unsigned long long bytes_sent,
+ unsigned long long total_bytes_to_be_sent);
+ virtual void didReceiveResponse(
+ WebKit::WebURLLoader* loader, const WebKit::WebURLResponse& response);
+ virtual void didReceiveCachedMetadata(
+ WebKit::WebURLLoader* loader, const char* data, int data_length);
+ virtual void didReceiveData(
+ WebKit::WebURLLoader* loader, const char* data, int data_length);
+ virtual void didFinishLoading(WebKit::WebURLLoader* loader);
+ virtual void didFail(
+ WebKit::WebURLLoader* loader, const WebKit::WebURLError& error);
+
+ scoped_ptr<WebKit::WebURLLoader> loader_;
+
+ // URL we're fetching
+ GURL url_;
+
+ // Callback when we're done
+ scoped_ptr<Callback> callback_;
+
+ // A copy of the original resource response
+ WebKit::WebURLResponse response_;
+
+ // Set to true once the request is compelte.
+ bool completed_;
+
+ private:
+ // Start the actual download.
+ void Start(WebKit::WebFrame* frame);
+
+ // Buffer to hold the content from the server.
+ std::string data_;
+
+ // Buffer to hold metadata from the cache.
+ std::string metadata_;
+};
+
+/////////////////////////////////////////////////////////////////////////////
+// A resource fetcher with a timeout
+class ResourceFetcherWithTimeout : public ResourceFetcher {
+ public:
+ ResourceFetcherWithTimeout(const GURL& url, WebKit::WebFrame* frame,
+ int timeout_secs, Callback* c);
+ virtual ~ResourceFetcherWithTimeout() {}
+
+ private:
+ // Callback for timer that limits how long we wait for the alternate error
+ // page server. If this timer fires and the request hasn't completed, we
+ // kill the request.
+ void TimeoutFired();
+
+ // Limit how long we wait for the alternate error page server.
+ base::OneShotTimer<ResourceFetcherWithTimeout> timeout_timer_;
+};
+
+} // namespace webkit_glue
+
+#endif // WEBKIT_GLUE_RESOURCE_FETCHER_H_
diff --git a/webkit/glue/resource_fetcher_unittest.cc b/webkit/glue/resource_fetcher_unittest.cc
new file mode 100644
index 0000000..98cef0a
--- /dev/null
+++ b/webkit/glue/resource_fetcher_unittest.cc
@@ -0,0 +1,234 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/resource_fetcher.h"
+
+#include "base/callback.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebFrame.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURLResponse.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebView.h"
+#include "webkit/glue/unittest_test_server.h"
+#include "webkit/tools/test_shell/simple_resource_loader_bridge.h"
+#include "webkit/tools/test_shell/test_shell_test.h"
+
+#if defined(TOOLKIT_USES_GTK)
+#include <gtk/gtk.h>
+#endif
+
+using WebKit::WebFrame;
+using WebKit::WebURLResponse;
+using webkit_glue::ResourceFetcher;
+using webkit_glue::ResourceFetcherWithTimeout;
+
+namespace {
+
+class ResourceFetcherTests : public TestShellTest {
+ public:
+ void SetUp() {
+ TestShellTest::SetUp();
+ }
+ void TearDown() {
+ TestShellTest::TearDown();
+ }
+};
+
+static const int kMaxWaitTimeMs = 5000;
+static const int kWaitIntervalMs = 100;
+
+class FetcherDelegate {
+ public:
+ FetcherDelegate()
+ : timer_id_(0), completed_(false), time_elapsed_ms_(0) {
+ // Start a repeating timer waiting for the download to complete. The
+ // callback has to be a static function, so we hold on to our instance.
+ FetcherDelegate::instance_ = this;
+ CreateTimer(kWaitIntervalMs);
+ }
+
+ ResourceFetcher::Callback* NewCallback() {
+ return ::NewCallback(this, &FetcherDelegate::OnURLFetchComplete);
+ }
+
+ void OnURLFetchComplete(const WebURLResponse& response,
+ const std::string& data) {
+ response_ = response;
+ data_ = data;
+ completed_ = true;
+ DestroyTimer();
+ MessageLoop::current()->Quit();
+ }
+
+ bool completed() const { return completed_; }
+ bool timed_out() const { return time_elapsed_ms_ > kMaxWaitTimeMs; }
+
+ int time_elapsed_ms() const { return time_elapsed_ms_; }
+ std::string data() const { return data_; }
+ const WebURLResponse& response() const { return response_; }
+
+ // Wait for the request to complete or timeout. We use a loop here b/c the
+ // testing infrastructure (test_shell) can generate spurious calls to the
+ // MessageLoop's Quit method.
+ void WaitForResponse() {
+ while (!completed() && !timed_out())
+ MessageLoop::current()->Run();
+ }
+
+ void CreateTimer(int interval) {
+#if defined(OS_WIN)
+ timer_id_ = ::SetTimer(NULL, NULL, interval,
+ &FetcherDelegate::TimerCallback);
+#elif defined(TOOLKIT_USES_GTK)
+ timer_id_ = g_timeout_add(interval, &FetcherDelegate::TimerCallback, NULL);
+#elif defined(OS_MACOSX)
+ // CFAbsoluteTime is in seconds and |interval| is in ms, so make sure we
+ // keep the units correct.
+ CFTimeInterval interval_in_seconds = static_cast<double>(interval) / 1000.0;
+ CFAbsoluteTime fire_date =
+ CFAbsoluteTimeGetCurrent() + interval_in_seconds;
+ timer_id_ = CFRunLoopTimerCreate(NULL, fire_date, interval_in_seconds, 0,
+ 0, FetcherDelegate::TimerCallback, NULL);
+ CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer_id_, kCFRunLoopCommonModes);
+#endif
+ }
+
+ void DestroyTimer() {
+#if defined(OS_WIN)
+ ::KillTimer(NULL, timer_id_);
+#elif defined(TOOLKIT_USES_GTK)
+ g_source_remove(timer_id_);
+#elif defined(OS_MACOSX)
+ CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), timer_id_,
+ kCFRunLoopCommonModes);
+ CFRelease(timer_id_);
+#endif
+ }
+
+#if defined(OS_WIN)
+ // Static timer callback, just passes through to instance version.
+ static VOID CALLBACK TimerCallback(HWND hwnd, UINT msg, UINT_PTR timer_id,
+ DWORD ms) {
+ instance_->TimerFired();
+ }
+#elif defined(TOOLKIT_USES_GTK)
+ static gboolean TimerCallback(gpointer data) {
+ instance_->TimerFired();
+ return true;
+ }
+#elif defined(OS_MACOSX)
+ static void TimerCallback(CFRunLoopTimerRef timer, void* info) {
+ instance_->TimerFired();
+ }
+#endif
+
+ void TimerFired() {
+ ASSERT_FALSE(completed_);
+
+ if (timed_out()) {
+ DestroyTimer();
+ MessageLoop::current()->Quit();
+ FAIL() << "fetch timed out";
+ return;
+ }
+
+ time_elapsed_ms_ += kWaitIntervalMs;
+ }
+
+ static FetcherDelegate* instance_;
+
+ private:
+#if defined(OS_WIN)
+ UINT_PTR timer_id_;
+#elif defined(TOOLKIT_USES_GTK)
+ guint timer_id_;
+#elif defined(OS_MACOSX)
+ CFRunLoopTimerRef timer_id_;
+#endif
+ bool completed_;
+ int time_elapsed_ms_;
+ WebURLResponse response_;
+ std::string data_;
+};
+
+FetcherDelegate* FetcherDelegate::instance_ = NULL;
+
+// Test a fetch from the test server.
+TEST_F(ResourceFetcherTests, ResourceFetcherDownload) {
+ scoped_refptr<UnittestTestServer> server =
+ UnittestTestServer::CreateServer();
+ ASSERT_TRUE(NULL != server.get());
+
+ WebFrame* frame = test_shell_->webView()->mainFrame();
+
+ GURL url = server->TestServerPage("files/test_shell/index.html");
+ scoped_ptr<FetcherDelegate> delegate(new FetcherDelegate);
+ scoped_ptr<ResourceFetcher> fetcher(new ResourceFetcher(
+ url, frame, delegate->NewCallback()));
+
+ delegate->WaitForResponse();
+
+ ASSERT_TRUE(delegate->completed());
+ EXPECT_EQ(delegate->response().httpStatusCode(), 200);
+ std::string text = delegate->data();
+ EXPECT_TRUE(text.find("What is this page?") != std::string::npos);
+
+ // Test 404 response.
+ url = server->TestServerPage("files/thisfiledoesntexist.html");
+ delegate.reset(new FetcherDelegate);
+ fetcher.reset(new ResourceFetcher(url, frame, delegate->NewCallback()));
+
+ delegate->WaitForResponse();
+
+ ASSERT_TRUE(delegate->completed());
+ EXPECT_EQ(delegate->response().httpStatusCode(), 404);
+ EXPECT_TRUE(delegate->data().find("Not Found.") != std::string::npos);
+}
+
+TEST_F(ResourceFetcherTests, ResourceFetcherDidFail) {
+ scoped_refptr<UnittestTestServer> server =
+ UnittestTestServer::CreateServer();
+ ASSERT_TRUE(NULL != server.get());
+
+ WebFrame* frame = test_shell_->webView()->mainFrame();
+
+ // Try to fetch a page on a site that doesn't exist.
+ GURL url("http://localhost:1339/doesnotexist");
+ scoped_ptr<FetcherDelegate> delegate(new FetcherDelegate);
+ scoped_ptr<ResourceFetcher> fetcher(new ResourceFetcher(
+ url, frame, delegate->NewCallback()));
+
+ delegate->WaitForResponse();
+
+ // When we fail, we still call the Delegate callback but we pass in empty
+ // values.
+ EXPECT_TRUE(delegate->completed());
+ EXPECT_TRUE(delegate->response().isNull());
+ EXPECT_EQ(delegate->data(), std::string());
+ EXPECT_TRUE(delegate->time_elapsed_ms() < kMaxWaitTimeMs);
+}
+
+TEST_F(ResourceFetcherTests, ResourceFetcherTimeout) {
+ scoped_refptr<UnittestTestServer> server =
+ UnittestTestServer::CreateServer();
+ ASSERT_TRUE(NULL != server.get());
+
+ WebFrame* frame = test_shell_->webView()->mainFrame();
+
+ // Grab a page that takes at least 1 sec to respond, but set the fetcher to
+ // timeout in 0 sec.
+ GURL url = server->TestServerPage("slow?1");
+ scoped_ptr<FetcherDelegate> delegate(new FetcherDelegate);
+ scoped_ptr<ResourceFetcher> fetcher(new ResourceFetcherWithTimeout(
+ url, frame, 0, delegate->NewCallback()));
+
+ delegate->WaitForResponse();
+
+ // When we timeout, we still call the Delegate callback but we pass in empty
+ // values.
+ EXPECT_TRUE(delegate->completed());
+ EXPECT_TRUE(delegate->response().isNull());
+ EXPECT_EQ(delegate->data(), std::string());
+ EXPECT_TRUE(delegate->time_elapsed_ms() < kMaxWaitTimeMs);
+}
+
+} // namespace
diff --git a/webkit/glue/resource_loader_bridge.cc b/webkit/glue/resource_loader_bridge.cc
new file mode 100644
index 0000000..8845256
--- /dev/null
+++ b/webkit/glue/resource_loader_bridge.cc
@@ -0,0 +1,68 @@
+// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/resource_loader_bridge.h"
+
+#include "webkit/appcache/appcache_interfaces.h"
+#include "net/http/http_response_headers.h"
+
+namespace webkit_glue {
+
+ResourceLoaderBridge::RequestInfo::RequestInfo()
+ : load_flags(0),
+ requestor_pid(0),
+ request_type(ResourceType::MAIN_FRAME),
+ request_context(0),
+ appcache_host_id(0),
+ routing_id(0) {
+}
+
+ResourceLoaderBridge::RequestInfo::~RequestInfo() {
+}
+
+ResourceLoaderBridge::LoadTimingInfo::LoadTimingInfo() {
+ proxy_start = -1;
+ proxy_end = -1;
+ dns_start = -1;
+ dns_end = -1;
+ connect_start = -1;
+ connect_end = -1;
+ ssl_start = -1;
+ ssl_end = -1;
+ send_start = 0;
+ send_end = 0;
+ receive_headers_start = 0;
+ receive_headers_end = 0;
+}
+
+ResourceLoaderBridge::LoadTimingInfo::~LoadTimingInfo() {
+}
+
+ResourceLoaderBridge::ResponseInfo::ResponseInfo() {
+ content_length = -1;
+ appcache_id = appcache::kNoCacheId;
+ was_fetched_via_spdy = false;
+ was_npn_negotiated = false;
+ connection_id = 0;
+ connection_reused = false;
+ was_alternate_protocol_available = false;
+ was_fetched_via_proxy = false;
+}
+
+ResourceLoaderBridge::ResponseInfo::~ResponseInfo() {
+}
+
+ResourceLoaderBridge::SyncLoadResponse::SyncLoadResponse() {
+}
+
+ResourceLoaderBridge::SyncLoadResponse::~SyncLoadResponse() {
+}
+
+ResourceLoaderBridge::ResourceLoaderBridge() {
+}
+
+ResourceLoaderBridge::~ResourceLoaderBridge() {
+}
+
+} // namespace webkit_glue
diff --git a/webkit/glue/resource_loader_bridge.h b/webkit/glue/resource_loader_bridge.h
new file mode 100644
index 0000000..e66181f
--- /dev/null
+++ b/webkit/glue/resource_loader_bridge.h
@@ -0,0 +1,345 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// The intent of this file is to provide a type-neutral abstraction between
+// Chrome and WebKit for resource loading. This pure-virtual interface is
+// implemented by the embedder, which also provides a factory method Create
+// to instantiate this object.
+//
+// One of these objects will be created by WebKit for each request. WebKit
+// will own the pointer to the bridge, and will delete it when the request is
+// no longer needed.
+//
+// In turn, the bridge's owner on the WebKit end will implement the Peer
+// interface, which we will use to communicate notifications back.
+
+#ifndef WEBKIT_GLUE_RESOURCE_LOADER_BRIDGE_H_
+#define WEBKIT_GLUE_RESOURCE_LOADER_BRIDGE_H_
+
+#include "build/build_config.h"
+#if defined(OS_POSIX)
+#include "base/file_descriptor_posix.h"
+#endif
+#include "base/platform_file.h"
+#include "base/ref_counted.h"
+#include "base/time.h"
+#include "googleurl/src/gurl.h"
+#include "net/url_request/url_request_status.h"
+#include "webkit/glue/resource_type.h"
+
+namespace net {
+class HttpResponseHeaders;
+}
+
+class FilePath;
+
+namespace webkit_glue {
+
+class ResourceLoaderBridge {
+ public:
+ // Structure used when calling ResourceLoaderBridge::Create().
+ struct RequestInfo {
+ RequestInfo();
+ ~RequestInfo();
+
+ // HTTP-style method name (e.g., "GET" or "POST").
+ std::string method;
+
+ // Absolute URL encoded in ASCII per the rules of RFC-2396.
+ GURL url;
+
+ // URL of the document in the top-level window, which may be checked by the
+ // third-party cookie blocking policy.
+ GURL first_party_for_cookies;
+
+ // Optional parameter, a URL with similar constraints in how it must be
+ // encoded as the url member.
+ GURL referrer;
+
+ std::string frame_origin;
+ std::string main_frame_origin;
+
+ // For HTTP(S) requests, the headers parameter can be a \r\n-delimited and
+ // \r\n-terminated list of MIME headers. They should be ASCII-encoded using
+ // the standard MIME header encoding rules. The headers parameter can also
+ // be null if no extra request headers need to be set.
+ std::string headers;
+
+ // Composed of the values defined in url_request_load_flags.h.
+ int load_flags;
+
+ // Process id of the process making the request.
+ int requestor_pid;
+
+ // Indicates if the current request is the main frame load, a sub-frame
+ // load, or a sub objects load.
+ ResourceType::Type request_type;
+
+ // Used for plugin to browser requests.
+ uint32 request_context;
+
+ // Identifies what appcache host this request is associated with.
+ int appcache_host_id;
+
+ // Used to associated the bridge with a frame's network context.
+ int routing_id;
+ };
+
+ // Structure containing timing information for the request. It addresses
+ // http://groups.google.com/group/http-archive-specification/web/har-1-1-spec
+ // and http://dev.w3.org/2006/webapi/WebTiming/ needs.
+ //
+ // All the values for starts and ends are given in milliseconds and are
+ // offsets with respect to the given base time.
+ struct LoadTimingInfo {
+ LoadTimingInfo();
+ ~LoadTimingInfo();
+
+ // All the values in this struct are given as offsets in milliseconds wrt
+ // this base time.
+ base::Time base_time;
+
+ // The time that proxy processing started. For requests with no proxy phase,
+ // this time is -1.
+ int32 proxy_start;
+
+ // The time that proxy processing ended. For reused sockets this time
+ // is -1.
+ int32 proxy_end;
+
+ // The time that DNS lookup started. For reused sockets this time is -1.
+ int32 dns_start;
+
+ // The time that DNS lookup ended. For reused sockets this time is -1.
+ int32 dns_end;
+
+ // The time that establishing connection started. For reused sockets
+ // this time is -1. Connect time includes dns time.
+ int32 connect_start;
+
+ // The time that establishing connection ended. For reused sockets this
+ // time is -1. Connect time includes dns time.
+ int32 connect_end;
+
+ // The time at which SSL handshake started. For non-HTTPS requests this
+ // is 0.
+ int32 ssl_start;
+
+ // The time at which SSL handshake ended. For non-HTTPS requests this is 0.
+ int32 ssl_end;
+
+ // The time that HTTP request started. For non-HTTP requests this is 0.
+ int32 send_start;
+
+ // The time that HTTP request ended. For non-HTTP requests this is 0.
+ int32 send_end;
+
+ // The time at which receiving HTTP headers started. For non-HTTP requests
+ // this is 0.
+ int32 receive_headers_start;
+
+ // The time at which receiving HTTP headers ended. For non-HTTP requests
+ // this is 0.
+ int32 receive_headers_end;
+ };
+
+ struct ResponseInfo {
+ ResponseInfo();
+ ~ResponseInfo();
+
+ // The time at which the request was made that resulted in this response.
+ // For cached responses, this time could be "far" in the past.
+ base::Time request_time;
+
+ // The time at which the response headers were received. For cached
+ // responses, this time could be "far" in the past.
+ base::Time response_time;
+
+ // The response headers or NULL if the URL type does not support headers.
+ scoped_refptr<net::HttpResponseHeaders> headers;
+
+ // The mime type of the response. This may be a derived value.
+ std::string mime_type;
+
+ // The character encoding of the response or none if not applicable to the
+ // response's mime type. This may be a derived value.
+ std::string charset;
+
+ // An opaque string carrying security information pertaining to this
+ // response. This may include information about the SSL connection used.
+ std::string security_info;
+
+ // Content length if available. -1 if not available
+ int64 content_length;
+
+ // The appcache this response was loaded from, or kNoCacheId.
+ int64 appcache_id;
+
+ // The manifest url of the appcache this response was loaded from.
+ // Note: this value is only populated for main resource requests.
+ GURL appcache_manifest_url;
+
+ // Connection identifier from the underlying network stack. In case there
+ // is no associated connection, contains 0.
+ uint32 connection_id;
+
+ // Determines whether physical connection reused.
+ bool connection_reused;
+
+ // Detailed timing information used by the WebTiming, HAR and Developer
+ // Tools.
+ LoadTimingInfo load_timing;
+
+ // True if the response was delivered using SPDY.
+ bool was_fetched_via_spdy;
+
+ // True if the response was delivered after NPN is negotiated.
+ bool was_npn_negotiated;
+
+ // True if response could use alternate protocol. However, browser will
+ // ignore the alternate protocol when spdy is not enabled on browser side.
+ bool was_alternate_protocol_available;
+
+ // True if the response was fetched via an explicit proxy (as opposed to a
+ // transparent proxy). The proxy could be any type of proxy, HTTP or SOCKS.
+ // Note: we cannot tell if a transparent proxy may have been involved.
+ bool was_fetched_via_proxy;
+ };
+
+ // See the SyncLoad method declared below. (The name of this struct is not
+ // suffixed with "Info" because it also contains the response data.)
+ struct SyncLoadResponse : ResponseInfo {
+ SyncLoadResponse();
+ ~SyncLoadResponse();
+
+ // The response status.
+ URLRequestStatus status;
+
+ // The final URL of the response. This may differ from the request URL in
+ // the case of a server redirect.
+ GURL url;
+
+ // The response data.
+ std::string data;
+ };
+
+ // Generated by the bridge. This is implemented by our custom resource loader
+ // within webkit. The Peer and it's bridge should have identical lifetimes
+ // as they represent each end of a communication channel.
+ //
+ // These callbacks mirror URLRequest::Delegate and the order and conditions
+ // in which they will be called are identical. See url_request.h for more
+ // information.
+ class Peer {
+ public:
+ virtual ~Peer() {}
+
+ // Called as upload progress is made.
+ // note: only for requests with LOAD_ENABLE_UPLOAD_PROGRESS set
+ virtual void OnUploadProgress(uint64 position, uint64 size) = 0;
+
+ // Called when a redirect occurs. The implementation may return false to
+ // suppress the redirect. The given ResponseInfo provides complete
+ // information about the redirect, and new_url is the URL that will be
+ // loaded if this method returns true. If this method returns true, the
+ // output parameter *has_new_first_party_for_cookies indicates whether the
+ // output parameter *new_first_party_for_cookies contains the new URL that
+ // should be consulted for the third-party cookie blocking policy.
+ virtual bool OnReceivedRedirect(const GURL& new_url,
+ const ResponseInfo& info,
+ bool* has_new_first_party_for_cookies,
+ GURL* new_first_party_for_cookies) = 0;
+
+ // Called when response headers are available (after all redirects have
+ // been followed). |content_filtered| is set to true if the contents is
+ // altered or replaced (usually for security reasons when the resource is
+ // deemed unsafe).
+ virtual void OnReceivedResponse(const ResponseInfo& info,
+ bool content_filtered) = 0;
+
+ // Called when a chunk of response data is available. This method may
+ // be called multiple times or not at all if an error occurs.
+ virtual void OnReceivedData(const char* data, int len) = 0;
+
+ // Called when metadata generated by the renderer is retrieved from the
+ // cache. This method may be called zero or one times.
+ virtual void OnReceivedCachedMetadata(const char* data, int len) { }
+
+ // Called when the response is complete. This method signals completion of
+ // the resource load.ff
+ virtual void OnCompletedRequest(const URLRequestStatus& status,
+ const std::string& security_info) = 0;
+
+ // Returns the URL of the request, which allows us to display it in
+ // debugging situations.
+ virtual GURL GetURLForDebugging() const = 0;
+ };
+
+ // use Create() for construction, but anybody can delete at any time,
+ // INCLUDING during processing of callbacks.
+ virtual ~ResourceLoaderBridge();
+
+ // Call this method to make a new instance.
+ //
+ // For HTTP(S) POST requests, the AppendDataToUpload and AppendFileToUpload
+ // methods may be called to construct the body of the request.
+ static ResourceLoaderBridge* Create(const RequestInfo& request_info);
+
+ // Call this method before calling Start() to append a chunk of binary data
+ // to the request body. May only be used with HTTP(S) POST requests.
+ virtual void AppendDataToUpload(const char* data, int data_len) = 0;
+
+ // Call this method before calling Start() to append the contents of a file
+ // to the request body. May only be used with HTTP(S) POST requests.
+ void AppendFileToUpload(const FilePath& file_path) {
+ AppendFileRangeToUpload(file_path, 0, kuint64max, base::Time());
+ }
+
+ // Call this method before calling Start() to append the contents of a file
+ // to the request body. May only be used with HTTP(S) POST requests.
+ virtual void AppendFileRangeToUpload(
+ const FilePath& file_path,
+ uint64 offset,
+ uint64 length,
+ const base::Time& expected_modification_time) = 0;
+
+ // Call this method before calling Start() to assign an upload identifier to
+ // this request. This is used to enable caching of POST responses. A value
+ // of 0 implies the unspecified identifier.
+ virtual void SetUploadIdentifier(int64 identifier) = 0;
+
+ // Call this method to initiate the request. If this method succeeds, then
+ // the peer's methods will be called asynchronously to report various events.
+ virtual bool Start(Peer* peer) = 0;
+
+ // Call this method to cancel a request that is in progress. This method
+ // causes the request to immediately transition into the 'done' state. The
+ // OnCompletedRequest method will be called asynchronously; this assumes
+ // the peer is still valid.
+ virtual void Cancel() = 0;
+
+ // Call this method to suspend or resume a load that is in progress. This
+ // method may only be called after a successful call to the Start method.
+ virtual void SetDefersLoading(bool value) = 0;
+
+ // Call this method to load the resource synchronously (i.e., in one shot).
+ // This is an alternative to the Start method. Be warned that this method
+ // will block the calling thread until the resource is fully downloaded or an
+ // error occurs. It could block the calling thread for a long time, so only
+ // use this if you really need it! There is also no way for the caller to
+ // interrupt this method. Errors are reported via the status field of the
+ // response parameter.
+ virtual void SyncLoad(SyncLoadResponse* response) = 0;
+
+ protected:
+ // construction must go through Create()
+ ResourceLoaderBridge();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ResourceLoaderBridge);
+};
+
+} // namespace webkit_glue
+
+#endif // WEBKIT_GLUE_RESOURCE_LOADER_BRIDGE_H_
diff --git a/webkit/glue/resource_type.h b/webkit/glue/resource_type.h
new file mode 100644
index 0000000..aa4a072
--- /dev/null
+++ b/webkit/glue/resource_type.h
@@ -0,0 +1,59 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_RESOURCE_TYPE_H__
+#define WEBKIT_GLUE_RESOURCE_TYPE_H__
+
+#include "base/basictypes.h"
+
+class ResourceType {
+ public:
+ enum Type {
+ MAIN_FRAME = 0, // top level page
+ SUB_FRAME, // frame or iframe
+ STYLESHEET, // a CSS stylesheet
+ SCRIPT, // an external script
+ IMAGE, // an image (jpg/gif/png/etc)
+ FONT_RESOURCE, // a font
+ SUB_RESOURCE, // an "other" subresource.
+ OBJECT, // an object (or embed) tag for a plugin,
+ // or a resource that a plugin requested.
+ MEDIA, // a media resource.
+ WORKER, // the main resource of a dedicated worker.
+ SHARED_WORKER, // the main resource of a shared worker.
+ LAST_TYPE // Place holder so we don't need to change ValidType
+ // everytime.
+ };
+
+ static bool ValidType(int32 type) {
+ return type >= MAIN_FRAME && type < LAST_TYPE;
+ }
+
+ static Type FromInt(int32 type) {
+ return static_cast<Type>(type);
+ }
+
+ static bool IsFrame(ResourceType::Type type) {
+ return type == MAIN_FRAME || type == SUB_FRAME;
+ }
+
+ static bool IsSharedWorker(ResourceType::Type type) {
+ return type == SHARED_WORKER;
+ }
+
+ static bool IsSubresource(ResourceType::Type type) {
+ return type == STYLESHEET ||
+ type == SCRIPT ||
+ type == IMAGE ||
+ type == FONT_RESOURCE ||
+ type == SUB_RESOURCE ||
+ type == WORKER;
+ }
+
+ private:
+ // Don't instantiate this class.
+ ResourceType();
+ ~ResourceType();
+};
+#endif // WEBKIT_GLUE_RESOURCE_TYPE_H__
diff --git a/webkit/glue/resources/README.txt b/webkit/glue/resources/README.txt
new file mode 100644
index 0000000..c5aaa7a
--- /dev/null
+++ b/webkit/glue/resources/README.txt
@@ -0,0 +1,97 @@
+The files listed below and found in this directory are copied from the
+Mozilla source tree, where they are licensed under the MPL/GPL/LGPL. While
+binary files do not contain a license block, I have pasted the relevant
+Mozilla license block below.
+
+aliasb.cur
+broken-image.gif
+cell.cur
+col_resize.cur
+copy.cur
+grab.cur
+grabbing.cur
+row_resize.cur
+vertical_text.cur
+zoom_in.cur
+zoom_out.cur
+
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/////////////////////////////////////////////////////////////////////////////
+
+The files listed below and found in this directory are copied from the WebKit
+source tree or are derivative works of files from the WebKit source tree.
+They are licensed under a BSD license. While binary files do not contain
+a license block, I have pasted the relevant WebKit license block below.
+
+pan_icon.png
+pan_east.cur
+pan_middle.cur
+pan_north.cur
+pan_north_east.cur
+pan_north_west.cur
+pan_south.cur
+pan_south_east.cur
+pan_south_west.cur
+pan_west.cur
+textarea_resize_corner.png
+
+// ***** BEGIN LICENSE BLOCK *****
+
+Copyright (C) 2005 Apple Computer, Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ its contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/webkit/glue/resources/aliasb.cur b/webkit/glue/resources/aliasb.cur
new file mode 100644
index 0000000..8d9ac94
--- /dev/null
+++ b/webkit/glue/resources/aliasb.cur
Binary files differ
diff --git a/webkit/glue/resources/broken-image.gif b/webkit/glue/resources/broken-image.gif
new file mode 100644
index 0000000..735e10c
--- /dev/null
+++ b/webkit/glue/resources/broken-image.gif
Binary files differ
diff --git a/webkit/glue/resources/cell.cur b/webkit/glue/resources/cell.cur
new file mode 100644
index 0000000..decfbdc
--- /dev/null
+++ b/webkit/glue/resources/cell.cur
Binary files differ
diff --git a/webkit/glue/resources/col_resize.cur b/webkit/glue/resources/col_resize.cur
new file mode 100644
index 0000000..8f7f675
--- /dev/null
+++ b/webkit/glue/resources/col_resize.cur
Binary files differ
diff --git a/webkit/glue/resources/copy.cur b/webkit/glue/resources/copy.cur
new file mode 100644
index 0000000..87f1519
--- /dev/null
+++ b/webkit/glue/resources/copy.cur
Binary files differ
diff --git a/webkit/glue/resources/dash.png b/webkit/glue/resources/dash.png
new file mode 100644
index 0000000..1747604
--- /dev/null
+++ b/webkit/glue/resources/dash.png
Binary files differ
diff --git a/webkit/glue/resources/linux-checkbox-disabled-indeterminate.png b/webkit/glue/resources/linux-checkbox-disabled-indeterminate.png
new file mode 100644
index 0000000..171d001
--- /dev/null
+++ b/webkit/glue/resources/linux-checkbox-disabled-indeterminate.png
Binary files differ
diff --git a/webkit/glue/resources/linux-checkbox-disabled-off.png b/webkit/glue/resources/linux-checkbox-disabled-off.png
new file mode 100644
index 0000000..c916674
--- /dev/null
+++ b/webkit/glue/resources/linux-checkbox-disabled-off.png
Binary files differ
diff --git a/webkit/glue/resources/linux-checkbox-disabled-on.png b/webkit/glue/resources/linux-checkbox-disabled-on.png
new file mode 100644
index 0000000..d9ff1c6
--- /dev/null
+++ b/webkit/glue/resources/linux-checkbox-disabled-on.png
Binary files differ
diff --git a/webkit/glue/resources/linux-checkbox-indeterminate.png b/webkit/glue/resources/linux-checkbox-indeterminate.png
new file mode 100644
index 0000000..dcde3c1
--- /dev/null
+++ b/webkit/glue/resources/linux-checkbox-indeterminate.png
Binary files differ
diff --git a/webkit/glue/resources/linux-checkbox-off.png b/webkit/glue/resources/linux-checkbox-off.png
new file mode 100644
index 0000000..15cc949
--- /dev/null
+++ b/webkit/glue/resources/linux-checkbox-off.png
Binary files differ
diff --git a/webkit/glue/resources/linux-checkbox-on.png b/webkit/glue/resources/linux-checkbox-on.png
new file mode 100644
index 0000000..4c72e8a
--- /dev/null
+++ b/webkit/glue/resources/linux-checkbox-on.png
Binary files differ
diff --git a/webkit/glue/resources/linux-progress-bar.png b/webkit/glue/resources/linux-progress-bar.png
new file mode 100644
index 0000000..82a20f5
--- /dev/null
+++ b/webkit/glue/resources/linux-progress-bar.png
Binary files differ
diff --git a/webkit/glue/resources/linux-progress-border-left.png b/webkit/glue/resources/linux-progress-border-left.png
new file mode 100644
index 0000000..0dd0e50
--- /dev/null
+++ b/webkit/glue/resources/linux-progress-border-left.png
Binary files differ
diff --git a/webkit/glue/resources/linux-progress-border-right.png b/webkit/glue/resources/linux-progress-border-right.png
new file mode 100644
index 0000000..d351b11
--- /dev/null
+++ b/webkit/glue/resources/linux-progress-border-right.png
Binary files differ
diff --git a/webkit/glue/resources/linux-progress-value.png b/webkit/glue/resources/linux-progress-value.png
new file mode 100644
index 0000000..251917b
--- /dev/null
+++ b/webkit/glue/resources/linux-progress-value.png
Binary files differ
diff --git a/webkit/glue/resources/linux-radio-disabled-off.png b/webkit/glue/resources/linux-radio-disabled-off.png
new file mode 100644
index 0000000..97aa6f9
--- /dev/null
+++ b/webkit/glue/resources/linux-radio-disabled-off.png
Binary files differ
diff --git a/webkit/glue/resources/linux-radio-disabled-on.png b/webkit/glue/resources/linux-radio-disabled-on.png
new file mode 100644
index 0000000..07aed36
--- /dev/null
+++ b/webkit/glue/resources/linux-radio-disabled-on.png
Binary files differ
diff --git a/webkit/glue/resources/linux-radio-off.png b/webkit/glue/resources/linux-radio-off.png
new file mode 100644
index 0000000..6a60a70
--- /dev/null
+++ b/webkit/glue/resources/linux-radio-off.png
Binary files differ
diff --git a/webkit/glue/resources/linux-radio-on.png b/webkit/glue/resources/linux-radio-on.png
new file mode 100644
index 0000000..cb47411
--- /dev/null
+++ b/webkit/glue/resources/linux-radio-on.png
Binary files differ
diff --git a/webkit/glue/resources/media_pause.png b/webkit/glue/resources/media_pause.png
new file mode 100644
index 0000000..5334d7e
--- /dev/null
+++ b/webkit/glue/resources/media_pause.png
Binary files differ
diff --git a/webkit/glue/resources/media_play.png b/webkit/glue/resources/media_play.png
new file mode 100644
index 0000000..0027929
--- /dev/null
+++ b/webkit/glue/resources/media_play.png
Binary files differ
diff --git a/webkit/glue/resources/media_play_disabled.png b/webkit/glue/resources/media_play_disabled.png
new file mode 100644
index 0000000..cd9a4a5
--- /dev/null
+++ b/webkit/glue/resources/media_play_disabled.png
Binary files differ
diff --git a/webkit/glue/resources/media_slider_thumb.png b/webkit/glue/resources/media_slider_thumb.png
new file mode 100644
index 0000000..21909ce
--- /dev/null
+++ b/webkit/glue/resources/media_slider_thumb.png
Binary files differ
diff --git a/webkit/glue/resources/media_sound_disabled.png b/webkit/glue/resources/media_sound_disabled.png
new file mode 100644
index 0000000..d7b2c1c
--- /dev/null
+++ b/webkit/glue/resources/media_sound_disabled.png
Binary files differ
diff --git a/webkit/glue/resources/media_sound_full.png b/webkit/glue/resources/media_sound_full.png
new file mode 100644
index 0000000..2ecc142
--- /dev/null
+++ b/webkit/glue/resources/media_sound_full.png
Binary files differ
diff --git a/webkit/glue/resources/media_sound_none.png b/webkit/glue/resources/media_sound_none.png
new file mode 100644
index 0000000..378a005
--- /dev/null
+++ b/webkit/glue/resources/media_sound_none.png
Binary files differ
diff --git a/webkit/glue/resources/media_volume_slider_thumb.png b/webkit/glue/resources/media_volume_slider_thumb.png
new file mode 100644
index 0000000..4789937
--- /dev/null
+++ b/webkit/glue/resources/media_volume_slider_thumb.png
Binary files differ
diff --git a/webkit/glue/resources/pan_east.cur b/webkit/glue/resources/pan_east.cur
new file mode 100644
index 0000000..f7c0df3
--- /dev/null
+++ b/webkit/glue/resources/pan_east.cur
Binary files differ
diff --git a/webkit/glue/resources/pan_icon.png b/webkit/glue/resources/pan_icon.png
new file mode 100644
index 0000000..db4e372
--- /dev/null
+++ b/webkit/glue/resources/pan_icon.png
Binary files differ
diff --git a/webkit/glue/resources/pan_middle.cur b/webkit/glue/resources/pan_middle.cur
new file mode 100644
index 0000000..52ae366
--- /dev/null
+++ b/webkit/glue/resources/pan_middle.cur
Binary files differ
diff --git a/webkit/glue/resources/pan_north.cur b/webkit/glue/resources/pan_north.cur
new file mode 100644
index 0000000..0c3a4ee
--- /dev/null
+++ b/webkit/glue/resources/pan_north.cur
Binary files differ
diff --git a/webkit/glue/resources/pan_north_east.cur b/webkit/glue/resources/pan_north_east.cur
new file mode 100644
index 0000000..4388fb8
--- /dev/null
+++ b/webkit/glue/resources/pan_north_east.cur
Binary files differ
diff --git a/webkit/glue/resources/pan_north_west.cur b/webkit/glue/resources/pan_north_west.cur
new file mode 100644
index 0000000..d47089e
--- /dev/null
+++ b/webkit/glue/resources/pan_north_west.cur
Binary files differ
diff --git a/webkit/glue/resources/pan_south.cur b/webkit/glue/resources/pan_south.cur
new file mode 100644
index 0000000..f5d813b
--- /dev/null
+++ b/webkit/glue/resources/pan_south.cur
Binary files differ
diff --git a/webkit/glue/resources/pan_south_east.cur b/webkit/glue/resources/pan_south_east.cur
new file mode 100644
index 0000000..ba9742e
--- /dev/null
+++ b/webkit/glue/resources/pan_south_east.cur
Binary files differ
diff --git a/webkit/glue/resources/pan_south_west.cur b/webkit/glue/resources/pan_south_west.cur
new file mode 100644
index 0000000..4f62984
--- /dev/null
+++ b/webkit/glue/resources/pan_south_west.cur
Binary files differ
diff --git a/webkit/glue/resources/pan_west.cur b/webkit/glue/resources/pan_west.cur
new file mode 100644
index 0000000..9068925
--- /dev/null
+++ b/webkit/glue/resources/pan_west.cur
Binary files differ
diff --git a/webkit/glue/resources/row_resize.cur b/webkit/glue/resources/row_resize.cur
new file mode 100644
index 0000000..a7369d3
--- /dev/null
+++ b/webkit/glue/resources/row_resize.cur
Binary files differ
diff --git a/webkit/glue/resources/search_cancel.png b/webkit/glue/resources/search_cancel.png
new file mode 100644
index 0000000..49f3f47
--- /dev/null
+++ b/webkit/glue/resources/search_cancel.png
Binary files differ
diff --git a/webkit/glue/resources/search_cancel_pressed.png b/webkit/glue/resources/search_cancel_pressed.png
new file mode 100644
index 0000000..b699d81
--- /dev/null
+++ b/webkit/glue/resources/search_cancel_pressed.png
Binary files differ
diff --git a/webkit/glue/resources/search_magnifier.png b/webkit/glue/resources/search_magnifier.png
new file mode 100644
index 0000000..f9b8cae
--- /dev/null
+++ b/webkit/glue/resources/search_magnifier.png
Binary files differ
diff --git a/webkit/glue/resources/search_magnifier_results.png b/webkit/glue/resources/search_magnifier_results.png
new file mode 100644
index 0000000..9aa1b36
--- /dev/null
+++ b/webkit/glue/resources/search_magnifier_results.png
Binary files differ
diff --git a/webkit/glue/resources/textarea_resize_corner.png b/webkit/glue/resources/textarea_resize_corner.png
new file mode 100644
index 0000000..023615e
--- /dev/null
+++ b/webkit/glue/resources/textarea_resize_corner.png
Binary files differ
diff --git a/webkit/glue/resources/vertical_text.cur b/webkit/glue/resources/vertical_text.cur
new file mode 100644
index 0000000..3de04eb
--- /dev/null
+++ b/webkit/glue/resources/vertical_text.cur
Binary files differ
diff --git a/webkit/glue/resources/webkit_strings_am.xtb b/webkit/glue/resources/webkit_strings_am.xtb
new file mode 100644
index 0000000..acba326
--- /dev/null
+++ b/webkit/glue/resources/webkit_strings_am.xtb
@@ -0,0 +1,43 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="am">
+<translation id="4420062214988137980">የPlugin ጭነት ተሰናክሏል</translation>
+<translation id="1235745349614807883">የቅርብ ጊዜ ፍለጋዎችን አስወግድ</translation>
+<translation id="3825324228893189080">ተጨማሪ plugin ያስፈልጋል</translation>
+<translation id="2965480764085142436"><ph name="PLUGIN"/> plugin አልተጫነም</translation>
+<translation id="5048533449481078685">ዝርዝር አመልካች</translation>
+<translation id="4202807286478387388">ዝለል</translation>
+<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation>
+<translation id="4317653869502688143">እባክዎ <ph name="PLUGIN"/> plugin ለመጫን መፈለግዎን ያረጋግጡ። የሚያምኗቸውን plugins ብቻ መጫን ይኖርብዎታል።</translation>
+<translation id="7658239707568436148">ሰርዝ</translation>
+<translation id="795667975304826397">ምንም ፋይል አልተመረጠም</translation>
+<translation id="1275511093094545429"><ph name="PLUGIN"/> plugin ያስፈልጋል</translation>
+<translation id="8662565117025751661">የሚወርድ plugin…</translation>
+<translation id="8141602879876242471">ይህ ሊፈለግ የሚችል መረጃ ጠቋሚ ነው። የፍለጋ ቁልፍ ቃላት አስገባ፦</translation>
+<translation id="6845533974506654842">ተጫን</translation>
+<translation id="8244226242650769279">የምስል ካርታ</translation>
+<translation id="1383141426028388991">ከ<ph name="URL"/> plugin ለመጫን አልተቻለም</translation>
+<translation id="2548326553472216322">የቅርብ ጊዜ ፍለጋዎች የሉም</translation>
+<translation id="5944544982112848342">2048(ከፍተኛ ደረጃ)</translation>
+<translation id="3040011195152428237">አገናኝ</translation>
+<translation id="8281246460978372009">plugin ከተጫነ በኋላ፣ ለማደስ እዚህ ይጫኑ</translation>
+<translation id="7364796246159120393">ፋይል ምረጥ</translation>
+<translation id="8964020114565522021">ፋይል ወደዚህ ጎትት</translation>
+<translation id="838869780401515933">አመልክት</translation>
+<translation id="2846343701378493991">1024(መካከለኛ ደረጃ)</translation>
+<translation id="5476505524087279545">አታመልክት</translation>
+<translation id="679352192834563463">ይህን ይዘት ለማሳየት ምንም plugin የለም</translation>
+<translation id="3789841737615482174">ጫን</translation>
+<translation id="6663448176199120256">የቅርብ ጊዜ ፍለጋዎችን</translation>
+<translation id="3600343118165084788">plugin ለማውረድ እዚህ ይጫኑ</translation>
+<translation id="6807599807928161586">የድር ክልል</translation>
+<translation id="5939518447894949180">ዳግም አስጀምር</translation>
+<translation id="3771786644471114952">Plugin አግኝ</translation>
+<translation id="1842960171412779397">ምረጥ</translation>
+<translation id="6119846243427417423">አንቃ</translation>
+<translation id="8444882422881193423"><ph name="NUMBER_OF_FILES"/> ፋይሎች</translation>
+<translation id="3926627843712816530">እባክዎ ይህን plugin ለመጫን መፈለግዎን ያረጋግጡ። የሚያምኗቸውን plugins ብቻ መጫን ይኖርብዎታል።</translation>
+<translation id="4838490908464673667">የሚፈለገው plugin አልተጫነም</translation>
+<translation id="8597182159515967513">ርእስ</translation>
+<translation id="2653659639078652383">አስገባ</translation>
+</translationbundle> \ No newline at end of file
diff --git a/webkit/glue/resources/webkit_strings_ar.xtb b/webkit/glue/resources/webkit_strings_ar.xtb
new file mode 100644
index 0000000..a558190
--- /dev/null
+++ b/webkit/glue/resources/webkit_strings_ar.xtb
@@ -0,0 +1,43 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="ar">
+<translation id="4420062214988137980">إخفاق محاولة تثبيت المكوّن الإضافي</translation>
+<translation id="1235745349614807883">محو آخر عمليات البحث</translation>
+<translation id="3825324228893189080">مطلوب مكوّن إضافيّ آخر.</translation>
+<translation id="2965480764085142436">المكوّن الإضافي <ph name="PLUGIN"/> غير متوفّر</translation>
+<translation id="5048533449481078685">محدّد القائمة</translation>
+<translation id="4202807286478387388">الدخول</translation>
+<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation>
+<translation id="4317653869502688143">الرّجاء تأكيد طلب تثبيت المكوّن الإضافي <ph name="PLUGIN"/>. يجب تثبيت المكوّنات الإضافية الموثوق بها فقط.</translation>
+<translation id="7658239707568436148">إلغاء</translation>
+<translation id="795667975304826397">ّلم يتمّ اختيار أيّ ملفّ</translation>
+<translation id="1275511093094545429">يلزم المكوّن الإضافيّ <ph name="PLUGIN"/></translation>
+<translation id="8662565117025751661">تحميل المكوّن الإضافي...</translation>
+<translation id="8141602879876242471">يمكن البحث في هذا الفهرس بإدخال كلمات مفتاحية:</translation>
+<translation id="6845533974506654842">اضغط</translation>
+<translation id="8244226242650769279">مخطّط صورة</translation>
+<translation id="1383141426028388991">فشل تثبيت المكون الإضافي من <ph name="URL"/></translation>
+<translation id="2548326553472216322">لا عمليات بحث حديثة</translation>
+<translation id="5944544982112848342">2048 (درجة عالية)</translation>
+<translation id="3040011195152428237">رابط</translation>
+<translation id="8281246460978372009">بعد تثبيت المكوّن الإضافي، انقر هنا لتحديث النافذة</translation>
+<translation id="7364796246159120393">اختيار ملفّ</translation>
+<translation id="8964020114565522021">سحب الملفّ إلى هنا</translation>
+<translation id="838869780401515933">الاختيار</translation>
+<translation id="2846343701378493991">1024 (درجة متوسطة)</translation>
+<translation id="5476505524087279545">إزالة علامة الاختيار</translation>
+<translation id="679352192834563463">لا يتوفر أي مكوّن إضافي لعرض هذا المحتوى</translation>
+<translation id="3789841737615482174">تثبيت</translation>
+<translation id="6663448176199120256">آخر عمليات البحث</translation>
+<translation id="3600343118165084788">انقر هنا لتنزيل المكوّن الإضافي</translation>
+<translation id="6807599807928161586">منطقة الويب</translation>
+<translation id="5939518447894949180">إعادة</translation>
+<translation id="3771786644471114952">جلب المكوّن الإضافي</translation>
+<translation id="1842960171412779397">الاختيار</translation>
+<translation id="6119846243427417423">تنشيط</translation>
+<translation id="8444882422881193423">عدد الملفات: <ph name="NUMBER_OF_FILES"/></translation>
+<translation id="3926627843712816530">يرجى تأكيد طلب تثبيت المكوّن الإضافي. يجب تثبيت المكوّنات الإضافية الموثوق بها فقط.</translation>
+<translation id="4838490908464673667">المكوّن الإضافيّ المطلوب غير متوفّر</translation>
+<translation id="8597182159515967513">العنوان</translation>
+<translation id="2653659639078652383">إرسال</translation>
+</translationbundle> \ No newline at end of file
diff --git a/webkit/glue/resources/webkit_strings_bg.xtb b/webkit/glue/resources/webkit_strings_bg.xtb
new file mode 100644
index 0000000..91d0b4b
--- /dev/null
+++ b/webkit/glue/resources/webkit_strings_bg.xtb
@@ -0,0 +1,43 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="bg">
+<translation id="4420062214988137980">Инсталирането на приставката не бе успешно</translation>
+<translation id="1235745349614807883">Изчистване на скорошните търсения</translation>
+<translation id="3825324228893189080">Изисква се допълнителна приставка</translation>
+<translation id="2965480764085142436">Приставката за <ph name="PLUGIN"/> не е инсталирана</translation>
+<translation id="5048533449481078685">списъчен показалец</translation>
+<translation id="4202807286478387388">преминаване</translation>
+<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation>
+<translation id="4317653869502688143">Моля, потвърдете, че искате да инсталирате приставката за <ph name="PLUGIN"/>. Инсталирайте само тези приставки, които считате за надеждни.</translation>
+<translation id="7658239707568436148">Отказ</translation>
+<translation id="795667975304826397">Няма избран файл</translation>
+<translation id="1275511093094545429">Необходима е приставка за <ph name="PLUGIN"/></translation>
+<translation id="8662565117025751661">Приставката се изтегля...</translation>
+<translation id="8141602879876242471">В този индекс може да се търси. Въведете ключови думи за търсене:</translation>
+<translation id="6845533974506654842">натискане</translation>
+<translation id="8244226242650769279">карта с изображения</translation>
+<translation id="1383141426028388991">Неуспешно инсталиране на приставката от <ph name="URL"/></translation>
+<translation id="2548326553472216322">Няма скорошни търсения</translation>
+<translation id="5944544982112848342">2048 (висока степен на сложност)</translation>
+<translation id="3040011195152428237">връзка</translation>
+<translation id="8281246460978372009">След като инсталирате приставката, кликнете тук, за да опресните прозореца</translation>
+<translation id="7364796246159120393">Избор на файл</translation>
+<translation id="8964020114565522021">Плъзнете файла тук</translation>
+<translation id="838869780401515933">отмятане</translation>
+<translation id="2846343701378493991">1024 (средна степен на сложност)</translation>
+<translation id="5476505524087279545">премахване на отметката</translation>
+<translation id="679352192834563463">Няма приставка, с която може да се покаже това съдържание</translation>
+<translation id="3789841737615482174">Инсталиране</translation>
+<translation id="6663448176199120256">Скорошни търсения</translation>
+<translation id="3600343118165084788">Кликнете тук, за да изтеглите приставката</translation>
+<translation id="6807599807928161586">уеб зона</translation>
+<translation id="5939518447894949180">Повторно задаване</translation>
+<translation id="3771786644471114952">Изтегляне на приставката</translation>
+<translation id="1842960171412779397">Избиране</translation>
+<translation id="6119846243427417423">активиране</translation>
+<translation id="8444882422881193423"><ph name="NUMBER_OF_FILES"/> файла</translation>
+<translation id="3926627843712816530">Моля, потвърдете, че искате да инсталирате тази приставка. Инсталирайте само тези приставки, които считате за надеждни.</translation>
+<translation id="4838490908464673667">Необходимата приставка не е инсталирана</translation>
+<translation id="8597182159515967513">заглавие</translation>
+<translation id="2653659639078652383">Изпращане</translation>
+</translationbundle> \ No newline at end of file
diff --git a/webkit/glue/resources/webkit_strings_bn.xtb b/webkit/glue/resources/webkit_strings_bn.xtb
new file mode 100644
index 0000000..098985f
--- /dev/null
+++ b/webkit/glue/resources/webkit_strings_bn.xtb
@@ -0,0 +1,43 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="bn">
+<translation id="4420062214988137980">প্ল্যাগইন ইনস্টলেশন ব্যর্থ</translation>
+<translation id="1235745349614807883">সাম্প্রতিক অনুসন্ধানগুলি সাফ করুন</translation>
+<translation id="3825324228893189080">অতিরিক্ত প্ল্যাগইন প্রয়োজনীয়</translation>
+<translation id="2965480764085142436"><ph name="PLUGIN"/> প্ল্যাগইন ইনস্টল হয় নি</translation>
+<translation id="5048533449481078685">তালিকা নির্দেশক</translation>
+<translation id="4202807286478387388">লাফ দিন</translation>
+<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation>
+<translation id="4317653869502688143">দয়া করে নিশ্চিত করুন, আপনি <ph name="PLUGIN"/> প্ল্যাগইন ইনস্টল করতে চান৷ আপনি বিশ্বাস করেন কেবল এমন প্ল্যাগইনই ইনস্টল করা উচিত৷</translation>
+<translation id="7658239707568436148">বাতিল</translation>
+<translation id="795667975304826397">কোনও ফাইল চয়ন করা হয় নি</translation>
+<translation id="1275511093094545429"><ph name="PLUGIN"/> প্ল্যাগইন প্রয়োজনীয়</translation>
+<translation id="8662565117025751661">প্ল্যাগইন ডাউনলোড হচ্ছে...</translation>
+<translation id="8141602879876242471">এটি অনুসন্ধানযোগ্য সূচি৷ অনুসন্ধানের মূলশব্দ প্রবেশ করান:</translation>
+<translation id="6845533974506654842">টিপুন</translation>
+<translation id="8244226242650769279">ছবি মানচিত্র</translation>
+<translation id="1383141426028388991"><ph name="URL"/> থেকে প্ল্যাগইন ইনস্টল করতে ব্যর্থ হয়েছে</translation>
+<translation id="2548326553472216322">কোন সাম্প্রতিক অনুসন্ধান নেই</translation>
+<translation id="5944544982112848342">2048 (উচ্চ গ্রেড)</translation>
+<translation id="3040011195152428237">লিঙ্ক</translation>
+<translation id="8281246460978372009">প্ল্যাগইন ইনস্টল করার পরে, রিফ্রেশ করার জন্য এখানে ক্লিক করুন</translation>
+<translation id="7364796246159120393">ফাইল চয়ন করুন</translation>
+<translation id="8964020114565522021">ফাইল এখানে টেনে আনুন</translation>
+<translation id="838869780401515933">চেক করুন</translation>
+<translation id="2846343701378493991">1024 (মধ্যম গ্রেড)</translation>
+<translation id="5476505524087279545">আনচেক</translation>
+<translation id="679352192834563463">এই সামগ্রী প্রদর্শন করার জন্য কোনও প্ল্যাগইন উপলব্ধ নেই</translation>
+<translation id="3789841737615482174">ইনস্টল করুন</translation>
+<translation id="6663448176199120256">সাম্প্রতিক অনুসন্ধানগুলি</translation>
+<translation id="3600343118165084788">প্ল্যাগ ডাউনলোড করার জন্য এখানে ডাউনলোড করুন</translation>
+<translation id="6807599807928161586">ওয়েব এলাকা</translation>
+<translation id="5939518447894949180">রিসেট করুন</translation>
+<translation id="3771786644471114952">প্ল্যাগইন প্রাপ্ত করুন</translation>
+<translation id="1842960171412779397">নির্বাচন করুন</translation>
+<translation id="6119846243427417423">সক্রিয় করুন</translation>
+<translation id="8444882422881193423"><ph name="NUMBER_OF_FILES"/> টি ফাইল</translation>
+<translation id="3926627843712816530">আপনি প্ল্যাগইন ইন্স্টল করতে চান তা দয়া করে নিশ্চিত করুন৷ আপনি বিশ্বাস করেন কেবল এমন প্ল্যাগইনই ইনস্টল করা উচিত৷</translation>
+<translation id="4838490908464673667">প্রয়োজনীয় প্ল্যাগইন ইনস্টল করা নেই</translation>
+<translation id="8597182159515967513">শিরোনামা</translation>
+<translation id="2653659639078652383">জমা</translation>
+</translationbundle> \ No newline at end of file
diff --git a/webkit/glue/resources/webkit_strings_ca.xtb b/webkit/glue/resources/webkit_strings_ca.xtb
new file mode 100644
index 0000000..b5b1aa7
--- /dev/null
+++ b/webkit/glue/resources/webkit_strings_ca.xtb
@@ -0,0 +1,43 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="ca">
+<translation id="4420062214988137980">Error en la instal·lació del complement</translation>
+<translation id="1235745349614807883">Esborra les cerques recents</translation>
+<translation id="3825324228893189080">Es necessita un complement addicional</translation>
+<translation id="2965480764085142436">El complement <ph name="PLUGIN"/> no està instal·lat</translation>
+<translation id="5048533449481078685">marcador de llistes</translation>
+<translation id="4202807286478387388">salta</translation>
+<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation>
+<translation id="4317653869502688143">Confirma que vols instal·lar el complement <ph name="PLUGIN"/>. Només hauries d'instal·lar complements fiables.</translation>
+<translation id="7658239707568436148">Cancel·la</translation>
+<translation id="795667975304826397">No heu seleccionat cap fitxer.</translation>
+<translation id="1275511093094545429">Es necessita el complement <ph name="PLUGIN"/></translation>
+<translation id="8662565117025751661">S'està baixant el complement...</translation>
+<translation id="8141602879876242471">És un índex on es poden realitzar cerques. Introdueix els termes de cerca:</translation>
+<translation id="6845533974506654842">prem</translation>
+<translation id="8244226242650769279">mapa d'imatges</translation>
+<translation id="1383141426028388991">S'ha produït un error en instal·lar el connector des de <ph name="URL"/></translation>
+<translation id="2548326553472216322">No hi ha cerques recents</translation>
+<translation id="5944544982112848342">2048 (Gran)</translation>
+<translation id="3040011195152428237">enllaç</translation>
+<translation id="8281246460978372009">Després d'instal·lar el complement, feu clic aquí per actualitzar.</translation>
+<translation id="7364796246159120393">Selecciona el fitxer</translation>
+<translation id="8964020114565522021">Arrossegueu el fitxer aquí.</translation>
+<translation id="838869780401515933">marca</translation>
+<translation id="2846343701378493991">1024 (Mitjà)</translation>
+<translation id="5476505524087279545">desmarca</translation>
+<translation id="679352192834563463">No hi ha cap complement disponible per mostrar aquest contingut</translation>
+<translation id="3789841737615482174">Instal·la</translation>
+<translation id="6663448176199120256">Cerques recents</translation>
+<translation id="3600343118165084788">Feu clic aquí per baixar el complement</translation>
+<translation id="6807599807928161586">àrea web</translation>
+<translation id="5939518447894949180">Restablir</translation>
+<translation id="3771786644471114952">Obtén el complement</translation>
+<translation id="1842960171412779397">selecciona</translation>
+<translation id="6119846243427417423">activa</translation>
+<translation id="8444882422881193423"><ph name="NUMBER_OF_FILES"/> fitxers</translation>
+<translation id="3926627843712816530">Confirma que vols instal·lar aquest complement. Només hauries d'instal·lar complements fiables.</translation>
+<translation id="4838490908464673667">El complement necessari no està instal·lat</translation>
+<translation id="8597182159515967513">Capçalera</translation>
+<translation id="2653659639078652383">Envia</translation>
+</translationbundle> \ No newline at end of file
diff --git a/webkit/glue/resources/webkit_strings_cs.xtb b/webkit/glue/resources/webkit_strings_cs.xtb
new file mode 100644
index 0000000..33026e2
--- /dev/null
+++ b/webkit/glue/resources/webkit_strings_cs.xtb
@@ -0,0 +1,43 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="cs">
+<translation id="4420062214988137980">Nepodařilo se nainstalovat plugin</translation>
+<translation id="1235745349614807883">Smazat nedávná vyhledávání</translation>
+<translation id="3825324228893189080">Je potřeba další plugin</translation>
+<translation id="2965480764085142436">Není nainstalován plugin <ph name="PLUGIN"/></translation>
+<translation id="5048533449481078685">značka seznamu</translation>
+<translation id="4202807286478387388">přejít</translation>
+<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation>
+<translation id="4317653869502688143">Potvrďte prosím, že si přejete instalovat plugin <ph name="PLUGIN"/>. Měli byste instalovat pouze takové pluginy, kterým důvěřujete.</translation>
+<translation id="7658239707568436148">Zrušit</translation>
+<translation id="795667975304826397">Soubor nevybrán</translation>
+<translation id="1275511093094545429">Je potřeba plugin <ph name="PLUGIN"/></translation>
+<translation id="8662565117025751661">Stahování pluginu...</translation>
+<translation id="8141602879876242471">Toto je prohledávatelný index. Zadejte hledaná klíčová slova:</translation>
+<translation id="6845533974506654842">zmáčknout</translation>
+<translation id="8244226242650769279">obrázková mapa</translation>
+<translation id="1383141426028388991">Nepodařilo se nainstalovat plugin z adresy <ph name="URL"/></translation>
+<translation id="2548326553472216322">Žádná nedávná vyhledávání</translation>
+<translation id="5944544982112848342">2048 (vysoká kvalita)</translation>
+<translation id="3040011195152428237">odkaz</translation>
+<translation id="8281246460978372009">Po instalaci pluginu klikněte sem pro obnovení</translation>
+<translation id="7364796246159120393">Vybrat soubor</translation>
+<translation id="8964020114565522021">Přetáhnout soubor sem</translation>
+<translation id="838869780401515933">zaškrtnout</translation>
+<translation id="2846343701378493991">1024 (Střední kvalita)</translation>
+<translation id="5476505524087279545">odstranit zaškrtnutí</translation>
+<translation id="679352192834563463">Není k dispozici žádný plugin pro zobrazení tohoto obsahu</translation>
+<translation id="3789841737615482174">Instalovat</translation>
+<translation id="6663448176199120256">Nedávná vyhledávání</translation>
+<translation id="3600343118165084788">Pro stažení pluginu klikněte sem</translation>
+<translation id="6807599807928161586">oblast webu</translation>
+<translation id="5939518447894949180">Resetovat</translation>
+<translation id="3771786644471114952">Získat plugin</translation>
+<translation id="1842960171412779397">zvolit</translation>
+<translation id="6119846243427417423">aktivovat</translation>
+<translation id="8444882422881193423">Počet souborů: <ph name="NUMBER_OF_FILES"/></translation>
+<translation id="3926627843712816530">Potvrďte, prosím, že si přejete instalovat tento plugin. Měli byste instalovat pouze takové pluginy, kterým důvěřujete.</translation>
+<translation id="4838490908464673667">Požadovaný plugin není nainstalován</translation>
+<translation id="8597182159515967513">záhlaví</translation>
+<translation id="2653659639078652383">Odeslat</translation>
+</translationbundle> \ No newline at end of file
diff --git a/webkit/glue/resources/webkit_strings_da.xtb b/webkit/glue/resources/webkit_strings_da.xtb
new file mode 100644
index 0000000..a923897
--- /dev/null
+++ b/webkit/glue/resources/webkit_strings_da.xtb
@@ -0,0 +1,43 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="da">
+<translation id="4420062214988137980">Installation af plugin mislykkedes</translation>
+<translation id="1235745349614807883">Slet nylige søgninger</translation>
+<translation id="3825324228893189080">Ekstra plugin påkrævet</translation>
+<translation id="2965480764085142436"><ph name="PLUGIN"/> plugin er ikke installeret</translation>
+<translation id="5048533449481078685">listemarkering</translation>
+<translation id="4202807286478387388">hop</translation>
+<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation>
+<translation id="4317653869502688143">Bekræft venligst, at du ønsker at installere plugin'et <ph name="PLUGIN"/>. Du bør udelukkende installere plugins, som du stoler på.</translation>
+<translation id="7658239707568436148">Annuller</translation>
+<translation id="795667975304826397">Der er ikke valgt nogen fil</translation>
+<translation id="1275511093094545429"><ph name="PLUGIN"/> Plugin påkrævet</translation>
+<translation id="8662565117025751661">Downloader plugin ...</translation>
+<translation id="8141602879876242471">Der kan søges i dette indeks. Indtast søge-nøgleord:</translation>
+<translation id="6845533974506654842">tryk</translation>
+<translation id="8244226242650769279">billedekort</translation>
+<translation id="1383141426028388991">Mislykket installation af plugin fra <ph name="URL"/></translation>
+<translation id="2548326553472216322">Ingen nylige søgninger</translation>
+<translation id="5944544982112848342">2048 (Høj klasse)</translation>
+<translation id="3040011195152428237">link</translation>
+<translation id="8281246460978372009">Når du har installeret plugin'et, skal du klikke her for at opdatere</translation>
+<translation id="7364796246159120393">Vælg fil</translation>
+<translation id="8964020114565522021">Træk filer hertil</translation>
+<translation id="838869780401515933">marker</translation>
+<translation id="2846343701378493991">1024 (Mellemklasse)</translation>
+<translation id="5476505524087279545">fjern markering</translation>
+<translation id="679352192834563463">Intet tilgængeligt plugin kan vise dette indhold</translation>
+<translation id="3789841737615482174">Installer</translation>
+<translation id="6663448176199120256">Nylige søgninger</translation>
+<translation id="3600343118165084788">Klik her for at downloade plugin</translation>
+<translation id="6807599807928161586">webområde</translation>
+<translation id="5939518447894949180">Nulstil</translation>
+<translation id="3771786644471114952">Hent plugin</translation>
+<translation id="1842960171412779397">vælg</translation>
+<translation id="6119846243427417423">aktiver</translation>
+<translation id="8444882422881193423"><ph name="NUMBER_OF_FILES"/> filer</translation>
+<translation id="3926627843712816530">Bekræft venligst, at du gerne vil installere dette plugin. Du bør udelukkende installere plugins, som du stoler på.</translation>
+<translation id="4838490908464673667">Det krævede plugin er ikke installeret</translation>
+<translation id="8597182159515967513">overskrift</translation>
+<translation id="2653659639078652383">Indsend</translation>
+</translationbundle> \ No newline at end of file
diff --git a/webkit/glue/resources/webkit_strings_de.xtb b/webkit/glue/resources/webkit_strings_de.xtb
new file mode 100644
index 0000000..76411ed
--- /dev/null
+++ b/webkit/glue/resources/webkit_strings_de.xtb
@@ -0,0 +1,43 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="de">
+<translation id="4420062214988137980">Plug-in-Installation fehlgeschlagen</translation>
+<translation id="1235745349614807883">Vor kurzem durchgeführte Suchanfragen löschen</translation>
+<translation id="3825324228893189080">Zusätzliches Plug-in erforderlich</translation>
+<translation id="2965480764085142436">Plug-in <ph name="PLUGIN"/> nicht installiert</translation>
+<translation id="5048533449481078685">Listenmarkierung</translation>
+<translation id="4202807286478387388">springen</translation>
+<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation>
+<translation id="4317653869502688143">Bestätigen Sie, dass Sie das Plug-in <ph name="PLUGIN"/> installieren möchten. Sie sollten nur vertrauenswürdige Plug-ins installieren.</translation>
+<translation id="7658239707568436148">Abbrechen</translation>
+<translation id="795667975304826397">Keine Datei ausgewählt</translation>
+<translation id="1275511093094545429">Plug-in <ph name="PLUGIN"/> erforderlich</translation>
+<translation id="8662565117025751661">Plug-in wird heruntergeladen...</translation>
+<translation id="8141602879876242471">Dieser Index kann durchsucht werden. Geben Sie Suchbegriffe ein:</translation>
+<translation id="6845533974506654842">klicken</translation>
+<translation id="8244226242650769279">Imagemap</translation>
+<translation id="1383141426028388991">Installation des Plug-ins von <ph name="URL"/> fehlgeschlagen</translation>
+<translation id="2548326553472216322">Keine vor kurzem durchgeführte Suchanfragen</translation>
+<translation id="5944544982112848342">2048 (High Grade)</translation>
+<translation id="3040011195152428237">Link</translation>
+<translation id="8281246460978372009">Klicken Sie nach der Installation des Plug-ins hier, um eine Aktualisierung durchzuführen.</translation>
+<translation id="7364796246159120393">Datei auswählen</translation>
+<translation id="8964020114565522021">Datei hier ablegen</translation>
+<translation id="838869780401515933">auswählen</translation>
+<translation id="2846343701378493991">1024 (mittlere Stufe)</translation>
+<translation id="5476505524087279545">Auswahl aufheben</translation>
+<translation id="679352192834563463">Kein Plug-in zum Anzeigen dieses Contents verfügbar</translation>
+<translation id="3789841737615482174">Installieren</translation>
+<translation id="6663448176199120256">Vor kurzem durchgeführte Suchanfragen</translation>
+<translation id="3600343118165084788">Klicken Sie hier, um das Plug-in herunterzuladen.</translation>
+<translation id="6807599807928161586">Webbereich</translation>
+<translation id="5939518447894949180">Zurücksetzen</translation>
+<translation id="3771786644471114952">Plug-in abrufen</translation>
+<translation id="1842960171412779397">auswählen</translation>
+<translation id="6119846243427417423">aktivieren</translation>
+<translation id="8444882422881193423"><ph name="NUMBER_OF_FILES"/> Dateien</translation>
+<translation id="3926627843712816530">Bestätigen Sie, dass Sie dieses Plug-in installieren möchten. Sie sollten nur vertrauenswürdige Plug-ins installieren.</translation>
+<translation id="4838490908464673667">Das erforderliche Plug-in ist nicht installiert</translation>
+<translation id="8597182159515967513">Kopfzeile</translation>
+<translation id="2653659639078652383">Senden</translation>
+</translationbundle> \ No newline at end of file
diff --git a/webkit/glue/resources/webkit_strings_el.xtb b/webkit/glue/resources/webkit_strings_el.xtb
new file mode 100644
index 0000000..dd75011
--- /dev/null
+++ b/webkit/glue/resources/webkit_strings_el.xtb
@@ -0,0 +1,43 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="el">
+<translation id="4420062214988137980">Η εγκατάσταση της προσθήκης απέτυχε</translation>
+<translation id="1235745349614807883">Εκκαθάριση πρόσφατων αναζητήσεων</translation>
+<translation id="3825324228893189080">Απαιτείται επιπλέον προσθήκη</translation>
+<translation id="2965480764085142436">Η προσθήκη <ph name="PLUGIN"/> δεν έχει εγκατασταθεί</translation>
+<translation id="5048533449481078685">δείκτης λίστας</translation>
+<translation id="4202807286478387388">μεταπήδηση</translation>
+<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation>
+<translation id="4317653869502688143">Επιβεβαιώστε ότι θέλετε να εγκαταστήσετε την προσθήκη <ph name="PLUGIN"/>. Πρέπει να εγκαθιστάτε μόνο προσθήκες που θεωρείτε αξιόπιστες.</translation>
+<translation id="7658239707568436148">Ακύρωση</translation>
+<translation id="795667975304826397">Δεν έχει επιλεγεί κανένα αρχείο</translation>
+<translation id="1275511093094545429">Χρειάζεται η προσθήκη <ph name="PLUGIN"/></translation>
+<translation id="8662565117025751661">Λήψη προσθήκης...</translation>
+<translation id="8141602879876242471">Πρόκειται για ευρετήριο με δυνατότητα αναζήτησης. Πληκτρολογήστε λέξεις-κλειδιά αναζήτησης:</translation>
+<translation id="6845533974506654842">πατήστε</translation>
+<translation id="8244226242650769279">χάρτης εικόνας</translation>
+<translation id="1383141426028388991">Η εγκατάσταση της προσθήκης από τη διεύθυνση <ph name="URL"/> απέτυχε</translation>
+<translation id="2548326553472216322">Δεν υπάρχουν πρόσφατες αναζητήσεις</translation>
+<translation id="5944544982112848342">2048 (Υψηλός βαθμός)</translation>
+<translation id="3040011195152428237">σύνδεσμος</translation>
+<translation id="8281246460978372009">Μετά την εγκατάσταση της προσθήκης, κάντε κλικ εδώ για ανανέωση</translation>
+<translation id="7364796246159120393">Επιλογή αρχείου</translation>
+<translation id="8964020114565522021">Σύρετε το αρχείο εδώ</translation>
+<translation id="838869780401515933">ενεργοποίηση</translation>
+<translation id="2846343701378493991">1024 (Μέτριος βαθμός)</translation>
+<translation id="5476505524087279545">απενεργοποίηση</translation>
+<translation id="679352192834563463">Δεν υπάρχει διαθέσιμη προσθήκη για την εμφάνιση του περιεχομένου</translation>
+<translation id="3789841737615482174">Εγκατάσταση</translation>
+<translation id="6663448176199120256">Πρόσφατες αναζητήσεις</translation>
+<translation id="3600343118165084788">Κάντε κλικ εδώ για να κατεβάσετε την προσθήκη</translation>
+<translation id="6807599807928161586">περιοχή ιστού</translation>
+<translation id="5939518447894949180">Επαναφορά</translation>
+<translation id="3771786644471114952">Λήψη προσθήκης</translation>
+<translation id="1842960171412779397">επιλογή</translation>
+<translation id="6119846243427417423">ενεργοποίηση</translation>
+<translation id="8444882422881193423"><ph name="NUMBER_OF_FILES"/> αρχεία</translation>
+<translation id="3926627843712816530">Επιβεβαιώστε ότι θέλετε να εγκαταστήσετε αυτή την προσθήκη. Πρέπει να εγκαθιστάτε μόνο προσθήκες που θεωρείτε αξιόπιστες.</translation>
+<translation id="4838490908464673667">Η απαιτούμενη προσθήκη δεν έχει εγκατασταθεί</translation>
+<translation id="8597182159515967513">επικεφαλίδα</translation>
+<translation id="2653659639078652383">Υποβολή</translation>
+</translationbundle> \ No newline at end of file
diff --git a/webkit/glue/resources/webkit_strings_en-GB.xtb b/webkit/glue/resources/webkit_strings_en-GB.xtb
new file mode 100644
index 0000000..ed7a1b6
--- /dev/null
+++ b/webkit/glue/resources/webkit_strings_en-GB.xtb
@@ -0,0 +1,43 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="en-GB">
+<translation id="4420062214988137980">Plug-in installation failed</translation>
+<translation id="1235745349614807883">Clear Recent Searches</translation>
+<translation id="3825324228893189080">Additional plug-in needed</translation>
+<translation id="2965480764085142436"><ph name="PLUGIN"/> plug-in is not installed</translation>
+<translation id="5048533449481078685">list marker</translation>
+<translation id="4202807286478387388">jump</translation>
+<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation>
+<translation id="4317653869502688143">Please confirm that you would like to install the <ph name="PLUGIN"/> plug-in. You should only install plug-ins that you trust.</translation>
+<translation id="7658239707568436148">Cancel</translation>
+<translation id="795667975304826397">No file chosen</translation>
+<translation id="1275511093094545429"><ph name="PLUGIN"/> plug-in needed</translation>
+<translation id="8662565117025751661">Downloading plug-in...</translation>
+<translation id="8141602879876242471">This is a searchable index. Enter search keywords:</translation>
+<translation id="6845533974506654842">press</translation>
+<translation id="8244226242650769279">image map</translation>
+<translation id="1383141426028388991">Failed to install plug-in from <ph name="URL"/></translation>
+<translation id="2548326553472216322">No recent searches</translation>
+<translation id="5944544982112848342">2048 (High Grade)</translation>
+<translation id="3040011195152428237">link</translation>
+<translation id="8281246460978372009">After installing the plug-in, click here to refresh</translation>
+<translation id="7364796246159120393">Choose File</translation>
+<translation id="8964020114565522021">Drag file here</translation>
+<translation id="838869780401515933">tick</translation>
+<translation id="2846343701378493991">1024 (Medium Grade)</translation>
+<translation id="5476505524087279545">untick</translation>
+<translation id="679352192834563463">No plug-in available to display this content</translation>
+<translation id="3789841737615482174">Install</translation>
+<translation id="6663448176199120256">Recent Searches</translation>
+<translation id="3600343118165084788">Click here to download plug-in</translation>
+<translation id="6807599807928161586">web area</translation>
+<translation id="5939518447894949180">Reset</translation>
+<translation id="3771786644471114952">Get Plug-in</translation>
+<translation id="1842960171412779397">select</translation>
+<translation id="6119846243427417423">activate</translation>
+<translation id="8444882422881193423"><ph name="NUMBER_OF_FILES"/> files</translation>
+<translation id="3926627843712816530">Please confirm that you would like to install this plug-in. You should only install plug-ins that you trust.</translation>
+<translation id="4838490908464673667">The required plug-in is not installed</translation>
+<translation id="8597182159515967513">heading</translation>
+<translation id="2653659639078652383">Submit</translation>
+</translationbundle> \ No newline at end of file
diff --git a/webkit/glue/resources/webkit_strings_es-419.xtb b/webkit/glue/resources/webkit_strings_es-419.xtb
new file mode 100644
index 0000000..d7e05f4
--- /dev/null
+++ b/webkit/glue/resources/webkit_strings_es-419.xtb
@@ -0,0 +1,43 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="es-419">
+<translation id="4420062214988137980">Error en la instalación del plug-in</translation>
+<translation id="1235745349614807883">Eliminar búsquedas recientes</translation>
+<translation id="3825324228893189080">Se necesita un plug-in adicional</translation>
+<translation id="2965480764085142436">El plug-in <ph name="PLUGIN"/> no está instalado</translation>
+<translation id="5048533449481078685">marcador de listas</translation>
+<translation id="4202807286478387388">saltar</translation>
+<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation>
+<translation id="4317653869502688143">Confirma que deseas instalar el plug-in <ph name="PLUGIN"/>. Solo debes instalar plug-in fiables.</translation>
+<translation id="7658239707568436148">Cancelar</translation>
+<translation id="795667975304826397">No se eligió ningún archivo</translation>
+<translation id="1275511093094545429">Se requiere el plug-in <ph name="PLUGIN"/></translation>
+<translation id="8662565117025751661">Descargando plug-in...</translation>
+<translation id="8141602879876242471">Se trata de un índice que admite búsquedas. Escribe las palabras clave de búsqueda:</translation>
+<translation id="6845533974506654842">hacer clic</translation>
+<translation id="8244226242650769279">mapa de imágenes</translation>
+<translation id="1383141426028388991">Error al instalar el complemento desde <ph name="URL"/></translation>
+<translation id="2548326553472216322">No hay búsquedas recientes</translation>
+<translation id="5944544982112848342">2048 (Grado elevado)</translation>
+<translation id="3040011195152428237">enlace</translation>
+<translation id="8281246460978372009">Tras instalar el complemento, haz clic aquí para actualizar.</translation>
+<translation id="7364796246159120393">Seleccionar archivo</translation>
+<translation id="8964020114565522021">Arrastre el archivo hasta aquí</translation>
+<translation id="838869780401515933">marcar</translation>
+<translation id="2846343701378493991">1024 (Mediano)</translation>
+<translation id="5476505524087279545">desmarcar</translation>
+<translation id="679352192834563463">No hay ningún plug-in disponible para mostrar este contenido</translation>
+<translation id="3789841737615482174">Instalar</translation>
+<translation id="6663448176199120256">Búsquedas recientes</translation>
+<translation id="3600343118165084788">Haz clic aquí para descargar el plug-in.</translation>
+<translation id="6807599807928161586">área web</translation>
+<translation id="5939518447894949180">Restablecer</translation>
+<translation id="3771786644471114952">Obtener plug-in</translation>
+<translation id="1842960171412779397">seleccionar</translation>
+<translation id="6119846243427417423">activar</translation>
+<translation id="8444882422881193423"><ph name="NUMBER_OF_FILES"/> archivos</translation>
+<translation id="3926627843712816530">Confirma que deseas instalar este plug-in. Solo debes instalar plug-in fiables.</translation>
+<translation id="4838490908464673667">El plug-in necesario no está instalado.</translation>
+<translation id="8597182159515967513">cabecera</translation>
+<translation id="2653659639078652383">Enviar</translation>
+</translationbundle> \ No newline at end of file
diff --git a/webkit/glue/resources/webkit_strings_es.xtb b/webkit/glue/resources/webkit_strings_es.xtb
new file mode 100644
index 0000000..c95525d
--- /dev/null
+++ b/webkit/glue/resources/webkit_strings_es.xtb
@@ -0,0 +1,43 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="es">
+<translation id="4420062214988137980">Error en la instalación del plug-in</translation>
+<translation id="1235745349614807883">Eliminar búsquedas recientes</translation>
+<translation id="3825324228893189080">Se necesita un plug-in adicional</translation>
+<translation id="2965480764085142436">El plug-in <ph name="PLUGIN"/> no está instalado</translation>
+<translation id="5048533449481078685">marcador de listas</translation>
+<translation id="4202807286478387388">saltar</translation>
+<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation>
+<translation id="4317653869502688143">Confirma que deseas instalar el plug-in <ph name="PLUGIN"/>. Solo debes instalar plug-in fiables.</translation>
+<translation id="7658239707568436148">Cancelar</translation>
+<translation id="795667975304826397">No se ha seleccionado ningun archivo</translation>
+<translation id="1275511093094545429">Se requiere el plug-in <ph name="PLUGIN"/></translation>
+<translation id="8662565117025751661">Descargando plug-in...</translation>
+<translation id="8141602879876242471">Se trata de un índice que admite búsquedas. Introduce las palabras clave de búsqueda:</translation>
+<translation id="6845533974506654842">pulsar</translation>
+<translation id="8244226242650769279">mapa de imágenes</translation>
+<translation id="1383141426028388991">Error al instalar el plugin desde <ph name="URL"/></translation>
+<translation id="2548326553472216322">No hay búsquedas recientes</translation>
+<translation id="5944544982112848342">2048 (Grado elevado)</translation>
+<translation id="3040011195152428237">enlace</translation>
+<translation id="8281246460978372009">Tras instalar el complemento, haz clic aquí para actualizar.</translation>
+<translation id="7364796246159120393">Seleccionar archivo</translation>
+<translation id="8964020114565522021">Arrastrar archivo hasta aquí</translation>
+<translation id="838869780401515933">marcar</translation>
+<translation id="2846343701378493991">1024 (Mediano)</translation>
+<translation id="5476505524087279545">desmarcar</translation>
+<translation id="679352192834563463">No hay ningún plug-in disponible para mostrar este contenido</translation>
+<translation id="3789841737615482174">Instalar</translation>
+<translation id="6663448176199120256">Búsquedas recientes</translation>
+<translation id="3600343118165084788">Haz clic aquí para descargar el plug-in.</translation>
+<translation id="6807599807928161586">área web</translation>
+<translation id="5939518447894949180">Restablecer</translation>
+<translation id="3771786644471114952">Obtener plug-in</translation>
+<translation id="1842960171412779397">seleccionar</translation>
+<translation id="6119846243427417423">activar</translation>
+<translation id="8444882422881193423"><ph name="NUMBER_OF_FILES"/> archivos</translation>
+<translation id="3926627843712816530">Confirma que deseas instalar este plug-in. Solo debes instalar plug-in fiables.</translation>
+<translation id="4838490908464673667">El plug-in necesario no está instalado.</translation>
+<translation id="8597182159515967513">cabecera</translation>
+<translation id="2653659639078652383">Enviar</translation>
+</translationbundle> \ No newline at end of file
diff --git a/webkit/glue/resources/webkit_strings_et.xtb b/webkit/glue/resources/webkit_strings_et.xtb
new file mode 100644
index 0000000..c449c06
--- /dev/null
+++ b/webkit/glue/resources/webkit_strings_et.xtb
@@ -0,0 +1,43 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="et">
+<translation id="4420062214988137980">Lisandmooduli install ebaõnnestus</translation>
+<translation id="1235745349614807883">Kustuta viimased otsingud</translation>
+<translation id="3825324228893189080">Vajalik täiendav lisandmoodul</translation>
+<translation id="2965480764085142436"><ph name="PLUGIN"/> lisandmoodul ei ole installitud</translation>
+<translation id="5048533449481078685">loendilooja</translation>
+<translation id="4202807286478387388">liigu</translation>
+<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation>
+<translation id="4317653869502688143">Palun kinnitage, et soovite installida selle lisandmooduli <ph name="PLUGIN"/>. Te peaksite installima lisandmooduleid, mida usaldate.</translation>
+<translation id="7658239707568436148">Loobu</translation>
+<translation id="795667975304826397">Ühtegi faili pole valitud</translation>
+<translation id="1275511093094545429"><ph name="PLUGIN"/> vajalik lisandmoodul</translation>
+<translation id="8662565117025751661">Lisandmooduli allalaadimine...</translation>
+<translation id="8141602879876242471">See on otsitav indeks. Sisestage otsingu jaoks märksõnad:</translation>
+<translation id="6845533974506654842">vajuta</translation>
+<translation id="8244226242650769279">hüperpilt</translation>
+<translation id="1383141426028388991">Lisandmooduli installimine aadressilt <ph name="URL"/> ebaõnnestus.</translation>
+<translation id="2548326553472216322">Pole viimaseid otsingud</translation>
+<translation id="5944544982112848342">2048 (kõrge)</translation>
+<translation id="3040011195152428237">link</translation>
+<translation id="8281246460978372009">Pärast lisandmooduli installimist klõpsake värskendamiseks siia</translation>
+<translation id="7364796246159120393">Vali fail</translation>
+<translation id="8964020114565522021">Lohistage fail siia</translation>
+<translation id="838869780401515933">mrgista</translation>
+<translation id="2846343701378493991">1024 (keskmine)</translation>
+<translation id="5476505524087279545">eemalda mrgistus</translation>
+<translation id="679352192834563463">Selle sisu kuvamiseks pole saadaval lisandmoodulit</translation>
+<translation id="3789841737615482174">Installi</translation>
+<translation id="6663448176199120256">Viimased otsingud</translation>
+<translation id="3600343118165084788">Lisandmooduli allalaadimiseks klõpsake siia.</translation>
+<translation id="6807599807928161586">veebiala</translation>
+<translation id="5939518447894949180">Lähtesta</translation>
+<translation id="3771786644471114952">Hangi lisandmoodul</translation>
+<translation id="1842960171412779397">vali</translation>
+<translation id="6119846243427417423">aktiveeri</translation>
+<translation id="8444882422881193423"><ph name="NUMBER_OF_FILES"/> faili</translation>
+<translation id="3926627843712816530">Palun kinnitage, et soovite installida selle lisandmooduli. Te peaksite installima lisandmooduleid, mida usaldate.</translation>
+<translation id="4838490908464673667">Nõutav lisandmoodul pole installitud</translation>
+<translation id="8597182159515967513">pealkiri</translation>
+<translation id="2653659639078652383">Esita</translation>
+</translationbundle> \ No newline at end of file
diff --git a/webkit/glue/resources/webkit_strings_fi.xtb b/webkit/glue/resources/webkit_strings_fi.xtb
new file mode 100644
index 0000000..50f3a69
--- /dev/null
+++ b/webkit/glue/resources/webkit_strings_fi.xtb
@@ -0,0 +1,43 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="fi">
+<translation id="4420062214988137980">Laajennuksen asennus epäonnistui</translation>
+<translation id="1235745349614807883">Poista viimeisimmät haut</translation>
+<translation id="3825324228893189080">Lisälaajennus tarvitaan</translation>
+<translation id="2965480764085142436">Laajennusta <ph name="PLUGIN"/> ei asennettu</translation>
+<translation id="5048533449481078685">luettelon merkitsijä</translation>
+<translation id="4202807286478387388">siirry</translation>
+<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation>
+<translation id="4317653869502688143">Vahvista, että haluat asentaa laajennuksen <ph name="PLUGIN"/>. Kannattaa asentaa vain luotettavia laajennuksia.</translation>
+<translation id="7658239707568436148">Peruuta</translation>
+<translation id="795667975304826397">Ei valittua tiedostoa</translation>
+<translation id="1275511093094545429"><ph name="PLUGIN"/> laajennus tarvitaan</translation>
+<translation id="8662565117025751661">Ladataan laajennusta...</translation>
+<translation id="8141602879876242471">Tämä on haettavissa oleva hakemisto. Anna hakusanat:</translation>
+<translation id="6845533974506654842">paina</translation>
+<translation id="8244226242650769279">kuvakartta</translation>
+<translation id="1383141426028388991">Laajennuksen asennus osoitteesta <ph name="URL"/> epäonnistui</translation>
+<translation id="2548326553472216322">Ei viimeisimpiä hakuja</translation>
+<translation id="5944544982112848342">2048 (korkea taso)</translation>
+<translation id="3040011195152428237">linkki</translation>
+<translation id="8281246460978372009">Päivitä laajennuksen asennuksen jälkeen napsauttamalla tästä</translation>
+<translation id="7364796246159120393">Valitse tiedosto</translation>
+<translation id="8964020114565522021">Vedä tiedosto tähän</translation>
+<translation id="838869780401515933">valitse</translation>
+<translation id="2846343701378493991">1024 (keskitaso)</translation>
+<translation id="5476505524087279545">poista valinta</translation>
+<translation id="679352192834563463">Tämän sisällön näyttämiseen ei ole saatavissa laajennusta</translation>
+<translation id="3789841737615482174">Asenna</translation>
+<translation id="6663448176199120256">Viimeisimmät haut</translation>
+<translation id="3600343118165084788">Lataa laajennus napsauttamalla tätä</translation>
+<translation id="6807599807928161586">verkkoalue</translation>
+<translation id="5939518447894949180">Tyhjennä</translation>
+<translation id="3771786644471114952">Hae laajennus</translation>
+<translation id="1842960171412779397">Valitse</translation>
+<translation id="6119846243427417423">aktivoi</translation>
+<translation id="8444882422881193423"><ph name="NUMBER_OF_FILES"/> tiedostoa</translation>
+<translation id="3926627843712816530">Vahvista, että haluat asentaa tämän laajennuksen. Kannattaa asentaa vain luotettavia laajennuksia.</translation>
+<translation id="4838490908464673667">Vaadittua laajennusta ei ole asennettu</translation>
+<translation id="8597182159515967513">otsikko</translation>
+<translation id="2653659639078652383">Lähetä</translation>
+</translationbundle> \ No newline at end of file
diff --git a/webkit/glue/resources/webkit_strings_fil.xtb b/webkit/glue/resources/webkit_strings_fil.xtb
new file mode 100644
index 0000000..5ed7e04
--- /dev/null
+++ b/webkit/glue/resources/webkit_strings_fil.xtb
@@ -0,0 +1,43 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="fil">
+<translation id="4420062214988137980">Nabigong pag-install sa plugin</translation>
+<translation id="1235745349614807883">Lisiman ang Kasalukuyang Mga Paghahanap</translation>
+<translation id="3825324228893189080">Kailangan ng karagdagang plugin</translation>
+<translation id="2965480764085142436"><ph name="PLUGIN"/> hindi na-install ang plugin</translation>
+<translation id="5048533449481078685">Ilista ang marker</translation>
+<translation id="4202807286478387388">tumalon</translation>
+<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation>
+<translation id="4317653869502688143">Mangyaring kumpirmahin na nais mong i-install ang <ph name="PLUGIN"/> plugin. Dapat mo lamang i-install ang mga plugin na pinagkakatiwalaan mo.</translation>
+<translation id="7658239707568436148">Ikansela</translation>
+<translation id="795667975304826397">Walang napiling file</translation>
+<translation id="1275511093094545429"><ph name="PLUGIN"/> kinakailangang plugin</translation>
+<translation id="8662565117025751661">Nagda-download ng plugin...</translation>
+<translation id="8141602879876242471">Isa itong paghahanap ng index. Ipasok ang paghahanap sa mga keyword:</translation>
+<translation id="6845533974506654842">pindutin</translation>
+<translation id="8244226242650769279">mapa ng imahe</translation>
+<translation id="1383141426028388991">Nabigong i-install ang plugin mula sa <ph name="URL"/></translation>
+<translation id="2548326553472216322">Walang kamakailang mga paghahanap</translation>
+<translation id="5944544982112848342">2048 (Pinakamataas na Marka)</translation>
+<translation id="3040011195152428237">link</translation>
+<translation id="8281246460978372009">Matapos ang pag-install ng plugin, mag-click dito upang mag-refresh</translation>
+<translation id="7364796246159120393">Pumili ng File</translation>
+<translation id="8964020114565522021">Kaldkarin dito ang file</translation>
+<translation id="838869780401515933">I-tsek</translation>
+<translation id="2846343701378493991">1024 (Katamtamang Grado)</translation>
+<translation id="5476505524087279545">i-uncheck</translation>
+<translation id="679352192834563463">Walang magagamit na plugin na ipapakita sa nilalaman nito</translation>
+<translation id="3789841737615482174">Install</translation>
+<translation id="6663448176199120256">Kasalukuyang Mga Paghahanap</translation>
+<translation id="3600343118165084788">Mag-click dito upagn mai-download ang plugin</translation>
+<translation id="6807599807928161586">web area</translation>
+<translation id="5939518447894949180">I-reset</translation>
+<translation id="3771786644471114952">Kunin ang Plugin</translation>
+<translation id="1842960171412779397">piliin</translation>
+<translation id="6119846243427417423">isaaktibo</translation>
+<translation id="8444882422881193423"><ph name="NUMBER_OF_FILES"/> mga file</translation>
+<translation id="3926627843712816530">Mangyaring kumpirmahin na dapat gusto mong i-install sa plugin na ito. Dapat mo lamang i-install ang mga plugin na pinagkakatiwalaan mo.</translation>
+<translation id="4838490908464673667">Ang kinakailangan na plugin ay hindi na-install</translation>
+<translation id="8597182159515967513">heading</translation>
+<translation id="2653659639078652383">Isumite</translation>
+</translationbundle> \ No newline at end of file
diff --git a/webkit/glue/resources/webkit_strings_fr.xtb b/webkit/glue/resources/webkit_strings_fr.xtb
new file mode 100644
index 0000000..1f78547
--- /dev/null
+++ b/webkit/glue/resources/webkit_strings_fr.xtb
@@ -0,0 +1,43 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="fr">
+<translation id="4420062214988137980">Échec de l'installation du plug-in</translation>
+<translation id="1235745349614807883">Effacer les recherches récentes</translation>
+<translation id="3825324228893189080">Plug-in supplémentaire requis</translation>
+<translation id="2965480764085142436">Le plug-in <ph name="PLUGIN"/> n'est pas installé.</translation>
+<translation id="5048533449481078685">marqueur de liste</translation>
+<translation id="4202807286478387388">accéder</translation>
+<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/> × <ph name="HEIGHT"/></translation>
+<translation id="4317653869502688143">Veuillez confirmer que vous souhaitez installer le plug-in <ph name="PLUGIN"/>. Veillez à installer uniquement des plug-ins approuvés.</translation>
+<translation id="7658239707568436148">Annuler</translation>
+<translation id="795667975304826397">Aucun fichier choisi</translation>
+<translation id="1275511093094545429">Plug-in <ph name="PLUGIN"/> requis</translation>
+<translation id="8662565117025751661">Téléchargement du plug-in...</translation>
+<translation id="8141602879876242471">Vous pouvez lancer des recherches dans cet index. Pour cela, entrez des mots clés de recherche :</translation>
+<translation id="6845533974506654842">appuyer</translation>
+<translation id="8244226242650769279">image map</translation>
+<translation id="1383141426028388991">Échec de l'installation du plugin depuis <ph name="URL"/></translation>
+<translation id="2548326553472216322">Aucune recherche récente</translation>
+<translation id="5944544982112848342">2048 (haute sécurité)</translation>
+<translation id="3040011195152428237">Lien</translation>
+<translation id="8281246460978372009">Après l'installation du plug-in, cliquez ici pour actualiser.</translation>
+<translation id="7364796246159120393">Choisissez un fichier</translation>
+<translation id="8964020114565522021">Placer le fichier ici</translation>
+<translation id="838869780401515933">cocher</translation>
+<translation id="2846343701378493991">1024 (sécurité moyenne)</translation>
+<translation id="5476505524087279545">décocher</translation>
+<translation id="679352192834563463">Aucun plug-in disponible pour afficher ce contenu</translation>
+<translation id="3789841737615482174">Installer</translation>
+<translation id="6663448176199120256">Recherches récentes</translation>
+<translation id="3600343118165084788">Cliquez ici pour télécharger le plug-in</translation>
+<translation id="6807599807928161586">Zone Web</translation>
+<translation id="5939518447894949180">Réinitialiser</translation>
+<translation id="3771786644471114952">Ajouter le plug-in</translation>
+<translation id="1842960171412779397">sélectionner</translation>
+<translation id="6119846243427417423">activer</translation>
+<translation id="8444882422881193423"><ph name="NUMBER_OF_FILES"/> fichiers</translation>
+<translation id="3926627843712816530">Veuillez confirmer que vous souhaitez installer ce plug-in. Vous devez uniquement installer des plug-ins approuvés.</translation>
+<translation id="4838490908464673667">Le plug-in requis n'est pas installé.</translation>
+<translation id="8597182159515967513">en-tête</translation>
+<translation id="2653659639078652383">Valider</translation>
+</translationbundle> \ No newline at end of file
diff --git a/webkit/glue/resources/webkit_strings_gu.xtb b/webkit/glue/resources/webkit_strings_gu.xtb
new file mode 100644
index 0000000..fb40c72
--- /dev/null
+++ b/webkit/glue/resources/webkit_strings_gu.xtb
@@ -0,0 +1,43 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="gu">
+<translation id="4420062214988137980">પ્લગઇન ઇન્સ્ટોલેશન નિષ્ફળ થયું છે</translation>
+<translation id="1235745349614807883">હાલની શોધને સાફ કરો</translation>
+<translation id="3825324228893189080">વધારાના પ્લગઇનની જરૂરિયાત</translation>
+<translation id="2965480764085142436"><ph name="PLUGIN"/> પ્લગઇન ઇન્સ્ટોલ કરેલું નથી</translation>
+<translation id="5048533449481078685">સૂચિ માર્કર</translation>
+<translation id="4202807286478387388">જંપ કરો</translation>
+<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation>
+<translation id="4317653869502688143">કૃપા કરીને પુષ્ટિ કરો કે તમને <ph name="PLUGIN"/> પ્લગઇન ઇન્સ્ટોલ કરવું ગમશે. તમારે ફક્ત તે જ પ્લગઇન્સ ઇન્સ્ટોલ કરવા જોઈએ જેના પર તમે વિશ્વાસ કરો છો.</translation>
+<translation id="7658239707568436148">રદ કરો</translation>
+<translation id="795667975304826397">કોઈ ફાઇલ પસંદ કરેલી નથી</translation>
+<translation id="1275511093094545429"><ph name="PLUGIN"/> પ્લગઇનની જરૂરિયાત છે</translation>
+<translation id="8662565117025751661">પ્લગઇન ડાઉનલોડ કરી રહ્યું છે...</translation>
+<translation id="8141602879876242471">આ એક શોધસક્ષમ અનુક્રમણિકા છે. શોધ કીવર્ડ્સ દાખલ કરો:</translation>
+<translation id="6845533974506654842">દબાવો</translation>
+<translation id="8244226242650769279">છબી નકશો</translation>
+<translation id="1383141426028388991"><ph name="URL"/> માંથી પ્લગઇન ઇન્સ્ટોલ કરવામાં નિષ્ફળ</translation>
+<translation id="2548326553472216322">હાલની શોધો નથી</translation>
+<translation id="5944544982112848342">2048 (ઉચ્ચ ગ્રેડ)</translation>
+<translation id="3040011195152428237">લિંક</translation>
+<translation id="8281246460978372009">પ્લગઇન ઇન્સ્ટોલ કર્યા પછી, રીફ્રેશ કરવા અહીં ક્લિક કરો</translation>
+<translation id="7364796246159120393">ફાઇલ પસંદ કરો</translation>
+<translation id="8964020114565522021">ફાઇલને અહીં ખેંચો</translation>
+<translation id="838869780401515933">તપાસો</translation>
+<translation id="2846343701378493991">1024 (મધ્યમ ગ્રેડ)</translation>
+<translation id="5476505524087279545">અનચેક કરો</translation>
+<translation id="679352192834563463">આ સમાગ્રી પ્રદર્શિત કરવા માટે કોઈ પ્લગઇન ઉપલબ્ધ નથી</translation>
+<translation id="3789841737615482174">ઇન્સ્ટોલ કરો</translation>
+<translation id="6663448176199120256">તાજેતરની શોધ</translation>
+<translation id="3600343118165084788">પ્લગઇન ડાઉનલોડ કરવા માટે અહીં ક્લિક કરો</translation>
+<translation id="6807599807928161586">વેબ ક્ષેત્ર</translation>
+<translation id="5939518447894949180">રીસેટ કરો</translation>
+<translation id="3771786644471114952">પ્લગઇન મેળવો</translation>
+<translation id="1842960171412779397">પસંદ કરો</translation>
+<translation id="6119846243427417423">સક્રિય કરો</translation>
+<translation id="8444882422881193423"><ph name="NUMBER_OF_FILES"/> ફાઇલો</translation>
+<translation id="3926627843712816530">કૃપા કરીને પુષ્ટિ કરો કે તમને આ પ્લગઇન ઇન્સ્ટોલ કરવું ગમશે. તમારે ફક્ત તે જ પ્લગઇન્સ ઇન્સ્ટોલ કરવા જોઈએ જેના પર તમે વિશ્વાસ કરો છો.</translation>
+<translation id="4838490908464673667">જોઈતું પ્લગઇન ઇન્સ્ટોલ કરેલું નથી</translation>
+<translation id="8597182159515967513">મથાળું</translation>
+<translation id="2653659639078652383">સબમિટ કરો</translation>
+</translationbundle> \ No newline at end of file
diff --git a/webkit/glue/resources/webkit_strings_hi.xtb b/webkit/glue/resources/webkit_strings_hi.xtb
new file mode 100644
index 0000000..8522437
--- /dev/null
+++ b/webkit/glue/resources/webkit_strings_hi.xtb
@@ -0,0 +1,43 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="hi">
+<translation id="4420062214988137980">प्लगइन स्थापना असफ़ल रही</translation>
+<translation id="1235745349614807883">हाल ही की खोजें साफ़ करें</translation>
+<translation id="3825324228893189080">अतिरिक्त प्लगइन आवश्यक</translation>
+<translation id="2965480764085142436"><ph name="PLUGIN"/> प्लगइन स्थापित नहीं किया गया है</translation>
+<translation id="5048533449481078685">सूची चिन्हक</translation>
+<translation id="4202807286478387388">जाएँ</translation>
+<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation>
+<translation id="4317653869502688143">कृपया पुष्टि करें कि आप <ph name="PLUGIN"/> प्लगइन स्थापित करना चाहते हैं. आपके केवल वे प्लगइन स्थापित करने चाहिए जिनपर आप भरोसा करते हों.</translation>
+<translation id="7658239707568436148">रद्द करें</translation>
+<translation id="795667975304826397">कोई फाइल नहीं चुनी गई</translation>
+<translation id="1275511093094545429"><ph name="PLUGIN"/> प्लगइन आवश्यक</translation>
+<translation id="8662565117025751661">प्लगइन डाउनलोड कर रहा है ...</translation>
+<translation id="8141602879876242471">यह एक खोजने योग्य इंडेक्स है. खोज कुंजीशब्द प्रविष्ट करें :</translation>
+<translation id="6845533974506654842">दबाएँ</translation>
+<translation id="8244226242650769279">चित्र मैप</translation>
+<translation id="1383141426028388991"><ph name="URL"/> से प्लगइन इंस्टाल करने में असफ़ल रहा</translation>
+<translation id="2548326553472216322">हाल ही में कोई खोज नहीं</translation>
+<translation id="5944544982112848342">2048 (उच्च ग्रेड)</translation>
+<translation id="3040011195152428237">लिंक</translation>
+<translation id="8281246460978372009">प्लगइन स्थापित करने के बाद, पुन: ताज़ा करने के लिए यहाँ क्लिक करें</translation>
+<translation id="7364796246159120393">फ़ाइल चुनें</translation>
+<translation id="8964020114565522021">फाइल खींचकर यहाँ लाएं</translation>
+<translation id="838869780401515933">चेक करें</translation>
+<translation id="2846343701378493991">1024 (मध्यम ग्रेड)</translation>
+<translation id="5476505524087279545">अनचेक करें</translation>
+<translation id="679352192834563463">इस सामग्री को प्रदर्शित करने के लिए कोई प्लगइन उपलब्ध नहीं है</translation>
+<translation id="3789841737615482174">स्थापित करें</translation>
+<translation id="6663448176199120256">हाल ही में की गई खोजें</translation>
+<translation id="3600343118165084788">प्लगइन डाउनलोड करने के लिए यहाँ क्लिक करें</translation>
+<translation id="6807599807928161586">वेब क्षेत्र</translation>
+<translation id="5939518447894949180">पुन: सेट करें</translation>
+<translation id="3771786644471114952">प्लगइन प्राप्त करें</translation>
+<translation id="1842960171412779397">चुनें</translation>
+<translation id="6119846243427417423">सक्रिय करें</translation>
+<translation id="8444882422881193423"><ph name="NUMBER_OF_FILES"/> फ़ाइलें</translation>
+<translation id="3926627843712816530">कृपया पुष्टि करें कि आप यह प्लगइन स्थापित करना चाहते हैं. आपके केवल वे प्लगइन स्थापित करने चाहिए जिनपर आप भरोसा करते हों.</translation>
+<translation id="4838490908464673667">आवश्यक प्लगइन स्थापित नहीं है</translation>
+<translation id="8597182159515967513">हेडिंग</translation>
+<translation id="2653659639078652383">जमा करें</translation>
+</translationbundle> \ No newline at end of file
diff --git a/webkit/glue/resources/webkit_strings_hr.xtb b/webkit/glue/resources/webkit_strings_hr.xtb
new file mode 100644
index 0000000..bc93649
--- /dev/null
+++ b/webkit/glue/resources/webkit_strings_hr.xtb
@@ -0,0 +1,43 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="hr">
+<translation id="4420062214988137980">Nije uspjela instalacija dodatka</translation>
+<translation id="1235745349614807883">Obriši najnovija pretraživanja</translation>
+<translation id="3825324228893189080">Potreban je dodatak</translation>
+<translation id="2965480764085142436">Nije instaliran dodatak za <ph name="PLUGIN"/></translation>
+<translation id="5048533449481078685">oznaka popisa</translation>
+<translation id="4202807286478387388">skoči</translation>
+<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation>
+<translation id="4317653869502688143">Potvrdite da želite instalirati dodatak za <ph name="PLUGIN"/> Instalirajte samo one dodatke kojima vjerujete.</translation>
+<translation id="7658239707568436148">Odustani</translation>
+<translation id="795667975304826397">Nije odabrana niti jedna datoteka.</translation>
+<translation id="1275511093094545429">Potreban je dodatak za <ph name="PLUGIN"/></translation>
+<translation id="8662565117025751661">Preuzimanje dodatka...</translation>
+<translation id="8141602879876242471">Ovaj je indeks moguće pretraživati. Unesite ključne riječi za pretraživanje:</translation>
+<translation id="6845533974506654842">pritisni</translation>
+<translation id="8244226242650769279">karta slika</translation>
+<translation id="1383141426028388991">Nije uspjela instalacija dodatka s <ph name="URL"/></translation>
+<translation id="2548326553472216322">Nema najnovijih pretraživanja</translation>
+<translation id="5944544982112848342">2048 (visoki stupanj)</translation>
+<translation id="3040011195152428237">veza</translation>
+<translation id="8281246460978372009">Nakon instalacije dodatka, kliknite ovdje za osvježenje stranice</translation>
+<translation id="7364796246159120393">Odaberi datoteku</translation>
+<translation id="8964020114565522021">Povucite datoteku ovamo</translation>
+<translation id="838869780401515933">označi</translation>
+<translation id="2846343701378493991">1024 (srednji)</translation>
+<translation id="5476505524087279545">ukloni oznaku</translation>
+<translation id="679352192834563463">Nema dostupnog dodatka za prikaz ovog sadržaja</translation>
+<translation id="3789841737615482174">Instaliraj</translation>
+<translation id="6663448176199120256">Najnovija pretraživanja</translation>
+<translation id="3600343118165084788">Kliknite ovdje za preuzimanje dodatka</translation>
+<translation id="6807599807928161586">web područje</translation>
+<translation id="5939518447894949180">Ponovno postavi</translation>
+<translation id="3771786644471114952">Dohvati dodatak</translation>
+<translation id="1842960171412779397">odaberi</translation>
+<translation id="6119846243427417423">aktiviraj</translation>
+<translation id="8444882422881193423">Broj datoteka: <ph name="NUMBER_OF_FILES"/></translation>
+<translation id="3926627843712816530">Potvrdite da želite instalirati ovaj dodatak. Instalirajte samo one dodatke kojima vjerujete.</translation>
+<translation id="4838490908464673667">Potreban dodatak nije instaliran</translation>
+<translation id="8597182159515967513">naslov</translation>
+<translation id="2653659639078652383">Pošalji</translation>
+</translationbundle> \ No newline at end of file
diff --git a/webkit/glue/resources/webkit_strings_hu.xtb b/webkit/glue/resources/webkit_strings_hu.xtb
new file mode 100644
index 0000000..7a04612
--- /dev/null
+++ b/webkit/glue/resources/webkit_strings_hu.xtb
@@ -0,0 +1,43 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="hu">
+<translation id="4420062214988137980">A plugin telepítése nem sikerült</translation>
+<translation id="1235745349614807883">Friss keresések törlése</translation>
+<translation id="3825324228893189080">További plugin szükséges</translation>
+<translation id="2965480764085142436">A(z) <ph name="PLUGIN"/> plugin nem lett telepítve</translation>
+<translation id="5048533449481078685">listajelölő</translation>
+<translation id="4202807286478387388">Mehet</translation>
+<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation>
+<translation id="4317653869502688143">Kérjük, erősítse meg, hogy telepíteni szeretné a(z) <ph name="PLUGIN"/> plugint. Csak azokat a plugineket telepítse, amelyekben megbízik.</translation>
+<translation id="7658239707568436148">Mégse</translation>
+<translation id="795667975304826397">Nem lett fájl kiválasztva</translation>
+<translation id="1275511093094545429"><ph name="PLUGIN"/> plugin szükséges</translation>
+<translation id="8662565117025751661">Plugin letöltése...</translation>
+<translation id="8141602879876242471">Ez egy kereshető index. Írjon be keresési kulcsszavakat:</translation>
+<translation id="6845533974506654842">Gomb lenyomása</translation>
+<translation id="8244226242650769279">képtérkép</translation>
+<translation id="1383141426028388991">A plugin telepítése nem sikerült a következő helyről: <ph name="URL"/></translation>
+<translation id="2548326553472216322">Nincsenek friss keresések</translation>
+<translation id="5944544982112848342">2048 (magasfokú)</translation>
+<translation id="3040011195152428237">link</translation>
+<translation id="8281246460978372009">A plugin telepítését követően ide kattintson a frissítéshez</translation>
+<translation id="7364796246159120393">Fájl kiválasztása</translation>
+<translation id="8964020114565522021">Húzza át ide a fájlt</translation>
+<translation id="838869780401515933">Megjelölés</translation>
+<translation id="2846343701378493991">1024 (Közepes)</translation>
+<translation id="5476505524087279545">Megjelölés eltávolítása</translation>
+<translation id="679352192834563463">Nincs elérhető plugin a tartalom megjelenítéséhez</translation>
+<translation id="3789841737615482174">Telepítés</translation>
+<translation id="6663448176199120256">Friss keresések</translation>
+<translation id="3600343118165084788">Ha ide kattint, letöltheti a plugint</translation>
+<translation id="6807599807928161586">internetes terület</translation>
+<translation id="5939518447894949180">Visszaállítás</translation>
+<translation id="3771786644471114952">Plugin beszerzése</translation>
+<translation id="1842960171412779397">Kiválasztás</translation>
+<translation id="6119846243427417423">Aktiválás</translation>
+<translation id="8444882422881193423"><ph name="NUMBER_OF_FILES"/> fájl</translation>
+<translation id="3926627843712816530">Kérjük, erősítse meg, hogy telepíteni szeretné ezt a plugint. Csak azokat a plugineket telepítse, amelyekben megbízik.</translation>
+<translation id="4838490908464673667">A szükséges plugin nem lett telepítve</translation>
+<translation id="8597182159515967513">fejléc</translation>
+<translation id="2653659639078652383">Elküldés</translation>
+</translationbundle> \ No newline at end of file
diff --git a/webkit/glue/resources/webkit_strings_id.xtb b/webkit/glue/resources/webkit_strings_id.xtb
new file mode 100644
index 0000000..d174e84
--- /dev/null
+++ b/webkit/glue/resources/webkit_strings_id.xtb
@@ -0,0 +1,43 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="id">
+<translation id="4420062214988137980">Penginstalan plug-in gagal</translation>
+<translation id="1235745349614807883">Hapus Penelusuran Barusan</translation>
+<translation id="3825324228893189080">Diperlukan plug-in tambahan</translation>
+<translation id="2965480764085142436"><ph name="PLUGIN"/> plug-in belum terinstal</translation>
+<translation id="5048533449481078685">penanda daftar</translation>
+<translation id="4202807286478387388">lompati</translation>
+<translation id="4611115858363067980"><ph name="FILENAME"/> - <ph name="WIDTH"/>×<ph name="HEIGHT"/></translation>
+<translation id="4317653869502688143">Konfirmasikan bahwa Anda ingin menginstal plug-in<ph name="PLUGIN"/>. Sebaiknya hanya instal plug-in yang Anda percaya.</translation>
+<translation id="7658239707568436148">Batal</translation>
+<translation id="795667975304826397">Tidak ada file yang dipilih</translation>
+<translation id="1275511093094545429"><ph name="PLUGIN"/> diperlukan plug-in</translation>
+<translation id="8662565117025751661">Mengunduh plug-in...</translation>
+<translation id="8141602879876242471">Terdapat indeks yang dapat dicari. Masukkan kata kunci penelusuran:</translation>
+<translation id="6845533974506654842">tekan</translation>
+<translation id="8244226242650769279">gambar peta</translation>
+<translation id="1383141426028388991">Gagal menginstal plug-in dari <ph name="URL"/></translation>
+<translation id="2548326553472216322">Tidak ada penelusuran terkini</translation>
+<translation id="5944544982112848342">2048 (Tingkat Tinggi)</translation>
+<translation id="3040011195152428237">tautan</translation>
+<translation id="8281246460978372009">Setelah menginstal plug-in, klik di sini untuk me-refresh</translation>
+<translation id="7364796246159120393">Pilih Berkas</translation>
+<translation id="8964020114565522021">Tarik file ke sini</translation>
+<translation id="838869780401515933">centangi</translation>
+<translation id="2846343701378493991">1024 (Tingkat Menengah)</translation>
+<translation id="5476505524087279545">batalkan centang</translation>
+<translation id="679352192834563463">Tidak tersedia plug-in untuk menampilkan konten ini</translation>
+<translation id="3789841737615482174">Instal</translation>
+<translation id="6663448176199120256">Penelusuran Barusan</translation>
+<translation id="3600343118165084788">Klik di sini untuk mengunduh plug-in</translation>
+<translation id="6807599807928161586">area Web</translation>
+<translation id="5939518447894949180">Atur ulang</translation>
+<translation id="3771786644471114952">Dapatkan Plug-in</translation>
+<translation id="1842960171412779397">pilih</translation>
+<translation id="6119846243427417423">aktifkan</translation>
+<translation id="8444882422881193423"><ph name="NUMBER_OF_FILES"/> berkas</translation>
+<translation id="3926627843712816530">Konfirmasikan bahwa Anda ingin menginstal plug-in ini. Sebaiknya hanya instal plug-in yang Anda percaya.</translation>
+<translation id="4838490908464673667">Plug-in yang diperlukan belum terinstal</translation>
+<translation id="8597182159515967513">kepala</translation>
+<translation id="2653659639078652383">Kirim</translation>
+</translationbundle> \ No newline at end of file
diff --git a/webkit/glue/resources/webkit_strings_it.xtb b/webkit/glue/resources/webkit_strings_it.xtb
new file mode 100644
index 0000000..f599db0
--- /dev/null
+++ b/webkit/glue/resources/webkit_strings_it.xtb
@@ -0,0 +1,43 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="it">
+<translation id="4420062214988137980">Installazione plug-in non riuscita</translation>
+<translation id="1235745349614807883">Cancella ricerche recenti</translation>
+<translation id="3825324228893189080">Plug-in aggiuntivo necessario</translation>
+<translation id="2965480764085142436">Plug-in <ph name="PLUGIN"/> non installato</translation>
+<translation id="5048533449481078685">indicatore elenco</translation>
+<translation id="4202807286478387388">vai</translation>
+<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation>
+<translation id="4317653869502688143">Conferma l'installazione del plug-in <ph name="PLUGIN"/>. Ti invitiamo a installare solo i plug-in che ritieni affidabili.</translation>
+<translation id="7658239707568436148">Annulla</translation>
+<translation id="795667975304826397">Nessun file selezionato</translation>
+<translation id="1275511093094545429">Plug-in <ph name="PLUGIN"/> necessario</translation>
+<translation id="8662565117025751661">Download del plug-in in corso...</translation>
+<translation id="8141602879876242471">Questo è un indice di ricerca. Inserisci le parole chiave di ricerca:</translation>
+<translation id="6845533974506654842">premi</translation>
+<translation id="8244226242650769279">image map</translation>
+<translation id="1383141426028388991">Installazione plug-in da <ph name="URL"/> non riuscita</translation>
+<translation id="2548326553472216322">Nessuna ricerca recente</translation>
+<translation id="5944544982112848342">2048 (alta qualità)</translation>
+<translation id="3040011195152428237">link</translation>
+<translation id="8281246460978372009">Dopo aver installato il plug-in, fai clic qui per aggiornare</translation>
+<translation id="7364796246159120393">Scegli file</translation>
+<translation id="8964020114565522021">Trascina il file qui</translation>
+<translation id="838869780401515933">seleziona</translation>
+<translation id="2846343701378493991">1024 (Medium Grade)</translation>
+<translation id="5476505524087279545">deseleziona</translation>
+<translation id="679352192834563463">Nessun plug-in disponibile per visualizzare il contenuto</translation>
+<translation id="3789841737615482174">Installa</translation>
+<translation id="6663448176199120256">Ricerche recenti</translation>
+<translation id="3600343118165084788">Fai clic qui per scaricare il plug-in</translation>
+<translation id="6807599807928161586">area web</translation>
+<translation id="5939518447894949180">Ripristina</translation>
+<translation id="3771786644471114952">Aggiungi plug-in</translation>
+<translation id="1842960171412779397">seleziona</translation>
+<translation id="6119846243427417423">attiva</translation>
+<translation id="8444882422881193423"><ph name="NUMBER_OF_FILES"/> file</translation>
+<translation id="3926627843712816530">Conferma l'installazione del plug-in. Ti invitiamo a installare solo i plug-in che ritieni affidabili.</translation>
+<translation id="4838490908464673667">Il plug-in richiesto non è installato</translation>
+<translation id="8597182159515967513">intestazione</translation>
+<translation id="2653659639078652383">Invia</translation>
+</translationbundle> \ No newline at end of file
diff --git a/webkit/glue/resources/webkit_strings_iw.xtb b/webkit/glue/resources/webkit_strings_iw.xtb
new file mode 100644
index 0000000..652d600
--- /dev/null
+++ b/webkit/glue/resources/webkit_strings_iw.xtb
@@ -0,0 +1,43 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="iw">
+<translation id="4420062214988137980">התקנת פלגא-אין נכשלה</translation>
+<translation id="1235745349614807883">הסר חיפושים אחרונים</translation>
+<translation id="3825324228893189080">דרוש תוסף נוסף</translation>
+<translation id="2965480764085142436"><ph name="PLUGIN"/> אינו מותקן</translation>
+<translation id="5048533449481078685">סמן רשימה</translation>
+<translation id="4202807286478387388">קפוץ</translation>
+<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation>
+<translation id="4317653869502688143">אשר שברצונך להתקין את התוסף <ph name="PLUGIN"/>. עליך להתקין רק תוספים אתה בוטח.</translation>
+<translation id="7658239707568436148">ביטול</translation>
+<translation id="795667975304826397">לא נבחר קובץ</translation>
+<translation id="1275511093094545429">נדרש plugin <ph name="PLUGIN"/></translation>
+<translation id="8662565117025751661">מוריד תוסף...</translation>
+<translation id="8141602879876242471">זהו אינדקס שניתן לבצע בו חיפוש. הזן מילות מפתח לחיפוש:</translation>
+<translation id="6845533974506654842">לחץ</translation>
+<translation id="8244226242650769279">מפת תמונות</translation>
+<translation id="1383141426028388991">התקנת התוסף (plugin) מ-<ph name="URL"/> נכשלה</translation>
+<translation id="2548326553472216322">אין חיפושים אחרונים</translation>
+<translation id="5944544982112848342">2048 (High Grade)</translation>
+<translation id="3040011195152428237">קישור</translation>
+<translation id="8281246460978372009">לאחר התקנת התוסף, לחץ כאן לרענון</translation>
+<translation id="7364796246159120393">בחר קובץ</translation>
+<translation id="8964020114565522021">גרור את הקובץ לכאן</translation>
+<translation id="838869780401515933">סמן</translation>
+<translation id="2846343701378493991">1024 (Medium Grade)</translation>
+<translation id="5476505524087279545">בטל סימון</translation>
+<translation id="679352192834563463">אין תוסף זמין להצגת תוכן זה</translation>
+<translation id="3789841737615482174">התקן</translation>
+<translation id="6663448176199120256">חיפושים אחרונים</translation>
+<translation id="3600343118165084788">לחץ כאן להורדת התוסף</translation>
+<translation id="6807599807928161586">אזור אינטרנט</translation>
+<translation id="5939518447894949180">אפס</translation>
+<translation id="3771786644471114952">קבל תוסף</translation>
+<translation id="1842960171412779397">בחר</translation>
+<translation id="6119846243427417423">הפעל</translation>
+<translation id="8444882422881193423"><ph name="NUMBER_OF_FILES"/> קבצים</translation>
+<translation id="3926627843712816530">אנא אשר שברצונך להתקין תוסף זה. עליך להתקין רק תוספים שבהם אתה בוטח.</translation>
+<translation id="4838490908464673667">התוסף המבוקש אינו מותקן</translation>
+<translation id="8597182159515967513">כותרת</translation>
+<translation id="2653659639078652383">שלח</translation>
+</translationbundle> \ No newline at end of file
diff --git a/webkit/glue/resources/webkit_strings_ja.xtb b/webkit/glue/resources/webkit_strings_ja.xtb
new file mode 100644
index 0000000..c16df86
--- /dev/null
+++ b/webkit/glue/resources/webkit_strings_ja.xtb
@@ -0,0 +1,43 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="ja">
+<translation id="4420062214988137980">プラグインのインストールが失敗しました</translation>
+<translation id="1235745349614807883">最近の検索履歴を消去</translation>
+<translation id="3825324228893189080">追加の必要なプラグイン</translation>
+<translation id="2965480764085142436"><ph name="PLUGIN"/> プラグインはインストールされていません</translation>
+<translation id="5048533449481078685">リスト マーカー</translation>
+<translation id="4202807286478387388">ジャンプ</translation>
+<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation>
+<translation id="4317653869502688143"><ph name="PLUGIN"/> プラグインのインストールを確認してください。信頼できるプラグインだけインストールしてください。</translation>
+<translation id="7658239707568436148">キャンセル</translation>
+<translation id="795667975304826397">選択されていません</translation>
+<translation id="1275511093094545429"><ph name="PLUGIN"/> プラグインが必要です</translation>
+<translation id="8662565117025751661">プラグインをダウンロードしています...</translation>
+<translation id="8141602879876242471">このインデックスは検索できます。キーワードを入力してください:</translation>
+<translation id="6845533974506654842">押す</translation>
+<translation id="8244226242650769279">イメージ マップ</translation>
+<translation id="1383141426028388991"><ph name="URL"/> からプラグインをインストールできませんでした</translation>
+<translation id="2548326553472216322">最近の検索はありません</translation>
+<translation id="5944544982112848342">2048 (高)</translation>
+<translation id="3040011195152428237">リンク</translation>
+<translation id="8281246460978372009">プラグインをインストールした後は、ここをクリックして更新してください</translation>
+<translation id="7364796246159120393">ファイルを選択</translation>
+<translation id="8964020114565522021">ファイルをここにドラッグ</translation>
+<translation id="838869780401515933">チェックを付ける</translation>
+<translation id="2846343701378493991">1024 (中)</translation>
+<translation id="5476505524087279545">チェックを外す</translation>
+<translation id="679352192834563463">このコンテンツの表示に使用できるプラグインはありません</translation>
+<translation id="3789841737615482174">インストール</translation>
+<translation id="6663448176199120256">最近の検索</translation>
+<translation id="3600343118165084788">ここをクリックしてプラグインをダウンロード</translation>
+<translation id="6807599807928161586">ウェブ領域</translation>
+<translation id="5939518447894949180">リセット</translation>
+<translation id="3771786644471114952">プラグインの取得</translation>
+<translation id="1842960171412779397">選択</translation>
+<translation id="6119846243427417423">アクティブにする</translation>
+<translation id="8444882422881193423"><ph name="NUMBER_OF_FILES"/> ファイル</translation>
+<translation id="3926627843712816530">このプラグインのインストールを確認してください。信頼できるプラグインだけインストールしてください。</translation>
+<translation id="4838490908464673667">必要なプラグインがインストールされていません</translation>
+<translation id="8597182159515967513">見出し</translation>
+<translation id="2653659639078652383">送信</translation>
+</translationbundle> \ No newline at end of file
diff --git a/webkit/glue/resources/webkit_strings_kn.xtb b/webkit/glue/resources/webkit_strings_kn.xtb
new file mode 100644
index 0000000..1dacc42
--- /dev/null
+++ b/webkit/glue/resources/webkit_strings_kn.xtb
@@ -0,0 +1,43 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="kn">
+<translation id="4420062214988137980">ಪ್ಲಗ್ಇನ್ ಇನ್‌ಸ್ಟಾಲೇಶನ್ ವಿಫಲವಾಗಿದೆ</translation>
+<translation id="1235745349614807883">ಇತ್ತೀಚಿನ ಹುಡುಕಾಟವನ್ನು ತೆರವುಗೊಳಿಸಿ</translation>
+<translation id="3825324228893189080">ಹೆಚ್ಚುವರಿ ಪ್ಲಗ್ಇನ್‌ಗಳ ಅವಶ್ಯಕತೆಯಿದೆ</translation>
+<translation id="2965480764085142436"><ph name="PLUGIN"/> ಪ್ಲಗ್ಇನ್ ಇನ್‌ಸ್ಟಾಲ್ ಆಗಿಲ್ಲ</translation>
+<translation id="5048533449481078685">ಪಟ್ಟಿ ಗುರುತು</translation>
+<translation id="4202807286478387388">ಹಾರು</translation>
+<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation>
+<translation id="4317653869502688143">ದಯವಿಟ್ಟು <ph name="PLUGIN"/> ಪ್ಲಗ್‌ಇನ್ ಅನ್ನು ನೀವು ಇನ್‌ಸ್ಟಾಲ್ ಮಾಡಲು ಬಯಸುತ್ತೀರಾ ಎಂದು ಖಚಿತಪಡಿಸಿ. ನೀವು ನಂಬಿರುವ ಪ್ಲಗ್‌ಇನ್ಸ್ ಅನ್ನು ಮಾತ್ರ ನೀವು ಇನ್‌ಸ್ಟಾಲ್ ಮಾಡಬೇಕು.</translation>
+<translation id="7658239707568436148">ರದ್ದುಮಾಡು</translation>
+<translation id="795667975304826397">ಯಾವುದೇ ಫೈಲ್ ಆಯ್ಕೆ ಮಾಡಿಲ್ಲ</translation>
+<translation id="1275511093094545429"><ph name="PLUGIN"/> ಪ್ಲಗ್ಇನ್ ಅವಶ್ಯಕತೆಯಿದೆ</translation>
+<translation id="8662565117025751661">ಡೌನ್ಲೋಡ್ ಆಗುತ್ತಿರುವ ಪ್ಲಗ್ ಇನ್...</translation>
+<translation id="8141602879876242471">ಇದು ಹುಡುಕಾಡಬಹುದಾದ ಸೂಚಿಕೆ ಹುಡುಕಾಟ ಕೀವರ್ಡ್‌ಗಳನ್ನು ನಮೂದಿಸಿ:</translation>
+<translation id="6845533974506654842">ಒತ್ತಿ</translation>
+<translation id="8244226242650769279">ಇಮೇಜ್ ನಕ್ಷೆ</translation>
+<translation id="1383141426028388991"><ph name="URL"/> ನಿಂದ ಪ್ಲಗ್ಇನ್ ಅನ್ನು ಇನ್‌ಸ್ಟಾಲ್ ಮಾಡಲು ವಿಫಲವಾಗಿದೆ</translation>
+<translation id="2548326553472216322">ಇತ್ತೀಚಿನ ಹುಡುಕಾಟಗಳು ಇಲ್ಲ</translation>
+<translation id="5944544982112848342">2048 (ಉನ್ನತ ಶ್ರೇಣಿ)</translation>
+<translation id="3040011195152428237">ಲಿಂಕ್</translation>
+<translation id="8281246460978372009">ಪ್ಲಗ್ಇನ್ ಇನ್‌ಸ್ಟಾಲ್ ಮಾಡಿದ ನಂತರ, ತಾಜಾಮಾಡಲು ಇಲ್ಲಿ ಕ್ಲಿಕ್ ಮಾಡಿ</translation>
+<translation id="7364796246159120393">ಫೈಲ್ ಆಯ್ಕೆ ಮಾಡಿ</translation>
+<translation id="8964020114565522021">ಇಲ್ಲಿ ಫೈಲ್ ಅನ್ನು ಎಳೆಯಿರಿ</translation>
+<translation id="838869780401515933">ಪರಿಶೀಲಿಸು</translation>
+<translation id="2846343701378493991">1024 (ಮದ್ಯಮ ಶ್ರೇಣಿ)</translation>
+<translation id="5476505524087279545">ಪರೀಕ್ಷಿಸಬೇಡಿ</translation>
+<translation id="679352192834563463">ಈ ವಿಷಯವನ್ನು ಪ್ರದರ್ಶಿಸಲು ಯಾವುದೇ ಪ್ಲಗ್ಇನ್ ಲಭ್ಯವಿಲ್ಲ</translation>
+<translation id="3789841737615482174">ಇನ್‌ಸ್ಟಾಲ್</translation>
+<translation id="6663448176199120256">ಇತ್ತೀಚಿನ ಹುಡುಕಾಟಗಳು</translation>
+<translation id="3600343118165084788">ಪ್ಲಗ್ಇನ್ ಅನ್ನು ಡೌನ್‌ಲೋಡ್ ಮಾಡಲು ಇಲ್ಲಿ ಕ್ಲಿಕ್ ಮಾಡಿ</translation>
+<translation id="6807599807928161586">ವೆಬ್ ಪ್ರದೇಶ</translation>
+<translation id="5939518447894949180">ಮರುಹೊಂದಿಸು</translation>
+<translation id="3771786644471114952">ಪ್ಲಗ್ಇನ್ ಪಡೆಯಿರಿ</translation>
+<translation id="1842960171412779397">ಆಯ್ಕೆ ಮಾಡಿ</translation>
+<translation id="6119846243427417423">ಸಕ್ರಿಯಗೊಳಿಸು</translation>
+<translation id="8444882422881193423"><ph name="NUMBER_OF_FILES"/> ಫೈಲ್‌ಗಳು</translation>
+<translation id="3926627843712816530">ಈ ಪ್ಲಗ್‌ಇನ್ ಅನ್ನು ನೀವು ಇನ್‌ಸ್ಟಾಲ್‌ಮಾಡಲು ಬಯಸುತ್ತೀರಾ ಎಂದು ದಯವಿಟ್ಟು ಖಚಿತಪಡಿಸಿ. ನೀವು ನಂಬಿರುವ ಪ್ಲಗಿನ್ಸ್ ಅನ್ನು ಮಾತ್ರ ನೀವು ಇನ್‌ಸ್ಟಾಲ್ ಮಾಡಬೇಕು.</translation>
+<translation id="4838490908464673667">ಅವಶ್ಯಕವಾಗಿರುವ ಪ್ಲಗ್ಇನ್ ಇನ್‌ಸ್ಟಾಲ್ ಆಗಿಲ್ಲ</translation>
+<translation id="8597182159515967513">ಶೀರ್ಷಿಕೆ</translation>
+<translation id="2653659639078652383">ಸಲ್ಲಿಸು</translation>
+</translationbundle> \ No newline at end of file
diff --git a/webkit/glue/resources/webkit_strings_ko.xtb b/webkit/glue/resources/webkit_strings_ko.xtb
new file mode 100644
index 0000000..c3b3e3e
--- /dev/null
+++ b/webkit/glue/resources/webkit_strings_ko.xtb
@@ -0,0 +1,43 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="ko">
+<translation id="4420062214988137980">플러그인 설치 실패</translation>
+<translation id="1235745349614807883">최근 검색 삭제</translation>
+<translation id="3825324228893189080">추가 플러그인 필요</translation>
+<translation id="2965480764085142436"><ph name="PLUGIN"/> 플러그인이 설치되지 않음</translation>
+<translation id="5048533449481078685">목록 표시기</translation>
+<translation id="4202807286478387388">건너뛰기</translation>
+<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation>
+<translation id="4317653869502688143"><ph name="PLUGIN"/> 플러그인을 설치할 것인지 확인해 주세요. 신뢰하는 플러그인만 설치해야 합니다.</translation>
+<translation id="7658239707568436148">취소</translation>
+<translation id="795667975304826397">선택된 파일 없음</translation>
+<translation id="1275511093094545429"><ph name="PLUGIN"/> 플러그인 필요</translation>
+<translation id="8662565117025751661">플러그인 다운로드 중...</translation>
+<translation id="8141602879876242471">이것은 검색 색인합니다. 검색 키워드 입력:</translation>
+<translation id="6845533974506654842">누르기</translation>
+<translation id="8244226242650769279">이미지 지도</translation>
+<translation id="1383141426028388991"><ph name="URL"/>의 플러그인 설치 실패</translation>
+<translation id="2548326553472216322">최근 수행된 검색 없음</translation>
+<translation id="5944544982112848342">2048(높은 등급)</translation>
+<translation id="3040011195152428237">링크</translation>
+<translation id="8281246460978372009">플러그인 설치 후 여기를 클릭하여 새로고침</translation>
+<translation id="7364796246159120393">파일 선택</translation>
+<translation id="8964020114565522021">여기로 파일 드래그</translation>
+<translation id="838869780401515933">선택</translation>
+<translation id="2846343701378493991">1024(중간 등급)</translation>
+<translation id="5476505524087279545">선택취소</translation>
+<translation id="679352192834563463">이 콘텐츠를 표시하는 데 사용할 플러그인 없음</translation>
+<translation id="3789841737615482174">설치</translation>
+<translation id="6663448176199120256">최근 수행된 검색</translation>
+<translation id="3600343118165084788">여기를 클릭하여 플러그인을 다운로드합니다.</translation>
+<translation id="6807599807928161586">웹 영역</translation>
+<translation id="5939518447894949180">재설정</translation>
+<translation id="3771786644471114952">플러그인 가져오기</translation>
+<translation id="1842960171412779397">선택</translation>
+<translation id="6119846243427417423">활성화</translation>
+<translation id="8444882422881193423">파일 <ph name="NUMBER_OF_FILES"/>개</translation>
+<translation id="3926627843712816530">해당 플러그인을 설치할 것인지 확인해 주세요. 신뢰하는 플러그인만 설치해야 합니다.</translation>
+<translation id="4838490908464673667">필수 플러그인이 설치되지 않음</translation>
+<translation id="8597182159515967513">항목</translation>
+<translation id="2653659639078652383">제출</translation>
+</translationbundle> \ No newline at end of file
diff --git a/webkit/glue/resources/webkit_strings_lt.xtb b/webkit/glue/resources/webkit_strings_lt.xtb
new file mode 100644
index 0000000..9975381
--- /dev/null
+++ b/webkit/glue/resources/webkit_strings_lt.xtb
@@ -0,0 +1,43 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="lt">
+<translation id="4420062214988137980">Papildinio įdiegti nepavyko</translation>
+<translation id="1235745349614807883">Išvalyti pastarąsias paieškas</translation>
+<translation id="3825324228893189080">Reikia papidomo papildinio</translation>
+<translation id="2965480764085142436"><ph name="PLUGIN"/> papildinys neįdiegtas</translation>
+<translation id="5048533449481078685">sąrašo žymeklis</translation>
+<translation id="4202807286478387388">peršokti</translation>
+<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation>
+<translation id="4317653869502688143">Patvirtinkite, kad norite diegti <ph name="PLUGIN"/> papildinį. Diekite tik tuos papildinius, kuriais tikrai pasitikite.</translation>
+<translation id="7658239707568436148">Atšaukti</translation>
+<translation id="795667975304826397">Nepasirinktas joks failas</translation>
+<translation id="1275511093094545429">Reikia <ph name="PLUGIN"/> papildinio (-ių)</translation>
+<translation id="8662565117025751661">Siunčiamas papildinys...</translation>
+<translation id="8141602879876242471">Tai yra ieškotinas indeksas. Įveskite paieškos raktinių žodžių:</translation>
+<translation id="6845533974506654842">paspausti</translation>
+<translation id="8244226242650769279">paveikslėlio žemėlapis</translation>
+<translation id="1383141426028388991">Nepavyko įdiegti papildinio iš <ph name="URL"/></translation>
+<translation id="2548326553472216322">Pastaruoju metu paieškų nevykdyta</translation>
+<translation id="5944544982112848342">2048 (Aukšto laipsnio)</translation>
+<translation id="3040011195152428237">nuoroda</translation>
+<translation id="8281246460978372009">Įdiegę papildinį paspauskite čia, kad būtų perkrautas puslapis</translation>
+<translation id="7364796246159120393">Pasirinkti failą</translation>
+<translation id="8964020114565522021">Vilkite failą čia</translation>
+<translation id="838869780401515933">tikrinti</translation>
+<translation id="2846343701378493991">1024 (vidutinio lygio)</translation>
+<translation id="5476505524087279545">Nuimti žymėjimą</translation>
+<translation id="679352192834563463">Nėra papildinio, galinčio parodyti šį turinį</translation>
+<translation id="3789841737615482174">Diegti</translation>
+<translation id="6663448176199120256">Naujausios paieškos</translation>
+<translation id="3600343118165084788">Spustelėkite čia, kad atsisiųstumėte papildinį</translation>
+<translation id="6807599807928161586">interneto sritis</translation>
+<translation id="5939518447894949180">Nustatyti iš naujo</translation>
+<translation id="3771786644471114952">Siųsti papildinį</translation>
+<translation id="1842960171412779397">pasirinkti</translation>
+<translation id="6119846243427417423">aktyvinti</translation>
+<translation id="8444882422881193423"><ph name="NUMBER_OF_FILES"/> failai (-ų)</translation>
+<translation id="3926627843712816530">Patvirtinkite, kad norite diegti šį papildinį. Diekite tik tuos papildinius, kuriais tikrai pasitikite.</translation>
+<translation id="4838490908464673667">Neįdiegtas reikalingas papildinys</translation>
+<translation id="8597182159515967513">antraštė</translation>
+<translation id="2653659639078652383">Pateikti</translation>
+</translationbundle> \ No newline at end of file
diff --git a/webkit/glue/resources/webkit_strings_lv.xtb b/webkit/glue/resources/webkit_strings_lv.xtb
new file mode 100644
index 0000000..6b7dfd5
--- /dev/null
+++ b/webkit/glue/resources/webkit_strings_lv.xtb
@@ -0,0 +1,43 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="lv">
+<translation id="4420062214988137980">Spraudņa iestatīšana neizdevās</translation>
+<translation id="1235745349614807883">Dzēst nesenos meklējumus</translation>
+<translation id="3825324228893189080">Nepieciešams papildus spraudnis</translation>
+<translation id="2965480764085142436"><ph name="PLUGIN"/> spraudnis nav iestatīts</translation>
+<translation id="5048533449481078685">sarakstu marķieris</translation>
+<translation id="4202807286478387388">lekt</translation>
+<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation>
+<translation id="4317653869502688143">Lūdzu, apstipriniet, ka vēlaties iestatīt <ph name="PLUGIN"/> spraudni. Jums vajadzētu iestatīt tikai tos spraudņus, kuriem uzticaties.</translation>
+<translation id="7658239707568436148">Atcelt</translation>
+<translation id="795667975304826397">Nav izvēlēts neviens fails</translation>
+<translation id="1275511093094545429"><ph name="PLUGIN"/> nepieciešams spraudnis</translation>
+<translation id="8662565117025751661">Spraudņu lejupielāde...</translation>
+<translation id="8141602879876242471">Šis ir indekss ar meklēšanas iespējām. Ievadīt meklēšanas atslēgvārdus:</translation>
+<translation id="6845533974506654842">nospiest</translation>
+<translation id="8244226242650769279">attēlu karte</translation>
+<translation id="1383141426028388991">Neizdevās instalēt spraudni no <ph name="URL"/></translation>
+<translation id="2548326553472216322">Nav nesenu meklējumu</translation>
+<translation id="5944544982112848342">2048 (Augsta Atzīme)</translation>
+<translation id="3040011195152428237">saite</translation>
+<translation id="8281246460978372009">Pēc spraudņa iestatīšanas, nospiediet šeit, lai atsvaidzinātu</translation>
+<translation id="7364796246159120393">Izvēlieties failu</translation>
+<translation id="8964020114565522021">Ievelciet failu šeit</translation>
+<translation id="838869780401515933">prbaudt</translation>
+<translation id="2846343701378493991">1024 (Vidēja Atzīme)</translation>
+<translation id="5476505524087279545">neprbaudt</translation>
+<translation id="679352192834563463">Nav pieejamu spraudņu, lai attēlotu saturu</translation>
+<translation id="3789841737615482174">Iestatīt</translation>
+<translation id="6663448176199120256">Neseni meklējumi</translation>
+<translation id="3600343118165084788">Klikšķiniet šeit, lai lejupielādētu spraudni</translation>
+<translation id="6807599807928161586">tīmekļa apgabals</translation>
+<translation id="5939518447894949180">Atiestatīt</translation>
+<translation id="3771786644471114952">Iegūt spraudni</translation>
+<translation id="1842960171412779397">Atlasiet</translation>
+<translation id="6119846243427417423">aktivizt</translation>
+<translation id="8444882422881193423"><ph name="NUMBER_OF_FILES"/> faili</translation>
+<translation id="3926627843712816530">Lūdzu, apstipriniet, ka vēlaties iestatīt šo spraudni. Jums vajadzētu iestatīt tikai tos spraudņus, kuriem uzticaties.</translation>
+<translation id="4838490908464673667">Nepieciešamais spraudnis nav iestatīts</translation>
+<translation id="8597182159515967513">Virsraksts</translation>
+<translation id="2653659639078652383">Iesniegt</translation>
+</translationbundle> \ No newline at end of file
diff --git a/webkit/glue/resources/webkit_strings_ml.xtb b/webkit/glue/resources/webkit_strings_ml.xtb
new file mode 100644
index 0000000..d7db730
--- /dev/null
+++ b/webkit/glue/resources/webkit_strings_ml.xtb
@@ -0,0 +1,43 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="ml">
+<translation id="4420062214988137980">പ്ലഗിന്‍ ഇന്‍സ്റ്റൊളേഷന്‍ പരാജയപ്പെട്ടു</translation>
+<translation id="1235745349614807883">അടുത്തിടെയുള്ള തിരയലുകള്‍ മായ്ക്കുക</translation>
+<translation id="3825324228893189080">കൂടുതല്‍ പ്ലഗിന്‍ ആവശ്യമാണ്</translation>
+<translation id="2965480764085142436"><ph name="PLUGIN"/> പ്ലഗിന്‍ ഇന്‍സ്റ്റോള്‍ ചെയ്തില്ല</translation>
+<translation id="5048533449481078685">പട്ടിക മാര്‍ക്കര്‍</translation>
+<translation id="4202807286478387388">jump</translation>
+<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation>
+<translation id="4317653869502688143"><ph name="PLUGIN"/> പ്ലഗിന്‍ ഇന്‍സ്റ്റാള്‍ ചെയ്യാ‍ന്‍ നിങ്ങള്‍ താല്പര്യപ്പെടുന്നുവെന്ന് ദയവായി ഉറപ്പുവരുത്തുക. നിങ്ങള്‍ക്ക് വിശ്വാസ്യതയുള്ള പ്ലഗിനുകള്‍ മാ‍ത്രം നിങ്ങള്‍ ഇന്‍സ്റ്റാള്‍ ചെയ്യുക.</translation>
+<translation id="7658239707568436148">റദ്ദാക്കുക</translation>
+<translation id="795667975304826397">ഒരു ഫയലും തിരഞ്ഞെടുത്തിട്ടില്ല</translation>
+<translation id="1275511093094545429"><ph name="PLUGIN"/> പ്ലഗിന്‍ ആവശ്യമുണ്ട്</translation>
+<translation id="8662565117025751661">പ്ലഗ് ഇന്‍ ഡൌണ്‍ലോഡ് ചെയ്യുകയാണ്...</translation>
+<translation id="8141602879876242471">ഇത് തിരയാവുന്ന സൂചികയാണ്. തിരയല്‍ കീവേഡുകള്‍ നല്‍കുക:</translation>
+<translation id="6845533974506654842">അമര്‍ത്തുക</translation>
+<translation id="8244226242650769279">ഇമേജ് മാപ്പ്</translation>
+<translation id="1383141426028388991"><ph name="URL"/> ല്‍ നിന്ന് പ്ലഗ്-ഇന്‍ ഇന്‍സ്റ്റോള്‍ ചെയ്യുന്നതില്‍ പരാജയപ്പെട്ടു</translation>
+<translation id="2548326553472216322">സമീപകാല തിരയലുകള്‍ ഇല്ല</translation>
+<translation id="5944544982112848342">2048 (High Grade)</translation>
+<translation id="3040011195152428237">ലിങ്ക്</translation>
+<translation id="8281246460978372009">പ്ലഗിന്‍ ഇന്‍സ്റ്റാള്‍ ചെയ്തതിന് ശേഷം, പുതുക്കുന്നതിനായി ഇവിടെ ക്ലിക്കുചെയ്യുക</translation>
+<translation id="7364796246159120393">ഫയല്‍ തിരഞ്ഞെടുക്കൂ</translation>
+<translation id="8964020114565522021">ഇവിടെ ഫയല്‍ ഇഴയ്ക്കുക</translation>
+<translation id="838869780401515933">പരിശോധിക്കൂ</translation>
+<translation id="2846343701378493991">1024 (മീഡിയം ഗ്രേഡ്)</translation>
+<translation id="5476505524087279545">അണ്‍ചെക്ക് ചെയ്യുക</translation>
+<translation id="679352192834563463">ഈ ഉള്ളടക്കം പ്രദര്‍ശിപ്പിക്കുന്നതിന് ഒരു പ്ലഗിനും ലഭ്യമല്ല</translation>
+<translation id="3789841737615482174">ഇന്‍സ്റ്റോള്‍ ചെയ്യുക</translation>
+<translation id="6663448176199120256">സമീപകാല തിരയലുകള്‍</translation>
+<translation id="3600343118165084788">പ്ലഗ്-ഇന്‍ ഡൌണ്‍ലോഡ് ചെയ്യുന്നതിനായി ഇവിടെ ക്ലിക്കുചെയ്യുക</translation>
+<translation id="6807599807928161586">വെബ് മേഖല</translation>
+<translation id="5939518447894949180">വീണ്ടും സജ്ജീകരിക്കുക</translation>
+<translation id="3771786644471114952">പ്ലഗിന്‍ നേടുക</translation>
+<translation id="1842960171412779397">തിരഞ്ഞെടുക്കൂ</translation>
+<translation id="6119846243427417423">ആക്റ്റിവേറ്റ് ചെയ്യുക</translation>
+<translation id="8444882422881193423"><ph name="NUMBER_OF_FILES"/> ഫയലുകള്‍</translation>
+<translation id="3926627843712816530">പ്ലഗിന്‍ ഇന്‍സ്റ്റോള്‍ ചെയ്യാ‍ന്‍ നിങ്ങള്‍ താല്പര്യപ്പെടുന്നുവെന്ന് ദയവായി ഉറപ്പുവരുത്തുക. നിങ്ങള്‍ക്ക് വിശ്വാസ്യതയുള്ള പ്ലഗിനുകള്‍ മാ‍ത്രം നിങ്ങള്‍ ഇന്‍സ്റ്റോള്‍ ചെയ്യുക.</translation>
+<translation id="4838490908464673667">ആവശ്യമായ പ്ലഗിന്‍ ഇന്‍സ്റ്റാള്‍ ചെയ്തില്ല</translation>
+<translation id="8597182159515967513">തലക്കെട്ട്</translation>
+<translation id="2653659639078652383">സമര്‍പ്പിക്കൂ</translation>
+</translationbundle> \ No newline at end of file
diff --git a/webkit/glue/resources/webkit_strings_mr.xtb b/webkit/glue/resources/webkit_strings_mr.xtb
new file mode 100644
index 0000000..fd6c4a4
--- /dev/null
+++ b/webkit/glue/resources/webkit_strings_mr.xtb
@@ -0,0 +1,43 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="mr">
+<translation id="4420062214988137980">प्लगइन स्थापना अयशस्वी</translation>
+<translation id="1235745349614807883">अलीकडील शोध साफ करा</translation>
+<translation id="3825324228893189080">अतिरिक्त प्लगइन आवश्यक</translation>
+<translation id="2965480764085142436"><ph name="PLUGIN"/> प्लगइन स्थापित नाही</translation>
+<translation id="5048533449481078685">सूची चिन्हक</translation>
+<translation id="4202807286478387388">जंप करा</translation>
+<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation>
+<translation id="4317653869502688143">कृपया आपण <ph name="PLUGIN"/> हे प्लगइन स्थापित करू इच्छिता याची पुष्टी करा. आपण केवळ आपल्याला विश्वास असलेले प्लगइन स्थापित केले पाहिजेत.</translation>
+<translation id="7658239707568436148">रद्द करा</translation>
+<translation id="795667975304826397">कोणतीही फाइल निवडलेली नाही</translation>
+<translation id="1275511093094545429"><ph name="PLUGIN"/> प्लगइन आवश्यक</translation>
+<translation id="8662565117025751661">प्लगइन डाउनलोड करीत आहे...</translation>
+<translation id="8141602879876242471">ही शोध घेण्यायोग्य अनुक्रमणिका आहे. शोध कीवर्ड प्रविष्ट करा:</translation>
+<translation id="6845533974506654842">दाबा</translation>
+<translation id="8244226242650769279">प्रतिमा नकाशा</translation>
+<translation id="1383141426028388991"><ph name="URL"/> वरून प्लगइन स्थापित करण्‍यात अयशस्वी</translation>
+<translation id="2548326553472216322">अलीकडील शोध नाहीत</translation>
+<translation id="5944544982112848342">2048 (उच्च ग्रेड)</translation>
+<translation id="3040011195152428237">दुवा</translation>
+<translation id="8281246460978372009">प्लगइन स्थापित केल्यानंतर, रीफ्रेश करण्यासाठी येथे क्लिक करा</translation>
+<translation id="7364796246159120393">फाइल निवडा</translation>
+<translation id="8964020114565522021">फाइल येथे ड्रॅग करा</translation>
+<translation id="838869780401515933">तपासा</translation>
+<translation id="2846343701378493991">1024 (मध्यम प्रत)</translation>
+<translation id="5476505524087279545">अनचेक</translation>
+<translation id="679352192834563463">ही सामग्री प्रदर्शित करण्यासाठी कोणतेही प्लगइन उपलब्ध नाही</translation>
+<translation id="3789841737615482174">स्थापना करा</translation>
+<translation id="6663448176199120256">अलीकडील शोध</translation>
+<translation id="3600343118165084788">प्लगइन डाउनलोड करण्यासाठी येथे क्लिक करा</translation>
+<translation id="6807599807928161586">वेब क्षेत्र</translation>
+<translation id="5939518447894949180">रीसेट करा</translation>
+<translation id="3771786644471114952">प्लगइन मिळवा</translation>
+<translation id="1842960171412779397">निवडा</translation>
+<translation id="6119846243427417423">सक्रिय करा</translation>
+<translation id="8444882422881193423"><ph name="NUMBER_OF_FILES"/> फायली</translation>
+<translation id="3926627843712816530">कृपया आपण हे प्लगइन स्थापित करू इच्छिता याची पुष्टी करा. आपण ज्यांचेवर विश्वास करता केवळ असेच प्लगइन स्थापित करावेत.</translation>
+<translation id="4838490908464673667">आवश्यक प्लगइन स्थापित नाही</translation>
+<translation id="8597182159515967513">शीर्षलेख</translation>
+<translation id="2653659639078652383">सबमिट करा</translation>
+</translationbundle> \ No newline at end of file
diff --git a/webkit/glue/resources/webkit_strings_nl.xtb b/webkit/glue/resources/webkit_strings_nl.xtb
new file mode 100644
index 0000000..c9cea68
--- /dev/null
+++ b/webkit/glue/resources/webkit_strings_nl.xtb
@@ -0,0 +1,43 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="nl">
+<translation id="4420062214988137980">Installatie van plug-in mislukt</translation>
+<translation id="1235745349614807883">Recente zoekopdrachten wissen</translation>
+<translation id="3825324228893189080">Aanvullende plug-in vereist</translation>
+<translation id="2965480764085142436">De plug-in <ph name="PLUGIN"/> is niet geïnstalleerd</translation>
+<translation id="5048533449481078685">lijstmarkering</translation>
+<translation id="4202807286478387388">Gaan naar</translation>
+<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation>
+<translation id="4317653869502688143">Bevestig dat u de plugin <ph name="PLUGIN"/> wilt installeren. Installeer alleen plug-ins die u vertrouwt.</translation>
+<translation id="7658239707568436148">Annuleren</translation>
+<translation id="795667975304826397">Geen bestand gekozen</translation>
+<translation id="1275511093094545429">Plug-in <ph name="PLUGIN"/> vereist</translation>
+<translation id="8662565117025751661">Plug-in downloaden...</translation>
+<translation id="8141602879876242471">Dit is een doorzoekbare index. Geef zoekwoorden op:</translation>
+<translation id="6845533974506654842">Indrukken</translation>
+<translation id="8244226242650769279">image map</translation>
+<translation id="1383141426028388991">Kan plug-in niet installeren van <ph name="URL"/></translation>
+<translation id="2548326553472216322">Geen recente zoekopdrachten</translation>
+<translation id="5944544982112848342">2048 (hoog niveau)</translation>
+<translation id="3040011195152428237">link</translation>
+<translation id="8281246460978372009">Klik hier na het installeren van de plug-in om te vernieuwen</translation>
+<translation id="7364796246159120393">Bestand kiezen</translation>
+<translation id="8964020114565522021">Sleep bestand hier naartoe</translation>
+<translation id="838869780401515933">Selecteren</translation>
+<translation id="2846343701378493991">1024 (gemiddeld niveau)</translation>
+<translation id="5476505524087279545">Deselecteren</translation>
+<translation id="679352192834563463">Er is geen plug-in beschikbaar om deze inhoud weer te geven</translation>
+<translation id="3789841737615482174">Installeren</translation>
+<translation id="6663448176199120256">Recente zoekopdrachten</translation>
+<translation id="3600343118165084788">Klik hier om de plug-in te downloaden</translation>
+<translation id="6807599807928161586">webgedeelte</translation>
+<translation id="5939518447894949180">Herstellen</translation>
+<translation id="3771786644471114952">Plug-in ophalen</translation>
+<translation id="1842960171412779397">Selecteren</translation>
+<translation id="6119846243427417423">Activeren</translation>
+<translation id="8444882422881193423"><ph name="NUMBER_OF_FILES"/> bestanden</translation>
+<translation id="3926627843712816530">Bevestig dat u deze plug-in wilt installeren. Installeer alleen plug-ins die u vertrouwt.</translation>
+<translation id="4838490908464673667">De vereiste plug-in is niet geïnstalleerd</translation>
+<translation id="8597182159515967513">kop</translation>
+<translation id="2653659639078652383">Verzenden</translation>
+</translationbundle> \ No newline at end of file
diff --git a/webkit/glue/resources/webkit_strings_no.xtb b/webkit/glue/resources/webkit_strings_no.xtb
new file mode 100644
index 0000000..f8f419a
--- /dev/null
+++ b/webkit/glue/resources/webkit_strings_no.xtb
@@ -0,0 +1,43 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="no">
+<translation id="4420062214988137980">Installasjon av programtillegg mislyktes</translation>
+<translation id="1235745349614807883">Fjern nylige søk</translation>
+<translation id="3825324228893189080">Ytterligere programtillegg kreves</translation>
+<translation id="2965480764085142436">Programtillegget <ph name="PLUGIN"/> er ikke installert</translation>
+<translation id="5048533449481078685">listemarkør</translation>
+<translation id="4202807286478387388">hopp</translation>
+<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation>
+<translation id="4317653869502688143">Du må bekrefte om du vil installere programtillegget <ph name="PLUGIN"/>. Du bør bare installere programtillegg du stoler på.</translation>
+<translation id="7658239707568436148">Avbryt</translation>
+<translation id="795667975304826397">Ingen fil valgt</translation>
+<translation id="1275511093094545429">Programtillegget <ph name="PLUGIN"/> kreves</translation>
+<translation id="8662565117025751661">Laster ned programtillegg...</translation>
+<translation id="8141602879876242471">Dette er en søkbar indeks. Angi søkeordene:</translation>
+<translation id="6845533974506654842">trykk</translation>
+<translation id="8244226242650769279">bildekart</translation>
+<translation id="1383141426028388991">Kunne ikke installere programtillegget fra <ph name="URL"/></translation>
+<translation id="2548326553472216322">Ingen nylige søk</translation>
+<translation id="5944544982112848342">2048 (sterk)</translation>
+<translation id="3040011195152428237">kobling</translation>
+<translation id="8281246460978372009">Når programtillegget er installert, klikker du her for å oppdatere</translation>
+<translation id="7364796246159120393">Velg fil</translation>
+<translation id="8964020114565522021">Dra filen hit</translation>
+<translation id="838869780401515933">merk av</translation>
+<translation id="2846343701378493991">1024 (middels)</translation>
+<translation id="5476505524087279545">fjern merke</translation>
+<translation id="679352192834563463">Intet programtillegg tilgjengelig for å vise dette innholdet</translation>
+<translation id="3789841737615482174">Installer</translation>
+<translation id="6663448176199120256">Nylige søk</translation>
+<translation id="3600343118165084788">Klikk her for å laste ned programtillegget</translation>
+<translation id="6807599807928161586">nettområde</translation>
+<translation id="5939518447894949180">Tilbakestill</translation>
+<translation id="3771786644471114952">Få programtillegg</translation>
+<translation id="1842960171412779397">velg</translation>
+<translation id="6119846243427417423">aktiver</translation>
+<translation id="8444882422881193423"><ph name="NUMBER_OF_FILES"/> filer</translation>
+<translation id="3926627843712816530">Du må bekrefte om du vil installere programtillegget. Du bør bare installere programtillegg du stoler på.</translation>
+<translation id="4838490908464673667">Det nødvendige programtillegget er ikke installert</translation>
+<translation id="8597182159515967513">overskrift</translation>
+<translation id="2653659639078652383">Send</translation>
+</translationbundle> \ No newline at end of file
diff --git a/webkit/glue/resources/webkit_strings_pl.xtb b/webkit/glue/resources/webkit_strings_pl.xtb
new file mode 100644
index 0000000..c666eb9
--- /dev/null
+++ b/webkit/glue/resources/webkit_strings_pl.xtb
@@ -0,0 +1,43 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="pl">
+<translation id="4420062214988137980">Instalacja dodatku plug-in nie powiodła się</translation>
+<translation id="1235745349614807883">Wyczyść ostatnie wyszukiwania</translation>
+<translation id="3825324228893189080">Wymagany dodatkowy dodatek plug-in</translation>
+<translation id="2965480764085142436">Dodatek plug-in <ph name="PLUGIN"/> nie został zainstalowany</translation>
+<translation id="5048533449481078685">znacznik listy</translation>
+<translation id="4202807286478387388">przejdź</translation>
+<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation>
+<translation id="4317653869502688143">Potwierdź instalację dodatku plug-in <ph name="PLUGIN"/>. Należy instalować wyłącznie zaufane dodatki plug-in.</translation>
+<translation id="7658239707568436148">Anuluj</translation>
+<translation id="795667975304826397">Nie wybrano pliku</translation>
+<translation id="1275511093094545429">Wymagany dodatek plug-in <ph name="PLUGIN"/></translation>
+<translation id="8662565117025751661">Pobieranie dodatku plug-in...</translation>
+<translation id="8141602879876242471">Ten indeks można przeszukiwać. Wprowadź wyszukiwane słowa kluczowe:</translation>
+<translation id="6845533974506654842">naciśnij</translation>
+<translation id="8244226242650769279">mapa grafiki</translation>
+<translation id="1383141426028388991">Nie można zainstalować wtyczki z adresu <ph name="URL"/></translation>
+<translation id="2548326553472216322">Brak ostatnich wyszukiwań</translation>
+<translation id="5944544982112848342">2048 (wysoki poziom)</translation>
+<translation id="3040011195152428237">link</translation>
+<translation id="8281246460978372009">Po zainstalowaniu dodatku plug-in kliknij tutaj, aby odświeżyć</translation>
+<translation id="7364796246159120393">Wybierz plik</translation>
+<translation id="8964020114565522021">Przeciągnij plik tutaj</translation>
+<translation id="838869780401515933">zaznacz</translation>
+<translation id="2846343701378493991">1024 (średni poziom)</translation>
+<translation id="5476505524087279545">odznacz</translation>
+<translation id="679352192834563463">Brak dostępnego dodatku plug-in umożliwiającego wyświetlenie tej treści</translation>
+<translation id="3789841737615482174">Zainstaluj</translation>
+<translation id="6663448176199120256">Ostatnie wyszukiwania</translation>
+<translation id="3600343118165084788">Kliknij tutaj, aby pobrać dodatek plug-in</translation>
+<translation id="6807599807928161586">obszar sieci</translation>
+<translation id="5939518447894949180">Resetuj</translation>
+<translation id="3771786644471114952">Pobierz dodatek plug-in</translation>
+<translation id="1842960171412779397">wybierz</translation>
+<translation id="6119846243427417423">aktywuj</translation>
+<translation id="8444882422881193423">Liczba plików: <ph name="NUMBER_OF_FILES"/></translation>
+<translation id="3926627843712816530">Potwierdź instalację tego dodatku plug-in. Należy instalować wyłącznie zaufane dodatki plug-in.</translation>
+<translation id="4838490908464673667">Wymagany dodatek plug-in nie został zainstalowany</translation>
+<translation id="8597182159515967513">nagłówek</translation>
+<translation id="2653659639078652383">Prześlij</translation>
+</translationbundle> \ No newline at end of file
diff --git a/webkit/glue/resources/webkit_strings_pt-BR.xtb b/webkit/glue/resources/webkit_strings_pt-BR.xtb
new file mode 100644
index 0000000..5dfc2d3
--- /dev/null
+++ b/webkit/glue/resources/webkit_strings_pt-BR.xtb
@@ -0,0 +1,43 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="pt-BR">
+<translation id="4420062214988137980">Falha de instalação de plug-in</translation>
+<translation id="1235745349614807883">Limpar pesquisas recentes</translation>
+<translation id="3825324228893189080">Plug-in adicional necessário</translation>
+<translation id="2965480764085142436">O plug-in <ph name="PLUGIN"/> não está instalado</translation>
+<translation id="5048533449481078685">marcador de lista</translation>
+<translation id="4202807286478387388">pular</translation>
+<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation>
+<translation id="4317653869502688143">Confirme se deseja instalar este plug-in <ph name="PLUGIN"/>. Instale somente plug-ins confiáveis.</translation>
+<translation id="7658239707568436148">Cancelar</translation>
+<translation id="795667975304826397">Nenhum arquivo selecionado</translation>
+<translation id="1275511093094545429"><ph name="PLUGIN"/> plug-in necessário</translation>
+<translation id="8662565117025751661">Fazendo download do plug-in...</translation>
+<translation id="8141602879876242471">Este é um índice pesquisável. Insira palavras-chave de pesquisa:</translation>
+<translation id="6845533974506654842">pressione</translation>
+<translation id="8244226242650769279">mapa de imagens</translation>
+<translation id="1383141426028388991">Falha ao instalar o plug-in de <ph name="URL"/></translation>
+<translation id="2548326553472216322">Nenhuma pesquisa recente</translation>
+<translation id="5944544982112848342">2048 (Nível alto)</translation>
+<translation id="3040011195152428237">link</translation>
+<translation id="8281246460978372009">Depois de instalar o plug-in, clique aqui para atualizar</translation>
+<translation id="7364796246159120393">Escolher arquivo</translation>
+<translation id="8964020114565522021">Arraste o arquivo até aquil</translation>
+<translation id="838869780401515933">marcar</translation>
+<translation id="2846343701378493991">1024 (Nível médio)</translation>
+<translation id="5476505524087279545">desmarcar</translation>
+<translation id="679352192834563463">Nenhum plug-in disponível para exibir este conteúdo</translation>
+<translation id="3789841737615482174">Instalar</translation>
+<translation id="6663448176199120256">Pesquisas recentes</translation>
+<translation id="3600343118165084788">Clique aqui para fazer download do plug-in</translation>
+<translation id="6807599807928161586">área da web</translation>
+<translation id="5939518447894949180">Redefinir</translation>
+<translation id="3771786644471114952">Obter plug-in</translation>
+<translation id="1842960171412779397">selecione</translation>
+<translation id="6119846243427417423">ativar</translation>
+<translation id="8444882422881193423"><ph name="NUMBER_OF_FILES"/> arquivos</translation>
+<translation id="3926627843712816530">Confirme se deseja instalar este plug-in. Instale somente plug-ins confiáveis.</translation>
+<translation id="4838490908464673667">O plug-in necessário não está instalado</translation>
+<translation id="8597182159515967513">cabeçalho</translation>
+<translation id="2653659639078652383">Enviar</translation>
+</translationbundle> \ No newline at end of file
diff --git a/webkit/glue/resources/webkit_strings_pt-PT.xtb b/webkit/glue/resources/webkit_strings_pt-PT.xtb
new file mode 100644
index 0000000..4b585e5
--- /dev/null
+++ b/webkit/glue/resources/webkit_strings_pt-PT.xtb
@@ -0,0 +1,43 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="pt-PT">
+<translation id="4420062214988137980">A instalação do plug-in falhou</translation>
+<translation id="1235745349614807883">Limpar pesquisas recentes</translation>
+<translation id="3825324228893189080">Plug-in adicional necessário</translation>
+<translation id="2965480764085142436">O plug-in <ph name="PLUGIN"/> não está instalado</translation>
+<translation id="5048533449481078685">marcador de lista</translation>
+<translation id="4202807286478387388">ir para</translation>
+<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation>
+<translation id="4317653869502688143">Confirme que pretende instalar o plug-in <ph name="PLUGIN"/>. Apenas deverá instalar plug-ins fidedignos.</translation>
+<translation id="7658239707568436148">Cancelar</translation>
+<translation id="795667975304826397">Nenhum ficheiro seleccionado</translation>
+<translation id="1275511093094545429">Plug-in <ph name="PLUGIN"/> necessário</translation>
+<translation id="8662565117025751661">A transferir o plug-in…</translation>
+<translation id="8141602879876242471">Este índice é pesquisável. Introduza palavras-chave de pesquisa:</translation>
+<translation id="6845533974506654842">premir</translation>
+<translation id="8244226242650769279">mapa de imagem</translation>
+<translation id="1383141426028388991">Falha ao instalar plug-in de <ph name="URL"/></translation>
+<translation id="2548326553472216322">Nenhuma pesquisa recente</translation>
+<translation id="5944544982112848342">2048 (Tamanho grande)</translation>
+<translation id="3040011195152428237">link</translation>
+<translation id="8281246460978372009">Após instalar o plug-in, clique aqui para actualizar</translation>
+<translation id="7364796246159120393">Escolher ficheiro</translation>
+<translation id="8964020114565522021">Arraste o ficheiro para aqui</translation>
+<translation id="838869780401515933">verificar</translation>
+<translation id="2846343701378493991">1024 (Tamanho médio)</translation>
+<translation id="5476505524087279545">desmarcar</translation>
+<translation id="679352192834563463">Nenhum plug-in disponível para apresentar este conteúdo</translation>
+<translation id="3789841737615482174">Instalar</translation>
+<translation id="6663448176199120256">Pesquisas recentes</translation>
+<translation id="3600343118165084788">Clique aqui para transferir o plug-in</translation>
+<translation id="6807599807928161586">Área Web</translation>
+<translation id="5939518447894949180">Repor</translation>
+<translation id="3771786644471114952">Obter plug-in</translation>
+<translation id="1842960171412779397">seleccionar</translation>
+<translation id="6119846243427417423">activar</translation>
+<translation id="8444882422881193423"><ph name="NUMBER_OF_FILES"/> ficheiros</translation>
+<translation id="3926627843712816530">Confirme que pretende instalar este plug-in. Apenas deverá instalar plug-ins fidedignos.</translation>
+<translation id="4838490908464673667">O plug-in necessário não está instalado</translation>
+<translation id="8597182159515967513">cabeçalho</translation>
+<translation id="2653659639078652383">Submeter</translation>
+</translationbundle> \ No newline at end of file
diff --git a/webkit/glue/resources/webkit_strings_ro.xtb b/webkit/glue/resources/webkit_strings_ro.xtb
new file mode 100644
index 0000000..72c8f43
--- /dev/null
+++ b/webkit/glue/resources/webkit_strings_ro.xtb
@@ -0,0 +1,43 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="ro">
+<translation id="4420062214988137980">Instalarea plug-in-ului a eşuat</translation>
+<translation id="1235745349614807883">Ştergeţi căutările recente</translation>
+<translation id="3825324228893189080">Este necesar un plug-in suplimentar</translation>
+<translation id="2965480764085142436">Plug-in-ul <ph name="PLUGIN"/> nu este instalat</translation>
+<translation id="5048533449481078685">marcator listă</translation>
+<translation id="4202807286478387388">Salt</translation>
+<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation>
+<translation id="4317653869502688143">Confirmaţi că doriţi să instalaţi plug-in-ul <ph name="PLUGIN"/>. Ar trebui să instalaţi numai plug-in-uri în care aveţi încredere.</translation>
+<translation id="7658239707568436148">Anulaţi</translation>
+<translation id="795667975304826397">Nu s-au ales fişiere</translation>
+<translation id="1275511093094545429">Este necesar plug-in-ul <ph name="PLUGIN"/></translation>
+<translation id="8662565117025751661">Se descarcă plug-inul...</translation>
+<translation id="8141602879876242471">Acesta este un index în care se poate căuta. Introduceţi cuvintele cheie pentru căutare:</translation>
+<translation id="6845533974506654842">Apăsaţi</translation>
+<translation id="8244226242650769279">hartă cu imagini</translation>
+<translation id="1383141426028388991">Instalarea plug-in-ului de la <ph name="URL"/> a eşuat</translation>
+<translation id="2548326553472216322">Nicio căutare recentă</translation>
+<translation id="5944544982112848342">2048 (Grad înalt)</translation>
+<translation id="3040011195152428237">link</translation>
+<translation id="8281246460978372009">După instalarea plug-in-ului, faceţi clic aici pentru a reactualiza</translation>
+<translation id="7364796246159120393">Alegeţi fişierul</translation>
+<translation id="8964020114565522021">Trageţi fişierul aici</translation>
+<translation id="838869780401515933">Bifaţi</translation>
+<translation id="2846343701378493991">1024 (Grad mediu)</translation>
+<translation id="5476505524087279545">Debifaţi</translation>
+<translation id="679352192834563463">Niciun plug-in disponibil pentru a afişa acest conţinut</translation>
+<translation id="3789841737615482174">Instalaţi</translation>
+<translation id="6663448176199120256">Căutări recente</translation>
+<translation id="3600343118165084788">Faceţi clic aici pentru a descărca plug-in-ul</translation>
+<translation id="6807599807928161586">zona Web</translation>
+<translation id="5939518447894949180">Resetaţi</translation>
+<translation id="3771786644471114952">Obţineţi plug-in-ul</translation>
+<translation id="1842960171412779397">Selectaţi</translation>
+<translation id="6119846243427417423">Activaţi</translation>
+<translation id="8444882422881193423"><ph name="NUMBER_OF_FILES"/> fişiere</translation>
+<translation id="3926627843712816530">Confirmaţi că doriţi să instalaţi acest plug-in. Ar trebui să instalaţi numai plug-in-uri în care aveţi încredere.</translation>
+<translation id="4838490908464673667">Plug-in-ul necesar nu este instalat</translation>
+<translation id="8597182159515967513">titlu</translation>
+<translation id="2653659639078652383">Trimiteţi</translation>
+</translationbundle> \ No newline at end of file
diff --git a/webkit/glue/resources/webkit_strings_ru.xtb b/webkit/glue/resources/webkit_strings_ru.xtb
new file mode 100644
index 0000000..b9fe2f3
--- /dev/null
+++ b/webkit/glue/resources/webkit_strings_ru.xtb
@@ -0,0 +1,43 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="ru">
+<translation id="4420062214988137980">Ошибка при установке плагина</translation>
+<translation id="1235745349614807883">Очистить недавние поиски</translation>
+<translation id="3825324228893189080">Необходим дополнительный плагин</translation>
+<translation id="2965480764085142436">Плагин <ph name="PLUGIN"/> не установлен</translation>
+<translation id="5048533449481078685">маркер списка</translation>
+<translation id="4202807286478387388">перейти</translation>
+<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation>
+<translation id="4317653869502688143">Подтвердите, что хотите установить плагин <ph name="PLUGIN"/>. Устанавливать следует модули только тех издателей, которым вы доверяете.</translation>
+<translation id="7658239707568436148">Отмена</translation>
+<translation id="795667975304826397">Файл не выбран</translation>
+<translation id="1275511093094545429">Необходим плагин <ph name="PLUGIN"/></translation>
+<translation id="8662565117025751661">Загрузка подключаемого модуля...</translation>
+<translation id="8141602879876242471">Это индекс с возможностью поиска. Введите ключевые слова для поиска:</translation>
+<translation id="6845533974506654842">нажать</translation>
+<translation id="8244226242650769279">графическая карта</translation>
+<translation id="1383141426028388991">Не удалось установить подключаемый модуль со следующего адреса: <ph name="URL"/></translation>
+<translation id="2548326553472216322">Нет недавних поисков</translation>
+<translation id="5944544982112848342">2048 (Крупный размер)</translation>
+<translation id="3040011195152428237">ссылка</translation>
+<translation id="8281246460978372009">После установки подключаемого модуля нажмите здесь для обновления</translation>
+<translation id="7364796246159120393">Выберите файл</translation>
+<translation id="8964020114565522021">Перетащите файл сюда</translation>
+<translation id="838869780401515933">поставить галочку</translation>
+<translation id="2846343701378493991">1024 (Средний размер)</translation>
+<translation id="5476505524087279545">снять галочку</translation>
+<translation id="679352192834563463">Недоступен плагин для отображения этого содержания</translation>
+<translation id="3789841737615482174">Установить</translation>
+<translation id="6663448176199120256">Недавние поиски</translation>
+<translation id="3600343118165084788">Нажмите здесь, чтобы загрузить плагин</translation>
+<translation id="6807599807928161586">область Интернетеа</translation>
+<translation id="5939518447894949180">Изменить</translation>
+<translation id="3771786644471114952">Получить плагин</translation>
+<translation id="1842960171412779397">выбрать</translation>
+<translation id="6119846243427417423">активировать</translation>
+<translation id="8444882422881193423">Число файлов: <ph name="NUMBER_OF_FILES"/></translation>
+<translation id="3926627843712816530">Подтвердите, что хотите установить этот плагин. Устанавливать следует модули только тех издателей, которым вы доверяете.</translation>
+<translation id="4838490908464673667">Не установлен необходимый плагин.</translation>
+<translation id="8597182159515967513">заголовок</translation>
+<translation id="2653659639078652383">Отправить</translation>
+</translationbundle> \ No newline at end of file
diff --git a/webkit/glue/resources/webkit_strings_sk.xtb b/webkit/glue/resources/webkit_strings_sk.xtb
new file mode 100644
index 0000000..0804eff
--- /dev/null
+++ b/webkit/glue/resources/webkit_strings_sk.xtb
@@ -0,0 +1,43 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="sk">
+<translation id="4420062214988137980">Nedal sa nainštalovať doplnkový modul</translation>
+<translation id="1235745349614807883">Vyčistiť posledné vyhľadávania</translation>
+<translation id="3825324228893189080">Vyžaduje sa ďalší doplnkový modul</translation>
+<translation id="2965480764085142436">Nie je nainštalovaný doplnkový modul <ph name="PLUGIN"/></translation>
+<translation id="5048533449481078685">ukazovateľ v zozname</translation>
+<translation id="4202807286478387388">skok</translation>
+<translation id="4611115858363067980"><ph name="FILENAME"/> <ph name="WIDTH"/>×<ph name="HEIGHT"/></translation>
+<translation id="4317653869502688143">Potvrďte, že naozaj chcete nainštalovať doplnkový modul <ph name="PLUGIN"/>. Mali by ste inštalovať len doplnkové moduly, ktorým dôverujete.</translation>
+<translation id="7658239707568436148">Zrušiť</translation>
+<translation id="795667975304826397">Nie je vybratý žiadny súbor</translation>
+<translation id="1275511093094545429">Vyžaduje sa doplnkový modul <ph name="PLUGIN"/></translation>
+<translation id="8662565117025751661">Preberá sa doplnkový modul...</translation>
+<translation id="8141602879876242471">Tento index sa dá prehľadávať. Zadajte kľúčové slová na vyhľadanie:</translation>
+<translation id="6845533974506654842">stlačiť</translation>
+<translation id="8244226242650769279">mapa obrázka</translation>
+<translation id="1383141426028388991">Nepodarilo sa nainštalovať doplnok z adresy <ph name="URL"/></translation>
+<translation id="2548326553472216322">Žiadne posledné vyhľadávania</translation>
+<translation id="5944544982112848342">2048 (vysoký stupeň)</translation>
+<translation id="3040011195152428237">odkaz</translation>
+<translation id="8281246460978372009">Po inštalácii doplnkového modulu kliknite sem kvôli obnoveniu obsahu</translation>
+<translation id="7364796246159120393">Vybrať súbor</translation>
+<translation id="8964020114565522021">Súbor presunúť sem</translation>
+<translation id="838869780401515933">označiť</translation>
+<translation id="2846343701378493991">1024 (stredný stupeň)</translation>
+<translation id="5476505524087279545">zrušiť označenie</translation>
+<translation id="679352192834563463">Na zobrazenie tohto obsahu nie je k dispozícii žiadny doplnkový modul</translation>
+<translation id="3789841737615482174">Inštalovať</translation>
+<translation id="6663448176199120256">Posledné vyhľadávania</translation>
+<translation id="3600343118165084788">Kliknite sem, ak chcete prevziať doplnkový modul</translation>
+<translation id="6807599807928161586">webová oblasť</translation>
+<translation id="5939518447894949180">Vynulovať</translation>
+<translation id="3771786644471114952">Získať doplnkový modul</translation>
+<translation id="1842960171412779397">vybrať</translation>
+<translation id="6119846243427417423">aktivovať</translation>
+<translation id="8444882422881193423">Počet súborov: <ph name="NUMBER_OF_FILES"/></translation>
+<translation id="3926627843712816530">Potvrďte, že naozaj chcete nainštalovať tento doplnkový modul. Mali by ste inštalovať len doplnkové moduly, ktorým dôverujete.</translation>
+<translation id="4838490908464673667">Nie je nainštalovaný vyžadovaný doplnkový modul.</translation>
+<translation id="8597182159515967513">nadpis</translation>
+<translation id="2653659639078652383">Odoslať</translation>
+</translationbundle> \ No newline at end of file
diff --git a/webkit/glue/resources/webkit_strings_sl.xtb b/webkit/glue/resources/webkit_strings_sl.xtb
new file mode 100644
index 0000000..4108745
--- /dev/null
+++ b/webkit/glue/resources/webkit_strings_sl.xtb
@@ -0,0 +1,43 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="sl">
+<translation id="4420062214988137980">Namestitev vtičnika ni bila uspešna</translation>
+<translation id="1235745349614807883">Počisti zadnja iskanja</translation>
+<translation id="3825324228893189080">Potreben je dodatni vtičnik</translation>
+<translation id="2965480764085142436">Vtičnik <ph name="PLUGIN"/> ni nameščen</translation>
+<translation id="5048533449481078685">označevalnik seznama</translation>
+<translation id="4202807286478387388">skoči</translation>
+<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation>
+<translation id="4317653869502688143">Prosimo potrdite, da želite namestiti vtičnik <ph name="PLUGIN"/>. Priporočamo vam, da namestite le vtičnike, ki so vredni zaupanja.</translation>
+<translation id="7658239707568436148">Prekliči</translation>
+<translation id="795667975304826397">Nobena datoteka ni izbrana</translation>
+<translation id="1275511093094545429">Potreben je vtičnik <ph name="PLUGIN"/></translation>
+<translation id="8662565117025751661">Prenos vtičnika ...</translation>
+<translation id="8141602879876242471">To je kazalo, ki omogoča iskanje. Vnesite ključne besede za iskanje:</translation>
+<translation id="6845533974506654842">pritisni</translation>
+<translation id="8244226242650769279">slikovni zemljevid</translation>
+<translation id="1383141426028388991">Namestitev vtičnika z naslova <ph name="URL"/> ni bila uspešna</translation>
+<translation id="2548326553472216322">Ni zadnjih iskanj</translation>
+<translation id="5944544982112848342">2048 (visoka stopnja)</translation>
+<translation id="3040011195152428237">povezava</translation>
+<translation id="8281246460978372009">Po namestitvi vtičnika za osvežitev kliknite tu</translation>
+<translation id="7364796246159120393">Izberi datoteko</translation>
+<translation id="8964020114565522021">Datoteko povleci sem</translation>
+<translation id="838869780401515933">potrdi</translation>
+<translation id="2846343701378493991">1024 (srednja stopnja)</translation>
+<translation id="5476505524087279545">počisti izbor</translation>
+<translation id="679352192834563463">Za prikaz te vsebine ni na voljo noben vtičnik</translation>
+<translation id="3789841737615482174">Namesti</translation>
+<translation id="6663448176199120256">Zadnja iskanja</translation>
+<translation id="3600343118165084788">Za prenos vtičnika kliknite tukaj</translation>
+<translation id="6807599807928161586">spletno področje</translation>
+<translation id="5939518447894949180">Ponastavi</translation>
+<translation id="3771786644471114952">Dobite vtičnik</translation>
+<translation id="1842960171412779397">izberi</translation>
+<translation id="6119846243427417423">aktiviraj</translation>
+<translation id="8444882422881193423">Število datotek: <ph name="NUMBER_OF_FILES"/></translation>
+<translation id="3926627843712816530">Potrdite, da želite namestiti ta vtičnik. Priporočamo vam, da namestite le vtičnike, ki so vredni zaupanja.</translation>
+<translation id="4838490908464673667">Potrebni vtičnik ni nameščen</translation>
+<translation id="8597182159515967513">naslov</translation>
+<translation id="2653659639078652383">Pošlji</translation>
+</translationbundle> \ No newline at end of file
diff --git a/webkit/glue/resources/webkit_strings_sr.xtb b/webkit/glue/resources/webkit_strings_sr.xtb
new file mode 100644
index 0000000..f4da790
--- /dev/null
+++ b/webkit/glue/resources/webkit_strings_sr.xtb
@@ -0,0 +1,43 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="sr">
+<translation id="4420062214988137980">Инсталирање додатне компоненте није успело</translation>
+<translation id="1235745349614807883">Обриши недавне претраге</translation>
+<translation id="3825324228893189080">Потребна је додатна компонента</translation>
+<translation id="2965480764085142436">Додатна компонента <ph name="PLUGIN"/> није инсталирана</translation>
+<translation id="5048533449481078685">означивач листе</translation>
+<translation id="4202807286478387388">прескочи</translation>
+<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation>
+<translation id="4317653869502688143">Потврдите да желите да инсталирате додатну компоненту <ph name="PLUGIN"/>. Требало би да инсталирате само додатне компоненте које сматрате поузданима.</translation>
+<translation id="7658239707568436148">Откажи</translation>
+<translation id="795667975304826397">Није одабрана ниједна датотека</translation>
+<translation id="1275511093094545429">Потребна је додатна компонента <ph name="PLUGIN"/></translation>
+<translation id="8662565117025751661">Преузимање додатне компоненте...</translation>
+<translation id="8141602879876242471">Ово је индекс који може да се претражује. Унесите кључне речи за претрагу:</translation>
+<translation id="6845533974506654842">притисни</translation>
+<translation id="8244226242650769279">мапа слике</translation>
+<translation id="1383141426028388991">Неуспешно инсталирање додатне компоненте са <ph name="URL"/></translation>
+<translation id="2548326553472216322">Нема недавних претрага</translation>
+<translation id="5944544982112848342">2048 (високи степен)</translation>
+<translation id="3040011195152428237">веза</translation>
+<translation id="8281246460978372009">Након инсталирања додатне компоненте, кликните овде да бисте освежили</translation>
+<translation id="7364796246159120393">Одаберите датотеку</translation>
+<translation id="8964020114565522021">Превуците датотеку овде</translation>
+<translation id="838869780401515933">изабери</translation>
+<translation id="2846343701378493991">1024 (средњи степен)</translation>
+<translation id="5476505524087279545">опозови избор</translation>
+<translation id="679352192834563463">Нема доступне додатне компоненте за приказивање овог садржаја</translation>
+<translation id="3789841737615482174">Инсталирај</translation>
+<translation id="6663448176199120256">Недавне претраге</translation>
+<translation id="3600343118165084788">Кликните овде да бисте преузели додатну компоненту</translation>
+<translation id="6807599807928161586">веб област</translation>
+<translation id="5939518447894949180">Ресетуј</translation>
+<translation id="3771786644471114952">Набави додатну компоненту</translation>
+<translation id="1842960171412779397">изабери</translation>
+<translation id="6119846243427417423">активирај</translation>
+<translation id="8444882422881193423"><ph name="NUMBER_OF_FILES"/> датотеке(а)</translation>
+<translation id="3926627843712816530">Потврдите да желите да инсталирате ову додатну компоненту. Требало би да инсталирате само додатне компоненте које сматрате поузданима.</translation>
+<translation id="4838490908464673667">Потребна додатна компонента није инсталирана</translation>
+<translation id="8597182159515967513">наслов</translation>
+<translation id="2653659639078652383">Пошаљи</translation>
+</translationbundle> \ No newline at end of file
diff --git a/webkit/glue/resources/webkit_strings_sv.xtb b/webkit/glue/resources/webkit_strings_sv.xtb
new file mode 100644
index 0000000..4b7323c
--- /dev/null
+++ b/webkit/glue/resources/webkit_strings_sv.xtb
@@ -0,0 +1,43 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="sv">
+<translation id="4420062214988137980">Installationen av plugin-programmet misslyckades</translation>
+<translation id="1235745349614807883">Rensa senaste sökningar</translation>
+<translation id="3825324228893189080">Ytterligare plugin-program krävs</translation>
+<translation id="2965480764085142436"><ph name="PLUGIN"/>-plugin-programmet har inte installerats</translation>
+<translation id="5048533449481078685">listmarkör</translation>
+<translation id="4202807286478387388">fortsätta</translation>
+<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation>
+<translation id="4317653869502688143">Bekräfta att du vill installera <ph name="PLUGIN"/>-plugin-programmet. Installera bara tillförlitliga plugin-program.</translation>
+<translation id="7658239707568436148">Avbryt</translation>
+<translation id="795667975304826397">Ingen fil har valts</translation>
+<translation id="1275511093094545429"><ph name="PLUGIN"/>-pluginprogram krävs</translation>
+<translation id="8662565117025751661">Laddar ned plugin-program...</translation>
+<translation id="8141602879876242471">Det här är ett sökbart index. Skriv sökord:</translation>
+<translation id="6845533974506654842">tryck</translation>
+<translation id="8244226242650769279">bildkarta</translation>
+<translation id="1383141426028388991">Det gick inte att installera plugin-program från <ph name="URL"/></translation>
+<translation id="2548326553472216322">Inga nya sökningar</translation>
+<translation id="5944544982112848342">2048 (hög)</translation>
+<translation id="3040011195152428237">länk</translation>
+<translation id="8281246460978372009">Klicka här för att uppdatera när du har installerat plugin-programmet</translation>
+<translation id="7364796246159120393">Välj fil</translation>
+<translation id="8964020114565522021">Dra filen hit</translation>
+<translation id="838869780401515933">kryssa för</translation>
+<translation id="2846343701378493991">1024 (medel)</translation>
+<translation id="5476505524087279545">kryssa av</translation>
+<translation id="679352192834563463">Det finns inget plugin-program för att visa det här innehållet</translation>
+<translation id="3789841737615482174">Installera</translation>
+<translation id="6663448176199120256">Senaste sökningar</translation>
+<translation id="3600343118165084788">Klicka här för att ladda ned plugin-program</translation>
+<translation id="6807599807928161586">webbområde</translation>
+<translation id="5939518447894949180">Återställ</translation>
+<translation id="3771786644471114952">Hämta plugin-program</translation>
+<translation id="1842960171412779397">välj</translation>
+<translation id="6119846243427417423">aktivera</translation>
+<translation id="8444882422881193423"><ph name="NUMBER_OF_FILES"/> filer</translation>
+<translation id="3926627843712816530">Bekräfta att du vill installera det här plugin-programmet. Installera bara tillförlitliga plugin-program.</translation>
+<translation id="4838490908464673667">De begärda plugin-programmen har inte installerats</translation>
+<translation id="8597182159515967513">rubrik</translation>
+<translation id="2653659639078652383">Skicka</translation>
+</translationbundle> \ No newline at end of file
diff --git a/webkit/glue/resources/webkit_strings_sw.xtb b/webkit/glue/resources/webkit_strings_sw.xtb
new file mode 100644
index 0000000..9d798d8
--- /dev/null
+++ b/webkit/glue/resources/webkit_strings_sw.xtb
@@ -0,0 +1,43 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="sw">
+<translation id="4420062214988137980">Usanidi wa programu-jalizi haukufaulu</translation>
+<translation id="1235745349614807883">Futa Utafutaji wa Hivi Karibuni</translation>
+<translation id="3825324228893189080">Programu-jalizi inahitajika</translation>
+<translation id="2965480764085142436">Programu-jalizi <ph name="PLUGIN"/> haijasanidiwa</translation>
+<translation id="5048533449481078685">kialamishi orodha</translation>
+<translation id="4202807286478387388">ruka</translation>
+<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation>
+<translation id="4317653869502688143">Tafadali thibitisha kuwa ungependa kusanidi programu-jalizi <ph name="PLUGIN"/>. Unastahili kusanidi progrmau-jalizi unazoamini pekee.</translation>
+<translation id="7658239707568436148">Ghairi</translation>
+<translation id="795667975304826397">Hakuna faili iliyochaguliwa</translation>
+<translation id="1275511093094545429">Programu-jalizi <ph name="PLUGIN"/> inahitajika</translation>
+<translation id="8662565117025751661">Programu-jalizi inapakuliwa</translation>
+<translation id="8141602879876242471">Hii ni fahirisi inayoweza kutafutwa. Weka maneno muhimu ya utafutaji.</translation>
+<translation id="6845533974506654842">bofya</translation>
+<translation id="8244226242650769279">ramani ya picha</translation>
+<translation id="1383141426028388991">Imeshindwa kusanidi programu-jalizi kutoka <ph name="URL"/></translation>
+<translation id="2548326553472216322">Hakuna utafutaji wa hivi karibuni</translation>
+<translation id="5944544982112848342">2048 (Gredi ya Juu)</translation>
+<translation id="3040011195152428237">kiungo</translation>
+<translation id="8281246460978372009">Baada ya kusakinisha programu-jalizi, bonyeza hapa kuonyesha upya</translation>
+<translation id="7364796246159120393">Chagua Faili</translation>
+<translation id="8964020114565522021">Vuta faili hapa</translation>
+<translation id="838869780401515933">chunguza</translation>
+<translation id="2846343701378493991">1024 (Gredi Wastani)</translation>
+<translation id="5476505524087279545">toa tiki</translation>
+<translation id="679352192834563463">Hakuna programu-jalizi ya kuonyesha maudhui haya</translation>
+<translation id="3789841737615482174">Sanidi</translation>
+<translation id="6663448176199120256">Utafutaji wa hivi karibuni</translation>
+<translation id="3600343118165084788">Bofya hapa kupakua programu-jalizi</translation>
+<translation id="6807599807928161586">eneo wavuti</translation>
+<translation id="5939518447894949180">Weka upya</translation>
+<translation id="3771786644471114952">Pata programu-jalizi</translation>
+<translation id="1842960171412779397">chagua</translation>
+<translation id="6119846243427417423">wezesha</translation>
+<translation id="8444882422881193423">faili <ph name="NUMBER_OF_FILES"/></translation>
+<translation id="3926627843712816530">Tafadali thibitisha kuwa ungependa kusanidi programu-jalizi hii. Unastahili kusanidi progrmau-jalizi unazoamini pekee.</translation>
+<translation id="4838490908464673667">Programu-jalizi inayohitajika haijasanidiwa</translation>
+<translation id="8597182159515967513">kichwa</translation>
+<translation id="2653659639078652383">Wasilisha</translation>
+</translationbundle> \ No newline at end of file
diff --git a/webkit/glue/resources/webkit_strings_ta.xtb b/webkit/glue/resources/webkit_strings_ta.xtb
new file mode 100644
index 0000000..3b19b4c
--- /dev/null
+++ b/webkit/glue/resources/webkit_strings_ta.xtb
@@ -0,0 +1,43 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="ta">
+<translation id="4420062214988137980">செருகுநிரல் நிறுவல் தோல்வியடைந்தது</translation>
+<translation id="1235745349614807883">சமீபத்திய தேடல்களை சுத்தமாக்கு</translation>
+<translation id="3825324228893189080">கூடுதல் செருகுநிரல் தேவைப்படுகிறது</translation>
+<translation id="2965480764085142436"><ph name="PLUGIN"/> செருகுநிரல் நிறுவப்படவில்லை</translation>
+<translation id="5048533449481078685">பட்டியல் குறிப்பான்</translation>
+<translation id="4202807286478387388">தாவு</translation>
+<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation>
+<translation id="4317653869502688143"><ph name="PLUGIN"/> செருகுநிரலை நிறுவ விரும்புகிறீர்கள் என்பதை உறுதிப்படுத்துக. நீங்கள் நம்பும் செருகுநிரல்களை மட்டுமே நிறுவ வேண்டும்.</translation>
+<translation id="7658239707568436148">ரத்துசெய்</translation>
+<translation id="795667975304826397">எந்த கோப்பும் தேர்ந்தெடுக்கப்டவில்லை</translation>
+<translation id="1275511093094545429"><ph name="PLUGIN"/> செருகுநிரல் தேவைப்படுகிறது</translation>
+<translation id="8662565117025751661">செருகுநிரலைப் பதிவிறக்குகிறது...</translation>
+<translation id="8141602879876242471">இது தேடக்கூடிய பொருளடக்கம். தேடல் சொற்களை உள்ளிடுக:</translation>
+<translation id="6845533974506654842">அழுத்துக</translation>
+<translation id="8244226242650769279">பட மேப்</translation>
+<translation id="1383141426028388991"><ph name="URL"/> இலிருந்து செருகுநிரலை நிறுவுவதில் தோல்வியடைந்தது</translation>
+<translation id="2548326553472216322">சமீபத்திய தேடல்கள் எதுவுமில்லை</translation>
+<translation id="5944544982112848342">2048 (உயர் தரம்)</translation>
+<translation id="3040011195152428237">இணைப்பு</translation>
+<translation id="8281246460978372009">செருகுநிரலை நிறுவிய பின், புதுப்பிக்க இங்கு கிளிக் செய்க</translation>
+<translation id="7364796246159120393">கோப்பைத் தேர்வு செய்க</translation>
+<translation id="8964020114565522021">கோப்பை இங்கே இழுத்து வருக</translation>
+<translation id="838869780401515933">சரிபார்</translation>
+<translation id="2846343701378493991">1024 (இடைநிலைத் தரம்)</translation>
+<translation id="5476505524087279545">தேர்வு நீக்கு</translation>
+<translation id="679352192834563463">இந்த உள்ளடக்கத்தைக் காண்பிப்பதற்கான செருகுநிரல் கிடைக்கவில்லை</translation>
+<translation id="3789841737615482174">நிறுவு</translation>
+<translation id="6663448176199120256">சமீபத்திய தேடல்கள்</translation>
+<translation id="3600343118165084788">செருகுநிரலைப் பதிவிறக்க இங்கே கிளிக் செய்க</translation>
+<translation id="6807599807928161586">வலைப் பகுதி</translation>
+<translation id="5939518447894949180">மீட்டமை</translation>
+<translation id="3771786644471114952">செருகுநிரலைப் பெறுக</translation>
+<translation id="1842960171412779397">தேர்ந்தெடு</translation>
+<translation id="6119846243427417423">செயல்படுத்து</translation>
+<translation id="8444882422881193423"><ph name="NUMBER_OF_FILES"/> கோப்புகள்</translation>
+<translation id="3926627843712816530">இந்த செருகுநிரலை நிறுவ விரும்புகிறீர்கள் என்பதை உறுதிப்படுத்துக. நீங்கள் நம்பும் செருகுநிரல்களை மட்டுமே நிறுவ வேண்டும்.</translation>
+<translation id="4838490908464673667">தேவையான செருகுநிரல் நிறுவப்படவில்லை</translation>
+<translation id="8597182159515967513">தலைப்பு</translation>
+<translation id="2653659639078652383">சமர்ப்பி</translation>
+</translationbundle> \ No newline at end of file
diff --git a/webkit/glue/resources/webkit_strings_te.xtb b/webkit/glue/resources/webkit_strings_te.xtb
new file mode 100644
index 0000000..37e3265
--- /dev/null
+++ b/webkit/glue/resources/webkit_strings_te.xtb
@@ -0,0 +1,43 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="te">
+<translation id="4420062214988137980">ప్లగ్ ఇన్ ఇన్‌స్టాలేషన్ విఫలమయ్యింది</translation>
+<translation id="1235745349614807883">ఇటీవల శోధనలను క్లియర్ చెయ్యి</translation>
+<translation id="3825324228893189080">అదనపు ప్లగ్‌ఇన్ అవసరం</translation>
+<translation id="2965480764085142436"><ph name="PLUGIN"/> ప్లగ్‌ఇన్ ఇన్‌స్టాల్ అవ్వలేదు</translation>
+<translation id="5048533449481078685">జాబితా మార్కర్</translation>
+<translation id="4202807286478387388">వెళ్ళు</translation>
+<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation>
+<translation id="4317653869502688143">దయచేసి <ph name="PLUGIN"/> ప్లగ్‌ఇన్‌ను ఇన్‌స్టాల్ చెయ్యాలనుకుంటునట్లు నిర్ధారించండి. మీరు నమ్మే ప్లగ్‌ఇన్‌లను మాత్రమే మీరు ఇన్‌స్టాల్ చెయ్యాలి.</translation>
+<translation id="7658239707568436148">రద్దు చెయ్యి</translation>
+<translation id="795667975304826397">ఫైల్ ఏదీ ఎంచుకోలేదు</translation>
+<translation id="1275511093094545429"><ph name="PLUGIN"/> ప్లగ్‌ఇన్ అవసరం</translation>
+<translation id="8662565117025751661">ప్లగిన్‌ను డౌన్‌లోడ్ చేస్తోంది...</translation>
+<translation id="8141602879876242471">ఇది ఒక శోధించగల సూచిక. శోధన కీవర్డ్‌లను ఎంటర్ చెయ్యండి:</translation>
+<translation id="6845533974506654842">నొక్కండి</translation>
+<translation id="8244226242650769279">చిత్రం మాప్</translation>
+<translation id="1383141426028388991"><ph name="URL"/> నుండి ప్లగ్‌ఇన్‌ను ఇన్‌స్టాల్ చెయ్యడంలో విఫలమైంది</translation>
+<translation id="2548326553472216322">ఇటీవల శోధనలు లేవు</translation>
+<translation id="5944544982112848342">2048 (ఉత్తమ గ్రేడ్)</translation>
+<translation id="3040011195152428237">లింక్</translation>
+<translation id="8281246460978372009">ప్లగ్‌ఇన్‌‌ను ఇన్‌స్టాల్ చేసిన తర్వాత, రిఫ్రెష్ చెయ్యడానికి ఇక్కడ క్లిక్ చెయ్యండి.</translation>
+<translation id="7364796246159120393">ఫైల్‌ను ఎంచుకోండి</translation>
+<translation id="8964020114565522021">ఫైల్‌ను ఇక్కడకు లాగండి</translation>
+<translation id="838869780401515933">తనిఖీ చెయ్యి</translation>
+<translation id="2846343701378493991">1024 (మధ్యస్థ గ్రేడ్)</translation>
+<translation id="5476505524087279545">ఎంపిక చెయ్యబడలేదు</translation>
+<translation id="679352192834563463">ఈ కంటెంట్‌ను ప్రదర్శించడానికి ఏ ప్లగ్‌ఇన్ అందుబాటులో లేదు</translation>
+<translation id="3789841737615482174">ఇన్‌స్టాల్ చెయ్యి</translation>
+<translation id="6663448176199120256">ఇటీవల శోధనలు</translation>
+<translation id="3600343118165084788">ప్లగ్‌ఇన్‌ను దిగుమతి చెయ్యడానికి ఇక్కడ క్లిక్ చెయ్యండి</translation>
+<translation id="6807599807928161586">వెబ్ ప్రాంతం</translation>
+<translation id="5939518447894949180">తిరిగి అమర్చండి</translation>
+<translation id="3771786644471114952">ప్లగ్‌ఇన్‌ను పొందండి</translation>
+<translation id="1842960171412779397">ఎంచుకోండి</translation>
+<translation id="6119846243427417423">ఆక్టివేట్ చెయ్యి</translation>
+<translation id="8444882422881193423"><ph name="NUMBER_OF_FILES"/> ఫైళ్ళు</translation>
+<translation id="3926627843712816530">దయచేసి ఈ ప్లగ్ ఇన్‌ను ఇన్‌స్టాల్ చెయ్యాలనుకుంటునట్లు నిర్ధారించండి. మీరు నమ్మే ప్లగ్‌ఇన్‌లను మాత్రమే మీరు ఇన్‌స్టాల్ చెయ్యాలి.</translation>
+<translation id="4838490908464673667">అవసరమైన ప్లగిన్ ఇన్‌స్టాల్ చెయ్యబడలేదు</translation>
+<translation id="8597182159515967513">శీర్షిక</translation>
+<translation id="2653659639078652383">సమర్పించు</translation>
+</translationbundle> \ No newline at end of file
diff --git a/webkit/glue/resources/webkit_strings_th.xtb b/webkit/glue/resources/webkit_strings_th.xtb
new file mode 100644
index 0000000..a6c8b4b
--- /dev/null
+++ b/webkit/glue/resources/webkit_strings_th.xtb
@@ -0,0 +1,43 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="th">
+<translation id="4420062214988137980">การติดตั้งปลั๊กอินล้มเหลว</translation>
+<translation id="1235745349614807883">ลบการค้นหาล่าสุด</translation>
+<translation id="3825324228893189080">ต้องการปลั๊กอินเพิ่มเติม</translation>
+<translation id="2965480764085142436"><ph name="PLUGIN"/> ไม่ได้ติดตั้งปลั๊กอิน</translation>
+<translation id="5048533449481078685">ผู้สร้างรายการ</translation>
+<translation id="4202807286478387388">ข้าม</translation>
+<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation>
+<translation id="4317653869502688143">โปรดยืนยันว่าคุณต้องการที่จะติดตั้ง <ph name="PLUGIN"/> ปลั๊กอิน คุณควรติดตั้งปลั๊กอินที่คุณเชื่อถือเท่านั้น</translation>
+<translation id="7658239707568436148">ยกเลิก</translation>
+<translation id="795667975304826397">ไม่ได้เลือกไฟล์ใด</translation>
+<translation id="1275511093094545429">ต้องใช้ปลั๊กอิน <ph name="PLUGIN"/></translation>
+<translation id="8662565117025751661">กำลังดาวน์โหลดปลั๊กอิน...</translation>
+<translation id="8141602879876242471">นี่คือดัชนีที่สามารถค้นหาได้ ป้อนคำหลักในการค้นหา:</translation>
+<translation id="6845533974506654842">กด</translation>
+<translation id="8244226242650769279">แผนที่รูปภาพ</translation>
+<translation id="1383141426028388991">การติดตั้งปลั๊กอินจาก <ph name="URL"/> ล้มเหลว</translation>
+<translation id="2548326553472216322">ไม่พบการค้นหาล่าสุด</translation>
+<translation id="5944544982112848342">2048 (เกรดสูง)</translation>
+<translation id="3040011195152428237">ลิงก์</translation>
+<translation id="8281246460978372009">หลังจากติดตั้งปลั๊กอิน คลิกที่นี่เพื่อรีเฟรซ</translation>
+<translation id="7364796246159120393">เลือกไฟล์</translation>
+<translation id="8964020114565522021">ลากไฟล์มาที่นี่</translation>
+<translation id="838869780401515933">ทำเครื่องหมาย</translation>
+<translation id="2846343701378493991">1024 (เกรดปานกลาง)</translation>
+<translation id="5476505524087279545">ยกเลิกการทำเครื่องหมาย</translation>
+<translation id="679352192834563463">ไม่มีปลั๊กอินที่จะแสดงเนื้อหานี้</translation>
+<translation id="3789841737615482174">ติดตั้ง</translation>
+<translation id="6663448176199120256">การค้นหาล่าสุด</translation>
+<translation id="3600343118165084788">คลิกที่นี่เพื่อดาวน์โหลดปลั๊กอิน</translation>
+<translation id="6807599807928161586">พื้นที่เว็บ</translation>
+<translation id="5939518447894949180">ตั้งค่าใหม่</translation>
+<translation id="3771786644471114952">ติดตั้งปลั๊กอิน</translation>
+<translation id="1842960171412779397">เลือก</translation>
+<translation id="6119846243427417423">เปิดใช้งาน</translation>
+<translation id="8444882422881193423"><ph name="NUMBER_OF_FILES"/> ไฟล์</translation>
+<translation id="3926627843712816530">โปรดยืนยันว่าคุณต้องการติดตั้งปลั๊กอินนี้ คุณควรติดตั้งปลั๊กอินที่คุณเชื่อถือเท่านั้น</translation>
+<translation id="4838490908464673667">ไม่ได้ติดตั้งปลั๊กอินที่จำเป็น</translation>
+<translation id="8597182159515967513">ส่วนหัว</translation>
+<translation id="2653659639078652383">ส่ง</translation>
+</translationbundle> \ No newline at end of file
diff --git a/webkit/glue/resources/webkit_strings_tr.xtb b/webkit/glue/resources/webkit_strings_tr.xtb
new file mode 100644
index 0000000..1f5571a
--- /dev/null
+++ b/webkit/glue/resources/webkit_strings_tr.xtb
@@ -0,0 +1,43 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="tr">
+<translation id="4420062214988137980">Eklenti kurulumu başarısız oldu</translation>
+<translation id="1235745349614807883">Son Aramaları Temizle</translation>
+<translation id="3825324228893189080">Ek eklenti gerekiyor</translation>
+<translation id="2965480764085142436"><ph name="PLUGIN"/> eklentisi yüklü değil</translation>
+<translation id="5048533449481078685">liste işaretçisi</translation>
+<translation id="4202807286478387388">git</translation>
+<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation>
+<translation id="4317653869502688143">Lütfen <ph name="PLUGIN"/> eklentisini yüklemek istediğinizi onaylayın. Yalnızca güvendiğiniz eklentileri yüklemelisiniz.</translation>
+<translation id="7658239707568436148">İptal</translation>
+<translation id="795667975304826397">Dosya seçilmedi</translation>
+<translation id="1275511093094545429"><ph name="PLUGIN"/> eklentisi gerekiyor</translation>
+<translation id="8662565117025751661">Eklenti indiriliyor...</translation>
+<translation id="8141602879876242471">Bu dizinde arama yapılabilir. Arama anahtar kelimeleri girin:</translation>
+<translation id="6845533974506654842">bas</translation>
+<translation id="8244226242650769279">resim haritası</translation>
+<translation id="1383141426028388991">Eklenti, <ph name="URL"/> kaynağından yüklenemedi</translation>
+<translation id="2548326553472216322">Yeni arama yok</translation>
+<translation id="5944544982112848342">2048 (Yüksek Düzey)</translation>
+<translation id="3040011195152428237">bağlantı</translation>
+<translation id="8281246460978372009">Eklentiyi yükledikten sonra yenilemek için burayı tıklayın</translation>
+<translation id="7364796246159120393">Dosya Seç</translation>
+<translation id="8964020114565522021">Dosyayı buraya sürükleyin</translation>
+<translation id="838869780401515933">işaretle</translation>
+<translation id="2846343701378493991">1024 (Orta Düzey)</translation>
+<translation id="5476505524087279545">işareti kaldır</translation>
+<translation id="679352192834563463">Bu içeriği görüntüleyecek eklenti yok</translation>
+<translation id="3789841737615482174">Yükle</translation>
+<translation id="6663448176199120256">Son Aramalar</translation>
+<translation id="3600343118165084788">Eklentiyi indirmek için burayı tıklayın</translation>
+<translation id="6807599807928161586">web alanı</translation>
+<translation id="5939518447894949180">Sıfırla</translation>
+<translation id="3771786644471114952">Eklenti Al</translation>
+<translation id="1842960171412779397">seç</translation>
+<translation id="6119846243427417423">etkinleştir</translation>
+<translation id="8444882422881193423"><ph name="NUMBER_OF_FILES"/> dosya</translation>
+<translation id="3926627843712816530">Lütfen bu eklentiyi yüklemek istediğinizi onaylayın. Yalnızca güvendiğiniz eklentileri yüklemelisiniz.</translation>
+<translation id="4838490908464673667">Gereken eklenti yüklü değil</translation>
+<translation id="8597182159515967513">başlık</translation>
+<translation id="2653659639078652383">Gönder</translation>
+</translationbundle> \ No newline at end of file
diff --git a/webkit/glue/resources/webkit_strings_uk.xtb b/webkit/glue/resources/webkit_strings_uk.xtb
new file mode 100644
index 0000000..ae193fe
--- /dev/null
+++ b/webkit/glue/resources/webkit_strings_uk.xtb
@@ -0,0 +1,43 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="uk">
+<translation id="4420062214988137980">Не вдалось інсталювати модуль</translation>
+<translation id="1235745349614807883">Очистити останні пошуки</translation>
+<translation id="3825324228893189080">Потрібен додатковий модуль.</translation>
+<translation id="2965480764085142436">Модуль <ph name="PLUGIN"/> не інстальовано</translation>
+<translation id="5048533449481078685">маркер списку</translation>
+<translation id="4202807286478387388">перейти</translation>
+<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation>
+<translation id="4317653869502688143">Підтвердьте інсталяцію модуля <ph name="PLUGIN"/>. Слід інсталювати лише модулі, які вважаєте надійними.</translation>
+<translation id="7658239707568436148">Скасувати</translation>
+<translation id="795667975304826397">Файл не вибрано</translation>
+<translation id="1275511093094545429">Потрібен модуль <ph name="PLUGIN"/></translation>
+<translation id="8662565117025751661">Завантаження модуля...</translation>
+<translation id="8141602879876242471">Цей доступний для пошуку індекс. Введіть ключові слова пошуку:</translation>
+<translation id="6845533974506654842">натиснути</translation>
+<translation id="8244226242650769279">мапа зображення</translation>
+<translation id="1383141426028388991">Не вдалося встановити модуль із <ph name="URL"/></translation>
+<translation id="2548326553472216322">Немає останніх пошуків</translation>
+<translation id="5944544982112848342">2048 (Високий рівень)</translation>
+<translation id="3040011195152428237">посилання</translation>
+<translation id="8281246460978372009">Після інсталяції модуля натисніть тут, щоб оновити</translation>
+<translation id="7364796246159120393">Вибрати файл</translation>
+<translation id="8964020114565522021">Перетягніть файл сюди</translation>
+<translation id="838869780401515933">установити прапорець</translation>
+<translation id="2846343701378493991">1024 (Середній рівень)</translation>
+<translation id="5476505524087279545">зняти прапорець</translation>
+<translation id="679352192834563463">Немає доступного модуля для відображення цього вмісту</translation>
+<translation id="3789841737615482174">Інсталювати</translation>
+<translation id="6663448176199120256">Останні пошуки</translation>
+<translation id="3600343118165084788">Натисніть тут, щоб завантажити модуль</translation>
+<translation id="6807599807928161586">область Інтернету</translation>
+<translation id="5939518447894949180">Скинути</translation>
+<translation id="3771786644471114952">Отримати модуль</translation>
+<translation id="1842960171412779397">вибрати</translation>
+<translation id="6119846243427417423">активувати</translation>
+<translation id="8444882422881193423">файлів: <ph name="NUMBER_OF_FILES"/></translation>
+<translation id="3926627843712816530">Підтвердьте інсталяцію цього модуля. Слід інсталювати лише модулі, які вважаєте надійними.</translation>
+<translation id="4838490908464673667">Потрібний модуль не інстальовано</translation>
+<translation id="8597182159515967513">заголовок</translation>
+<translation id="2653659639078652383">Надіслати</translation>
+</translationbundle> \ No newline at end of file
diff --git a/webkit/glue/resources/webkit_strings_vi.xtb b/webkit/glue/resources/webkit_strings_vi.xtb
new file mode 100644
index 0000000..9e3f498
--- /dev/null
+++ b/webkit/glue/resources/webkit_strings_vi.xtb
@@ -0,0 +1,43 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="vi">
+<translation id="4420062214988137980">Không cài đặt plugin được</translation>
+<translation id="1235745349614807883">Xoá Tìm kiếm Gần đây</translation>
+<translation id="3825324228893189080">Cần plugin bổ sung</translation>
+<translation id="2965480764085142436"><ph name="PLUGIN"/> plgin không được cài đặt</translation>
+<translation id="5048533449481078685">đánh dấu danh sách</translation>
+<translation id="4202807286478387388">chuyển</translation>
+<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation>
+<translation id="4317653869502688143">Vui lòng xác nhận rằng bạn muốn cài đặt plugin <ph name="PLUGIN"/>. Bạn chỉ nên cài đặt plugin mà mình tin cậy.</translation>
+<translation id="7658239707568436148">Huỷ</translation>
+<translation id="795667975304826397">Không có tệp nào được chọn</translation>
+<translation id="1275511093094545429">Cần <ph name="PLUGIN"/> plugin</translation>
+<translation id="8662565117025751661">Đang tải xuống plugin...</translation>
+<translation id="8141602879876242471">Đây là chỉ mục có thể tìm kiếm. Nhập từ khoá tìm kiếm vào:</translation>
+<translation id="6845533974506654842">nhấn</translation>
+<translation id="8244226242650769279">bản đồ hình ảnh</translation>
+<translation id="1383141426028388991">Không cài đặt được plugin từ <ph name="URL"/></translation>
+<translation id="2548326553472216322">Không có tìm kiếm nào gần đây</translation>
+<translation id="5944544982112848342">2048 (Cấp độ cao)</translation>
+<translation id="3040011195152428237">liên kết</translation>
+<translation id="8281246460978372009">Sau khi cài đặt plugin, nhấp vào đây để làm mới</translation>
+<translation id="7364796246159120393">Chọn Tệp tin</translation>
+<translation id="8964020114565522021">Kéo tệp tại đây</translation>
+<translation id="838869780401515933">chọn</translation>
+<translation id="2846343701378493991">1024 (Loại Trung bình)</translation>
+<translation id="5476505524087279545">bỏ chọn</translation>
+<translation id="679352192834563463">Không plugin nào có sẵn để hiển thị nội dung này</translation>
+<translation id="3789841737615482174">Cài đặt</translation>
+<translation id="6663448176199120256">Tìm kiếm Gần đây</translation>
+<translation id="3600343118165084788">Nhấp vào đây để tải plugin xuống</translation>
+<translation id="6807599807928161586">khu vực web</translation>
+<translation id="5939518447894949180">Đặt lại</translation>
+<translation id="3771786644471114952">Tải Plugin</translation>
+<translation id="1842960171412779397">chọn</translation>
+<translation id="6119846243427417423">kích hoạt</translation>
+<translation id="8444882422881193423"><ph name="NUMBER_OF_FILES"/> tệp</translation>
+<translation id="3926627843712816530">Hãy xác nhận rằng bạn muốn cài đặt plugin này. Bạn chỉ nên cài đặt plugin mà mình tin cậy.</translation>
+<translation id="4838490908464673667">Plugin yêu cầu không được cài đặt</translation>
+<translation id="8597182159515967513">đầu đề</translation>
+<translation id="2653659639078652383">Gửi</translation>
+</translationbundle> \ No newline at end of file
diff --git a/webkit/glue/resources/webkit_strings_zh-CN.xtb b/webkit/glue/resources/webkit_strings_zh-CN.xtb
new file mode 100644
index 0000000..c2d8176
--- /dev/null
+++ b/webkit/glue/resources/webkit_strings_zh-CN.xtb
@@ -0,0 +1,43 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="zh-CN">
+<translation id="4420062214988137980">安装插件失败</translation>
+<translation id="1235745349614807883">清除最近的搜索</translation>
+<translation id="3825324228893189080">需要使用其他插件</translation>
+<translation id="2965480764085142436">未安装 <ph name="PLUGIN"/> 插件</translation>
+<translation id="5048533449481078685">列表标记</translation>
+<translation id="4202807286478387388">略过</translation>
+<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation>
+<translation id="4317653869502688143">请确认您要安装 <ph name="PLUGIN"/> 插件。您只能安装自己信任的插件。</translation>
+<translation id="7658239707568436148">取消</translation>
+<translation id="795667975304826397">未选择文件</translation>
+<translation id="1275511093094545429">需要使用 <ph name="PLUGIN"/> 插件</translation>
+<translation id="8662565117025751661">正在下载插件...</translation>
+<translation id="8141602879876242471">这是一个可搜索的索引。请输入搜索关键字:</translation>
+<translation id="6845533974506654842">按</translation>
+<translation id="8244226242650769279">图片映射</translation>
+<translation id="1383141426028388991">无法从 <ph name="URL"/> 安装插件</translation>
+<translation id="2548326553472216322">最近未执行搜索</translation>
+<translation id="5944544982112848342">2048(高强度)</translation>
+<translation id="3040011195152428237">链接</translation>
+<translation id="8281246460978372009">安装插件后,点击此处可刷新</translation>
+<translation id="7364796246159120393">选择文件</translation>
+<translation id="8964020114565522021">将文件拖到此处</translation>
+<translation id="838869780401515933">选中</translation>
+<translation id="2846343701378493991">1024(中等强度)</translation>
+<translation id="5476505524087279545">取消选中</translation>
+<translation id="679352192834563463">没有用于显示这种内容的插件</translation>
+<translation id="3789841737615482174">安装</translation>
+<translation id="6663448176199120256">近期搜索</translation>
+<translation id="3600343118165084788">点击此处可下载插件</translation>
+<translation id="6807599807928161586">网络区域</translation>
+<translation id="5939518447894949180">重置</translation>
+<translation id="3771786644471114952">获取插件</translation>
+<translation id="1842960171412779397">选中</translation>
+<translation id="6119846243427417423">激活</translation>
+<translation id="8444882422881193423"><ph name="NUMBER_OF_FILES"/> 个文件</translation>
+<translation id="3926627843712816530">请确认您要安装此插件。您只能安装自己信任的插件。</translation>
+<translation id="4838490908464673667">未安装所需插件</translation>
+<translation id="8597182159515967513">标题</translation>
+<translation id="2653659639078652383">提交</translation>
+</translationbundle> \ No newline at end of file
diff --git a/webkit/glue/resources/webkit_strings_zh-TW.xtb b/webkit/glue/resources/webkit_strings_zh-TW.xtb
new file mode 100644
index 0000000..999207a
--- /dev/null
+++ b/webkit/glue/resources/webkit_strings_zh-TW.xtb
@@ -0,0 +1,43 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="zh-TW">
+<translation id="4420062214988137980">外掛程式安裝失敗</translation>
+<translation id="1235745349614807883">清除最近的搜尋記錄</translation>
+<translation id="3825324228893189080">需要額外的外掛程式</translation>
+<translation id="2965480764085142436"><ph name="PLUGIN"/> 外掛程式尚未安裝</translation>
+<translation id="5048533449481078685">清單標記</translation>
+<translation id="4202807286478387388">跳至另一頁</translation>
+<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation>
+<translation id="4317653869502688143">請確定您想要安裝 <ph name="PLUGIN"/> 外掛程式。建議您僅安裝可靠的外掛程式。</translation>
+<translation id="7658239707568436148">取消</translation>
+<translation id="795667975304826397">未選擇檔案</translation>
+<translation id="1275511093094545429">需要 <ph name="PLUGIN"/> 外掛程式</translation>
+<translation id="8662565117025751661">正在下載外掛程式...</translation>
+<translation id="8141602879876242471">這是可搜尋的索引,輸入搜尋關鍵字:</translation>
+<translation id="6845533974506654842">按下</translation>
+<translation id="8244226242650769279">影像地圖</translation>
+<translation id="1383141426028388991">無法從 <ph name="URL"/> 安裝外掛程式</translation>
+<translation id="2548326553472216322">沒有近期的搜尋</translation>
+<translation id="5944544982112848342">2048 (高級)</translation>
+<translation id="3040011195152428237">連結</translation>
+<translation id="8281246460978372009">安裝外掛程式後,請按一下這裡更新</translation>
+<translation id="7364796246159120393">選擇檔案</translation>
+<translation id="8964020114565522021">拖曳檔案至此</translation>
+<translation id="838869780401515933">選取</translation>
+<translation id="2846343701378493991">1024 (中等)</translation>
+<translation id="5476505524087279545">取消選取</translation>
+<translation id="679352192834563463">沒有外掛程式可供顯示目前內容</translation>
+<translation id="3789841737615482174">安裝</translation>
+<translation id="6663448176199120256">最近的搜尋</translation>
+<translation id="3600343118165084788">按一下這裡以下載掛程式</translation>
+<translation id="6807599807928161586">網頁範圍</translation>
+<translation id="5939518447894949180">重設</translation>
+<translation id="3771786644471114952">取得外掛程式</translation>
+<translation id="1842960171412779397">選取</translation>
+<translation id="6119846243427417423">啟動</translation>
+<translation id="8444882422881193423"><ph name="NUMBER_OF_FILES"/> 個檔案</translation>
+<translation id="3926627843712816530">請確定您想要安裝此外掛程式。建議您僅安裝可靠的外掛程式。</translation>
+<translation id="4838490908464673667">未安裝要求的外掛程式</translation>
+<translation id="8597182159515967513">標題</translation>
+<translation id="2653659639078652383">提交</translation>
+</translationbundle> \ No newline at end of file
diff --git a/webkit/glue/resources/zoom_in.cur b/webkit/glue/resources/zoom_in.cur
new file mode 100644
index 0000000..b594d79
--- /dev/null
+++ b/webkit/glue/resources/zoom_in.cur
Binary files differ
diff --git a/webkit/glue/resources/zoom_out.cur b/webkit/glue/resources/zoom_out.cur
new file mode 100644
index 0000000..7e495fb
--- /dev/null
+++ b/webkit/glue/resources/zoom_out.cur
Binary files differ
diff --git a/webkit/glue/scoped_clipboard_writer_glue.h b/webkit/glue/scoped_clipboard_writer_glue.h
new file mode 100644
index 0000000..7460ddf
--- /dev/null
+++ b/webkit/glue/scoped_clipboard_writer_glue.h
@@ -0,0 +1,32 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SCOPED_CLIPBOARD_WRITER_GLUE_H_
+#define SCOPED_CLIPBOARD_WRITER_GLUE_H_
+
+#include "app/clipboard/scoped_clipboard_writer.h"
+
+class SkBitmap;
+
+namespace base {
+class SharedMemory;
+}
+
+class ScopedClipboardWriterGlue : public ScopedClipboardWriter {
+ public:
+ ScopedClipboardWriterGlue(Clipboard* clipboard)
+ : ScopedClipboardWriter(clipboard),
+ shared_buf_(NULL) {
+ }
+
+ ~ScopedClipboardWriterGlue();
+
+ void WriteBitmapFromPixels(const void* pixels, const gfx::Size& size);
+
+ private:
+ base::SharedMemory* shared_buf_;
+ DISALLOW_COPY_AND_ASSIGN(ScopedClipboardWriterGlue);
+};
+
+#endif // SCOPED_CLIPBOARD_WRITER_GLUE_H_
diff --git a/webkit/glue/simple_webmimeregistry_impl.cc b/webkit/glue/simple_webmimeregistry_impl.cc
new file mode 100644
index 0000000..5dd227f
--- /dev/null
+++ b/webkit/glue/simple_webmimeregistry_impl.cc
@@ -0,0 +1,117 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved. Use of this
+// source code is governed by a BSD-style license that can be found in the
+// LICENSE file.
+
+#include "webkit/glue/simple_webmimeregistry_impl.h"
+
+#include "base/string_util.h"
+#include "base/sys_string_conversions.h"
+#include "net/base/mime_util.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebString.h"
+#include "webkit/glue/webkit_glue.h"
+
+using WebKit::WebString;
+using WebKit::WebMimeRegistry;
+
+namespace {
+
+// Convert a WebString to ASCII, falling back on an empty string in the case
+// of a non-ASCII string.
+std::string ToASCIIOrEmpty(const WebString& string) {
+ if (!IsStringASCII(string))
+ return std::string();
+ return UTF16ToASCII(string);
+}
+
+} // namespace
+
+namespace webkit_glue {
+
+WebMimeRegistry::SupportsType SimpleWebMimeRegistryImpl::supportsMIMEType(
+ const WebString& mime_type) {
+ if (!net::IsSupportedMimeType(ToASCIIOrEmpty(mime_type).c_str()))
+ return WebMimeRegistry::IsNotSupported;
+ return WebMimeRegistry::IsSupported;
+}
+
+WebMimeRegistry::SupportsType SimpleWebMimeRegistryImpl::supportsImageMIMEType(
+ const WebString& mime_type) {
+ if (!net::IsSupportedImageMimeType(ToASCIIOrEmpty(mime_type).c_str()))
+ return WebMimeRegistry::IsNotSupported;
+ return WebMimeRegistry::IsSupported;
+}
+
+WebMimeRegistry::SupportsType SimpleWebMimeRegistryImpl::supportsJavaScriptMIMEType(
+ const WebString& mime_type) {
+ if (!net::IsSupportedJavascriptMimeType(ToASCIIOrEmpty(mime_type).c_str()))
+ return WebMimeRegistry::IsNotSupported;
+ return WebMimeRegistry::IsSupported;
+}
+
+WebMimeRegistry::SupportsType SimpleWebMimeRegistryImpl::supportsMediaMIMEType(
+ const WebString& mime_type, const WebString& codecs) {
+ // Not supporting the container is a flat-out no.
+ if (!net::IsSupportedMediaMimeType(ToASCIIOrEmpty(mime_type).c_str()))
+ return IsNotSupported;
+
+ // Check list of strict codecs to see if it is supported.
+ if (net::IsStrictMediaMimeType(ToASCIIOrEmpty(mime_type).c_str())) {
+ // We support the container, but no codecs were specified.
+ if (codecs.isNull())
+ return MayBeSupported;
+
+ // Check if the codecs are a perfect match.
+ std::vector<std::string> strict_codecs;
+ net::ParseCodecString(ToASCIIOrEmpty(codecs).c_str(),
+ &strict_codecs,
+ false);
+ if (!net::IsSupportedStrictMediaMimeType(ToASCIIOrEmpty(mime_type).c_str(),
+ strict_codecs))
+ return IsNotSupported;
+
+ // Good to go!
+ return IsSupported;
+ }
+
+ // If we don't recognize the codec, it's possible we support it.
+ std::vector<std::string> parsed_codecs;
+ net::ParseCodecString(ToASCIIOrEmpty(codecs).c_str(), &parsed_codecs, true);
+ if (!net::AreSupportedMediaCodecs(parsed_codecs))
+ return MayBeSupported;
+
+ // Otherwise we have a perfect match.
+ return IsSupported;
+}
+
+WebMimeRegistry::SupportsType SimpleWebMimeRegistryImpl::supportsNonImageMIMEType(
+ const WebString& mime_type) {
+ if (!net::IsSupportedNonImageMimeType(ToASCIIOrEmpty(mime_type).c_str()))
+ return WebMimeRegistry::IsNotSupported;
+ return WebMimeRegistry::IsSupported;
+}
+
+WebString SimpleWebMimeRegistryImpl::mimeTypeForExtension(
+ const WebString& file_extension) {
+ std::string mime_type;
+ net::GetMimeTypeFromExtension(
+ WebStringToFilePathString(file_extension), &mime_type);
+ return ASCIIToUTF16(mime_type);
+}
+
+WebString SimpleWebMimeRegistryImpl::mimeTypeFromFile(
+ const WebString& file_path) {
+ std::string mime_type;
+ net::GetMimeTypeFromFile(
+ FilePath(WebStringToFilePathString(file_path)), &mime_type);
+ return ASCIIToUTF16(mime_type);
+}
+
+WebString SimpleWebMimeRegistryImpl::preferredExtensionForMIMEType(
+ const WebString& mime_type) {
+ FilePath::StringType file_extension;
+ net::GetPreferredExtensionForMimeType(ToASCIIOrEmpty(mime_type),
+ &file_extension);
+ return FilePathStringToWebString(file_extension);
+}
+
+} // namespace webkit_glue
diff --git a/webkit/glue/simple_webmimeregistry_impl.h b/webkit/glue/simple_webmimeregistry_impl.h
new file mode 100644
index 0000000..0056de0
--- /dev/null
+++ b/webkit/glue/simple_webmimeregistry_impl.h
@@ -0,0 +1,33 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved. Use of this
+// source code is governed by a BSD-style license that can be found in the
+// LICENSE file.
+
+#ifndef WEBMIMEREGISTRY_IMPL_H_
+#define WEBMIMEREGISTRY_IMPL_H_
+
+#include "third_party/WebKit/WebKit/chromium/public/WebMimeRegistry.h"
+
+namespace webkit_glue {
+
+class SimpleWebMimeRegistryImpl : public WebKit::WebMimeRegistry {
+ public:
+ // WebMimeRegistry methods:
+ virtual WebKit::WebMimeRegistry::SupportsType supportsMIMEType(
+ const WebKit::WebString&);
+ virtual WebKit::WebMimeRegistry::SupportsType supportsImageMIMEType(
+ const WebKit::WebString&);
+ virtual WebKit::WebMimeRegistry::SupportsType supportsJavaScriptMIMEType(
+ const WebKit::WebString&);
+ virtual WebKit::WebMimeRegistry::SupportsType supportsMediaMIMEType(
+ const WebKit::WebString&, const WebKit::WebString&);
+ virtual WebKit::WebMimeRegistry::SupportsType supportsNonImageMIMEType(
+ const WebKit::WebString&);
+ virtual WebKit::WebString mimeTypeForExtension(const WebKit::WebString&);
+ virtual WebKit::WebString mimeTypeFromFile(const WebKit::WebString&);
+ virtual WebKit::WebString preferredExtensionForMIMEType(
+ const WebKit::WebString&);
+};
+
+} // namespace webkit_glue
+
+#endif // WEBMIMEREGISTRY_IMPL_H_
diff --git a/webkit/glue/site_isolation_metrics.cc b/webkit/glue/site_isolation_metrics.cc
new file mode 100644
index 0000000..912263f
--- /dev/null
+++ b/webkit/glue/site_isolation_metrics.cc
@@ -0,0 +1,231 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/site_isolation_metrics.h"
+
+#include <set>
+
+#include "base/hash_tables.h"
+#include "base/histogram.h"
+#include "net/base/mime_sniffer.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebFrame.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebSecurityOrigin.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebString.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURL.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURLRequest.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURLResponse.h"
+
+using WebKit::WebFrame;
+using WebKit::WebSecurityOrigin;
+using WebKit::WebString;
+using WebKit::WebURL;
+using WebKit::WebURLRequest;
+using WebKit::WebURLResponse;
+
+namespace webkit_glue {
+
+typedef base::hash_map<unsigned, WebURLRequest::TargetType> TargetTypeMap;
+typedef base::hash_map<std::string, int> MimeTypeMap;
+typedef std::set<std::string> CrossOriginTextHtmlResponseSet;
+
+static TargetTypeMap* GetTargetTypeMap() {
+ static TargetTypeMap target_type_map_;
+ return &target_type_map_;
+}
+
+// Copied from net/base/mime_util.cc, supported_non_image_types[]
+static const char* const kCrossOriginMimeTypesToLog[] = {
+ "text/cache-manifest",
+ "text/html",
+ "text/xml",
+ "text/xsl",
+ "text/plain",
+ "text/vnd.chromium.ftp-dir",
+ "text/",
+ "text/css",
+ "image/svg+xml",
+ "application/xml",
+ "application/xhtml+xml",
+ "application/rss+xml",
+ "application/atom+xml",
+ "application/json",
+ "application/x-x509-user-cert",
+ "multipart/x-mixed-replace",
+ "(NONE)" // Keep track of missing MIME types as well
+};
+
+static MimeTypeMap* GetMimeTypeMap() {
+ static MimeTypeMap mime_type_map_;
+ if (!mime_type_map_.size()) {
+ for (size_t i = 0; i < arraysize(kCrossOriginMimeTypesToLog); ++i)
+ mime_type_map_[kCrossOriginMimeTypesToLog[i]] = i;
+ }
+ return &mime_type_map_;
+}
+
+// This is set is used to keep track of the response urls that we want to
+// sniff, since we will have to wait for the payload to arrive.
+static CrossOriginTextHtmlResponseSet* GetCrossOriginTextHtmlResponseSet() {
+ static CrossOriginTextHtmlResponseSet cross_origin_text_html_response_set_;
+ return &cross_origin_text_html_response_set_;
+}
+
+static void LogVerifiedTextHtmlResponse() {
+ UMA_HISTOGRAM_COUNTS(
+ "SiteIsolation.CrossSiteNonFrameResponse_verified_texthtml_BLOCK", 1);
+}
+
+static void LogMislabeledTextHtmlResponse() {
+ UMA_HISTOGRAM_COUNTS(
+ "SiteIsolation.CrossSiteNonFrameResponse_mislabeled_texthtml", 1);
+}
+
+void SiteIsolationMetrics::AddRequest(unsigned identifier,
+ WebURLRequest::TargetType target_type) {
+ TargetTypeMap& target_type_map = *GetTargetTypeMap();
+ target_type_map[identifier] = target_type;
+}
+
+// Check whether the given response is allowed due to access control headers.
+// This is basically a copy of the logic of passesAccessControlCheck() in
+// WebCore/loader/CrossOriginAccessControl.cpp.
+bool SiteIsolationMetrics::AllowedByAccessControlHeader(
+ WebFrame* frame, const WebURLResponse& response) {
+ WebString access_control_origin = response.httpHeaderField(
+ WebString::fromUTF8("Access-Control-Allow-Origin"));
+ WebSecurityOrigin security_origin =
+ WebSecurityOrigin::createFromString(access_control_origin);
+ return access_control_origin == WebString::fromUTF8("*") ||
+ frame->securityOrigin().canAccess(security_origin);
+}
+
+// We want to log any cross-site request that we don't think a renderer should
+// be allowed to make. We can safely ignore frame requests (since we'd like
+// those to be in a separate renderer) and plugin requests, even if they are
+// cross-origin.
+//
+// For comparison, we keep counts of:
+// - All requests made by a renderer
+// - All cross-site requests
+//
+// Then, for cross-site non-frame/plugin requests, we keep track of:
+// - Counts for MIME types of interest
+// - Counts of those MIME types that carry CORS headers
+// - Counts of mislabeled text/html responses (without CORS)
+// As well as those we would block:
+// - Counts of verified text/html responses (without CORS)
+// - Counts of XML/JSON responses (without CORS)
+//
+// This will let us say what percentage of requests we would end up blocking.
+void SiteIsolationMetrics::LogMimeTypeForCrossOriginRequest(
+ WebFrame* frame, unsigned identifier, const WebURLResponse& response) {
+ UMA_HISTOGRAM_COUNTS("SiteIsolation.Requests", 1);
+
+ TargetTypeMap& target_type_map = *GetTargetTypeMap();
+ TargetTypeMap::iterator iter = target_type_map.find(identifier);
+ if (iter != target_type_map.end()) {
+ WebURLRequest::TargetType target_type = iter->second;
+ target_type_map.erase(iter);
+
+ // Focus on cross-site requests.
+ if (!frame->securityOrigin().canAccess(
+ WebSecurityOrigin::create(response.url()))) {
+ UMA_HISTOGRAM_COUNTS("SiteIsolation.CrossSiteRequests", 1);
+
+ // Now focus on non-frame, non-plugin requests.
+ if (target_type != WebURLRequest::TargetIsMainFrame &&
+ target_type != WebURLRequest::TargetIsSubFrame &&
+ target_type != WebURLRequest::TargetIsObject) {
+ // If it is part of a MIME type we might block, log the MIME type.
+ std::string mime_type = response.mimeType().utf8();
+ MimeTypeMap mime_type_map = *GetMimeTypeMap();
+ // Also track it if it lacks a MIME type.
+ // TODO(creis): 304 responses have no MIME type, so we don't handle
+ // them correctly. Can we look up their MIME type from the cache?
+ if (mime_type == "")
+ mime_type = "(NONE)";
+ MimeTypeMap::iterator mime_type_iter = mime_type_map.find(mime_type);
+ if (mime_type_iter != mime_type_map.end()) {
+ UMA_HISTOGRAM_ENUMERATION(
+ "SiteIsolation.CrossSiteNonFrameResponse_MIME_Type",
+ mime_type_iter->second,
+ arraysize(kCrossOriginMimeTypesToLog));
+
+ // We also check access control headers, in case this
+ // cross-origin request has been explicitly permitted.
+ if (AllowedByAccessControlHeader(frame, response)) {
+ UMA_HISTOGRAM_ENUMERATION(
+ "SiteIsolation.CrossSiteNonFrameResponse_With_CORS_MIME_Type",
+ mime_type_iter->second,
+ arraysize(kCrossOriginMimeTypesToLog));
+ } else {
+ // Without access control headers, we might block this request.
+ // Sometimes resources are mislabled as text/html, though, and we
+ // should only block them if we can verify that. To do so, we sniff
+ // the content once we have some of the payload.
+ if (mime_type == "text/html") {
+ // Remember the response until we can sniff its contents.
+ GetCrossOriginTextHtmlResponseSet()->insert(
+ response.url().spec());
+ } else if (mime_type == "text/xml" ||
+ mime_type == "text/xsl" ||
+ mime_type == "application/xml" ||
+ mime_type == "application/xhtml+xml" ||
+ mime_type == "application/rss+xml" ||
+ mime_type == "application/atom+xml" ||
+ mime_type == "application/json") {
+ // We will also block XML and JSON MIME types for cross-site
+ // non-frame requests without CORS headers.
+ UMA_HISTOGRAM_COUNTS(
+ "SiteIsolation.CrossSiteNonFrameResponse_xml_or_json_BLOCK",
+ 1);
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+void SiteIsolationMetrics::SniffCrossOriginHTML(const WebURL& response_url,
+ const char* data,
+ int len) {
+ if (!response_url.isValid())
+ return;
+
+ // Look up the URL to see if it is a text/html request we are tracking.
+ CrossOriginTextHtmlResponseSet& cross_origin_text_html_response_set =
+ *GetCrossOriginTextHtmlResponseSet();
+ CrossOriginTextHtmlResponseSet::iterator request_iter =
+ cross_origin_text_html_response_set.find(response_url.spec());
+ if (request_iter != cross_origin_text_html_response_set.end()) {
+ // Log whether it actually looks like HTML.
+ std::string sniffed_mime_type;
+ bool successful = net::SniffMimeType(data, len, response_url,
+ "", &sniffed_mime_type);
+ if (successful && sniffed_mime_type == "text/html")
+ LogVerifiedTextHtmlResponse();
+ else
+ LogMislabeledTextHtmlResponse();
+ cross_origin_text_html_response_set.erase(request_iter);
+ }
+}
+
+void SiteIsolationMetrics::RemoveCompletedResponse(
+ const WebURL& response_url) {
+ if (!response_url.isValid())
+ return;
+
+ // Ensure we don't leave responses in the set after they've completed.
+ CrossOriginTextHtmlResponseSet& cross_origin_text_html_response_set =
+ *GetCrossOriginTextHtmlResponseSet();
+ CrossOriginTextHtmlResponseSet::iterator request_iter =
+ cross_origin_text_html_response_set.find(response_url.spec());
+ if (request_iter != cross_origin_text_html_response_set.end()) {
+ LogMislabeledTextHtmlResponse();
+ cross_origin_text_html_response_set.erase(request_iter);
+ }
+}
+
+} // namespace webkit_glue
diff --git a/webkit/glue/site_isolation_metrics.h b/webkit/glue/site_isolation_metrics.h
new file mode 100644
index 0000000..53f1630
--- /dev/null
+++ b/webkit/glue/site_isolation_metrics.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_SITE_ISOLATION_METRICS_H_
+#define WEBKIT_GLUE_SITE_ISOLATION_METRICS_H_
+
+#include "third_party/WebKit/WebKit/chromium/public/WebURLRequest.h"
+
+namespace WebKit {
+class WebFrame;
+class WebURL;
+class WebURLResponse;
+}
+
+namespace webkit_glue {
+
+// Metrics to check the feasability of blocking cross-site requests that
+// a renderer shouldn't be making (in case we try to move cross-site frames
+// into their own process someday). We're erring on the side of counting more
+// mime-types then we strictly need (we'd only consider blocking cross-site
+// requests with types similar to HTML, XML, or JSON).
+class SiteIsolationMetrics {
+ public:
+ static void AddRequest(unsigned identifier,
+ WebKit::WebURLRequest::TargetType target_type);
+ static bool AllowedByAccessControlHeader(
+ WebKit::WebFrame* frame, const WebKit::WebURLResponse& response);
+ static void LogMimeTypeForCrossOriginRequest(
+ WebKit::WebFrame* frame,
+ unsigned identifier,
+ const WebKit::WebURLResponse& response);
+ static void SniffCrossOriginHTML(const WebKit::WebURL& response_url,
+ const char* data,
+ int len);
+ static void RemoveCompletedResponse(const WebKit::WebURL& response_url);
+};
+
+} // namespace webkit_glue
+
+#endif // WEBKIT_GLUE_SITE_ISOLATION_METRICS_H_
diff --git a/webkit/glue/unittest_test_server.h b/webkit/glue/unittest_test_server.h
new file mode 100644
index 0000000..0b3c7d2
--- /dev/null
+++ b/webkit/glue/unittest_test_server.h
@@ -0,0 +1,65 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_UNITTEST_TEST_SERVER_H__
+#define WEBKIT_GLUE_UNITTEST_TEST_SERVER_H__
+
+#include "webkit/appcache/appcache_interfaces.h"
+#include "webkit/glue/resource_loader_bridge.h"
+#include "net/base/load_flags.h"
+#include "net/url_request/url_request_unittest.h"
+
+using webkit_glue::ResourceLoaderBridge;
+
+// We need to use ResourceLoaderBridge to communicate with the testserver
+// instead of using URLRequest directly because URLRequests need to be run on
+// the test_shell's IO thread.
+class UnittestTestServer : public HTTPTestServer {
+ protected:
+ UnittestTestServer() {
+ }
+
+ public:
+ static UnittestTestServer* CreateServer() {
+ UnittestTestServer* test_server = new UnittestTestServer();
+ FilePath no_cert;
+ FilePath docroot(FILE_PATH_LITERAL("webkit/data"));
+ if (!test_server->Start(net::TestServerLauncher::ProtoHTTP,
+ "localhost", 1337, docroot, no_cert, std::wstring())) {
+ delete test_server;
+ return NULL;
+ }
+ return test_server;
+ }
+
+ virtual bool MakeGETRequest(const std::string& page_name) {
+ GURL url(TestServerPage(page_name));
+ webkit_glue::ResourceLoaderBridge::RequestInfo request_info;
+ request_info.method = "GET";
+ request_info.url = url;
+ request_info.first_party_for_cookies = url;
+ request_info.referrer = GURL(); // No referrer.
+ request_info.frame_origin = "null";
+ request_info.main_frame_origin = "null";
+ request_info.headers = std::string(); // No extra headers.
+ request_info.load_flags = net::LOAD_NORMAL;
+ request_info.requestor_pid = 0;
+ request_info.request_type = ResourceType::SUB_RESOURCE;
+ request_info.request_context = 0;
+ request_info.appcache_host_id = appcache::kNoHostId;
+ request_info.routing_id = 0;
+ scoped_ptr<ResourceLoaderBridge> loader(
+ ResourceLoaderBridge::Create(request_info));
+ EXPECT_TRUE(loader.get());
+
+ ResourceLoaderBridge::SyncLoadResponse resp;
+ loader->SyncLoad(&resp);
+ return resp.status.is_success();
+ }
+
+ private:
+ virtual ~UnittestTestServer() {}
+};
+
+#endif // WEBKIT_GLUE_UNITTEST_TEST_SERVER_H__
diff --git a/webkit/glue/webaccessibility.cc b/webkit/glue/webaccessibility.cc
new file mode 100644
index 0000000..b6e0d5e
--- /dev/null
+++ b/webkit/glue/webaccessibility.cc
@@ -0,0 +1,300 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/webaccessibility.h"
+
+#include "third_party/WebKit/WebKit/chromium/public/WebAccessibilityCache.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebAccessibilityObject.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebAccessibilityRole.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebString.h"
+
+using WebKit::WebAccessibilityCache;
+using WebKit::WebAccessibilityRole;
+using WebKit::WebAccessibilityObject;
+
+namespace webkit_glue {
+
+// Provides a conversion between the WebKit::WebAccessibilityRole and a role
+// supported on the Browser side. Listed alphabetically by the
+// WebAccessibilityRole (except for default role).
+WebAccessibility::Role ConvertRole(WebKit::WebAccessibilityRole role) {
+ switch (role) {
+ case WebKit::WebAccessibilityRoleAnnotation:
+ return WebAccessibility::ROLE_ANNOTATION;
+ case WebKit::WebAccessibilityRoleApplication:
+ return WebAccessibility::ROLE_APPLICATION;
+ case WebKit::WebAccessibilityRoleApplicationAlert:
+ return WebAccessibility::ROLE_ALERT;
+ case WebKit::WebAccessibilityRoleApplicationAlertDialog:
+ return WebAccessibility::ROLE_ALERT_DIALOG;
+ case WebKit::WebAccessibilityRoleApplicationDialog:
+ return WebAccessibility::ROLE_DIALOG;
+ case WebKit::WebAccessibilityRoleApplicationLog:
+ return WebAccessibility::ROLE_LOG;
+ case WebKit::WebAccessibilityRoleApplicationMarquee:
+ return WebAccessibility::ROLE_MARQUEE;
+ case WebKit::WebAccessibilityRoleApplicationStatus:
+ return WebAccessibility::ROLE_STATUS;
+ case WebKit::WebAccessibilityRoleApplicationTimer:
+ return WebAccessibility::ROLE_TIMER;
+ case WebKit::WebAccessibilityRoleBrowser:
+ return WebAccessibility::ROLE_BROWSER;
+ case WebKit::WebAccessibilityRoleBusyIndicator:
+ return WebAccessibility::ROLE_BUSY_INDICATOR;
+ case WebKit::WebAccessibilityRoleButton:
+ return WebAccessibility::ROLE_BUTTON;
+ case WebKit::WebAccessibilityRoleCell:
+ return WebAccessibility::ROLE_CELL;
+ case WebKit::WebAccessibilityRoleCheckBox:
+ return WebAccessibility::ROLE_CHECKBOX;
+ case WebKit::WebAccessibilityRoleColorWell:
+ return WebAccessibility::ROLE_COLOR_WELL;
+ case WebKit::WebAccessibilityRoleColumn:
+ return WebAccessibility::ROLE_COLUMN;
+ case WebKit::WebAccessibilityRoleColumnHeader:
+ return WebAccessibility::ROLE_COLUMN_HEADER;
+ case WebKit::WebAccessibilityRoleComboBox:
+ return WebAccessibility::ROLE_COMBO_BOX;
+ case WebKit::WebAccessibilityRoleDefinitionListDefinition:
+ return WebAccessibility::ROLE_DEFINITION_LIST_DEFINITION;
+ case WebKit::WebAccessibilityRoleDefinitionListTerm:
+ return WebAccessibility::ROLE_DEFINITION_LIST_TERM;
+ case WebKit::WebAccessibilityRoleDirectory:
+ return WebAccessibility::ROLE_DIRECTORY;
+ case WebKit::WebAccessibilityRoleDisclosureTriangle:
+ return WebAccessibility::ROLE_DISCLOSURE_TRIANGLE;
+ case WebKit::WebAccessibilityRoleDocument:
+ return WebAccessibility::ROLE_DOCUMENT;
+ case WebKit::WebAccessibilityRoleDocumentArticle:
+ return WebAccessibility::ROLE_ARTICLE;
+ case WebKit::WebAccessibilityRoleDocumentMath:
+ return WebAccessibility::ROLE_MATH;
+ case WebKit::WebAccessibilityRoleDocumentNote:
+ return WebAccessibility::ROLE_NOTE;
+ case WebKit::WebAccessibilityRoleDocumentRegion:
+ return WebAccessibility::ROLE_REGION;
+ case WebKit::WebAccessibilityRoleDrawer:
+ return WebAccessibility::ROLE_DRAWER;
+ case WebKit::WebAccessibilityRoleEditableText:
+ return WebAccessibility::ROLE_EDITABLE_TEXT;
+ case WebKit::WebAccessibilityRoleGrid:
+ return WebAccessibility::ROLE_GRID;
+ case WebKit::WebAccessibilityRoleGroup:
+ return WebAccessibility::ROLE_GROUP;
+ case WebKit::WebAccessibilityRoleGrowArea:
+ return WebAccessibility::ROLE_GROW_AREA;
+ case WebKit::WebAccessibilityRoleHeading:
+ return WebAccessibility::ROLE_HEADING;
+ case WebKit::WebAccessibilityRoleHelpTag:
+ return WebAccessibility::ROLE_HELP_TAG;
+ case WebKit::WebAccessibilityRoleIgnored:
+ return WebAccessibility::ROLE_IGNORED;
+ case WebKit::WebAccessibilityRoleImage:
+ return WebAccessibility::ROLE_IMAGE;
+ case WebKit::WebAccessibilityRoleImageMap:
+ return WebAccessibility::ROLE_IMAGE_MAP;
+ case WebKit::WebAccessibilityRoleImageMapLink:
+ return WebAccessibility::ROLE_IMAGE_MAP_LINK;
+ case WebKit::WebAccessibilityRoleIncrementor:
+ return WebAccessibility::ROLE_INCREMENTOR;
+ case WebKit::WebAccessibilityRoleLandmarkApplication:
+ return WebAccessibility::ROLE_LANDMARK_APPLICATION;
+ case WebKit::WebAccessibilityRoleLandmarkBanner:
+ return WebAccessibility::ROLE_LANDMARK_BANNER;
+ case WebKit::WebAccessibilityRoleLandmarkComplementary:
+ return WebAccessibility::ROLE_LANDMARK_COMPLEMENTARY;
+ case WebKit::WebAccessibilityRoleLandmarkContentInfo:
+ return WebAccessibility::ROLE_LANDMARK_CONTENTINFO;
+ case WebKit::WebAccessibilityRoleLandmarkMain:
+ return WebAccessibility::ROLE_LANDMARK_MAIN;
+ case WebKit::WebAccessibilityRoleLandmarkNavigation:
+ return WebAccessibility::ROLE_LANDMARK_NAVIGATION;
+ case WebKit::WebAccessibilityRoleLandmarkSearch:
+ return WebAccessibility::ROLE_LANDMARK_SEARCH;
+ case WebKit::WebAccessibilityRoleLink:
+ return WebAccessibility::ROLE_LINK;
+ case WebKit::WebAccessibilityRoleList:
+ return WebAccessibility::ROLE_LIST;
+ case WebKit::WebAccessibilityRoleListBox:
+ return WebAccessibility::ROLE_LISTBOX;
+ case WebKit::WebAccessibilityRoleListBoxOption:
+ return WebAccessibility::ROLE_LISTBOX_OPTION;
+ case WebKit::WebAccessibilityRoleListItem:
+ return WebAccessibility::ROLE_LIST_ITEM;
+ case WebKit::WebAccessibilityRoleListMarker:
+ return WebAccessibility::ROLE_LIST_MARKER;
+ case WebKit::WebAccessibilityRoleMatte:
+ return WebAccessibility::ROLE_MATTE;
+ case WebKit::WebAccessibilityRoleMenu:
+ return WebAccessibility::ROLE_MENU;
+ case WebKit::WebAccessibilityRoleMenuBar:
+ return WebAccessibility::ROLE_MENU_BAR;
+ case WebKit::WebAccessibilityRoleMenuButton:
+ return WebAccessibility::ROLE_MENU_BUTTON;
+ case WebKit::WebAccessibilityRoleMenuItem:
+ return WebAccessibility::ROLE_MENU_ITEM;
+ case WebKit::WebAccessibilityRoleMenuListOption:
+ return WebAccessibility::ROLE_MENU_LIST_OPTION;
+ case WebKit::WebAccessibilityRoleMenuListPopup:
+ return WebAccessibility::ROLE_MENU_LIST_POPUP;
+ case WebKit::WebAccessibilityRoleOutline:
+ return WebAccessibility::ROLE_OUTLINE;
+ case WebKit::WebAccessibilityRolePopUpButton:
+ return WebAccessibility::ROLE_POPUP_BUTTON;
+ case WebKit::WebAccessibilityRoleProgressIndicator:
+ return WebAccessibility::ROLE_PROGRESS_INDICATOR;
+ case WebKit::WebAccessibilityRoleRadioButton:
+ return WebAccessibility::ROLE_RADIO_BUTTON;
+ case WebKit::WebAccessibilityRoleRadioGroup:
+ return WebAccessibility::ROLE_RADIO_GROUP;
+ case WebKit::WebAccessibilityRoleRow:
+ return WebAccessibility::ROLE_ROW;
+ case WebKit::WebAccessibilityRoleRowHeader:
+ return WebAccessibility::ROLE_ROW_HEADER;
+ case WebKit::WebAccessibilityRoleRuler:
+ return WebAccessibility::ROLE_RULER;
+ case WebKit::WebAccessibilityRoleRulerMarker:
+ return WebAccessibility::ROLE_RULER_MARKER;
+ case WebKit::WebAccessibilityRoleScrollArea:
+ return WebAccessibility::ROLE_SCROLLAREA;
+ case WebKit::WebAccessibilityRoleScrollBar:
+ return WebAccessibility::ROLE_SCROLLBAR;
+ case WebKit::WebAccessibilityRoleSheet:
+ return WebAccessibility::ROLE_SHEET;
+ case WebKit::WebAccessibilityRoleSlider:
+ return WebAccessibility::ROLE_SLIDER;
+ case WebKit::WebAccessibilityRoleSliderThumb:
+ return WebAccessibility::ROLE_SLIDER_THUMB;
+ case WebKit::WebAccessibilityRoleSplitGroup:
+ return WebAccessibility::ROLE_SPLIT_GROUP;
+ case WebKit::WebAccessibilityRoleSplitter:
+ return WebAccessibility::ROLE_SPLITTER;
+ case WebKit::WebAccessibilityRoleStaticText:
+ return WebAccessibility::ROLE_STATIC_TEXT;
+ case WebKit::WebAccessibilityRoleSystemWide:
+ return WebAccessibility::ROLE_SYSTEM_WIDE;
+ case WebKit::WebAccessibilityRoleTab:
+ return WebAccessibility::ROLE_TAB;
+ case WebKit::WebAccessibilityRoleTabGroup:
+ return WebAccessibility::ROLE_TAB_GROUP;
+ case WebKit::WebAccessibilityRoleTabList:
+ return WebAccessibility::ROLE_TAB_LIST;
+ case WebKit::WebAccessibilityRoleTabPanel:
+ return WebAccessibility::ROLE_TAB_PANEL;
+ case WebKit::WebAccessibilityRoleTable:
+ return WebAccessibility::ROLE_TABLE;
+ case WebKit::WebAccessibilityRoleTableHeaderContainer:
+ return WebAccessibility::ROLE_TABLE_HEADER_CONTAINER;
+ case WebKit::WebAccessibilityRoleTextArea:
+ return WebAccessibility::ROLE_TEXTAREA;
+ case WebKit::WebAccessibilityRoleTextField:
+ return WebAccessibility::ROLE_TEXT_FIELD;
+ case WebKit::WebAccessibilityRoleToolbar:
+ return WebAccessibility::ROLE_TOOLBAR;
+ case WebKit::WebAccessibilityRoleTreeGrid:
+ return WebAccessibility::ROLE_TREE_GRID;
+ case WebKit::WebAccessibilityRoleTreeItemRole:
+ return WebAccessibility::ROLE_TREE_ITEM;
+ case WebKit::WebAccessibilityRoleTreeRole:
+ return WebAccessibility::ROLE_TREE;
+ case WebKit::WebAccessibilityRoleUserInterfaceTooltip:
+ return WebAccessibility::ROLE_TOOLTIP;
+ case WebKit::WebAccessibilityRoleValueIndicator:
+ return WebAccessibility::ROLE_VALUE_INDICATOR;
+ case WebKit::WebAccessibilityRoleWebArea:
+ return WebAccessibility::ROLE_WEB_AREA;
+ case WebKit::WebAccessibilityRoleWebCoreLink:
+ return WebAccessibility::ROLE_WEBCORE_LINK;
+ case WebKit::WebAccessibilityRoleWindow:
+ return WebAccessibility::ROLE_WINDOW;
+
+ default:
+ return WebAccessibility::ROLE_UNKNOWN;
+ }
+}
+
+uint32 ConvertState(const WebAccessibilityObject& o) {
+ uint32 state = 0;
+ if (o.isChecked())
+ state |= (1 << WebAccessibility::STATE_CHECKED);
+
+ if (o.canSetFocusAttribute())
+ state |= (1 << WebAccessibility::STATE_FOCUSABLE);
+
+ if (o.isFocused())
+ state |= (1 << WebAccessibility::STATE_FOCUSED);
+
+ if (o.isHovered())
+ state |= (1 << WebAccessibility::STATE_HOTTRACKED);
+
+ if (o.isIndeterminate())
+ state |= (1 << WebAccessibility::STATE_INDETERMINATE);
+
+ if (o.isAnchor())
+ state |= (1 << WebAccessibility::STATE_LINKED);
+
+ if (o.isMultiSelectable())
+ state |= (1 << WebAccessibility::STATE_MULTISELECTABLE);
+
+ if (o.isOffScreen())
+ state |= (1 << WebAccessibility::STATE_OFFSCREEN);
+
+ if (o.isPressed())
+ state |= (1 << WebAccessibility::STATE_PRESSED);
+
+ if (o.isPasswordField())
+ state |= (1 << WebAccessibility::STATE_PROTECTED);
+
+ if (o.isReadOnly())
+ state |= (1 << WebAccessibility::STATE_READONLY);
+
+ if (o.isVisited())
+ state |= (1 << WebAccessibility::STATE_TRAVERSED);
+
+ if (!o.isEnabled())
+ state |= (1 << WebAccessibility::STATE_UNAVAILABLE);
+
+ return state;
+}
+
+WebAccessibility::WebAccessibility()
+ : id(-1),
+ role(ROLE_NONE),
+ state(-1) {
+}
+
+WebAccessibility::WebAccessibility(const WebKit::WebAccessibilityObject& src,
+ WebKit::WebAccessibilityCache* cache) {
+ Init(src, cache);
+}
+
+void WebAccessibility::Init(const WebKit::WebAccessibilityObject& src,
+ WebKit::WebAccessibilityCache* cache) {
+ name = src.title();
+ value = src.stringValue();
+ role = ConvertRole(src.roleValue());
+ state = ConvertState(src);
+ location = src.boundingBoxRect();
+
+ if (src.actionVerb().length())
+ attributes[ATTR_ACTION] = src.actionVerb();
+ if (src.accessibilityDescription().length())
+ attributes[ATTR_DESCRIPTION] = src.accessibilityDescription();
+ if (src.helpText().length())
+ attributes[ATTR_HELP] = src.helpText();
+ if (src.keyboardShortcut().length())
+ attributes[ATTR_SHORTCUT] = src.keyboardShortcut();
+
+ // Add the source object to the cache and store its id.
+ id = cache->addOrGetId(src);
+
+ // Recursively create children.
+ int child_count = src.childCount();
+ children.resize(child_count);
+ for (int i = 0; i < child_count; i++) {
+ children[i].Init(src.childAt(i), cache);
+ }
+}
+
+} // namespace webkit_glue
diff --git a/webkit/glue/webaccessibility.h b/webkit/glue/webaccessibility.h
new file mode 100644
index 0000000..305bc0c
--- /dev/null
+++ b/webkit/glue/webaccessibility.h
@@ -0,0 +1,187 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_WEBACCESSIBILITY_H_
+#define WEBKIT_GLUE_WEBACCESSIBILITY_H_
+
+#include <map>
+#include <vector>
+
+#include "base/string16.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebAccessibilityObject.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebAccessibilityRole.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebRect.h"
+
+namespace WebKit {
+class WebAccessibilityCache;
+}
+
+namespace webkit_glue {
+
+// A compact representation of the accessibility information for a
+// single web object, in a form that can be serialized and sent from
+// the renderer process to the browser process.
+struct WebAccessibility {
+ public:
+ // An alphabetical enumeration of accessibility roles.
+ enum Role {
+ ROLE_NONE = 0,
+
+ ROLE_UNKNOWN,
+
+ ROLE_ALERT,
+ ROLE_ALERT_DIALOG,
+ ROLE_ANNOTATION,
+ ROLE_APPLICATION,
+ ROLE_ARTICLE,
+ ROLE_BROWSER,
+ ROLE_BUSY_INDICATOR,
+ ROLE_BUTTON,
+ ROLE_CELL,
+ ROLE_CHECKBOX,
+ ROLE_COLOR_WELL,
+ ROLE_COLUMN,
+ ROLE_COLUMN_HEADER,
+ ROLE_COMBO_BOX,
+ ROLE_DEFINITION_LIST_DEFINITION,
+ ROLE_DEFINITION_LIST_TERM,
+ ROLE_DIALOG,
+ ROLE_DIRECTORY,
+ ROLE_DISCLOSURE_TRIANGLE,
+ ROLE_DOCUMENT,
+ ROLE_DRAWER,
+ ROLE_EDITABLE_TEXT,
+ ROLE_GRID,
+ ROLE_GROUP,
+ ROLE_GROW_AREA,
+ ROLE_HEADING,
+ ROLE_HELP_TAG,
+ ROLE_IGNORED,
+ ROLE_IMAGE,
+ ROLE_IMAGE_MAP,
+ ROLE_IMAGE_MAP_LINK,
+ ROLE_INCREMENTOR,
+ ROLE_LANDMARK_APPLICATION,
+ ROLE_LANDMARK_BANNER,
+ ROLE_LANDMARK_COMPLEMENTARY,
+ ROLE_LANDMARK_CONTENTINFO,
+ ROLE_LANDMARK_MAIN,
+ ROLE_LANDMARK_NAVIGATION,
+ ROLE_LANDMARK_SEARCH,
+ ROLE_LINK,
+ ROLE_LIST,
+ ROLE_LISTBOX,
+ ROLE_LISTBOX_OPTION,
+ ROLE_LIST_ITEM,
+ ROLE_LIST_MARKER,
+ ROLE_LOG,
+ ROLE_MARQUEE,
+ ROLE_MATH,
+ ROLE_MATTE,
+ ROLE_MENU,
+ ROLE_MENU_BAR,
+ ROLE_MENU_ITEM,
+ ROLE_MENU_BUTTON,
+ ROLE_MENU_LIST_OPTION,
+ ROLE_MENU_LIST_POPUP,
+ ROLE_NOTE,
+ ROLE_OUTLINE,
+ ROLE_POPUP_BUTTON,
+ ROLE_PROGRESS_INDICATOR,
+ ROLE_RADIO_BUTTON,
+ ROLE_RADIO_GROUP,
+ ROLE_REGION,
+ ROLE_ROW,
+ ROLE_ROW_HEADER,
+ ROLE_RULER,
+ ROLE_RULER_MARKER,
+ ROLE_SCROLLAREA,
+ ROLE_SCROLLBAR,
+ ROLE_SHEET,
+ ROLE_SLIDER,
+ ROLE_SLIDER_THUMB,
+ ROLE_SPLITTER,
+ ROLE_SPLIT_GROUP,
+ ROLE_STATIC_TEXT,
+ ROLE_STATUS,
+ ROLE_SYSTEM_WIDE,
+ ROLE_TAB,
+ ROLE_TABLE,
+ ROLE_TABLE_HEADER_CONTAINER,
+ ROLE_TAB_GROUP,
+ ROLE_TAB_LIST,
+ ROLE_TAB_PANEL,
+ ROLE_TEXTAREA,
+ ROLE_TEXT_FIELD,
+ ROLE_TIMER,
+ ROLE_TOOLBAR,
+ ROLE_TOOLTIP,
+ ROLE_TREE,
+ ROLE_TREE_GRID,
+ ROLE_TREE_ITEM,
+ ROLE_VALUE_INDICATOR,
+ ROLE_WEBCORE_LINK,
+ ROLE_WEB_AREA,
+ ROLE_WINDOW,
+ NUM_ROLES
+ };
+
+ // An alphabetical enumeration of accessibility states.
+ // A state bitmask is formed by shifting 1 to the left by each state,
+ // for example:
+ // int mask = (1 << STATE_CHECKED) | (1 << STATE_FOCUSED);
+ enum State {
+ STATE_CHECKED,
+ STATE_FOCUSABLE,
+ STATE_FOCUSED,
+ STATE_HOTTRACKED,
+ STATE_INDETERMINATE,
+ STATE_LINKED,
+ STATE_MULTISELECTABLE,
+ STATE_OFFSCREEN,
+ STATE_PRESSED,
+ STATE_PROTECTED,
+ STATE_READONLY,
+ STATE_TRAVERSED,
+ STATE_UNAVAILABLE
+ };
+
+ enum Attribute {
+ ATTR_ACTION,
+ ATTR_DESCRIPTION,
+ ATTR_HELP,
+ ATTR_HTML_TAG,
+ ATTR_LINK_TARGET,
+ ATTR_SHORTCUT,
+ NUM_ATTRIBUTES
+ };
+
+ // Empty constructor, for serialization.
+ WebAccessibility();
+
+ // Construct from a WebAccessibilityObject. Recursively creates child
+ // nodes as needed to complete the tree. Adds |src| to |cache| and
+ // stores its cache ID.
+ WebAccessibility(const WebKit::WebAccessibilityObject& src,
+ WebKit::WebAccessibilityCache* cache);
+
+ // Initialize an already-created struct, same as the constructor a
+ void Init(const WebKit::WebAccessibilityObject& src,
+ WebKit::WebAccessibilityCache* cache);
+
+ // This is a simple serializable struct. All member variables should be
+ // copyable.
+ int32 id;
+ string16 name;
+ string16 value;
+ Role role;
+ uint32 state;
+ WebKit::WebRect location;
+ std::map<int32, string16> attributes;
+ std::vector<WebAccessibility> children;
+};
+
+} // namespace webkit_glue
+
+#endif // WEBKIT_GLUE_WEBACCESSIBILITY_H_
diff --git a/webkit/glue/webclipboard_impl.cc b/webkit/glue/webclipboard_impl.cc
new file mode 100644
index 0000000..11bc96d
--- /dev/null
+++ b/webkit/glue/webclipboard_impl.cc
@@ -0,0 +1,231 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved. Use of this
+// source code is governed by a BSD-style license that can be found in the
+// LICENSE file.
+
+#include "webkit/glue/webclipboard_impl.h"
+
+#include "app/clipboard/clipboard.h"
+#include "base/logging.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "googleurl/src/gurl.h"
+#include "net/base/escape.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebImage.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebSize.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebString.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURL.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebVector.h"
+#include "webkit/glue/scoped_clipboard_writer_glue.h"
+#include "webkit/glue/webkit_glue.h"
+
+#if WEBKIT_USING_CG
+#include "skia/ext/skia_utils_mac.h"
+#endif
+
+using WebKit::WebClipboard;
+using WebKit::WebImage;
+using WebKit::WebString;
+using WebKit::WebURL;
+using WebKit::WebVector;
+
+namespace webkit_glue {
+
+// Static
+std::string WebClipboardImpl::URLToMarkup(const WebURL& url,
+ const WebString& title) {
+ std::string markup("<a href=\"");
+ markup.append(url.spec());
+ markup.append("\">");
+ // TODO(darin): HTML escape this
+ markup.append(EscapeForHTML(UTF16ToUTF8(title)));
+ markup.append("</a>");
+ return markup;
+}
+
+// Static
+std::string WebClipboardImpl::URLToImageMarkup(const WebURL& url,
+ const WebString& title) {
+ std::string markup("<img src=\"");
+ markup.append(url.spec());
+ markup.append("\"");
+ if (!title.isEmpty()) {
+ markup.append(" alt=\"");
+ markup.append(EscapeForHTML(UTF16ToUTF8(title)));
+ markup.append("\"");
+ }
+ markup.append("/>");
+ return markup;
+}
+
+bool WebClipboardImpl::isFormatAvailable(Format format, Buffer buffer) {
+ Clipboard::FormatType format_type;
+ Clipboard::Buffer buffer_type;
+
+ switch (format) {
+ case FormatHTML:
+ format_type = Clipboard::GetHtmlFormatType();
+ break;
+ case FormatSmartPaste:
+ format_type = Clipboard::GetWebKitSmartPasteFormatType();
+ break;
+ case FormatBookmark:
+#if defined(OS_WIN) || defined(OS_MACOSX)
+ format_type = Clipboard::GetUrlWFormatType();
+ break;
+#endif
+ default:
+ NOTREACHED();
+ return false;
+ }
+
+ if (!ConvertBufferType(buffer, &buffer_type))
+ return false;
+
+ return ClipboardIsFormatAvailable(format_type, buffer_type);
+}
+
+WebString WebClipboardImpl::readPlainText(Buffer buffer) {
+ Clipboard::Buffer buffer_type;
+ if (!ConvertBufferType(buffer, &buffer_type))
+ return WebString();
+
+ if (ClipboardIsFormatAvailable(Clipboard::GetPlainTextWFormatType(),
+ buffer_type)) {
+ string16 text;
+ ClipboardReadText(buffer_type, &text);
+ if (!text.empty())
+ return text;
+ }
+
+ if (ClipboardIsFormatAvailable(Clipboard::GetPlainTextFormatType(),
+ buffer_type)) {
+ std::string text;
+ ClipboardReadAsciiText(buffer_type, &text);
+ if (!text.empty())
+ return ASCIIToUTF16(text);
+ }
+
+ return WebString();
+}
+
+WebString WebClipboardImpl::readHTML(Buffer buffer, WebURL* source_url) {
+ Clipboard::Buffer buffer_type;
+ if (!ConvertBufferType(buffer, &buffer_type))
+ return WebString();
+
+ string16 html_stdstr;
+ GURL gurl;
+ ClipboardReadHTML(buffer_type, &html_stdstr, &gurl);
+ *source_url = gurl;
+ return html_stdstr;
+}
+
+void WebClipboardImpl::writeHTML(
+ const WebString& html_text, const WebURL& source_url,
+ const WebString& plain_text, bool write_smart_paste) {
+ ScopedClipboardWriterGlue scw(ClipboardGetClipboard());
+ scw.WriteHTML(html_text, source_url.spec());
+ scw.WriteText(plain_text);
+
+ if (write_smart_paste)
+ scw.WriteWebSmartPaste();
+}
+
+void WebClipboardImpl::writePlainText(const WebString& plain_text) {
+ ScopedClipboardWriterGlue scw(ClipboardGetClipboard());
+ scw.WriteText(plain_text);
+}
+
+void WebClipboardImpl::writeURL(const WebURL& url, const WebString& title) {
+ ScopedClipboardWriterGlue scw(ClipboardGetClipboard());
+
+ scw.WriteBookmark(title, url.spec());
+ scw.WriteHTML(UTF8ToUTF16(URLToMarkup(url, title)), "");
+ scw.WriteText(UTF8ToUTF16(url.spec()));
+}
+
+void WebClipboardImpl::writeImage(
+ const WebImage& image, const WebURL& url, const WebString& title) {
+ ScopedClipboardWriterGlue scw(ClipboardGetClipboard());
+
+ if (!image.isNull()) {
+#if WEBKIT_USING_SKIA
+ const SkBitmap& bitmap = image.getSkBitmap();
+#elif WEBKIT_USING_CG
+ const SkBitmap& bitmap = gfx::CGImageToSkBitmap(image.getCGImageRef());
+#endif
+ SkAutoLockPixels locked(bitmap);
+ scw.WriteBitmapFromPixels(bitmap.getPixels(), image.size());
+ }
+
+ // When writing the image, we also write the image markup so that pasting
+ // into rich text editors, such as Gmail, reveals the image. We also don't
+ // want to call writeText(), since some applications (WordPad) don't pick the
+ // image if there is also a text format on the clipboard.
+ if (!url.isEmpty()) {
+ scw.WriteBookmark(title, url.spec());
+ scw.WriteHTML(UTF8ToUTF16(URLToImageMarkup(url, title)), "");
+ }
+}
+
+void WebClipboardImpl::writeData(const WebKit::WebDragData& data) {
+ // TODO(dcheng): Implement this stub.
+}
+
+WebVector<WebString> WebClipboardImpl::readAvailableTypes(
+ Buffer buffer, bool* contains_filenames) {
+ Clipboard::Buffer buffer_type;
+ std::vector<string16> types;
+ if (ConvertBufferType(buffer, &buffer_type)) {
+ ClipboardReadAvailableTypes(buffer_type, &types, contains_filenames);
+ }
+ return types;
+}
+
+bool WebClipboardImpl::readData(Buffer buffer, const WebString& type,
+ WebString* data, WebString* metadata) {
+ Clipboard::Buffer buffer_type;
+ if (!ConvertBufferType(buffer, &buffer_type))
+ return false;
+
+ string16 data_out;
+ string16 metadata_out;
+ bool result = ClipboardReadData(buffer_type, type, &data_out, &metadata_out);
+ if (result) {
+ *data = data_out;
+ *metadata = metadata_out;
+ }
+ return result;
+}
+
+WebVector<WebString> WebClipboardImpl::readFilenames(Buffer buffer) {
+ Clipboard::Buffer buffer_type;
+ std::vector<string16> filenames;
+ if (ConvertBufferType(buffer, &buffer_type)) {
+ ClipboardReadFilenames(buffer_type, &filenames);
+ }
+ return filenames;
+}
+
+bool WebClipboardImpl::ConvertBufferType(Buffer buffer,
+ Clipboard::Buffer* result) {
+ switch (buffer) {
+ case BufferStandard:
+ *result = Clipboard::BUFFER_STANDARD;
+ break;
+ case BufferDrag:
+ *result = Clipboard::BUFFER_DRAG;
+ case BufferSelection:
+#if defined(USE_X11)
+ *result = Clipboard::BUFFER_SELECTION;
+ break;
+#endif
+ default:
+ NOTREACHED();
+ return false;
+ }
+ return true;
+}
+
+} // namespace webkit_glue
diff --git a/webkit/glue/webclipboard_impl.h b/webkit/glue/webclipboard_impl.h
new file mode 100644
index 0000000..92c4a5e
--- /dev/null
+++ b/webkit/glue/webclipboard_impl.h
@@ -0,0 +1,55 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved. Use of this
+// source code is governed by a BSD-style license that can be found in the
+// LICENSE file.
+
+#ifndef WEBCLIPBOARD_IMPL_H_
+#define WEBCLIPBOARD_IMPL_H_
+
+#include "app/clipboard/clipboard.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebClipboard.h"
+
+#include <string>
+
+namespace webkit_glue {
+
+class WebClipboardImpl : public WebKit::WebClipboard {
+ public:
+ static std::string URLToMarkup(const WebKit::WebURL& url,
+ const WebKit::WebString& title);
+ static std::string URLToImageMarkup(const WebKit::WebURL& url,
+ const WebKit::WebString& title);
+
+ virtual ~WebClipboardImpl() {}
+
+ // WebClipboard methods:
+ virtual bool isFormatAvailable(Format, Buffer);
+ virtual WebKit::WebString readPlainText(Buffer);
+ virtual WebKit::WebString readHTML(Buffer, WebKit::WebURL* source_url);
+ virtual void writeHTML(
+ const WebKit::WebString& html_text,
+ const WebKit::WebURL& source_url,
+ const WebKit::WebString& plain_text,
+ bool write_smart_paste);
+ virtual void writePlainText(const WebKit::WebString& plain_text);
+ virtual void writeURL(
+ const WebKit::WebURL&,
+ const WebKit::WebString& title);
+ virtual void writeImage(
+ const WebKit::WebImage&,
+ const WebKit::WebURL& source_url,
+ const WebKit::WebString& title);
+ virtual void writeData(const WebKit::WebDragData&);
+
+ virtual WebKit::WebVector<WebKit::WebString> readAvailableTypes(
+ Buffer, bool* contains_filenames);
+ virtual bool readData(Buffer, const WebKit::WebString& type,
+ WebKit::WebString* data, WebKit::WebString* metadata);
+ virtual WebKit::WebVector<WebKit::WebString> readFilenames(Buffer);
+
+ private:
+ bool ConvertBufferType(Buffer, Clipboard::Buffer*);
+};
+
+} // namespace webkit_glue
+
+#endif // WEBCLIPBOARD_IMPL_H_
diff --git a/webkit/glue/webcookie.h b/webkit/glue/webcookie.h
new file mode 100644
index 0000000..e2f11e2
--- /dev/null
+++ b/webkit/glue/webcookie.h
@@ -0,0 +1,77 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// A struct for managing data being dropped on a webview. This represents a
+// union of all the types of data that can be dropped in a platform neutral
+// way.
+
+#ifndef WEBKIT_GLUE_WEBCOOKIE_H_
+#define WEBKIT_GLUE_WEBCOOKIE_H_
+
+#include "net/base/cookie_monster.h"
+
+namespace webkit_glue {
+
+struct WebCookie {
+
+ WebCookie(const std::string& name, const std::string& value,
+ const std::string& domain, const std::string& path, double expires,
+ bool http_only, bool secure, bool session)
+ : name(name),
+ value(value),
+ domain(domain),
+ path(path),
+ expires(expires),
+ http_only(http_only),
+ secure(secure),
+ session(session) {
+ }
+
+ explicit WebCookie(const net::CookieMonster::CanonicalCookie& c)
+ : name(c.Name()),
+ value(c.Value()),
+ domain(c.Domain()),
+ path(c.Path()),
+ expires(c.ExpiryDate().ToDoubleT() * 1000),
+ http_only(c.IsHttpOnly()),
+ secure(c.IsSecure()),
+ session(!c.IsPersistent()) {
+ }
+
+ // For default constructions.
+ WebCookie()
+ : expires(0),
+ http_only(false),
+ secure(false),
+ session(false) {
+ }
+
+ // Cookie name.
+ std::string name;
+
+ // Cookie value.
+ std::string value;
+
+ // Cookie domain.
+ std::string domain;
+
+ // Cookie path.
+ std::string path;
+
+ // Cookie expires param if any.
+ double expires;
+
+ // Cookie HTTPOnly param.
+ bool http_only;
+
+ // Cookie secure param.
+ bool secure;
+
+ // Session cookie flag.
+ bool session;
+};
+
+} // namespace webkit_glue
+
+#endif // WEBKIT_GLUE_WEBCOOKIE_H_
diff --git a/webkit/glue/webcursor.cc b/webkit/glue/webcursor.cc
new file mode 100644
index 0000000..f09372c
--- /dev/null
+++ b/webkit/glue/webcursor.cc
@@ -0,0 +1,194 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/webcursor.h"
+
+#include "base/logging.h"
+#include "base/pickle.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebCursorInfo.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebImage.h"
+
+using WebKit::WebCursorInfo;
+using WebKit::WebImage;
+
+static const int kMaxCursorDimension = 1024;
+
+WebCursor::WebCursor()
+ : type_(WebCursorInfo::TypePointer) {
+ InitPlatformData();
+}
+
+WebCursor::WebCursor(const WebCursorInfo& cursor_info)
+ : type_(WebCursorInfo::TypePointer) {
+ InitPlatformData();
+ InitFromCursorInfo(cursor_info);
+}
+
+WebCursor::~WebCursor() {
+ Clear();
+}
+
+WebCursor::WebCursor(const WebCursor& other) {
+ InitPlatformData();
+ Copy(other);
+}
+
+const WebCursor& WebCursor::operator=(const WebCursor& other) {
+ if (this == &other)
+ return *this;
+
+ Clear();
+ Copy(other);
+ return *this;
+}
+
+void WebCursor::InitFromCursorInfo(const WebCursorInfo& cursor_info) {
+ Clear();
+
+#if defined(OS_WIN)
+ if (cursor_info.externalHandle) {
+ InitFromExternalCursor(cursor_info.externalHandle);
+ return;
+ }
+#endif
+
+ type_ = cursor_info.type;
+ hotspot_ = cursor_info.hotSpot;
+ if (IsCustom())
+ SetCustomData(cursor_info.customImage);
+}
+
+void WebCursor::GetCursorInfo(WebCursorInfo* cursor_info) const {
+ cursor_info->type = static_cast<WebCursorInfo::Type>(type_);
+ cursor_info->hotSpot = hotspot_;
+ ImageFromCustomData(&cursor_info->customImage);
+
+#if defined(OS_WIN)
+ cursor_info->externalHandle = external_cursor_;
+#endif
+}
+
+bool WebCursor::Deserialize(const Pickle* pickle, void** iter) {
+ int type, hotspot_x, hotspot_y, size_x, size_y, data_len;
+
+ const char* data;
+
+ // Leave |this| unmodified unless we are going to return success.
+ if (!pickle->ReadInt(iter, &type) ||
+ !pickle->ReadInt(iter, &hotspot_x) ||
+ !pickle->ReadInt(iter, &hotspot_y) ||
+ !pickle->ReadLength(iter, &size_x) ||
+ !pickle->ReadLength(iter, &size_y) ||
+ !pickle->ReadData(iter, &data, &data_len))
+ return false;
+
+ // Ensure the size is sane, and there is enough data.
+ if (size_x > kMaxCursorDimension ||
+ size_y > kMaxCursorDimension)
+ return false;
+
+ if (type == WebCursorInfo::TypeCustom && (size_x == 0 || size_y == 0))
+ return false;
+
+ // The * 4 is because the expected format is an array of RGBA pixel values.
+ if (size_x * size_y * 4 > data_len)
+ return false;
+
+ type_ = type;
+ hotspot_.set_x(hotspot_x);
+ hotspot_.set_y(hotspot_y);
+ custom_size_.set_width(size_x);
+ custom_size_.set_height(size_y);
+
+ custom_data_.clear();
+ if (data_len > 0) {
+ custom_data_.resize(data_len);
+ memcpy(&custom_data_[0], data, data_len);
+ }
+
+ return DeserializePlatformData(pickle, iter);
+}
+
+bool WebCursor::Serialize(Pickle* pickle) const {
+ if (!pickle->WriteInt(type_) ||
+ !pickle->WriteInt(hotspot_.x()) ||
+ !pickle->WriteInt(hotspot_.y()) ||
+ !pickle->WriteInt(custom_size_.width()) ||
+ !pickle->WriteInt(custom_size_.height()))
+ return false;
+
+ const char* data = NULL;
+ if (!custom_data_.empty())
+ data = &custom_data_[0];
+ if (!pickle->WriteData(data, custom_data_.size()))
+ return false;
+
+ return SerializePlatformData(pickle);
+}
+
+bool WebCursor::IsCustom() const {
+ return type_ == WebCursorInfo::TypeCustom;
+}
+
+bool WebCursor::IsEqual(const WebCursor& other) const {
+ if (type_ != other.type_)
+ return false;
+
+ if (!IsPlatformDataEqual(other))
+ return false;
+
+ return hotspot_ == other.hotspot_ &&
+ custom_size_ == other.custom_size_ &&
+ custom_data_ == other.custom_data_;
+}
+
+void WebCursor::Clear() {
+ type_ = WebCursorInfo::TypePointer;
+ hotspot_.set_x(0);
+ hotspot_.set_y(0);
+ custom_size_.set_width(0);
+ custom_size_.set_height(0);
+ custom_data_.clear();
+ CleanupPlatformData();
+}
+
+void WebCursor::Copy(const WebCursor& other) {
+ type_ = other.type_;
+ hotspot_ = other.hotspot_;
+ custom_size_ = other.custom_size_;
+ custom_data_ = other.custom_data_;
+ CopyPlatformData(other);
+}
+
+#if WEBKIT_USING_SKIA
+// The WEBKIT_USING_CG implementation is in webcursor_mac.mm.
+void WebCursor::SetCustomData(const WebImage& image) {
+ if (image.isNull())
+ return;
+
+ // Fill custom_data_ directly with the NativeImage pixels.
+ const SkBitmap& bitmap = image.getSkBitmap();
+ SkAutoLockPixels bitmap_lock(bitmap);
+ custom_data_.resize(bitmap.getSize());
+ if (!custom_data_.empty())
+ memcpy(&custom_data_[0], bitmap.getPixels(), bitmap.getSize());
+ custom_size_.set_width(bitmap.width());
+ custom_size_.set_height(bitmap.height());
+}
+
+void WebCursor::ImageFromCustomData(WebImage* image) const {
+ if (custom_data_.empty())
+ return;
+
+ SkBitmap bitmap;
+ bitmap.setConfig(SkBitmap::kARGB_8888_Config,
+ custom_size_.width(),
+ custom_size_.height());
+ if (!bitmap.allocPixels())
+ return;
+ memcpy(bitmap.getPixels(), &custom_data_[0], custom_data_.size());
+
+ image->assign(bitmap);
+}
+#endif
diff --git a/webkit/glue/webcursor.h b/webkit/glue/webcursor.h
new file mode 100644
index 0000000..293bd74
--- /dev/null
+++ b/webkit/glue/webcursor.h
@@ -0,0 +1,149 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_WEBCURSOR_H_
+#define WEBKIT_GLUE_WEBCURSOR_H_
+
+#include "base/basictypes.h"
+#include "gfx/point.h"
+#include "gfx/size.h"
+
+#include <vector>
+
+#if defined(OS_WIN)
+typedef struct HINSTANCE__* HINSTANCE;
+typedef struct HICON__* HICON;
+typedef HICON HCURSOR;
+#elif defined(USE_X11)
+typedef struct _GdkCursor GdkCursor;
+#elif defined(OS_MACOSX)
+#ifdef __OBJC__
+@class NSCursor;
+#else
+class NSCursor;
+#endif
+typedef UInt32 ThemeCursor;
+struct Cursor;
+#endif
+
+class Pickle;
+
+namespace WebKit {
+class WebImage;
+struct WebCursorInfo;
+}
+
+// This class encapsulates a cross-platform description of a cursor. Platform
+// specific methods are provided to translate the cross-platform cursor into a
+// platform specific cursor. It is also possible to serialize / de-serialize a
+// WebCursor.
+class WebCursor {
+ public:
+ WebCursor();
+ explicit WebCursor(const WebKit::WebCursorInfo& cursor_info);
+ ~WebCursor();
+
+ // Copy constructor/assignment operator combine.
+ WebCursor(const WebCursor& other);
+ const WebCursor& operator=(const WebCursor& other);
+
+ // Conversion from/to WebCursorInfo.
+ void InitFromCursorInfo(const WebKit::WebCursorInfo& cursor_info);
+ void GetCursorInfo(WebKit::WebCursorInfo* cursor_info) const;
+
+ // Serialization / De-serialization
+ bool Deserialize(const Pickle* pickle, void** iter);
+ bool Serialize(Pickle* pickle) const;
+
+ // Returns true if GetCustomCursor should be used to allocate a platform
+ // specific cursor object. Otherwise GetCursor should be used.
+ bool IsCustom() const;
+
+ // Returns true if the current cursor object contains the same cursor as the
+ // cursor object passed in. If the current cursor is a custom cursor, we also
+ // compare the bitmaps to verify whether they are equal.
+ bool IsEqual(const WebCursor& other) const;
+
+#if defined(OS_WIN)
+ // Returns a HCURSOR representing the current WebCursor instance.
+ // The ownership of the HCURSOR (does not apply to external cursors) remains
+ // with the WebCursor instance.
+ HCURSOR GetCursor(HINSTANCE module_handle);
+
+ // Initialize this from the given Windows cursor. The caller must ensure that
+ // the HCURSOR remains valid by not invoking the DestroyCursor/DestroyIcon
+ // APIs on it.
+ void InitFromExternalCursor(HCURSOR handle);
+
+#elif defined(USE_X11)
+ // Return the stock GdkCursorType for this cursor, or GDK_CURSOR_IS_PIXMAP
+ // if it's a custom cursor. Return GDK_LAST_CURSOR to indicate that the cursor
+ // should be set to the system default.
+ // Returns an int so we don't need to include GDK headers in this header file.
+ int GetCursorType() const;
+
+ // Return a new GdkCursor* for this cursor. Only valid if GetCursorType
+ // returns GDK_CURSOR_IS_PIXMAP.
+ GdkCursor* GetCustomCursor() const;
+#elif defined(OS_MACOSX)
+ // Gets an NSCursor* for this cursor.
+ NSCursor* GetCursor() const;
+
+ // Initialize this from the given Carbon ThemeCursor.
+ void InitFromThemeCursor(ThemeCursor cursor);
+
+ // Initialize this from the given Carbon Cursor.
+ void InitFromCursor(const Cursor* cursor);
+
+ // Initialize this from the given Cocoa NSCursor.
+ void InitFromNSCursor(NSCursor* cursor);
+#endif
+
+ private:
+ // Copies the contents of the WebCursor instance passed in.
+ void Copy(const WebCursor& other);
+
+ // Cleans up the WebCursor instance.
+ void Clear();
+
+ // Platform specific initialization goes here.
+ void InitPlatformData();
+
+ // Platform specific Serialization / De-serialization
+ bool SerializePlatformData(Pickle* pickle) const;
+ bool DeserializePlatformData(const Pickle* pickle, void** iter);
+
+ // Returns true if the platform data in the current cursor object
+ // matches that of the cursor passed in.
+ bool IsPlatformDataEqual(const WebCursor& other) const ;
+
+ // Copies platform specific data from the WebCursor instance passed in.
+ void CopyPlatformData(const WebCursor& other);
+
+ // Platform specific cleanup.
+ void CleanupPlatformData();
+
+ void SetCustomData(const WebKit::WebImage& image);
+ void ImageFromCustomData(WebKit::WebImage* image) const;
+
+ // WebCore::PlatformCursor type.
+ int type_;
+
+ gfx::Point hotspot_;
+
+ // Custom cursor data, as 32-bit RGBA.
+ // Platform-inspecific because it can be serialized.
+ gfx::Size custom_size_;
+ std::vector<char> custom_data_;
+
+#if defined(OS_WIN)
+ // An externally generated HCURSOR. We assume that it remains valid, i.e we
+ // don't attempt to copy the HCURSOR.
+ HCURSOR external_cursor_;
+ // A custom cursor created from custom bitmap data by Webkit.
+ HCURSOR custom_cursor_;
+#endif // OS_WIN
+};
+
+#endif // WEBKIT_GLUE_WEBCURSOR_H_
diff --git a/webkit/glue/webcursor_gtk.cc b/webkit/glue/webcursor_gtk.cc
new file mode 100644
index 0000000..70c0f46
--- /dev/null
+++ b/webkit/glue/webcursor_gtk.cc
@@ -0,0 +1,218 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/webcursor.h"
+
+#include <gdk/gdk.h>
+#include <gtk/gtk.h>
+
+#include "base/logging.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebCursorInfo.h"
+
+using WebKit::WebCursorInfo;
+
+namespace {
+
+// webcursor_gtk_data.h is taken directly from WebKit's CursorGtk.h.
+#include "webkit/glue/webcursor_gtk_data.h"
+
+// This helper function is taken directly from WebKit's CursorGtk.cpp.
+// It attempts to create a custom cursor from the data inlined in
+// webcursor_gtk_data.h.
+GdkCursor* GetInlineCustomCursor(CustomCursorType type) {
+ const CustomCursor& custom = CustomCursors[type];
+ GdkCursor* cursor = gdk_cursor_new_from_name(gdk_display_get_default(),
+ custom.name);
+ if (!cursor) {
+ const GdkColor fg = { 0, 0, 0, 0 };
+ const GdkColor bg = { 65535, 65535, 65535, 65535 };
+ GdkPixmap* source = gdk_bitmap_create_from_data(NULL, custom.bits,
+ 32, 32);
+ GdkPixmap* mask = gdk_bitmap_create_from_data(NULL, custom.mask_bits,
+ 32, 32);
+ cursor = gdk_cursor_new_from_pixmap(source, mask, &fg, &bg,
+ custom.hot_x, custom.hot_y);
+ g_object_unref(source);
+ g_object_unref(mask);
+ }
+ return cursor;
+}
+
+// For GTK 2.16 and beyond, GDK_BLANK_CURSOR is available. Before, we have to
+// use a custom cursor.
+#if !GTK_CHECK_VERSION(2, 16, 0)
+// Get/create a custom cursor which is invisible.
+GdkCursor* GetInvisibleCustomCursor() {
+ const char bits[] = { 0 };
+ const GdkColor color = { 0, 0, 0, 0 };
+ GdkPixmap* bitmap = gdk_bitmap_create_from_data(NULL, bits, 1, 1);
+ GdkCursor* cursor =
+ gdk_cursor_new_from_pixmap(bitmap, bitmap, &color, &color, 0, 0);
+ g_object_unref(bitmap);
+ return cursor;
+}
+#endif
+
+} // end anonymous namespace
+
+int WebCursor::GetCursorType() const {
+ // http://library.gnome.org/devel/gdk/2.12/gdk-Cursors.html has images
+ // of the default X theme, but beware that the user's cursor theme can
+ // change everything.
+ switch (type_) {
+ case WebCursorInfo::TypePointer:
+ return GDK_LAST_CURSOR;
+ case WebCursorInfo::TypeCross:
+ return GDK_CROSS;
+ case WebCursorInfo::TypeHand:
+ return GDK_HAND2;
+ case WebCursorInfo::TypeIBeam:
+ return GDK_XTERM;
+ case WebCursorInfo::TypeWait:
+ return GDK_WATCH;
+ case WebCursorInfo::TypeHelp:
+ return GDK_QUESTION_ARROW;
+ case WebCursorInfo::TypeEastResize:
+ return GDK_RIGHT_SIDE;
+ case WebCursorInfo::TypeNorthResize:
+ return GDK_TOP_SIDE;
+ case WebCursorInfo::TypeNorthEastResize:
+ return GDK_TOP_RIGHT_CORNER;
+ case WebCursorInfo::TypeNorthWestResize:
+ return GDK_TOP_LEFT_CORNER;
+ case WebCursorInfo::TypeSouthResize:
+ return GDK_BOTTOM_SIDE;
+ case WebCursorInfo::TypeSouthEastResize:
+ return GDK_BOTTOM_RIGHT_CORNER;
+ case WebCursorInfo::TypeSouthWestResize:
+ return GDK_BOTTOM_LEFT_CORNER;
+ case WebCursorInfo::TypeWestResize:
+ return GDK_LEFT_SIDE;
+ case WebCursorInfo::TypeNorthSouthResize:
+ NOTIMPLEMENTED(); return GDK_LAST_CURSOR;
+ case WebCursorInfo::TypeEastWestResize:
+ NOTIMPLEMENTED(); return GDK_LAST_CURSOR;
+ case WebCursorInfo::TypeNorthEastSouthWestResize:
+ NOTIMPLEMENTED(); return GDK_LAST_CURSOR;
+ case WebCursorInfo::TypeNorthWestSouthEastResize:
+ NOTIMPLEMENTED(); return GDK_LAST_CURSOR;
+ case WebCursorInfo::TypeColumnResize:
+ return GDK_SB_H_DOUBLE_ARROW; // TODO(evanm): is this correct?
+ case WebCursorInfo::TypeRowResize:
+ return GDK_SB_V_DOUBLE_ARROW; // TODO(evanm): is this correct?
+ case WebCursorInfo::TypeMiddlePanning:
+ return GDK_FLEUR;
+ case WebCursorInfo::TypeEastPanning:
+ return GDK_SB_RIGHT_ARROW;
+ case WebCursorInfo::TypeNorthPanning:
+ return GDK_SB_UP_ARROW;
+ case WebCursorInfo::TypeNorthEastPanning:
+ return GDK_TOP_RIGHT_CORNER;
+ case WebCursorInfo::TypeNorthWestPanning:
+ return GDK_TOP_LEFT_CORNER;
+ case WebCursorInfo::TypeSouthPanning:
+ return GDK_SB_DOWN_ARROW;
+ case WebCursorInfo::TypeSouthEastPanning:
+ return GDK_BOTTOM_RIGHT_CORNER;
+ case WebCursorInfo::TypeSouthWestPanning:
+ return GDK_BOTTOM_LEFT_CORNER;
+ case WebCursorInfo::TypeWestPanning:
+ return GDK_SB_LEFT_ARROW;
+ case WebCursorInfo::TypeMove:
+ return GDK_FLEUR;
+ case WebCursorInfo::TypeVerticalText:
+ NOTIMPLEMENTED(); return GDK_LAST_CURSOR;
+ case WebCursorInfo::TypeCell:
+ NOTIMPLEMENTED(); return GDK_LAST_CURSOR;
+ case WebCursorInfo::TypeContextMenu:
+ NOTIMPLEMENTED(); return GDK_LAST_CURSOR;
+ case WebCursorInfo::TypeAlias:
+ NOTIMPLEMENTED(); return GDK_LAST_CURSOR;
+ case WebCursorInfo::TypeProgress:
+ return GDK_WATCH;
+ case WebCursorInfo::TypeNoDrop:
+ NOTIMPLEMENTED(); return GDK_LAST_CURSOR;
+ case WebCursorInfo::TypeCopy:
+ NOTIMPLEMENTED(); return GDK_LAST_CURSOR;
+ case WebCursorInfo::TypeNone:
+// See comment above |GetInvisibleCustomCursor()|.
+#if !GTK_CHECK_VERSION(2, 16, 0)
+ return GDK_CURSOR_IS_PIXMAP;
+#else
+ return GDK_BLANK_CURSOR;
+#endif
+ case WebCursorInfo::TypeNotAllowed:
+ NOTIMPLEMENTED(); return GDK_LAST_CURSOR;
+ case WebCursorInfo::TypeZoomIn:
+ case WebCursorInfo::TypeZoomOut:
+ case WebCursorInfo::TypeCustom:
+ return GDK_CURSOR_IS_PIXMAP;
+ }
+ NOTREACHED();
+ return GDK_LAST_CURSOR;
+}
+
+GdkCursor* WebCursor::GetCustomCursor() const {
+ switch (type_) {
+// See comment above |GetInvisibleCustomCursor()|.
+#if !GTK_CHECK_VERSION(2, 16, 0)
+ case WebCursorInfo::TypeNone:
+ return GetInvisibleCustomCursor();
+#endif
+ case WebCursorInfo::TypeZoomIn:
+ return GetInlineCustomCursor(CustomCursorZoomIn);
+ case WebCursorInfo::TypeZoomOut:
+ return GetInlineCustomCursor(CustomCursorZoomOut);
+ }
+
+ if (type_ != WebCursorInfo::TypeCustom) {
+ NOTREACHED();
+ return NULL;
+ }
+
+ const guchar* data = reinterpret_cast<const guchar*>(&custom_data_[0]);
+ GdkPixbuf* pixbuf =
+ gdk_pixbuf_new_from_data(data,
+ GDK_COLORSPACE_RGB,
+ TRUE, // has_alpha
+ 8, // bits_per_sample
+ custom_size_.width(), // width
+ custom_size_.height(), // height
+ custom_size_.width() * 4, // row stride
+ NULL, // data destroy function
+ NULL); // data destroy function extra data
+
+ GdkCursor* cursor = gdk_cursor_new_from_pixbuf(gdk_display_get_default(),
+ pixbuf,
+ hotspot_.x(),
+ hotspot_.y());
+
+ gdk_pixbuf_unref(pixbuf);
+
+ return cursor;
+}
+
+void WebCursor::InitPlatformData() {
+ return;
+}
+
+bool WebCursor::SerializePlatformData(Pickle* pickle) const {
+ return true;
+}
+
+bool WebCursor::DeserializePlatformData(const Pickle* pickle, void** iter) {
+ return true;
+}
+
+bool WebCursor::IsPlatformDataEqual(const WebCursor& other) const {
+ return true;
+}
+
+void WebCursor::CleanupPlatformData() {
+ return;
+}
+
+void WebCursor::CopyPlatformData(const WebCursor& other) {
+ return;
+}
diff --git a/webkit/glue/webcursor_gtk_data.h b/webkit/glue/webcursor_gtk_data.h
new file mode 100644
index 0000000..8bffcb7
--- /dev/null
+++ b/webkit/glue/webcursor_gtk_data.h
@@ -0,0 +1,253 @@
+// This file is a cut'n'paste from WebKit/WebCore/platform/gtk/CursorGtk.h,
+// with slight modifications to fit within Chrome. Its original
+// copyright is below.
+
+// This file intentionally doesn't have a header guard, since it is just
+// data to be used within a C++ file.
+
+/*
+ * Copyright (C) 2001 Tim Copperfield <timecop@network.email.ne.jp>
+ * Copyright (C) 2007 Christian Dywan <christian@twotoasts.de>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ These cursors are copied from Mozilla code:
+ http://lxr.mozilla.org/mozilla1.8/source/widget/src/gtk2/nsGtkCursors.h
+*/
+
+/* MOZ_CURSOR_VERTICAL_TEXT */
+static const char moz_vertical_text_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00,
+ 0x06, 0x60, 0x00, 0x00, 0xfc, 0x3f, 0x00, 0x00, 0x06, 0x60, 0x00, 0x00,
+ 0x02, 0x40, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+static const char moz_vertical_text_mask_bits[] = {
+ 0x07, 0xe0, 0x00, 0x00, 0x07, 0xe0, 0x00, 0x00, 0x0f, 0xf0, 0x00, 0x00,
+ 0xff, 0xff, 0x00, 0x00, 0xfe, 0x7f, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00,
+ 0x0f, 0xf0, 0x00, 0x00, 0x07, 0xe0, 0x00, 0x00, 0x07, 0xe0, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+/* MOZ_CURSOR_CONTEXT_MENU */
+static const char moz_menu_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00,
+ 0x7c, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0xfc, 0xfd, 0x00, 0x00,
+ 0xfc, 0xff, 0x00, 0x00, 0x7c, 0x84, 0x00, 0x00, 0x6c, 0xfc, 0x00, 0x00,
+ 0xc4, 0x84, 0x00, 0x00, 0xc0, 0xfc, 0x00, 0x00, 0x80, 0x85, 0x00, 0x00,
+ 0x80, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+static const char moz_menu_mask_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00,
+ 0x1e, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00,
+ 0xfe, 0x00, 0x00, 0x00, 0xfe, 0xfd, 0x00, 0x00, 0xfe, 0xff, 0x01, 0x00,
+ 0xfe, 0xff, 0x01, 0x00, 0xfe, 0xff, 0x01, 0x00, 0xfe, 0xfe, 0x01, 0x00,
+ 0xee, 0xff, 0x01, 0x00, 0xe4, 0xff, 0x01, 0x00, 0xc0, 0xff, 0x01, 0x00,
+ 0xc0, 0xff, 0x01, 0x00, 0x80, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+/* MOZ_CURSOR_COPY */
+static const char moz_copy_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00,
+ 0x7c, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0xfc, 0x01, 0x00, 0x00,
+ 0xfc, 0x03, 0x00, 0x00, 0x7c, 0x30, 0x00, 0x00, 0x6c, 0x30, 0x00, 0x00,
+ 0xc4, 0xfc, 0x00, 0x00, 0xc0, 0xfc, 0x00, 0x00, 0x80, 0x31, 0x00, 0x00,
+ 0x80, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+static const char moz_copy_mask_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00,
+ 0x1e, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00,
+ 0xfe, 0x00, 0x00, 0x00, 0xfe, 0x01, 0x00, 0x00, 0xfe, 0x03, 0x00, 0x00,
+ 0xfe, 0x37, 0x00, 0x00, 0xfe, 0x7b, 0x00, 0x00, 0xfe, 0xfc, 0x00, 0x00,
+ 0xee, 0xff, 0x01, 0x00, 0xe4, 0xff, 0x01, 0x00, 0xc0, 0xff, 0x00, 0x00,
+ 0xc0, 0x7b, 0x00, 0x00, 0x80, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+/* MOZ_CURSOR_ALIAS */
+static const char moz_alias_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00,
+ 0x7c, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0xfc, 0x01, 0x00, 0x00,
+ 0xfc, 0x03, 0x00, 0x00, 0x7c, 0xf0, 0x00, 0x00, 0x6c, 0xe0, 0x00, 0x00,
+ 0xc4, 0xf0, 0x00, 0x00, 0xc0, 0xb0, 0x00, 0x00, 0x80, 0x19, 0x00, 0x00,
+ 0x80, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+static const char moz_alias_mask_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00,
+ 0x1e, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00,
+ 0xfe, 0x00, 0x00, 0x00, 0xfe, 0x01, 0x00, 0x00, 0xfe, 0x03, 0x00, 0x00,
+ 0xfe, 0xf7, 0x00, 0x00, 0xfe, 0xfb, 0x01, 0x00, 0xfe, 0xf0, 0x01, 0x00,
+ 0xee, 0xf9, 0x01, 0x00, 0xe4, 0xf9, 0x01, 0x00, 0xc0, 0xbf, 0x00, 0x00,
+ 0xc0, 0x3f, 0x00, 0x00, 0x80, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+/* MOZ_CURSOR_ZOOM_IN */
+static const char moz_zoom_in_bits[] = {
+ 0xf0, 0x00, 0x00, 0x00, 0x0c, 0x03, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00,
+ 0x62, 0x04, 0x00, 0x00, 0x61, 0x08, 0x00, 0x00, 0xf9, 0x09, 0x00, 0x00,
+ 0xf9, 0x09, 0x00, 0x00, 0x61, 0x08, 0x00, 0x00, 0x62, 0x04, 0x00, 0x00,
+ 0x02, 0x04, 0x00, 0x00, 0x0c, 0x0f, 0x00, 0x00, 0xf0, 0x1c, 0x00, 0x00,
+ 0x00, 0x38, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00,
+ 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+static const char moz_zoom_in_mask_bits[] = {
+ 0xf0, 0x00, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0xfe, 0x07, 0x00, 0x00,
+ 0xfe, 0x07, 0x00, 0x00, 0xff, 0x0f, 0x00, 0x00, 0xff, 0x0f, 0x00, 0x00,
+ 0xff, 0x0f, 0x00, 0x00, 0xff, 0x0f, 0x00, 0x00, 0xfe, 0x07, 0x00, 0x00,
+ 0xfe, 0x07, 0x00, 0x00, 0xfc, 0x0f, 0x00, 0x00, 0xf0, 0x1c, 0x00, 0x00,
+ 0x00, 0x38, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00,
+ 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+/* MOZ_CURSOR_ZOOM_OUT */
+static const char moz_zoom_out_bits[] = {
+ 0xf0, 0x00, 0x00, 0x00, 0x0c, 0x03, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00,
+ 0x02, 0x04, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0xf9, 0x09, 0x00, 0x00,
+ 0xf9, 0x09, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00,
+ 0x02, 0x04, 0x00, 0x00, 0x0c, 0x0f, 0x00, 0x00, 0xf0, 0x1c, 0x00, 0x00,
+ 0x00, 0x38, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00,
+ 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+static const char moz_zoom_out_mask_bits[] = {
+ 0xf0, 0x00, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0xfe, 0x07, 0x00, 0x00,
+ 0xfe, 0x07, 0x00, 0x00, 0xff, 0x0f, 0x00, 0x00, 0xff, 0x0f, 0x00, 0x00,
+ 0xff, 0x0f, 0x00, 0x00, 0xff, 0x0f, 0x00, 0x00, 0xfe, 0x07, 0x00, 0x00,
+ 0xfe, 0x07, 0x00, 0x00, 0xfc, 0x0f, 0x00, 0x00, 0xf0, 0x1c, 0x00, 0x00,
+ 0x00, 0x38, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00,
+ 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+enum CustomCursorType {
+ CustomCursorCopy = 0,
+ CustomCursorAlias,
+ CustomCursorContextMenu,
+ CustomCursorZoomIn,
+ CustomCursorZoomOut,
+ CustomCursorVerticalText
+} ;
+
+typedef struct {
+ const char* name;
+ const char* bits;
+ const char* mask_bits;
+ int hot_x;
+ int hot_y;
+} CustomCursor;
+
+// create custom pixmap cursor from cursors in nsGTKCursorData.h
+static const CustomCursor CustomCursors[] = {
+ { "copy", moz_copy_bits, moz_copy_mask_bits, 2, 2 },
+ { "alias", moz_alias_bits, moz_alias_mask_bits, 2, 2 },
+ { "context-menu", moz_menu_bits, moz_menu_mask_bits, 2, 2 },
+ { "zoom-in", moz_zoom_in_bits, moz_zoom_in_mask_bits, 6, 6 },
+ { "zoom-out", moz_zoom_out_bits, moz_zoom_out_mask_bits, 6, 6 },
+ { "vertical-text", moz_vertical_text_bits, moz_vertical_text_mask_bits, 8, 4 },
+};
+
+// This cursor intentionally left out of above structs. It is only used by
+// RenderWidgetHostViewGtk. For an explanation see
+// http://vektor-sigma.livejournal.com/1137.html (where it is referred
+// to as left_ptr_watch).
+
+/* MOZ_CURSOR_SPINNING */
+static const char moz_spinning_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00,
+ 0x7c, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0xfc, 0x01, 0x00, 0x00,
+ 0xfc, 0x3b, 0x00, 0x00, 0x7c, 0x38, 0x00, 0x00, 0x6c, 0x54, 0x00, 0x00,
+ 0xc4, 0xdc, 0x00, 0x00, 0xc0, 0x44, 0x00, 0x00, 0x80, 0x39, 0x00, 0x00,
+ 0x80, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+static const char moz_spinning_mask_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00,
+ 0x1e, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00,
+ 0xfe, 0x00, 0x00, 0x00, 0xfe, 0x01, 0x00, 0x00, 0xfe, 0x3b, 0x00, 0x00,
+ 0xfe, 0x7f, 0x00, 0x00, 0xfe, 0x7f, 0x00, 0x00, 0xfe, 0xfe, 0x00, 0x00,
+ 0xee, 0xff, 0x01, 0x00, 0xe4, 0xff, 0x00, 0x00, 0xc0, 0x7f, 0x00, 0x00,
+ 0xc0, 0x7f, 0x00, 0x00, 0x80, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
diff --git a/webkit/glue/webcursor_mac.mm b/webkit/glue/webcursor_mac.mm
new file mode 100644
index 0000000..a6e7fc0
--- /dev/null
+++ b/webkit/glue/webcursor_mac.mm
@@ -0,0 +1,384 @@
+// Copyright (c) 2008 The Chromium Authors. All rights reserved. Use of this
+// source code is governed by a BSD-style license that can be found in the
+// LICENSE file.
+
+#include "webkit/glue/webcursor.h"
+
+#import <AppKit/AppKit.h>
+#include <Carbon/Carbon.h>
+
+#include "base/logging.h"
+#include "base/nsimage_cache_mac.h"
+#include "base/scoped_cftyperef.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebCursorInfo.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebImage.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebSize.h"
+
+using WebKit::WebCursorInfo;
+using WebKit::WebImage;
+using WebKit::WebSize;
+
+namespace {
+
+// TODO: This image fetch can (and probably should) be serviced by the resource
+// resource bundle instead of going through nsimage_cache.
+NSCursor* LoadCursor(const char* name, int x, int y) {
+ NSString* file_name = [NSString stringWithUTF8String:name];
+ DCHECK(file_name);
+ NSImage* cursor_image = nsimage_cache::ImageNamed(file_name);
+ DCHECK(cursor_image);
+ return [[[NSCursor alloc] initWithImage:cursor_image
+ hotSpot:NSMakePoint(x, y)] autorelease];
+}
+
+CGImageRef CreateCGImageFromCustomData(const std::vector<char>& custom_data,
+ const gfx::Size& custom_size) {
+ scoped_cftyperef<CGColorSpaceRef> cg_color(CGColorSpaceCreateDeviceRGB());
+ // This is safe since we're not going to draw into the context we're creating.
+ void* data = const_cast<char*>(&custom_data[0]);
+ // The settings here match SetCustomData() below; keep in sync.
+ scoped_cftyperef<CGContextRef> context(
+ CGBitmapContextCreate(data,
+ custom_size.width(),
+ custom_size.height(),
+ 8,
+ custom_size.width()*4,
+ cg_color.get(),
+ kCGImageAlphaPremultipliedLast |
+ kCGBitmapByteOrder32Big));
+ return CGBitmapContextCreateImage(context.get());
+}
+
+NSCursor* CreateCustomCursor(const std::vector<char>& custom_data,
+ const gfx::Size& custom_size,
+ const gfx::Point& hotspot) {
+ // CG throws a cocoa exception if we try to create an empty image, which
+ // results in an infinite loop. This CHECK ensures that we crash instead.
+ CHECK(!custom_data.empty());
+
+ scoped_cftyperef<CGImageRef> cg_image(
+ CreateCGImageFromCustomData(custom_data, custom_size));
+
+ NSBitmapImageRep* ns_bitmap =
+ [[NSBitmapImageRep alloc] initWithCGImage:cg_image.get()];
+ NSImage* cursor_image = [[NSImage alloc] init];
+ DCHECK(cursor_image);
+ [cursor_image addRepresentation:ns_bitmap];
+ [ns_bitmap release];
+
+ NSCursor* cursor = [[NSCursor alloc] initWithImage:cursor_image
+ hotSpot:NSMakePoint(hotspot.x(),
+ hotspot.y())];
+ [cursor_image release];
+
+ return [cursor autorelease];
+}
+
+} // namespace
+
+// We're matching Safari's cursor choices; see platform/mac/CursorMac.mm
+NSCursor* WebCursor::GetCursor() const {
+ switch (type_) {
+ case WebCursorInfo::TypePointer:
+ return [NSCursor arrowCursor];
+ case WebCursorInfo::TypeCross:
+ return LoadCursor("crossHairCursor", 11, 11);
+ case WebCursorInfo::TypeHand:
+ return LoadCursor("linkCursor", 6, 1);
+ case WebCursorInfo::TypeIBeam:
+ return [NSCursor IBeamCursor];
+ case WebCursorInfo::TypeWait:
+ return LoadCursor("waitCursor", 7, 7);
+ case WebCursorInfo::TypeHelp:
+ return LoadCursor("helpCursor", 8, 8);
+ case WebCursorInfo::TypeEastResize:
+ case WebCursorInfo::TypeEastPanning:
+ return LoadCursor("eastResizeCursor", 14, 7);
+ case WebCursorInfo::TypeNorthResize:
+ case WebCursorInfo::TypeNorthPanning:
+ return LoadCursor("northResizeCursor", 7, 1);
+ case WebCursorInfo::TypeNorthEastResize:
+ case WebCursorInfo::TypeNorthEastPanning:
+ return LoadCursor("northEastResizeCursor", 14, 1);
+ case WebCursorInfo::TypeNorthWestResize:
+ case WebCursorInfo::TypeNorthWestPanning:
+ return LoadCursor("northWestResizeCursor", 0, 0);
+ case WebCursorInfo::TypeSouthResize:
+ case WebCursorInfo::TypeSouthPanning:
+ return LoadCursor("southResizeCursor", 7, 14);
+ case WebCursorInfo::TypeSouthEastResize:
+ case WebCursorInfo::TypeSouthEastPanning:
+ return LoadCursor("southEastResizeCursor", 14, 14);
+ case WebCursorInfo::TypeSouthWestResize:
+ case WebCursorInfo::TypeSouthWestPanning:
+ return LoadCursor("southWestResizeCursor", 1, 14);
+ case WebCursorInfo::TypeWestResize:
+ case WebCursorInfo::TypeWestPanning:
+ return LoadCursor("westResizeCursor", 1, 7);
+ case WebCursorInfo::TypeNorthSouthResize:
+ return LoadCursor("northSouthResizeCursor", 7, 7);
+ case WebCursorInfo::TypeEastWestResize:
+ return LoadCursor("eastWestResizeCursor", 7, 7);
+ case WebCursorInfo::TypeNorthEastSouthWestResize:
+ return LoadCursor("northEastSouthWestResizeCursor", 7, 7);
+ case WebCursorInfo::TypeNorthWestSouthEastResize:
+ return LoadCursor("northWestSouthEastResizeCursor", 7, 7);
+ case WebCursorInfo::TypeColumnResize:
+ return [NSCursor resizeLeftRightCursor];
+ case WebCursorInfo::TypeRowResize:
+ return [NSCursor resizeUpDownCursor];
+ case WebCursorInfo::TypeMiddlePanning:
+ case WebCursorInfo::TypeMove:
+ return LoadCursor("moveCursor", 7, 7);
+ case WebCursorInfo::TypeVerticalText:
+ return LoadCursor("verticalTextCursor", 7, 7);
+ case WebCursorInfo::TypeCell:
+ return LoadCursor("cellCursor", 7, 7);
+ case WebCursorInfo::TypeContextMenu:
+ return LoadCursor("contextMenuCursor", 3, 2);
+ case WebCursorInfo::TypeAlias:
+ return LoadCursor("aliasCursor", 11, 3);
+ case WebCursorInfo::TypeProgress:
+ return LoadCursor("progressCursor", 3, 2);
+ case WebCursorInfo::TypeNoDrop:
+ return LoadCursor("noDropCursor", 3, 1);
+ case WebCursorInfo::TypeCopy:
+ return LoadCursor("copyCursor", 3, 2);
+ case WebCursorInfo::TypeNone:
+ return LoadCursor("noneCursor", 7, 7);
+ case WebCursorInfo::TypeNotAllowed:
+ return LoadCursor("notAllowedCursor", 11, 11);
+ case WebCursorInfo::TypeZoomIn:
+ return LoadCursor("zoomInCursor", 7, 7);
+ case WebCursorInfo::TypeZoomOut:
+ return LoadCursor("zoomOutCursor", 7, 7);
+ case WebCursorInfo::TypeCustom:
+ return CreateCustomCursor(custom_data_, custom_size_, hotspot_);
+ }
+ NOTREACHED();
+ return nil;
+}
+
+void WebCursor::InitFromThemeCursor(ThemeCursor cursor) {
+ WebKit::WebCursorInfo cursor_info;
+
+ switch (cursor) {
+ case kThemeArrowCursor:
+ cursor_info.type = WebCursorInfo::TypePointer;
+ break;
+ case kThemeCopyArrowCursor:
+ cursor_info.type = WebCursorInfo::TypeCopy;
+ break;
+ case kThemeAliasArrowCursor:
+ cursor_info.type = WebCursorInfo::TypeAlias;
+ break;
+ case kThemeContextualMenuArrowCursor:
+ cursor_info.type = WebCursorInfo::TypeContextMenu;
+ break;
+ case kThemeIBeamCursor:
+ cursor_info.type = WebCursorInfo::TypeIBeam;
+ break;
+ case kThemeCrossCursor:
+ case kThemePlusCursor:
+ cursor_info.type = WebCursorInfo::TypeCross;
+ break;
+ case kThemeWatchCursor:
+ case kThemeSpinningCursor:
+ cursor_info.type = WebCursorInfo::TypeWait;
+ break;
+ case kThemeClosedHandCursor:
+ case kThemeOpenHandCursor:
+ case kThemePointingHandCursor:
+ case kThemeCountingUpHandCursor:
+ case kThemeCountingDownHandCursor:
+ case kThemeCountingUpAndDownHandCursor:
+ cursor_info.type = WebCursorInfo::TypeHand;
+ break;
+ case kThemeResizeLeftCursor:
+ cursor_info.type = WebCursorInfo::TypeWestResize;
+ break;
+ case kThemeResizeRightCursor:
+ cursor_info.type = WebCursorInfo::TypeEastResize;
+ break;
+ case kThemeResizeLeftRightCursor:
+ cursor_info.type = WebCursorInfo::TypeEastWestResize;
+ break;
+ case kThemeNotAllowedCursor:
+ cursor_info.type = WebCursorInfo::TypeNotAllowed;
+ break;
+ case kThemeResizeUpCursor:
+ cursor_info.type = WebCursorInfo::TypeNorthResize;
+ break;
+ case kThemeResizeDownCursor:
+ cursor_info.type = WebCursorInfo::TypeSouthResize;
+ break;
+ case kThemeResizeUpDownCursor:
+ cursor_info.type = WebCursorInfo::TypeNorthSouthResize;
+ break;
+ case kThemePoofCursor: // *shrug*
+ default:
+ cursor_info.type = WebCursorInfo::TypePointer;
+ break;
+ }
+
+ InitFromCursorInfo(cursor_info);
+}
+
+void WebCursor::InitFromCursor(const Cursor* cursor) {
+ // This conversion isn't perfect (in particular, the inversion effect of
+ // data==1, mask==0 won't work). Not planning on fixing it.
+
+ gfx::Size custom_size(16, 16);
+ std::vector<char> raw_data;
+ for (int row = 0; row < 16; ++row) {
+ unsigned short data = cursor->data[row];
+ unsigned short mask = cursor->mask[row];
+
+ // The Core Endian flipper callback for 'CURS' doesn't flip Bits16 as if it
+ // were a short (which it is), so we flip it here.
+ data = ((data << 8) & 0xFF00) | ((data >> 8) & 0x00FF);
+ mask = ((mask << 8) & 0xFF00) | ((mask >> 8) & 0x00FF);
+
+ for (int bit = 0; bit < 16; ++bit) {
+ if (data & 0x8000) {
+ raw_data.push_back(0x00);
+ raw_data.push_back(0x00);
+ raw_data.push_back(0x00);
+ } else {
+ raw_data.push_back(0xFF);
+ raw_data.push_back(0xFF);
+ raw_data.push_back(0xFF);
+ }
+ if (mask & 0x8000)
+ raw_data.push_back(0xFF);
+ else
+ raw_data.push_back(0x00);
+ data <<= 1;
+ mask <<= 1;
+ }
+ }
+
+ scoped_cftyperef<CGImageRef> cg_image(
+ CreateCGImageFromCustomData(raw_data, custom_size));
+
+ WebKit::WebCursorInfo cursor_info;
+ cursor_info.type = WebCursorInfo::TypeCustom;
+ cursor_info.hotSpot = WebKit::WebPoint(cursor->hotSpot.h, cursor->hotSpot.v);
+ cursor_info.customImage = cg_image.get();
+
+ InitFromCursorInfo(cursor_info);
+}
+
+void WebCursor::InitFromNSCursor(NSCursor* cursor) {
+ WebKit::WebCursorInfo cursor_info;
+
+ if ([cursor isEqual:[NSCursor arrowCursor]]) {
+ cursor_info.type = WebCursorInfo::TypePointer;
+ } else if ([cursor isEqual:[NSCursor IBeamCursor]]) {
+ cursor_info.type = WebCursorInfo::TypeIBeam;
+ } else if ([cursor isEqual:[NSCursor crosshairCursor]]) {
+ cursor_info.type = WebCursorInfo::TypeCross;
+ } else if ([cursor isEqual:[NSCursor pointingHandCursor]]) {
+ cursor_info.type = WebCursorInfo::TypeHand;
+ } else if ([cursor isEqual:[NSCursor resizeLeftCursor]]) {
+ cursor_info.type = WebCursorInfo::TypeWestResize;
+ } else if ([cursor isEqual:[NSCursor resizeRightCursor]]) {
+ cursor_info.type = WebCursorInfo::TypeEastResize;
+ } else if ([cursor isEqual:[NSCursor resizeLeftRightCursor]]) {
+ cursor_info.type = WebCursorInfo::TypeEastWestResize;
+ } else if ([cursor isEqual:[NSCursor resizeUpCursor]]) {
+ cursor_info.type = WebCursorInfo::TypeNorthResize;
+ } else if ([cursor isEqual:[NSCursor resizeDownCursor]]) {
+ cursor_info.type = WebCursorInfo::TypeSouthResize;
+ } else if ([cursor isEqual:[NSCursor resizeUpDownCursor]]) {
+ cursor_info.type = WebCursorInfo::TypeNorthSouthResize;
+ } else {
+ // Also handles the [NSCursor closedHandCursor], [NSCursor openHandCursor],
+ // and [NSCursor disappearingItemCursor] cases. Quick-and-dirty image
+ // conversion; TODO(avi): do better.
+ CGImageRef cg_image = nil;
+ NSImage* image = [cursor image];
+ for (id rep in [image representations]) {
+ if ([rep isKindOfClass:[NSBitmapImageRep class]]) {
+ cg_image = [rep CGImage];
+ break;
+ }
+ }
+
+ if (cg_image) {
+ cursor_info.type = WebCursorInfo::TypeCustom;
+ NSPoint hot_spot = [cursor hotSpot];
+ cursor_info.hotSpot = WebKit::WebPoint(hot_spot.x, hot_spot.y);
+ cursor_info.customImage = cg_image;
+ } else {
+ cursor_info.type = WebCursorInfo::TypePointer;
+ }
+ }
+
+ InitFromCursorInfo(cursor_info);
+}
+
+void WebCursor::SetCustomData(const WebImage& image) {
+ if (image.isNull())
+ return;
+
+ scoped_cftyperef<CGColorSpaceRef> cg_color(
+ CGColorSpaceCreateDeviceRGB());
+
+ const WebSize& image_dimensions = image.size();
+ int image_width = image_dimensions.width;
+ int image_height = image_dimensions.height;
+
+ size_t size = image_height * image_width * 4;
+ custom_data_.resize(size);
+ custom_size_.set_width(image_width);
+ custom_size_.set_height(image_height);
+
+ // These settings match up with the code in CreateCustomCursor() above; keep
+ // them in sync.
+ // TODO(avi): test to ensure that the flags here are correct for RGBA
+ scoped_cftyperef<CGContextRef> context(
+ CGBitmapContextCreate(&custom_data_[0],
+ image_width,
+ image_height,
+ 8,
+ image_width * 4,
+ cg_color.get(),
+ kCGImageAlphaPremultipliedLast |
+ kCGBitmapByteOrder32Big));
+ CGRect rect = CGRectMake(0, 0, image_width, image_height);
+ CGContextDrawImage(context.get(), rect, image.getCGImageRef());
+}
+
+void WebCursor::ImageFromCustomData(WebImage* image) const {
+ if (custom_data_.empty())
+ return;
+
+ scoped_cftyperef<CGImageRef> cg_image(
+ CreateCGImageFromCustomData(custom_data_, custom_size_));
+ *image = cg_image.get();
+}
+
+void WebCursor::InitPlatformData() {
+ return;
+}
+
+bool WebCursor::SerializePlatformData(Pickle* pickle) const {
+ return true;
+}
+
+bool WebCursor::DeserializePlatformData(const Pickle* pickle, void** iter) {
+ return true;
+}
+
+bool WebCursor::IsPlatformDataEqual(const WebCursor& other) const {
+ return true;
+}
+
+void WebCursor::CleanupPlatformData() {
+ return;
+}
+
+void WebCursor::CopyPlatformData(const WebCursor& other) {
+ return;
+}
diff --git a/webkit/glue/webcursor_unittest.cc b/webkit/glue/webcursor_unittest.cc
new file mode 100644
index 0000000..5c1ddfe
--- /dev/null
+++ b/webkit/glue/webcursor_unittest.cc
@@ -0,0 +1,82 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/pickle.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/glue/webcursor.h"
+#include "webkit/tools/test_shell/test_shell_test.h"
+
+TEST(WebCursorTest, CursorSerialization) {
+ WebCursor custom_cursor;
+ // This is a valid custom cursor.
+ Pickle ok_custom_pickle;
+ // Type and hotspots.
+ ok_custom_pickle.WriteInt(0);
+ ok_custom_pickle.WriteInt(0);
+ ok_custom_pickle.WriteInt(0);
+ // X & Y
+ ok_custom_pickle.WriteInt(1);
+ ok_custom_pickle.WriteInt(1);
+ // Data len including enough data for a 1x1 image.
+ ok_custom_pickle.WriteInt(4);
+ ok_custom_pickle.WriteUInt32(0);
+ // Custom Windows message.
+ ok_custom_pickle.WriteUInt32(0);
+ void* iter = NULL;
+ EXPECT_TRUE(custom_cursor.Deserialize(&ok_custom_pickle, &iter));
+
+ // This custom cursor has not been send with enough data.
+ Pickle short_custom_pickle;
+ // Type and hotspots.
+ short_custom_pickle.WriteInt(0);
+ short_custom_pickle.WriteInt(0);
+ short_custom_pickle.WriteInt(0);
+ // X & Y
+ short_custom_pickle.WriteInt(1);
+ short_custom_pickle.WriteInt(1);
+ // Data len not including enough data for a 1x1 image.
+ short_custom_pickle.WriteInt(3);
+ short_custom_pickle.WriteUInt32(0);
+ // Custom Windows message.
+ ok_custom_pickle.WriteUInt32(0);
+ iter = NULL;
+ EXPECT_FALSE(custom_cursor.Deserialize(&short_custom_pickle, &iter));
+
+ // This custom cursor has enough data but is too big.
+ Pickle large_custom_pickle;
+ // Type and hotspots.
+ large_custom_pickle.WriteInt(0);
+ large_custom_pickle.WriteInt(0);
+ large_custom_pickle.WriteInt(0);
+ // X & Y
+ static const int kTooBigSize = 4096 + 1;
+ large_custom_pickle.WriteInt(kTooBigSize);
+ large_custom_pickle.WriteInt(1);
+ // Data len including enough data for a 4097x1 image.
+ large_custom_pickle.WriteInt(kTooBigSize * 4);
+ for (int i = 0; i < kTooBigSize; ++i)
+ large_custom_pickle.WriteUInt32(0);
+ // Custom Windows message.
+ ok_custom_pickle.WriteUInt32(0);
+ iter = NULL;
+ EXPECT_FALSE(custom_cursor.Deserialize(&large_custom_pickle, &iter));
+
+ // This custom cursor uses negative lengths.
+ Pickle neg_custom_pickle;
+ // Type and hotspots.
+ neg_custom_pickle.WriteInt(0);
+ neg_custom_pickle.WriteInt(0);
+ neg_custom_pickle.WriteInt(0);
+ // X & Y
+ neg_custom_pickle.WriteInt(-1);
+ neg_custom_pickle.WriteInt(-1);
+ // Data len including enough data for a 1x1 image.
+ neg_custom_pickle.WriteInt(4);
+ neg_custom_pickle.WriteUInt32(0);
+ // Custom Windows message.
+ neg_custom_pickle.WriteUInt32(0);
+ iter = NULL;
+ EXPECT_FALSE(custom_cursor.Deserialize(&neg_custom_pickle, &iter));
+}
+
diff --git a/webkit/glue/webcursor_win.cc b/webkit/glue/webcursor_win.cc
new file mode 100644
index 0000000..4435e06
--- /dev/null
+++ b/webkit/glue/webcursor_win.cc
@@ -0,0 +1,236 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/logging.h"
+#include "base/pickle.h"
+#include "gfx/gdi_util.h"
+#include "grit/webkit_resources.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebCursorInfo.h"
+#include "webkit/glue/webcursor.h"
+
+using WebKit::WebCursorInfo;
+
+static LPCWSTR ToCursorID(WebCursorInfo::Type type) {
+ switch (type) {
+ case WebCursorInfo::TypePointer:
+ return IDC_ARROW;
+ case WebCursorInfo::TypeCross:
+ return IDC_CROSS;
+ case WebCursorInfo::TypeHand:
+ return IDC_HAND;
+ case WebCursorInfo::TypeIBeam:
+ return IDC_IBEAM;
+ case WebCursorInfo::TypeWait:
+ return IDC_WAIT;
+ case WebCursorInfo::TypeHelp:
+ return IDC_HELP;
+ case WebCursorInfo::TypeEastResize:
+ return IDC_SIZEWE;
+ case WebCursorInfo::TypeNorthResize:
+ return IDC_SIZENS;
+ case WebCursorInfo::TypeNorthEastResize:
+ return IDC_SIZENESW;
+ case WebCursorInfo::TypeNorthWestResize:
+ return IDC_SIZENWSE;
+ case WebCursorInfo::TypeSouthResize:
+ return IDC_SIZENS;
+ case WebCursorInfo::TypeSouthEastResize:
+ return IDC_SIZENWSE;
+ case WebCursorInfo::TypeSouthWestResize:
+ return IDC_SIZENESW;
+ case WebCursorInfo::TypeWestResize:
+ return IDC_SIZEWE;
+ case WebCursorInfo::TypeNorthSouthResize:
+ return IDC_SIZENS;
+ case WebCursorInfo::TypeEastWestResize:
+ return IDC_SIZEWE;
+ case WebCursorInfo::TypeNorthEastSouthWestResize:
+ return IDC_SIZENESW;
+ case WebCursorInfo::TypeNorthWestSouthEastResize:
+ return IDC_SIZENWSE;
+ case WebCursorInfo::TypeColumnResize:
+ return MAKEINTRESOURCE(IDC_COLRESIZE);
+ case WebCursorInfo::TypeRowResize:
+ return MAKEINTRESOURCE(IDC_ROWRESIZE);
+ case WebCursorInfo::TypeMiddlePanning:
+ return MAKEINTRESOURCE(IDC_PAN_MIDDLE);
+ case WebCursorInfo::TypeEastPanning:
+ return MAKEINTRESOURCE(IDC_PAN_EAST);
+ case WebCursorInfo::TypeNorthPanning:
+ return MAKEINTRESOURCE(IDC_PAN_NORTH);
+ case WebCursorInfo::TypeNorthEastPanning:
+ return MAKEINTRESOURCE(IDC_PAN_NORTH_EAST);
+ case WebCursorInfo::TypeNorthWestPanning:
+ return MAKEINTRESOURCE(IDC_PAN_NORTH_WEST);
+ case WebCursorInfo::TypeSouthPanning:
+ return MAKEINTRESOURCE(IDC_PAN_SOUTH);
+ case WebCursorInfo::TypeSouthEastPanning:
+ return MAKEINTRESOURCE(IDC_PAN_SOUTH_EAST);
+ case WebCursorInfo::TypeSouthWestPanning:
+ return MAKEINTRESOURCE(IDC_PAN_SOUTH_WEST);
+ case WebCursorInfo::TypeWestPanning:
+ return MAKEINTRESOURCE(IDC_PAN_WEST);
+ case WebCursorInfo::TypeMove:
+ return IDC_SIZEALL;
+ case WebCursorInfo::TypeVerticalText:
+ return MAKEINTRESOURCE(IDC_VERTICALTEXT);
+ case WebCursorInfo::TypeCell:
+ return MAKEINTRESOURCE(IDC_CELL);
+ case WebCursorInfo::TypeContextMenu:
+ return MAKEINTRESOURCE(IDC_ARROW);
+ case WebCursorInfo::TypeAlias:
+ return MAKEINTRESOURCE(IDC_ALIAS);
+ case WebCursorInfo::TypeProgress:
+ return IDC_APPSTARTING;
+ case WebCursorInfo::TypeNoDrop:
+ return IDC_NO;
+ case WebCursorInfo::TypeCopy:
+ return MAKEINTRESOURCE(IDC_COPYCUR);
+ case WebCursorInfo::TypeNone:
+ return IDC_ARROW;
+ case WebCursorInfo::TypeNotAllowed:
+ return IDC_NO;
+ case WebCursorInfo::TypeZoomIn:
+ return MAKEINTRESOURCE(IDC_ZOOMIN);
+ case WebCursorInfo::TypeZoomOut:
+ return MAKEINTRESOURCE(IDC_ZOOMOUT);
+ }
+ NOTREACHED();
+ return NULL;
+}
+
+static bool IsSystemCursorID(LPCWSTR cursor_id) {
+ return cursor_id >= IDC_ARROW; // See WinUser.h
+}
+
+static WebCursorInfo::Type ToCursorType(HCURSOR cursor) {
+ static struct {
+ HCURSOR cursor;
+ WebCursorInfo::Type type;
+ } kStandardCursors[] = {
+ { LoadCursor(NULL, IDC_ARROW), WebCursorInfo::TypePointer },
+ { LoadCursor(NULL, IDC_CROSS), WebCursorInfo::TypeCross },
+ { LoadCursor(NULL, IDC_HAND), WebCursorInfo::TypeHand },
+ { LoadCursor(NULL, IDC_IBEAM), WebCursorInfo::TypeIBeam },
+ { LoadCursor(NULL, IDC_WAIT), WebCursorInfo::TypeWait },
+ { LoadCursor(NULL, IDC_HELP), WebCursorInfo::TypeHelp },
+ { LoadCursor(NULL, IDC_SIZENESW), WebCursorInfo::TypeNorthEastResize },
+ { LoadCursor(NULL, IDC_SIZENWSE), WebCursorInfo::TypeNorthWestResize },
+ { LoadCursor(NULL, IDC_SIZENS), WebCursorInfo::TypeNorthSouthResize },
+ { LoadCursor(NULL, IDC_SIZEWE), WebCursorInfo::TypeEastWestResize },
+ { LoadCursor(NULL, IDC_SIZEALL), WebCursorInfo::TypeMove },
+ { LoadCursor(NULL, IDC_APPSTARTING), WebCursorInfo::TypeProgress },
+ { LoadCursor(NULL, IDC_NO), WebCursorInfo::TypeNotAllowed },
+ };
+ for (int i = 0; i < arraysize(kStandardCursors); i++) {
+ if (cursor == kStandardCursors[i].cursor)
+ return kStandardCursors[i].type;
+ }
+ return WebCursorInfo::TypeCustom;
+}
+
+HCURSOR WebCursor::GetCursor(HINSTANCE module_handle){
+ if (!IsCustom()) {
+ const wchar_t* cursor_id =
+ ToCursorID(static_cast<WebCursorInfo::Type>(type_));
+
+ if (IsSystemCursorID(cursor_id))
+ module_handle = NULL;
+
+ return LoadCursor(module_handle, cursor_id);
+ }
+
+ if (custom_cursor_) {
+ DCHECK(external_cursor_ == NULL);
+ return custom_cursor_;
+ }
+
+ if (external_cursor_)
+ return external_cursor_;
+
+ BITMAPINFO cursor_bitmap_info = {0};
+ gfx::CreateBitmapHeader(
+ custom_size_.width(), custom_size_.height(),
+ reinterpret_cast<BITMAPINFOHEADER*>(&cursor_bitmap_info));
+ HDC dc = GetDC(0);
+ HDC workingDC = CreateCompatibleDC(dc);
+ HBITMAP bitmap_handle = CreateDIBSection(
+ dc, &cursor_bitmap_info, DIB_RGB_COLORS, 0, 0, 0);
+ if (!custom_data_.empty())
+ SetDIBits(
+ 0, bitmap_handle, 0, custom_size_.height(), &custom_data_[0],
+ &cursor_bitmap_info, DIB_RGB_COLORS);
+
+ HBITMAP old_bitmap = reinterpret_cast<HBITMAP>(
+ SelectObject(workingDC, bitmap_handle));
+ SetBkMode(workingDC, TRANSPARENT);
+ SelectObject(workingDC, old_bitmap);
+
+ HBITMAP mask = CreateBitmap(
+ custom_size_.width(), custom_size_.height(), 1, 1, NULL);
+ ICONINFO ii = {0};
+ ii.fIcon = FALSE;
+ ii.xHotspot = hotspot_.x();
+ ii.yHotspot = hotspot_.y();
+ ii.hbmMask = mask;
+ ii.hbmColor = bitmap_handle;
+
+ custom_cursor_ = CreateIconIndirect(&ii);
+
+ DeleteObject(mask);
+ DeleteObject(bitmap_handle);
+ DeleteDC(workingDC);
+ ReleaseDC(0, dc);
+ return custom_cursor_;
+}
+
+void WebCursor::InitFromExternalCursor(HCURSOR cursor) {
+ WebCursorInfo::Type cursor_type = ToCursorType(cursor);
+
+ InitFromCursorInfo(WebCursorInfo(cursor_type));
+
+ if (cursor_type == WebCursorInfo::TypeCustom)
+ external_cursor_ = cursor;
+}
+
+void WebCursor::InitPlatformData() {
+ external_cursor_ = NULL;
+ custom_cursor_ = NULL;
+}
+
+bool WebCursor::SerializePlatformData(Pickle* pickle) const {
+ // There are some issues with converting certain HCURSORS to bitmaps. The
+ // HCURSOR being a user object can be marshaled as is.
+ // HCURSORs are always 32 bits on Windows, even on 64 bit systems.
+ return pickle->WriteUInt32(reinterpret_cast<uint32>(external_cursor_));
+}
+
+bool WebCursor::DeserializePlatformData(const Pickle* pickle, void** iter) {
+ return pickle->ReadUInt32(iter, reinterpret_cast<uint32*>(&external_cursor_));
+}
+
+bool WebCursor::IsPlatformDataEqual(const WebCursor& other) const {
+ if (!IsCustom())
+ return true;
+
+ return (external_cursor_ == other.external_cursor_);
+}
+
+void WebCursor::CopyPlatformData(const WebCursor& other) {
+ external_cursor_ = other.external_cursor_;
+ // The custom_cursor_ member will be initialized to a HCURSOR the next time
+ // the GetCursor member function is invoked on this WebCursor instance. The
+ // cursor is created using the data in the custom_data_ vector.
+ custom_cursor_ = NULL;
+}
+
+void WebCursor::CleanupPlatformData() {
+ external_cursor_ = NULL;
+
+ if (custom_cursor_) {
+ DestroyIcon(custom_cursor_);
+ custom_cursor_ = NULL;
+ }
+}
diff --git a/webkit/glue/webdropdata.cc b/webkit/glue/webdropdata.cc
new file mode 100644
index 0000000..d46987a
--- /dev/null
+++ b/webkit/glue/webdropdata.cc
@@ -0,0 +1,52 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/webdropdata.h"
+
+#include "third_party/WebKit/WebKit/chromium/public/WebData.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebDragData.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebString.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURL.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebVector.h"
+
+using WebKit::WebData;
+using WebKit::WebDragData;
+using WebKit::WebString;
+using WebKit::WebVector;
+
+WebDropData::WebDropData(const WebDragData& drag_data)
+ : identity(0),
+ url(drag_data.url()),
+ url_title(drag_data.urlTitle()),
+ download_metadata(drag_data.downloadMetadata()),
+ file_extension(drag_data.fileExtension()),
+ plain_text(drag_data.plainText()),
+ text_html(drag_data.htmlText()),
+ html_base_url(drag_data.htmlBaseURL()),
+ file_description_filename(drag_data.fileContentFileName()) {
+ if (drag_data.hasFileNames()) {
+ WebVector<WebString> fileNames;
+ drag_data.fileNames(fileNames);
+ for (size_t i = 0; i < fileNames.size(); ++i)
+ filenames.push_back(fileNames[i]);
+ }
+ WebData contents = drag_data.fileContent();
+ if (!contents.isEmpty())
+ file_contents.assign(contents.data(), contents.size());
+}
+
+WebDragData WebDropData::ToDragData() const {
+ WebDragData result;
+ result.initialize();
+ result.setURL(url);
+ result.setURLTitle(url_title);
+ result.setFileExtension(file_extension);
+ result.setFileNames(filenames);
+ result.setPlainText(plain_text);
+ result.setHTMLText(text_html);
+ result.setHTMLBaseURL(html_base_url);
+ result.setFileContentFileName(file_description_filename);
+ result.setFileContent(WebData(file_contents.data(), file_contents.size()));
+ return result;
+}
diff --git a/webkit/glue/webdropdata.h b/webkit/glue/webdropdata.h
new file mode 100644
index 0000000..8149d8e
--- /dev/null
+++ b/webkit/glue/webdropdata.h
@@ -0,0 +1,73 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// A struct for managing data being dropped on a webview. This represents a
+// union of all the types of data that can be dropped in a platform neutral
+// way.
+
+#ifndef WEBKIT_GLUE_WEBDROPDATA_H_
+#define WEBKIT_GLUE_WEBDROPDATA_H_
+
+#include <string>
+#include <vector>
+
+#include "base/string16.h"
+#include "googleurl/src/gurl.h"
+
+struct IDataObject;
+
+namespace WebKit {
+class WebDragData;
+}
+
+struct WebDropData {
+ // Construct with a given drag identity. Note: identity is an int32 because
+ // it is passed over the renderer NPAPI interface to gears.
+ explicit WebDropData(int32 drag_identity) : identity(drag_identity) {}
+
+ // Construct from a WebDragData object.
+ explicit WebDropData(const WebKit::WebDragData&);
+
+ // For default constructions, use drag |identity| 0.
+ WebDropData() : identity(0) {}
+
+ int32 identity;
+
+ // User is dragging a link into the webview.
+ GURL url;
+ string16 url_title; // The title associated with |url|.
+
+ // User is dragging a link out-of the webview.
+ string16 download_metadata;
+
+ // File extension for dragging images from a webview to the desktop.
+ string16 file_extension;
+
+ // User is dropping one or more files on the webview.
+ std::vector<string16> filenames;
+
+ // User is dragging plain text into the webview.
+ string16 plain_text;
+
+ // User is dragging text/html into the webview (e.g., out of Firefox).
+ // |html_base_url| is the URL that the html fragment is taken from (used to
+ // resolve relative links). It's ok for |html_base_url| to be empty.
+ string16 text_html;
+ GURL html_base_url;
+
+ // User is dragging data from the webview (e.g., an image).
+ string16 file_description_filename;
+ std::string file_contents;
+
+ // Convert to a WebDragData object.
+ WebKit::WebDragData ToDragData() const;
+
+ // Helper method for converting Window's specific IDataObject to a WebDropData
+ // object. TODO(tc): Move this to the browser side since it's Windows
+ // specific and no longer used in webkit.
+ static void PopulateWebDropData(IDataObject* data_object,
+ WebDropData* drop_data);
+};
+
+#endif // WEBKIT_GLUE_WEBDROPDATA_H_
diff --git a/webkit/glue/webdropdata_win.cc b/webkit/glue/webdropdata_win.cc
new file mode 100644
index 0000000..bd76090
--- /dev/null
+++ b/webkit/glue/webdropdata_win.cc
@@ -0,0 +1,33 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/webdropdata.h"
+
+#include <windows.h>
+#include <shellapi.h>
+#include <shlobj.h>
+
+#include "app/clipboard/clipboard_util_win.h"
+#include "googleurl/src/gurl.h"
+
+// static
+void WebDropData::PopulateWebDropData(IDataObject* data_object,
+ WebDropData* drop_data) {
+ std::wstring url_str;
+ if (ClipboardUtil::GetUrl(data_object, &url_str, &drop_data->url_title,
+ false)) {
+ GURL test_url(url_str);
+ if (test_url.is_valid())
+ drop_data->url = test_url;
+ }
+ ClipboardUtil::GetFilenames(data_object, &drop_data->filenames);
+ ClipboardUtil::GetPlainText(data_object, &drop_data->plain_text);
+ std::string base_url;
+ ClipboardUtil::GetHtml(data_object, &drop_data->text_html, &base_url);
+ if (!base_url.empty()) {
+ drop_data->html_base_url = GURL(base_url);
+ }
+ ClipboardUtil::GetFileContents(data_object,
+ &drop_data->file_description_filename, &drop_data->file_contents);
+}
diff --git a/webkit/glue/webfilesystem_impl.cc b/webkit/glue/webfilesystem_impl.cc
new file mode 100644
index 0000000..585287b
--- /dev/null
+++ b/webkit/glue/webfilesystem_impl.cc
@@ -0,0 +1,154 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved. Use of this
+// source code is governed by a BSD-style license that can be found in the
+// LICENSE file.
+
+#include "webkit/glue/webfilesystem_impl.h"
+
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "net/base/file_stream.h"
+#include "net/base/net_util.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebString.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURL.h"
+#include "webkit/glue/webkit_glue.h"
+
+using WebKit::WebString;
+
+namespace webkit_glue {
+
+WebFileSystemImpl::WebFileSystemImpl()
+ : sandbox_enabled_(true) {
+}
+
+bool WebFileSystemImpl::fileExists(const WebString& path) {
+ FilePath::StringType file_path = WebStringToFilePathString(path);
+ return file_util::PathExists(FilePath(file_path));
+}
+
+bool WebFileSystemImpl::deleteFile(const WebString& path) {
+ NOTREACHED();
+ return false;
+}
+
+bool WebFileSystemImpl::deleteEmptyDirectory(const WebString& path) {
+ NOTREACHED();
+ return false;
+}
+
+bool WebFileSystemImpl::getFileSize(const WebString& path, long long& result) {
+ if (sandbox_enabled_) {
+ NOTREACHED();
+ return false;
+ }
+ return file_util::GetFileSize(WebStringToFilePath(path),
+ reinterpret_cast<int64*>(&result));
+}
+
+bool WebFileSystemImpl::getFileModificationTime(const WebString& path,
+ double& result) {
+ if (sandbox_enabled_) {
+ NOTREACHED();
+ return false;
+ }
+ file_util::FileInfo info;
+ if (!file_util::GetFileInfo(WebStringToFilePath(path), &info))
+ return false;
+ result = info.last_modified.ToDoubleT();
+ return true;
+}
+
+WebString WebFileSystemImpl::directoryName(const WebString& path) {
+ FilePath file_path(WebStringToFilePathString(path));
+ return FilePathToWebString(file_path.DirName());
+}
+
+WebString WebFileSystemImpl::pathByAppendingComponent(
+ const WebString& webkit_path,
+ const WebString& webkit_component) {
+ FilePath path(WebStringToFilePathString(webkit_path));
+ FilePath component(WebStringToFilePathString(webkit_component));
+ FilePath combined_path = path.Append(component);
+ return FilePathStringToWebString(combined_path.value());
+}
+
+bool WebFileSystemImpl::makeAllDirectories(const WebString& path) {
+ DCHECK(!sandbox_enabled_);
+ FilePath::StringType file_path = WebStringToFilePathString(path);
+ return file_util::CreateDirectory(FilePath(file_path));
+}
+
+WebString WebFileSystemImpl::getAbsolutePath(const WebString& path) {
+ FilePath file_path(WebStringToFilePathString(path));
+ file_util::AbsolutePath(&file_path);
+ return FilePathStringToWebString(file_path.value());
+}
+
+bool WebFileSystemImpl::isDirectory(const WebString& path) {
+ FilePath file_path(WebStringToFilePathString(path));
+ return file_util::DirectoryExists(file_path);
+}
+
+WebKit::WebURL WebFileSystemImpl::filePathToURL(const WebString& path) {
+ return net::FilePathToFileURL(WebStringToFilePath(path));
+}
+
+base::PlatformFile WebFileSystemImpl::openFile(const WebString& path,
+ int mode) {
+ if (sandbox_enabled_) {
+ NOTREACHED();
+ return base::kInvalidPlatformFileValue;
+ }
+ return base::CreatePlatformFile(
+ WebStringToFilePath(path),
+ (mode == 0) ? (base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ)
+ : (base::PLATFORM_FILE_CREATE_ALWAYS |
+ base::PLATFORM_FILE_WRITE),
+ NULL);
+}
+
+void WebFileSystemImpl::closeFile(base::PlatformFile& handle) {
+ if (handle == base::kInvalidPlatformFileValue)
+ return;
+ if (base::ClosePlatformFile(handle))
+ handle = base::kInvalidPlatformFileValue;
+}
+
+long long WebFileSystemImpl::seekFile(base::PlatformFile handle,
+ long long offset,
+ int origin) {
+ if (handle == base::kInvalidPlatformFileValue)
+ return -1;
+ net::FileStream file_stream(handle, 0);
+ return file_stream.Seek(static_cast<net::Whence>(origin), offset);
+}
+
+bool WebFileSystemImpl::truncateFile(base::PlatformFile handle,
+ long long offset) {
+ if (handle == base::kInvalidPlatformFileValue || offset < 0)
+ return false;
+ net::FileStream file_stream(handle, base::PLATFORM_FILE_WRITE);
+ return file_stream.Truncate(offset) >= 0;
+}
+
+int WebFileSystemImpl::readFromFile(base::PlatformFile handle,
+ char* data,
+ int length) {
+ if (handle == base::kInvalidPlatformFileValue || !data || length <= 0)
+ return -1;
+ std::string buffer;
+ buffer.resize(length);
+ net::FileStream file_stream(handle, base::PLATFORM_FILE_READ);
+ return file_stream.Read(data, length, NULL);
+}
+
+int WebFileSystemImpl::writeToFile(base::PlatformFile handle,
+ const char* data,
+ int length) {
+ if (handle == base::kInvalidPlatformFileValue || !data || length <= 0)
+ return -1;
+ net::FileStream file_stream(handle, base::PLATFORM_FILE_WRITE);
+ return file_stream.Write(data, length, NULL);
+}
+
+} // namespace webkit_glue
diff --git a/webkit/glue/webfilesystem_impl.h b/webkit/glue/webfilesystem_impl.h
new file mode 100644
index 0000000..875a13b
--- /dev/null
+++ b/webkit/glue/webfilesystem_impl.h
@@ -0,0 +1,54 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved. Use of this
+// source code is governed by a BSD-style license that can be found in the
+// LICENSE file.
+
+#ifndef WEBFILESYSTEM_IMPL_H_
+#define WEBFILESYSTEM_IMPL_H_
+
+#include "base/platform_file.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebFileSystem.h"
+
+namespace webkit_glue {
+
+class WebFileSystemImpl : public WebKit::WebFileSystem {
+ public:
+ WebFileSystemImpl();
+ virtual ~WebFileSystemImpl() { }
+
+ // WebFileSystem methods:
+ virtual bool fileExists(const WebKit::WebString& path);
+ virtual bool deleteFile(const WebKit::WebString& path);
+ virtual bool deleteEmptyDirectory(const WebKit::WebString& path);
+ virtual bool getFileSize(const WebKit::WebString& path, long long& result);
+ virtual bool getFileModificationTime(
+ const WebKit::WebString& path,
+ double& result);
+ virtual WebKit::WebString directoryName(const WebKit::WebString& path);
+ virtual WebKit::WebString pathByAppendingComponent(
+ const WebKit::WebString& path, const WebKit::WebString& component);
+ virtual bool makeAllDirectories(const WebKit::WebString& path);
+ virtual WebKit::WebString getAbsolutePath(const WebKit::WebString& path);
+ virtual bool isDirectory(const WebKit::WebString& path);
+ virtual WebKit::WebURL filePathToURL(const WebKit::WebString& path);
+ virtual base::PlatformFile openFile(const WebKit::WebString& path, int mode);
+ virtual void closeFile(base::PlatformFile& handle);
+ virtual long long seekFile(base::PlatformFile handle,
+ long long offset,
+ int origin);
+ virtual bool truncateFile(base::PlatformFile handle, long long offset);
+ virtual int readFromFile(base::PlatformFile handle, char* data, int length);
+ virtual int writeToFile(base::PlatformFile handle,
+ const char* data,
+ int length);
+
+ void set_sandbox_enabled(bool sandbox_enabled) {
+ sandbox_enabled_ = sandbox_enabled;
+ }
+
+ protected:
+ bool sandbox_enabled_;
+};
+
+} // namespace webkit_glue
+
+#endif // WEBFILESYSTEM_IMPL_H_
diff --git a/webkit/glue/webframe_unittest.cc b/webkit/glue/webframe_unittest.cc
new file mode 100644
index 0000000..6ffaf84
--- /dev/null
+++ b/webkit/glue/webframe_unittest.cc
@@ -0,0 +1,95 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebData.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebFrame.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebString.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURL.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebView.h"
+#include "webkit/tools/test_shell/test_shell_test.h"
+
+using WebKit::WebFrame;
+using WebKit::WebString;
+using WebKit::WebView;
+
+class WebFrameTest : public TestShellTest {
+};
+
+TEST_F(WebFrameTest, GetContentAsPlainText) {
+ WebView* view = test_shell_->webView();
+ WebFrame* frame = view->mainFrame();
+
+ // Generate a simple test case.
+ const char simple_source[] = "<div>Foo bar</div><div></div>baz";
+ GURL test_url("http://foo/");
+ frame->loadHTMLString(simple_source, test_url);
+ test_shell_->WaitTestFinished();
+
+ // Make sure it comes out OK.
+ const string16 expected(ASCIIToUTF16("Foo bar\nbaz"));
+ string16 text = frame->contentAsText(std::numeric_limits<size_t>::max());
+ EXPECT_EQ(expected, text);
+
+ // Try reading the same one with clipping of the text.
+ const int len = 5;
+ text = frame->contentAsText(len);
+ EXPECT_EQ(expected.substr(0, len), text);
+
+ // Now do a new test with a subframe.
+ const char outer_frame_source[] = "Hello<iframe></iframe> world";
+ frame->loadHTMLString(outer_frame_source, test_url);
+ test_shell_->WaitTestFinished();
+
+ // Load something into the subframe.
+ WebFrame* subframe = frame->findChildByExpression(
+ WebString::fromUTF8("/html/body/iframe"));
+ ASSERT_TRUE(subframe);
+ subframe->loadHTMLString("sub<p>text", test_url);
+ test_shell_->WaitTestFinished();
+
+ text = frame->contentAsText(std::numeric_limits<size_t>::max());
+ EXPECT_EQ("Hello world\n\nsub\ntext", UTF16ToUTF8(text));
+
+ // Get the frame text where the subframe separator falls on the boundary of
+ // what we'll take. There used to be a crash in this case.
+ text = frame->contentAsText(12);
+ EXPECT_EQ("Hello world", UTF16ToUTF8(text));
+}
+
+TEST_F(WebFrameTest, GetFullHtmlOfPage) {
+ WebView* view = test_shell_->webView();
+ WebFrame* frame = view->mainFrame();
+
+ // Generate a simple test case.
+ const char simple_source[] = "<p>Hello</p><p>World</p>";
+ GURL test_url("http://hello/");
+ frame->loadHTMLString(simple_source, test_url);
+ test_shell_->WaitTestFinished();
+
+ string16 text = frame->contentAsText(std::numeric_limits<size_t>::max());
+ EXPECT_EQ("Hello\n\nWorld", UTF16ToUTF8(text));
+
+ const std::string html = frame->contentAsMarkup().utf8();
+
+ // Load again with the output html.
+ frame->loadHTMLString(html, test_url);
+ test_shell_->WaitTestFinished();
+
+ EXPECT_EQ(html, UTF16ToUTF8(frame->contentAsMarkup()));
+
+ text = frame->contentAsText(std::numeric_limits<size_t>::max());
+ EXPECT_EQ("Hello\n\nWorld", UTF16ToUTF8(text));
+
+ // Test selection check
+ EXPECT_FALSE(frame->hasSelection());
+ frame->executeCommand(WebString::fromUTF8("SelectAll"));
+ EXPECT_TRUE(frame->hasSelection());
+ frame->executeCommand(WebString::fromUTF8("Unselect"));
+ EXPECT_FALSE(frame->hasSelection());
+ WebString selection_html = frame->selectionAsMarkup();
+ EXPECT_TRUE(selection_html.isEmpty());
+}
diff --git a/webkit/glue/webkit_glue.cc b/webkit/glue/webkit_glue.cc
new file mode 100644
index 0000000..e6b0f75
--- /dev/null
+++ b/webkit/glue/webkit_glue.cc
@@ -0,0 +1,509 @@
+// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/webkit_glue.h"
+
+#if defined(OS_WIN)
+#include <objidl.h>
+#include <mlang.h>
+#elif defined(OS_POSIX) && !defined(OS_MACOSX)
+#include <sys/utsname.h>
+#endif
+
+#include "base/logging.h"
+#include "base/scoped_ptr.h"
+#include "base/singleton.h"
+#include "base/string_piece.h"
+#include "base/string_util.h"
+#include "base/sys_info.h"
+#include "base/sys_string_conversions.h"
+#include "base/utf_string_conversions.h"
+#include "net/base/escape.h"
+#include "skia/ext/platform_canvas.h"
+#if defined(OS_MACOSX)
+#include "skia/ext/skia_utils_mac.h"
+#endif
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebData.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebDocument.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebElement.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebFrame.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebGlyphCache.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebHistoryItem.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebImage.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebKit.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebSize.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebString.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebVector.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebView.h"
+#if defined(OS_WIN)
+#include "third_party/WebKit/WebKit/chromium/public/win/WebInputEventFactory.h"
+#endif
+#include "webkit/glue/glue_serialize.h"
+#include "v8/include/v8.h"
+
+#include "webkit_version.h" // Generated
+
+using WebKit::WebCanvas;
+using WebKit::WebData;
+using WebKit::WebElement;
+using WebKit::WebFrame;
+using WebKit::WebGlyphCache;
+using WebKit::WebHistoryItem;
+using WebKit::WebImage;
+using WebKit::WebSize;
+using WebKit::WebString;
+using WebKit::WebVector;
+using WebKit::WebView;
+
+static const char kLayoutTestsPattern[] = "/LayoutTests/";
+static const std::string::size_type kLayoutTestsPatternSize =
+ arraysize(kLayoutTestsPattern) - 1;
+static const char kFileUrlPattern[] = "file:/";
+static const char kDataUrlPattern[] = "data:";
+static const std::string::size_type kDataUrlPatternSize =
+ arraysize(kDataUrlPattern) - 1;
+static const char kFileTestPrefix[] = "(file test):";
+
+//------------------------------------------------------------------------------
+// webkit_glue impl:
+
+namespace webkit_glue {
+
+// Global variable used by the plugin quirk "die after unload".
+bool g_forcefully_terminate_plugin_process = false;
+
+void SetJavaScriptFlags(const std::wstring& str) {
+#if WEBKIT_USING_V8
+ std::string utf8_str = WideToUTF8(str);
+ v8::V8::SetFlagsFromString(
+ utf8_str.data(), static_cast<int>(utf8_str.size()));
+#endif
+}
+
+void EnableWebCoreNotImplementedLogging() {
+ WebKit::enableLogChannel("NotYetImplemented");
+}
+
+std::wstring DumpDocumentText(WebFrame* web_frame) {
+ // We use the document element's text instead of the body text here because
+ // not all documents have a body, such as XML documents.
+ WebElement document_element = web_frame->document().documentElement();
+ if (document_element.isNull())
+ return std::wstring();
+
+ return UTF16ToWideHack(document_element.innerText());
+}
+
+std::wstring DumpFramesAsText(WebFrame* web_frame, bool recursive) {
+ std::wstring result;
+
+ // Add header for all but the main frame. Skip empty frames.
+ if (web_frame->parent() &&
+ !web_frame->document().documentElement().isNull()) {
+ result.append(L"\n--------\nFrame: '");
+ result.append(UTF16ToWideHack(web_frame->name()));
+ result.append(L"'\n--------\n");
+ }
+
+ result.append(DumpDocumentText(web_frame));
+ result.append(L"\n");
+
+ if (recursive) {
+ WebFrame* child = web_frame->firstChild();
+ for (; child; child = child->nextSibling())
+ result.append(DumpFramesAsText(child, recursive));
+ }
+
+ return result;
+}
+
+std::wstring DumpRenderer(WebFrame* web_frame) {
+ return UTF16ToWideHack(web_frame->renderTreeAsText());
+}
+
+bool CounterValueForElementById(WebFrame* web_frame, const std::string& id,
+ std::wstring* counter_value) {
+ WebString result =
+ web_frame->counterValueForElementById(WebString::fromUTF8(id));
+ if (result.isNull())
+ return false;
+
+ *counter_value = UTF16ToWideHack(result);
+ return true;
+}
+
+int PageNumberForElementById(WebFrame* web_frame,
+ const std::string& id,
+ float page_width_in_pixels,
+ float page_height_in_pixels) {
+ return web_frame->pageNumberForElementById(WebString::fromUTF8(id),
+ page_width_in_pixels,
+ page_height_in_pixels);
+}
+
+int NumberOfPages(WebFrame* web_frame,
+ float page_width_in_pixels,
+ float page_height_in_pixels) {
+ WebSize size(static_cast<int>(page_width_in_pixels),
+ static_cast<int>(page_height_in_pixels));
+ int number_of_pages = web_frame->printBegin(size);
+ web_frame->printEnd();
+ return number_of_pages;
+}
+
+std::wstring DumpFrameScrollPosition(WebFrame* web_frame, bool recursive) {
+ gfx::Size offset = web_frame->scrollOffset();
+ std::wstring result;
+
+ if (offset.width() > 0 || offset.height() > 0) {
+ if (web_frame->parent()) {
+ StringAppendF(&result, L"frame '%ls' ", UTF16ToWide(
+ web_frame->name()).c_str());
+ }
+ StringAppendF(&result, L"scrolled to %d,%d\n",
+ offset.width(), offset.height());
+ }
+
+ if (recursive) {
+ WebFrame* child = web_frame->firstChild();
+ for (; child; child = child->nextSibling())
+ result.append(DumpFrameScrollPosition(child, recursive));
+ }
+
+ return result;
+}
+
+// Returns True if item1 < item2.
+static bool HistoryItemCompareLess(const WebHistoryItem& item1,
+ const WebHistoryItem& item2) {
+ string16 target1 = item1.target();
+ string16 target2 = item2.target();
+ std::transform(target1.begin(), target1.end(), target1.begin(), tolower);
+ std::transform(target2.begin(), target2.end(), target2.begin(), tolower);
+ return target1 < target2;
+}
+
+// Writes out a HistoryItem into a string in a readable format.
+static std::wstring DumpHistoryItem(const WebHistoryItem& item,
+ int indent, bool is_current) {
+ std::wstring result;
+
+ if (is_current) {
+ result.append(L"curr->");
+ result.append(indent - 6, L' '); // 6 == L"curr->".length()
+ } else {
+ result.append(indent, L' ');
+ }
+
+ std::string url = item.urlString().utf8();
+ size_t pos;
+ if (url.find(kFileUrlPattern) == 0 &&
+ ((pos = url.find(kLayoutTestsPattern)) != std::string::npos)) {
+ // adjust file URLs to match upstream results.
+ url.replace(0, pos + kLayoutTestsPatternSize, kFileTestPrefix);
+ } else if (url.find(kDataUrlPattern) == 0) {
+ // URL-escape data URLs to match results upstream.
+ std::string path = EscapePath(url.substr(kDataUrlPatternSize));
+ url.replace(kDataUrlPatternSize, url.length(), path);
+ }
+
+ result.append(UTF8ToWide(url));
+ if (!item.target().isEmpty())
+ result.append(L" (in frame \"" + UTF16ToWide(item.target()) + L"\")");
+ if (item.isTargetItem())
+ result.append(L" **nav target**");
+ result.append(L"\n");
+
+ const WebVector<WebHistoryItem>& children = item.children();
+ if (!children.isEmpty()) {
+ // Must sort to eliminate arbitrary result ordering which defeats
+ // reproducible testing.
+ // TODO(darin): WebVector should probably just be a std::vector!!
+ std::vector<WebHistoryItem> sorted_children;
+ for (size_t i = 0; i < children.size(); ++i)
+ sorted_children.push_back(children[i]);
+ std::sort(sorted_children.begin(), sorted_children.end(),
+ HistoryItemCompareLess);
+ for (size_t i = 0; i < sorted_children.size(); i++)
+ result += DumpHistoryItem(sorted_children[i], indent+4, false);
+ }
+
+ return result;
+}
+
+std::wstring DumpHistoryState(const std::string& history_state, int indent,
+ bool is_current) {
+ return DumpHistoryItem(HistoryItemFromString(history_state), indent,
+ is_current);
+}
+
+void ResetBeforeTestRun(WebView* view) {
+ WebFrame* web_frame = view->mainFrame();
+
+ // Reset the main frame name since tests always expect it to be empty. It
+ // is normally not reset between page loads (even in IE and FF).
+ if (web_frame)
+ web_frame->setName(WebString());
+
+#if defined(OS_WIN)
+ // Reset the last click information so the clicks generated from previous
+ // test aren't inherited (otherwise can mistake single/double/triple clicks)
+ WebKit::WebInputEventFactory::resetLastClickState();
+#endif
+}
+
+#ifndef NDEBUG
+// The log macro was having problems due to collisions with WTF, so we just
+// code here what that would have inlined.
+void DumpLeakedObject(const char* file, int line, const char* object, int count) {
+ std::string msg = StringPrintf("%s LEAKED %d TIMES", object, count);
+ AppendToLog(file, line, msg.c_str());
+}
+#endif
+
+void CheckForLeaks() {
+#ifndef NDEBUG
+ int count = WebFrame::instanceCount();
+ if (count)
+ DumpLeakedObject(__FILE__, __LINE__, "WebFrame", count);
+#endif
+}
+
+bool DecodeImage(const std::string& image_data, SkBitmap* image) {
+ WebData web_data(image_data.data(), image_data.length());
+ WebImage web_image(WebImage::fromData(web_data, WebSize()));
+ if (web_image.isNull())
+ return false;
+
+#if defined(OS_MACOSX)
+ *image = gfx::CGImageToSkBitmap(web_image.getCGImageRef());
+#else
+ *image = web_image.getSkBitmap();
+#endif
+ return true;
+}
+
+// NOTE: This pair of conversion functions are here instead of in glue_util.cc
+// since that file will eventually die. This pair of functions will need to
+// remain as the concept of a file-path specific character encoding string type
+// will most likely not make its way into WebKit.
+
+FilePath::StringType WebStringToFilePathString(const WebString& str) {
+#if defined(OS_POSIX)
+ return base::SysWideToNativeMB(UTF16ToWideHack(str));
+#elif defined(OS_WIN)
+ return UTF16ToWideHack(str);
+#endif
+}
+
+WebString FilePathStringToWebString(const FilePath::StringType& str) {
+#if defined(OS_POSIX)
+ return WideToUTF16Hack(base::SysNativeMBToWide(str));
+#elif defined(OS_WIN)
+ return WideToUTF16Hack(str);
+#endif
+}
+
+FilePath WebStringToFilePath(const WebString& str) {
+ return FilePath(WebStringToFilePathString(str));
+}
+
+WebString FilePathToWebString(const FilePath& file_path) {
+ return FilePathStringToWebString(file_path.value());
+}
+
+std::string GetWebKitVersion() {
+ return StringPrintf("%d.%d", WEBKIT_VERSION_MAJOR, WEBKIT_VERSION_MINOR);
+}
+
+namespace {
+
+struct UserAgentState {
+ UserAgentState()
+ : user_agent_requested(false),
+ user_agent_is_overridden(false) {
+ }
+
+ std::string user_agent;
+
+ // The UA string when we're pretending to be Windows Chrome.
+ std::string mimic_windows_user_agent;
+
+ bool user_agent_requested;
+ bool user_agent_is_overridden;
+};
+
+Singleton<UserAgentState> g_user_agent;
+
+std::string BuildOSCpuInfo() {
+ std::string os_cpu;
+
+#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_CHROMEOS)
+ int32 os_major_version = 0;
+ int32 os_minor_version = 0;
+ int32 os_bugfix_version = 0;
+ base::SysInfo::OperatingSystemVersionNumbers(&os_major_version,
+ &os_minor_version,
+ &os_bugfix_version);
+#endif
+#if defined(OS_POSIX) && !defined(OS_MACOSX)
+ // Should work on any Posix system.
+ struct utsname unixinfo;
+ uname(&unixinfo);
+
+ std::string cputype;
+ // special case for biarch systems
+ if (strcmp(unixinfo.machine, "x86_64") == 0 &&
+ sizeof(void*) == sizeof(int32)) {
+ cputype.assign("i686 (x86_64)");
+ } else {
+ cputype.assign(unixinfo.machine);
+ }
+#endif
+
+ StringAppendF(
+ &os_cpu,
+#if defined(OS_WIN)
+ "Windows NT %d.%d",
+ os_major_version,
+ os_minor_version
+#elif defined(OS_MACOSX)
+ "Intel Mac OS X %d_%d_%d",
+ os_major_version,
+ os_minor_version,
+ os_bugfix_version
+#elif defined(OS_CHROMEOS)
+ "CrOS %s %d.%d.%d",
+ cputype.c_str(), // e.g. i686
+ os_major_version,
+ os_minor_version,
+ os_bugfix_version
+#else
+ "%s %s",
+ unixinfo.sysname, // e.g. Linux
+ cputype.c_str() // e.g. i686
+#endif
+ );
+
+ return os_cpu;
+}
+
+// Construct the User-Agent header, filling in |result|.
+// The other parameters are workarounds for broken websites:
+// - If mimic_windows is true, produce a fake Windows Chrome string.
+void BuildUserAgent(bool mimic_windows, std::string* result) {
+ const char kUserAgentPlatform[] =
+#if defined(OS_WIN)
+ "Windows";
+#elif defined(OS_MACOSX)
+ "Macintosh";
+#elif defined(USE_X11)
+ "X11"; // strange, but that's what Firefox uses
+#else
+ "?";
+#endif
+
+ const char kUserAgentSecurity = 'U'; // "US" strength encryption
+
+ // TODO(port): figure out correct locale
+ const char kUserAgentLocale[] = "en-US";
+
+ // Get the product name and version, and replace Safari's Version/X string
+ // with it. This is done to expose our product name in a manner that is
+ // maximally compatible with Safari, we hope!!
+ std::string product = GetProductVersion();
+
+ // Derived from Safari's UA string.
+ StringAppendF(
+ result,
+ "Mozilla/5.0 (%s; %c; %s; %s) AppleWebKit/%d.%d"
+ " (KHTML, like Gecko) %s Safari/%d.%d",
+ mimic_windows ? "Windows" : kUserAgentPlatform,
+ kUserAgentSecurity,
+ ((mimic_windows ? "Windows " : "") + BuildOSCpuInfo()).c_str(),
+ kUserAgentLocale,
+ WEBKIT_VERSION_MAJOR,
+ WEBKIT_VERSION_MINOR,
+ product.c_str(),
+ WEBKIT_VERSION_MAJOR,
+ WEBKIT_VERSION_MINOR
+ );
+}
+
+void SetUserAgentToDefault() {
+ BuildUserAgent(false, &g_user_agent->user_agent);
+}
+
+} // namespace
+
+void SetUserAgent(const std::string& new_user_agent) {
+ // If you combine this with the previous line, the function only works the
+ // first time.
+ DCHECK(!g_user_agent->user_agent_requested) <<
+ "Setting the user agent after someone has "
+ "already requested it can result in unexpected behavior.";
+ g_user_agent->user_agent_is_overridden = true;
+ g_user_agent->user_agent = new_user_agent;
+}
+
+const std::string& GetUserAgent(const GURL& url) {
+ if (g_user_agent->user_agent.empty())
+ SetUserAgentToDefault();
+ g_user_agent->user_agent_requested = true;
+ if (!g_user_agent->user_agent_is_overridden) {
+ // Workarounds for sites that use misguided UA sniffing.
+#if defined(OS_POSIX) && !defined(OS_MACOSX)
+ if (MatchPatternASCII(url.host(), "*.mail.yahoo.com")) {
+ // mail.yahoo.com is ok with Windows Chrome but not Linux Chrome.
+ // http://bugs.chromium.org/11136
+ // TODO(evanm): remove this if Yahoo fixes their sniffing.
+ if (g_user_agent->mimic_windows_user_agent.empty())
+ BuildUserAgent(true, &g_user_agent->mimic_windows_user_agent);
+ return g_user_agent->mimic_windows_user_agent;
+ }
+#endif
+ }
+ return g_user_agent->user_agent;
+}
+
+void SetForcefullyTerminatePluginProcess(bool value) {
+ if (IsPluginRunningInRendererProcess()) {
+ // Ignore this quirk when the plugins are not running in their own process.
+ return;
+ }
+
+ g_forcefully_terminate_plugin_process = value;
+}
+
+bool ShouldForcefullyTerminatePluginProcess() {
+ return g_forcefully_terminate_plugin_process;
+}
+
+WebCanvas* ToWebCanvas(skia::PlatformCanvas* canvas) {
+#if WEBKIT_USING_SKIA
+ return canvas;
+#elif WEBKIT_USING_CG
+ return canvas->getTopPlatformDevice().GetBitmapContext();
+#else
+ NOTIMPLEMENTED();
+ return NULL;
+#endif
+}
+
+int GetGlyphPageCount() {
+ return WebGlyphCache::pageCount();
+}
+
+bool g_enable_media_cache = false;
+
+bool IsMediaCacheEnabled() {
+ return g_enable_media_cache;
+}
+
+void SetMediaCacheEnabled(bool enabled) {
+ g_enable_media_cache = enabled;
+}
+
+} // namespace webkit_glue
diff --git a/webkit/glue/webkit_glue.gypi b/webkit/glue/webkit_glue.gypi
new file mode 100644
index 0000000..49320fc
--- /dev/null
+++ b/webkit/glue/webkit_glue.gypi
@@ -0,0 +1,420 @@
+# Copyright (c) 2010 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'variables': {
+ 'conditions': [
+ ['inside_chromium_build==0', {
+ 'webkit_src_dir': '../../../..',
+ },{
+ 'webkit_src_dir': '../../third_party/WebKit',
+ }],
+ ],
+
+ 'grit_info_cmd': ['python', '<(DEPTH)/tools/grit/grit_info.py'],
+ 'grit_cmd': ['python', '<(DEPTH)/tools/grit/grit.py'],
+ },
+ 'targets': [
+ {
+ 'target_name': 'webkit_resources',
+ 'type': 'none',
+ 'msvs_guid': '0B469837-3D46-484A-AFB3-C5A6C68730B9',
+ 'variables': {
+ 'grit_out_dir': '<(SHARED_INTERMEDIATE_DIR)/webkit',
+ },
+ 'actions': [
+ {
+ 'action_name': 'webkit_resources',
+ 'variables': {
+ 'input_path': './webkit_resources.grd',
+ },
+ 'inputs': [
+ '<!@(<(grit_info_cmd) --inputs <(input_path))',
+ ],
+ 'outputs': [
+ '<!@(<(grit_info_cmd) --outputs \'<(grit_out_dir)\' <(input_path))',
+ ],
+ 'action': ['<@(grit_cmd)',
+ '-i', '<(input_path)', 'build',
+ '-o', '<(grit_out_dir)'],
+ 'message': 'Generating resources from <(input_path)',
+ },
+ {
+ 'action_name': 'webkit_chromium_resources',
+ 'variables': {
+ 'input_path': '<(webkit_src_dir)/WebKit/chromium/WebKit.grd',
+ },
+ 'inputs': [
+ '<!@(<(grit_info_cmd) --inputs <(input_path))',
+ ],
+ 'outputs': [
+ '<!@(<(grit_info_cmd) --outputs \'<(grit_out_dir)\' <(input_path))',
+ ],
+ 'action': ['<@(grit_cmd)',
+ '-i', '<(input_path)', 'build',
+ '-o', '<(grit_out_dir)'],
+ 'message': 'Generating resources from <(input_path)',
+ },
+ ],
+ 'direct_dependent_settings': {
+ 'include_dirs': [
+ '<(SHARED_INTERMEDIATE_DIR)/webkit',
+ ],
+ },
+ 'conditions': [
+ ['OS=="win"', {
+ 'dependencies': ['<(DEPTH)/build/win/system.gyp:cygwin'],
+ }],
+ ],
+ },
+ {
+ 'target_name': 'webkit_strings',
+ 'type': 'none',
+ 'msvs_guid': '60B43839-95E6-4526-A661-209F16335E0E',
+ 'variables': {
+ 'grit_out_dir': '<(SHARED_INTERMEDIATE_DIR)/webkit',
+ },
+ 'actions': [
+ {
+ 'action_name': 'webkit_strings',
+ 'variables': {
+ 'input_path': './webkit_strings.grd',
+ },
+ 'inputs': [
+ '<!@(<(grit_info_cmd) --inputs <(input_path))',
+ ],
+ 'outputs': [
+ '<!@(<(grit_info_cmd) --outputs \'<(grit_out_dir)\' <(input_path))',
+ ],
+ 'action': ['<@(grit_cmd)',
+ '-i', '<(input_path)', 'build',
+ '-o', '<(grit_out_dir)'],
+ 'message': 'Generating resources from <(input_path)',
+ },
+ ],
+ 'direct_dependent_settings': {
+ 'include_dirs': [
+ '<(SHARED_INTERMEDIATE_DIR)/webkit',
+ ],
+ },
+ 'conditions': [
+ ['OS=="win"', {
+ 'dependencies': ['<(DEPTH)/build/win/system.gyp:cygwin'],
+ }],
+ ],
+ },
+ {
+ 'target_name': 'glue',
+ 'type': '<(library)',
+ 'msvs_guid': 'C66B126D-0ECE-4CA2-B6DC-FA780AFBBF09',
+ 'dependencies': [
+ '<(DEPTH)/app/app.gyp:app_base',
+ '<(DEPTH)/base/base.gyp:base_i18n',
+ '<(DEPTH)/net/net.gyp:net',
+ '<(DEPTH)/printing/printing.gyp:printing',
+ '<(DEPTH)/skia/skia.gyp:skia',
+ '<(DEPTH)/third_party/icu/icu.gyp:icui18n',
+ '<(DEPTH)/third_party/icu/icu.gyp:icuuc',
+ '<(DEPTH)/third_party/npapi/npapi.gyp:npapi',
+ '<(DEPTH)/third_party/ppapi/ppapi.gyp:ppapi_c',
+ 'webkit_resources',
+ 'webkit_strings',
+ ],
+ 'actions': [
+ {
+ 'action_name': 'webkit_version',
+ 'inputs': [
+ '../build/webkit_version.py',
+ '<(webkit_src_dir)/WebCore/Configurations/Version.xcconfig',
+ ],
+ 'outputs': [
+ '<(INTERMEDIATE_DIR)/webkit_version.h',
+ ],
+ 'action': ['python', '<@(_inputs)', '<(INTERMEDIATE_DIR)'],
+ },
+ ],
+ 'include_dirs': [
+ '<(INTERMEDIATE_DIR)',
+ '<(SHARED_INTERMEDIATE_DIR)/webkit',
+ ],
+ 'sources': [
+ # This list contains all .h, .cc, and .mm files in glue except for
+ # those in the test subdirectory and those with unittest in in their
+ # names.
+ 'devtools_message_data.cc',
+ 'devtools_message_data.h',
+ 'media/buffered_data_source.cc',
+ 'media/buffered_data_source.h',
+ 'media/media_resource_loader_bridge_factory.cc',
+ 'media/media_resource_loader_bridge_factory.h',
+ 'media/simple_data_source.cc',
+ 'media/simple_data_source.h',
+ 'media/video_renderer_impl.cc',
+ 'media/video_renderer_impl.h',
+ 'media/web_video_renderer.h',
+ 'plugins/carbon_plugin_window_tracker_mac.h',
+ 'plugins/carbon_plugin_window_tracker_mac.cc',
+ 'plugins/coregraphics_private_symbols_mac.h',
+ 'plugins/default_plugin_shared.h',
+ 'plugins/nphostapi.h',
+ 'plugins/gtk_plugin_container.h',
+ 'plugins/gtk_plugin_container.cc',
+ 'plugins/gtk_plugin_container_manager.h',
+ 'plugins/gtk_plugin_container_manager.cc',
+ 'plugins/npapi_extension_thunk.cc',
+ 'plugins/npapi_extension_thunk.h',
+ 'plugins/pepper_buffer.cc',
+ 'plugins/pepper_buffer.h',
+ 'plugins/pepper_device_context_2d.cc',
+ 'plugins/pepper_device_context_2d.h',
+ 'plugins/pepper_directory_reader.cc',
+ 'plugins/pepper_directory_reader.h',
+ 'plugins/pepper_event_conversion.cc',
+ 'plugins/pepper_event_conversion.h',
+ 'plugins/pepper_file_chooser.cc',
+ 'plugins/pepper_file_chooser.h',
+ 'plugins/pepper_file_io.cc',
+ 'plugins/pepper_file_io.h',
+ 'plugins/pepper_file_ref.cc',
+ 'plugins/pepper_file_ref.h',
+ 'plugins/pepper_file_system.cc',
+ 'plugins/pepper_file_system.h',
+ 'plugins/pepper_font.cc',
+ 'plugins/pepper_font.h',
+ 'plugins/pepper_image_data.cc',
+ 'plugins/pepper_image_data.h',
+ 'plugins/pepper_plugin_delegate.h',
+ 'plugins/pepper_plugin_instance.cc',
+ 'plugins/pepper_plugin_instance.h',
+ 'plugins/pepper_plugin_module.cc',
+ 'plugins/pepper_plugin_module.h',
+ 'plugins/pepper_private.cc',
+ 'plugins/pepper_private.h',
+ 'plugins/pepper_resource_tracker.cc',
+ 'plugins/pepper_resource_tracker.h',
+ 'plugins/pepper_resource.cc',
+ 'plugins/pepper_resource.h',
+ 'plugins/pepper_scrollbar.cc',
+ 'plugins/pepper_scrollbar.h',
+ 'plugins/pepper_url_loader.cc',
+ 'plugins/pepper_url_loader.h',
+ 'plugins/pepper_url_request_info.cc',
+ 'plugins/pepper_url_request_info.h',
+ 'plugins/pepper_url_response_info.cc',
+ 'plugins/pepper_url_response_info.h',
+ 'plugins/pepper_var.cc',
+ 'plugins/pepper_var.h',
+ 'plugins/pepper_webplugin_impl.cc',
+ 'plugins/pepper_webplugin_impl.h',
+ 'plugins/pepper_widget.cc',
+ 'plugins/pepper_widget.h',
+ 'plugins/plugin_constants_win.h',
+ 'plugins/plugin_host.cc',
+ 'plugins/plugin_host.h',
+ 'plugins/plugin_instance.cc',
+ 'plugins/plugin_instance.h',
+ 'plugins/plugin_instance_mac.mm',
+ 'plugins/plugin_lib.cc',
+ 'plugins/plugin_lib.h',
+ 'plugins/plugin_lib_mac.mm',
+ 'plugins/plugin_lib_posix.cc',
+ 'plugins/plugin_lib_win.cc',
+ 'plugins/plugin_list.cc',
+ 'plugins/plugin_list.h',
+ 'plugins/plugin_list_mac.mm',
+ 'plugins/plugin_list_posix.cc',
+ 'plugins/plugin_list_win.cc',
+ 'plugins/plugin_stream.cc',
+ 'plugins/plugin_stream.h',
+ 'plugins/plugin_stream_posix.cc',
+ 'plugins/plugin_stream_url.cc',
+ 'plugins/plugin_stream_url.h',
+ 'plugins/plugin_stream_win.cc',
+ 'plugins/plugin_string_stream.cc',
+ 'plugins/plugin_string_stream.h',
+ 'plugins/plugin_stubs.cc',
+ 'plugins/plugin_switches.cc',
+ 'plugins/plugin_switches.h',
+ 'plugins/plugin_web_event_converter_mac.h',
+ 'plugins/plugin_web_event_converter_mac.mm',
+ 'plugins/ppb_private.h',
+ 'plugins/quickdraw_drawing_manager_mac.h',
+ 'plugins/quickdraw_drawing_manager_mac.cc',
+ 'plugins/webview_plugin.cc',
+ 'plugins/webview_plugin.h',
+ 'plugins/webplugin.cc',
+ 'plugins/webplugin.h',
+ 'plugins/webplugin_2d_device_delegate.h',
+ 'plugins/webplugin_3d_device_delegate.h',
+ 'plugins/webplugin_delegate.h',
+ 'plugins/webplugin_delegate_impl.cc',
+ 'plugins/webplugin_delegate_impl.h',
+ 'plugins/webplugin_delegate_impl_gtk.cc',
+ 'plugins/webplugin_delegate_impl_mac.mm',
+ 'plugins/webplugin_delegate_impl_win.cc',
+ 'plugins/webplugin_impl.cc',
+ 'plugins/webplugin_impl.h',
+ 'plugins/webplugininfo.h',
+ 'alt_error_page_resource_fetcher.cc',
+ 'alt_error_page_resource_fetcher.h',
+ 'context_menu.h',
+ 'cpp_binding_example.cc',
+ 'cpp_binding_example.h',
+ 'cpp_bound_class.cc',
+ 'cpp_bound_class.h',
+ 'cpp_variant.cc',
+ 'cpp_variant.h',
+ 'dom_operations.cc',
+ 'dom_operations.h',
+ 'form_data.h',
+ 'form_field.cc',
+ 'form_field.h',
+ 'ftp_directory_listing_response_delegate.cc',
+ 'ftp_directory_listing_response_delegate.h',
+ 'glue_serialize.cc',
+ 'glue_serialize.h',
+ 'image_decoder.cc',
+ 'image_decoder.h',
+ 'image_resource_fetcher.cc',
+ 'image_resource_fetcher.h',
+ 'multipart_response_delegate.cc',
+ 'multipart_response_delegate.h',
+ 'npruntime_util.cc',
+ 'npruntime_util.h',
+ 'password_form.h',
+ 'password_form_dom_manager.cc',
+ 'password_form_dom_manager.h',
+ 'resource_fetcher.cc',
+ 'resource_fetcher.h',
+ 'resource_loader_bridge.cc',
+ 'resource_loader_bridge.h',
+ 'resource_type.h',
+ 'scoped_clipboard_writer_glue.h',
+ 'simple_webmimeregistry_impl.cc',
+ 'simple_webmimeregistry_impl.h',
+ 'site_isolation_metrics.cc',
+ 'site_isolation_metrics.h',
+ 'webaccessibility.cc',
+ 'webaccessibility.h',
+ 'webclipboard_impl.cc',
+ 'webclipboard_impl.h',
+ 'webcookie.h',
+ 'webcursor.cc',
+ 'webcursor.h',
+ 'webcursor_gtk.cc',
+ 'webcursor_gtk_data.h',
+ 'webcursor_mac.mm',
+ 'webcursor_win.cc',
+ 'webdropdata.cc',
+ 'webdropdata_win.cc',
+ 'webdropdata.h',
+ 'webfilesystem_impl.cc',
+ 'webfilesystem_impl.h',
+ 'webkit_glue.cc',
+ 'webkit_glue.h',
+ 'webkitclient_impl.cc',
+ 'webkitclient_impl.h',
+ 'webmediaplayer_impl.h',
+ 'webmediaplayer_impl.cc',
+ 'webmenuitem.h',
+ 'webmenurunner_mac.h',
+ 'webmenurunner_mac.mm',
+ 'webpasswordautocompletelistener_impl.cc',
+ 'webpasswordautocompletelistener_impl.h',
+ 'webpreferences.cc',
+ 'webpreferences.h',
+ 'websocketstreamhandle_bridge.h',
+ 'websocketstreamhandle_delegate.h',
+ 'websocketstreamhandle_impl.cc',
+ 'websocketstreamhandle_impl.h',
+ 'webthemeengine_impl_win.cc',
+ 'weburlloader_impl.cc',
+ 'weburlloader_impl.h',
+ 'window_open_disposition.h',
+ 'window_open_disposition.cc',
+
+ # These files used to be built in the webcore target, but moved here
+ # since part of glue.
+ '../extensions/v8/benchmarking_extension.cc',
+ '../extensions/v8/benchmarking_extension.h',
+ '../extensions/v8/gc_extension.cc',
+ '../extensions/v8/gc_extension.h',
+ '../extensions/v8/gears_extension.cc',
+ '../extensions/v8/gears_extension.h',
+ '../extensions/v8/heap_profiler_extension.cc',
+ '../extensions/v8/heap_profiler_extension.h',
+ '../extensions/v8/interval_extension.cc',
+ '../extensions/v8/interval_extension.h',
+ '../extensions/v8/playback_extension.cc',
+ '../extensions/v8/playback_extension.h',
+ '../extensions/v8/profiler_extension.cc',
+ '../extensions/v8/profiler_extension.h',
+
+ ],
+ # When glue is a dependency, it needs to be a hard dependency.
+ # Dependents may rely on files generated by this target or one of its
+ # own hard dependencies.
+ 'hard_dependency': 1,
+ 'conditions': [
+ ['OS=="linux" or OS=="freebsd" or OS=="openbsd" or OS=="solaris"', {
+ 'dependencies': [
+ '<(DEPTH)/build/linux/system.gyp:gtk',
+ ],
+ 'sources!': [
+ 'plugins/plugin_stubs.cc',
+ ],
+ }, { # else: OS!="linux" and OS!="freebsd" and OS!="openbsd" \
+ # and OS!="solaris"'
+ 'sources/': [['exclude', '_(linux|gtk)(_data)?\\.cc$'],
+ ['exclude', r'/gtk_']],
+ }],
+ ['OS!="mac"', {
+ 'sources/': [['exclude', '_mac\\.(cc|mm)$']],
+ }, { # else: OS=="mac"
+ 'sources/': [['exclude', 'plugin_(lib|list)_posix\\.cc$']],
+ 'link_settings': {
+ 'libraries': [
+ '$(SDKROOT)/QuartzCore.framework',
+ ],
+ },
+ }],
+ ['enable_gpu==1 and inside_chromium_build==1', {
+ 'dependencies': [
+ '<(DEPTH)/gpu/gpu.gyp:gpu_plugin',
+ ],
+ }],
+ ['OS!="win"', {
+ 'sources/': [['exclude', '_win\\.cc$']],
+ 'sources!': [
+ 'webthemeengine_impl_win.cc',
+ ],
+ }, { # else: OS=="win"
+ 'sources/': [['exclude', '_posix\\.cc$']],
+ 'include_dirs': [
+ '<(DEPTH)/third_party/wtl/include',
+ ],
+ 'dependencies': [
+ '<(DEPTH)/build/win/system.gyp:cygwin',
+ ],
+ 'sources!': [
+ 'plugins/plugin_stubs.cc',
+ ],
+ 'conditions': [
+ ['inside_chromium_build==1 and component=="shared_library"', {
+ 'dependencies': [
+ '<(DEPTH)/third_party/WebKit/WebKit/chromium/WebKit.gyp:webkit',
+ '<(DEPTH)/v8/tools/gyp/v8.gyp:v8',
+ ],
+ }],
+ ],
+ }],
+ ['inside_chromium_build==0', {
+ 'dependencies': [
+ '<(DEPTH)/webkit/support/setup_third_party.gyp:third_party_headers',
+ ],
+ }],
+ ],
+ },
+ ],
+}
diff --git a/webkit/glue/webkit_glue.h b/webkit/glue/webkit_glue.h
new file mode 100644
index 0000000..541836b
--- /dev/null
+++ b/webkit/glue/webkit_glue.h
@@ -0,0 +1,293 @@
+// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_WEBKIT_GLUE_H_
+#define WEBKIT_GLUE_WEBKIT_GLUE_H_
+
+#include "base/basictypes.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#endif
+
+#include <string>
+#include <vector>
+
+#include "app/clipboard/clipboard.h"
+#include "base/file_path.h"
+#include "base/string16.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebCanvas.h"
+
+class GURL;
+class SkBitmap;
+struct WebPluginInfo;
+
+namespace base {
+class StringPiece;
+}
+
+namespace skia {
+class PlatformCanvas;
+}
+
+namespace WebKit {
+class WebFrame;
+class WebString;
+class WebView;
+}
+
+namespace webkit_glue {
+
+
+//---- BEGIN FUNCTIONS IMPLEMENTED BY WEBKIT/GLUE -----------------------------
+
+void SetJavaScriptFlags(const std::wstring& flags);
+
+// Turn on the logging for notImplemented() calls from WebCore.
+void EnableWebCoreNotImplementedLogging();
+
+// Returns the text of the document element.
+std::wstring DumpDocumentText(WebKit::WebFrame* web_frame);
+
+// Returns the text of the document element and optionally its child frames.
+// If recursive is false, this is equivalent to DumpDocumentText followed by
+// a newline. If recursive is true, it recursively dumps all frames as text.
+std::wstring DumpFramesAsText(WebKit::WebFrame* web_frame, bool recursive);
+
+// Returns the renderer's description of its tree (its externalRepresentation).
+std::wstring DumpRenderer(WebKit::WebFrame* web_frame);
+
+// Fill the value of counter in the element specified by the id into
+// counter_value. Return false when the specified id doesn't exist.
+bool CounterValueForElementById(WebKit::WebFrame* web_frame,
+ const std::string& id,
+ std::wstring* counter_value);
+
+// Returns the number of page where the specified element will be put.
+int PageNumberForElementById(WebKit::WebFrame* web_frame,
+ const std::string& id,
+ float page_width_in_pixels,
+ float page_height_in_pixels);
+
+// Returns the number of pages to be printed.
+int NumberOfPages(WebKit::WebFrame* web_frame,
+ float page_width_in_pixels,
+ float page_height_in_pixels);
+
+// Returns a dump of the scroll position of the webframe.
+std::wstring DumpFrameScrollPosition(WebKit::WebFrame* web_frame,
+ bool recursive);
+
+// Returns a dump of the given history state suitable for implementing the
+// dumpBackForwardList command of the layoutTestController.
+std::wstring DumpHistoryState(const std::string& history_state, int indent,
+ bool is_current);
+
+// Cleans up state left over from the previous test run.
+void ResetBeforeTestRun(WebKit::WebView* view);
+
+// Returns the WebKit version (major.minor).
+std::string GetWebKitVersion();
+
+// Called to override the default user agent with a custom one. Call this
+// before anyone actually asks for the user agent in order to prevent
+// inconsistent behavior.
+void SetUserAgent(const std::string& new_user_agent);
+
+// Returns the user agent to use for the given URL, which is usually the
+// default user agent but may be overriden by a call to SetUserAgent() (which
+// should be done at startup).
+const std::string& GetUserAgent(const GURL& url);
+
+// Creates serialized state for the specified URL. This is a variant of
+// HistoryItemToString (in glue_serialize) that is used during session restore
+// if the saved state is empty.
+std::string CreateHistoryStateForURL(const GURL& url);
+
+// Removes any form data state from the history state string |content_state|.
+std::string RemoveFormDataFromHistoryState(const std::string& content_state);
+
+#ifndef NDEBUG
+// Checks various important objects to see if there are any in memory, and
+// calls AppendToLog with any leaked objects. Designed to be called on shutdown
+void CheckForLeaks();
+#endif
+
+// Decodes the image from the data in |image_data| into |image|.
+// Returns false if the image could not be decoded.
+bool DecodeImage(const std::string& image_data, SkBitmap* image);
+
+// Tells the plugin thread to terminate the process forcefully instead of
+// exiting cleanly.
+void SetForcefullyTerminatePluginProcess(bool value);
+
+// Returns true if the plugin thread should terminate the process forcefully
+// instead of exiting cleanly.
+bool ShouldForcefullyTerminatePluginProcess();
+
+// File path string conversions.
+FilePath::StringType WebStringToFilePathString(const WebKit::WebString& str);
+WebKit::WebString FilePathStringToWebString(const FilePath::StringType& str);
+FilePath WebStringToFilePath(const WebKit::WebString& str);
+WebKit::WebString FilePathToWebString(const FilePath& file_path);
+
+// Returns a WebCanvas pointer associated with the given Skia canvas.
+WebKit::WebCanvas* ToWebCanvas(skia::PlatformCanvas*);
+
+// Returns the number of currently-active glyph pages this process is using.
+// There can be many such pages (maps of 256 character -> glyph) so this is
+// used to get memory usage statistics.
+int GetGlyphPageCount();
+
+// Methods to query and enable media cache.
+// TODO(hclam): Remove these methods when the cache is stable enough.
+bool IsMediaCacheEnabled();
+void SetMediaCacheEnabled(bool enabled);
+
+//---- END FUNCTIONS IMPLEMENTED BY WEBKIT/GLUE -------------------------------
+
+
+//---- BEGIN FUNCTIONS IMPLEMENTED BY EMBEDDER --------------------------------
+
+// This function is called to request a prefetch of the entire URL, loading it
+// into our cache for (expected) future needs. The given URL may NOT be in
+// canonical form and it will NOT be null-terminated; use the length instead.
+void PrecacheUrl(const char16* url, int url_length);
+
+// This function is called to add a line to the application's log file.
+void AppendToLog(const char* filename, int line, const char* message);
+
+// Gather usage statistics from the in-memory cache and inform our host, if
+// applicable.
+void NotifyCacheStats();
+
+// Glue to get resources from the embedder.
+
+// Gets a localized string given a message id. Returns an empty string if the
+// message id is not found.
+string16 GetLocalizedString(int message_id);
+
+// Returns the raw data for a resource. This resource must have been
+// specified as BINDATA in the relevant .rc file.
+base::StringPiece GetDataResource(int resource_id);
+
+#if defined(OS_WIN)
+// Loads and returns a cursor.
+HCURSOR LoadCursor(int cursor_id);
+#endif
+
+// Glue to access the clipboard.
+
+// Get a clipboard that can be used to construct a ScopedClipboardWriterGlue.
+Clipboard* ClipboardGetClipboard();
+
+// Tests whether the clipboard contains a certain format
+bool ClipboardIsFormatAvailable(const Clipboard::FormatType& format,
+ Clipboard::Buffer buffer);
+
+// Reads UNICODE text from the clipboard, if available.
+void ClipboardReadText(Clipboard::Buffer buffer, string16* result);
+
+// Reads ASCII text from the clipboard, if available.
+void ClipboardReadAsciiText(Clipboard::Buffer buffer, std::string* result);
+
+// Reads HTML from the clipboard, if available.
+void ClipboardReadHTML(Clipboard::Buffer buffer, string16* markup, GURL* url);
+
+// Reads the available types from the clipboard, if available.
+bool ClipboardReadAvailableTypes(Clipboard::Buffer buffer,
+ std::vector<string16>* types,
+ bool* contains_filenames);
+
+// Reads one type of data from the clipboard, if available.
+bool ClipboardReadData(Clipboard::Buffer buffer, const string16& type,
+ string16* data, string16* metadata);
+
+// Reads filenames from the clipboard, if available.
+bool ClipboardReadFilenames(Clipboard::Buffer buffer,
+ std::vector<string16>* filenames);
+
+// Gets the directory where the application data and libraries exist. This
+// may be a versioned subdirectory, or it may be the same directory as the
+// GetExeDirectory(), depending on the embedder's implementation.
+// Path is an output parameter to receive the path.
+// Returns true if successful, false otherwise.
+bool GetApplicationDirectory(FilePath* path);
+
+// Gets the directory where the launching executable resides on disk.
+// Path is an output parameter to receive the path.
+// Returns true if successful, false otherwise.
+bool GetExeDirectory(FilePath* path);
+
+// Embedders implement this function to return the list of plugins to Webkit.
+void GetPlugins(bool refresh, std::vector<WebPluginInfo>* plugins);
+
+// Returns true if the plugins run in the same process as the renderer, and
+// false otherwise.
+bool IsPluginRunningInRendererProcess();
+
+// Returns a bool indicating if the Null plugin should be enabled or not.
+bool IsDefaultPluginEnabled();
+
+// Returns true if the protocol implemented to serve |url| supports features
+// required by the media engine.
+bool IsProtocolSupportedForMedia(const GURL& url);
+
+#if defined(OS_WIN)
+// Downloads the file specified by the URL. On sucess a WM_COPYDATA message
+// will be sent to the caller_window.
+bool DownloadUrl(const std::string& url, HWND caller_window);
+#endif
+
+// Returns the plugin finder URL.
+bool GetPluginFinderURL(std::string* plugin_finder_url);
+
+// Resolves the proxies for the url, returns true on success.
+bool FindProxyForUrl(const GURL& url, std::string* proxy_list);
+
+// Returns the locale that this instance of webkit is running as. This is of
+// the form language-country (e.g., en-US or pt-BR).
+std::wstring GetWebKitLocale();
+
+// Close current connections. Used for debugging.
+void CloseCurrentConnections();
+
+// Enable or disable the disk cache. Used for debugging.
+void SetCacheMode(bool enabled);
+
+// Clear the disk cache. Used for debugging.
+void ClearCache();
+
+// Returns the product version. E.g., Chrome/4.1.333.0
+std::string GetProductVersion();
+
+// Returns true if the embedder is running in single process mode.
+bool IsSingleProcess();
+
+#if defined(OS_LINUX)
+// Return a read-only file descriptor to the font which best matches the given
+// properties or -1 on failure.
+// charset: specifies the language(s) that the font must cover. See
+// render_sandbox_host_linux.cc for more information.
+int MatchFontWithFallback(const std::string& face, bool bold,
+ bool italic, int charset);
+
+// GetFontTable loads a specified font table from an open SFNT file.
+// fd: a file descriptor to the SFNT file. The position doesn't matter.
+// table: the table in *big-endian* format, or 0 for the whole font file.
+// output: a buffer of size output_length that gets the data. can be 0, in
+// which case output_length will be set to the required size in bytes.
+// output_length: size of output, if it's not 0.
+//
+// returns: true on success.
+bool GetFontTable(int fd, uint32_t table, uint8_t* output,
+ size_t* output_length);
+#endif
+
+// ---- END FUNCTIONS IMPLEMENTED BY EMBEDDER ---------------------------------
+
+
+} // namespace webkit_glue
+
+#endif // WEBKIT_GLUE_WEBKIT_GLUE_H_
diff --git a/webkit/glue/webkit_glue_dummy.cc b/webkit/glue/webkit_glue_dummy.cc
new file mode 100644
index 0000000..9947c3c
--- /dev/null
+++ b/webkit/glue/webkit_glue_dummy.cc
@@ -0,0 +1,20 @@
+// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/webkit_glue.h"
+
+
+//------------------------------------------------------------------------------
+// webkit_glue impl:
+
+namespace webkit_glue {
+
+// Global variable used by the plugin quirk "die after unload".
+bool g_forcefully_terminate_plugin_process = false;
+
+void SetUserAgent(const std::string& new_user_agent) {
+}
+
+
+} // namespace webkit_glue
diff --git a/webkit/glue/webkit_glue_unittest.cc b/webkit/glue/webkit_glue_unittest.cc
new file mode 100644
index 0000000..9ba56e3
--- /dev/null
+++ b/webkit/glue/webkit_glue_unittest.cc
@@ -0,0 +1,37 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "webkit/glue/webkit_glue.h"
+
+namespace {
+
+TEST(WebkitGlueTest, DecodeImageFail) {
+ std::string data("not an image");
+ SkBitmap image;
+ EXPECT_FALSE(webkit_glue::DecodeImage(data, &image));
+ EXPECT_TRUE(image.isNull());
+}
+
+TEST(WebkitGlueTest, DecodeImage) {
+ std::string data("GIF87a\x02\x00\x02\x00\xa1\x04\x00\x00\x00\x00\x00\x00\xff"
+ "\xff\x00\x00\x00\xff\x00,\x00\x00\x00\x00\x02\x00\x02\x00"
+ "\x00\x02\x03\x84\x16\x05\x00;", 42);
+ EXPECT_EQ(42u, data.size());
+ SkBitmap image;
+ EXPECT_TRUE(webkit_glue::DecodeImage(data, &image));
+ EXPECT_FALSE(image.isNull());
+ EXPECT_EQ(2, image.width());
+ EXPECT_EQ(2, image.height());
+ EXPECT_EQ(SkBitmap::kARGB_8888_Config, image.config());
+ image.lockPixels();
+ EXPECT_EQ(SK_ColorBLACK, *image.getAddr32(0, 0));
+ EXPECT_EQ(SK_ColorRED, *image.getAddr32(1, 0));
+ EXPECT_EQ(SK_ColorGREEN, *image.getAddr32(0, 1));
+ EXPECT_EQ(SK_ColorBLUE, *image.getAddr32(1, 1));
+ image.unlockPixels();
+}
+
+} // namespace
diff --git a/webkit/glue/webkit_resources.grd b/webkit/glue/webkit_resources.grd
new file mode 100644
index 0000000..a2146fd
--- /dev/null
+++ b/webkit/glue/webkit_resources.grd
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<grit latest_public_release="0" current_release="1">
+ <outputs>
+ <output filename="grit/webkit_resources.h" type="rc_header">
+ <emit emit_type='prepend'></emit>
+ </output>
+ <output filename="webkit_resources.pak" type="data_package" />
+ <output filename="webkit_resources.rc" type="rc_all" />
+ </outputs>
+ <release seq="1">
+ <includes>
+ <include name="IDC_ALIAS" file="resources\aliasb.cur" type="CURSOR" />
+ <include name="IDR_BROKENIMAGE" file="resources\broken-image.gif" type="BINDATA" />
+ <include name="IDC_CELL" file="resources\cell.cur" type="CURSOR" />
+ <include name="IDC_COLRESIZE" file="resources\col_resize.cur" type="CURSOR" />
+ <include name="IDC_COPYCUR" file="resources\copy.cur" type="CURSOR" />
+ <include name="IDR_MEDIA_PAUSE_BUTTON" file="resources\media_pause.png" type="BINDATA" />
+ <include name="IDR_MEDIA_PLAY_BUTTON" file="resources\media_play.png" type="BINDATA" />
+ <include name="IDR_MEDIA_PLAY_BUTTON_DISABLED" file="resources\media_play_disabled.png" type="BINDATA" />
+ <include name="IDR_MEDIA_SOUND_DISABLED" file="resources\media_sound_disabled.png" type="BINDATA" />
+ <include name="IDR_MEDIA_SOUND_FULL_BUTTON" file="resources\media_sound_full.png" type="BINDATA" />
+ <include name="IDR_MEDIA_SOUND_NONE_BUTTON" file="resources\media_sound_none.png" type="BINDATA" />
+ <include name="IDR_MEDIA_SLIDER_THUMB" file="resources\media_slider_thumb.png" type="BINDATA" />
+ <include name="IDR_MEDIA_VOLUME_SLIDER_THUMB" file="resources\media_volume_slider_thumb.png" type="BINDATA" />
+ <include name="IDC_PAN_EAST" file="resources\pan_east.cur" type="CURSOR" />
+ <include name="IDC_PAN_MIDDLE" file="resources\pan_middle.cur" type="CURSOR" />
+ <include name="IDC_PAN_NORTH" file="resources\pan_north.cur" type="CURSOR" />
+ <include name="IDC_PAN_NORTH_EAST" file="resources\pan_north_east.cur" type="CURSOR" />
+ <include name="IDC_PAN_NORTH_WEST" file="resources\pan_north_west.cur" type="CURSOR" />
+ <include name="IDR_PAN_SCROLL_ICON" file="resources\pan_icon.png" type="BINDATA" />
+ <include name="IDC_PAN_SOUTH" file="resources\pan_south.cur" type="CURSOR" />
+ <include name="IDC_PAN_SOUTH_EAST" file="resources\pan_south_east.cur" type="CURSOR" />
+ <include name="IDC_PAN_SOUTH_WEST" file="resources\pan_south_west.cur" type="CURSOR" />
+ <include name="IDC_PAN_WEST" file="resources\pan_west.cur" type="CURSOR" />
+ <include name="IDC_ROWRESIZE" file="resources\row_resize.cur" type="CURSOR" />
+ <include name="IDR_SEARCH_CANCEL" file="resources\search_cancel.png" type="BINDATA" />
+ <include name="IDR_SEARCH_CANCEL_PRESSED" file="resources\search_cancel_pressed.png" type="BINDATA" />
+ <include name="IDR_SEARCH_MAGNIFIER" file="resources\search_magnifier.png" type="BINDATA" />
+ <include name="IDR_SEARCH_MAGNIFIER_RESULTS" file="resources\search_magnifier_results.png" type="BINDATA" />
+ <include name="IDR_TEXTAREA_RESIZER" file="resources\textarea_resize_corner.png" type="BINDATA" />
+ <include name="IDR_TICKMARK_DASH" file="resources\dash.png" type="BINDATA" />
+ <include name="IDC_VERTICALTEXT" file="resources\vertical_text.cur" type="CURSOR" />
+ <include name="IDC_ZOOMIN" file="resources\zoom_in.cur" type="CURSOR" />
+ <include name="IDC_ZOOMOUT" file="resources\zoom_out.cur" type="CURSOR" />
+
+ <if expr="os == 'linux2' or os.find('bsd') != -1 or os == 'sunos5'">
+ <include name="IDR_LINUX_CHECKBOX_DISABLED_INDETERMINATE" file="resources\linux-checkbox-disabled-indeterminate.png" type="BINDATA" />
+ <include name="IDR_LINUX_CHECKBOX_DISABLED_OFF" file="resources\linux-checkbox-disabled-off.png" type="BINDATA" />
+ <include name="IDR_LINUX_CHECKBOX_DISABLED_ON" file="resources\linux-checkbox-disabled-on.png" type="BINDATA" />
+ <include name="IDR_LINUX_CHECKBOX_INDETERMINATE" file="resources\linux-checkbox-indeterminate.png" type="BINDATA" />
+ <include name="IDR_LINUX_CHECKBOX_OFF" file="resources\linux-checkbox-off.png" type="BINDATA" />
+ <include name="IDR_LINUX_CHECKBOX_ON" file="resources\linux-checkbox-on.png" type="BINDATA" />
+ <include name="IDR_LINUX_RADIO_DISABLED_OFF" file="resources\linux-radio-disabled-off.png" type="BINDATA" />
+ <include name="IDR_LINUX_RADIO_DISABLED_ON" file="resources\linux-radio-disabled-on.png" type="BINDATA" />
+ <include name="IDR_LINUX_RADIO_OFF" file="resources\linux-radio-off.png" type="BINDATA" />
+ <include name="IDR_LINUX_RADIO_ON" file="resources\linux-radio-on.png" type="BINDATA" />
+ <include name="IDR_PROGRESS_BAR" file="resources\linux-progress-bar.png" type="BINDATA" />
+ <include name="IDR_PROGRESS_BORDER_LEFT" file="resources\linux-progress-border-left.png" type="BINDATA" />
+ <include name="IDR_PROGRESS_BORDER_RIGHT" file="resources\linux-progress-border-right.png" type="BINDATA" />
+ <include name="IDR_PROGRESS_VALUE" file="resources\linux-progress-value.png" type="BINDATA" />
+ </if>
+ </includes>
+ </release>
+</grit>
diff --git a/webkit/glue/webkit_strings.grd b/webkit/glue/webkit_strings.grd
new file mode 100644
index 0000000..cbb1c7f
--- /dev/null
+++ b/webkit/glue/webkit_strings.grd
@@ -0,0 +1,364 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!-- This file contains definitions of resources that will be translated for
+each locale. Specifically, these are UI strings that are used by webkit that
+need to be translated for each locale.-->
+
+<!-- Some of these strings and string descriptions were taken from
+WebKit/win/WebCoreLocalizedStrings.cpp so we include the original license
+below:
+
+/*
+ * Copyright (C) 2007 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+-->
+
+<grit base_dir="." latest_public_release="0" current_release="1"
+ source_lang_id="en" enc_check="möl">
+ <outputs>
+ <!-- TODO add each of your output files. Modify the three below, and add
+ your own for your various languages. See the user's guide
+ (http://wiki/Main/GritUsersGuide) for more details.
+ Note that all output references are relative to the output directory
+ which is specified at build time. -->
+ <output filename="grit/webkit_strings.h" type="rc_header">
+ <emit emit_type='prepend'></emit>
+ </output>
+ <output filename="webkit_strings_am.rc" type="rc_all" lang="am" />
+ <output filename="webkit_strings_ar.rc" type="rc_all" lang="ar" />
+ <output filename="webkit_strings_bg.rc" type="rc_all" lang="bg" />
+ <output filename="webkit_strings_bn.rc" type="rc_all" lang="bn" />
+ <output filename="webkit_strings_ca.rc" type="rc_all" lang="ca" />
+ <output filename="webkit_strings_cs.rc" type="rc_all" lang="cs" />
+ <output filename="webkit_strings_da.rc" type="rc_all" lang="da" />
+ <output filename="webkit_strings_de.rc" type="rc_all" lang="de" />
+ <output filename="webkit_strings_el.rc" type="rc_all" lang="el" />
+ <output filename="webkit_strings_en-GB.rc" type="rc_all" lang="en-GB" />
+ <output filename="webkit_strings_en-US.rc" type="rc_all" lang="en" />
+ <output filename="webkit_strings_es.rc" type="rc_all" lang="es" />
+ <output filename="webkit_strings_es-419.rc" type="rc_all" lang="es-419" />
+ <output filename="webkit_strings_et.rc" type="rc_all" lang="et" />
+ <output filename="webkit_strings_fi.rc" type="rc_all" lang="fi" />
+ <output filename="webkit_strings_fil.rc" type="rc_all" lang="fil" />
+ <output filename="webkit_strings_fr.rc" type="rc_all" lang="fr" />
+ <output filename="webkit_strings_gu.rc" type="rc_all" lang="gu" />
+ <output filename="webkit_strings_he.rc" type="rc_all" lang="he" />
+ <output filename="webkit_strings_hi.rc" type="rc_all" lang="hi" />
+ <output filename="webkit_strings_hr.rc" type="rc_all" lang="hr" />
+ <output filename="webkit_strings_hu.rc" type="rc_all" lang="hu" />
+ <output filename="webkit_strings_id.rc" type="rc_all" lang="id" />
+ <output filename="webkit_strings_it.rc" type="rc_all" lang="it" />
+ <output filename="webkit_strings_ja.rc" type="rc_all" lang="ja" />
+ <output filename="webkit_strings_kn.rc" type="rc_all" lang="kn" />
+ <output filename="webkit_strings_ko.rc" type="rc_all" lang="ko" />
+ <output filename="webkit_strings_lt.rc" type="rc_all" lang="lt" />
+ <output filename="webkit_strings_lv.rc" type="rc_all" lang="lv" />
+ <output filename="webkit_strings_ml.rc" type="rc_all" lang="ml" />
+ <output filename="webkit_strings_mr.rc" type="rc_all" lang="mr" />
+ <output filename="webkit_strings_nl.rc" type="rc_all" lang="nl" />
+ <!-- The translation console uses 'no' for Norwegian Bokmål. It should
+ be 'nb'. -->
+ <output filename="webkit_strings_nb.rc" type="rc_all" lang="no" />
+ <output filename="webkit_strings_pl.rc" type="rc_all" lang="pl" />
+ <output filename="webkit_strings_pt-BR.rc" type="rc_all" lang="pt-BR" />
+ <output filename="webkit_strings_pt-PT.rc" type="rc_all" lang="pt-PT" />
+ <output filename="webkit_strings_ro.rc" type="rc_all" lang="ro" />
+ <output filename="webkit_strings_ru.rc" type="rc_all" lang="ru" />
+ <output filename="webkit_strings_sk.rc" type="rc_all" lang="sk" />
+ <output filename="webkit_strings_sl.rc" type="rc_all" lang="sl" />
+ <output filename="webkit_strings_sr.rc" type="rc_all" lang="sr" />
+ <output filename="webkit_strings_sv.rc" type="rc_all" lang="sv" />
+ <output filename="webkit_strings_sw.rc" type="rc_all" lang="sw" />
+ <output filename="webkit_strings_ta.rc" type="rc_all" lang="ta" />
+ <output filename="webkit_strings_te.rc" type="rc_all" lang="te" />
+ <output filename="webkit_strings_th.rc" type="rc_all" lang="th" />
+ <output filename="webkit_strings_tr.rc" type="rc_all" lang="tr" />
+ <output filename="webkit_strings_uk.rc" type="rc_all" lang="uk" />
+ <output filename="webkit_strings_vi.rc" type="rc_all" lang="vi" />
+ <output filename="webkit_strings_zh-CN.rc" type="rc_all" lang="zh-CN" />
+ <output filename="webkit_strings_zh-TW.rc" type="rc_all" lang="zh-TW" />
+
+ <output filename="webkit_strings_am.pak" type="data_package" lang="am" />
+ <output filename="webkit_strings_ar.pak" type="data_package" lang="ar" />
+ <output filename="webkit_strings_bg.pak" type="data_package" lang="bg" />
+ <output filename="webkit_strings_bn.pak" type="data_package" lang="bn" />
+ <output filename="webkit_strings_ca.pak" type="data_package" lang="ca" />
+ <output filename="webkit_strings_cs.pak" type="data_package" lang="cs" />
+ <output filename="webkit_strings_da.pak" type="data_package" lang="da" />
+ <output filename="webkit_strings_de.pak" type="data_package" lang="de" />
+ <output filename="webkit_strings_el.pak" type="data_package" lang="el" />
+ <output filename="webkit_strings_en-GB.pak" type="data_package" lang="en-GB" />
+ <output filename="webkit_strings_en-US.pak" type="data_package" lang="en" />
+ <output filename="webkit_strings_es.pak" type="data_package" lang="es" />
+ <output filename="webkit_strings_es-419.pak" type="data_package" lang="es-419" />
+ <output filename="webkit_strings_et.pak" type="data_package" lang="et" />
+ <output filename="webkit_strings_fi.pak" type="data_package" lang="fi" />
+ <output filename="webkit_strings_fil.pak" type="data_package" lang="fil" />
+ <output filename="webkit_strings_fr.pak" type="data_package" lang="fr" />
+ <output filename="webkit_strings_gu.pak" type="data_package" lang="gu" />
+ <output filename="webkit_strings_he.pak" type="data_package" lang="he" />
+ <output filename="webkit_strings_hi.pak" type="data_package" lang="hi" />
+ <output filename="webkit_strings_hr.pak" type="data_package" lang="hr" />
+ <output filename="webkit_strings_hu.pak" type="data_package" lang="hu" />
+ <output filename="webkit_strings_id.pak" type="data_package" lang="id" />
+ <output filename="webkit_strings_it.pak" type="data_package" lang="it" />
+ <output filename="webkit_strings_ja.pak" type="data_package" lang="ja" />
+ <output filename="webkit_strings_kn.pak" type="data_package" lang="kn" />
+ <output filename="webkit_strings_ko.pak" type="data_package" lang="ko" />
+ <output filename="webkit_strings_lt.pak" type="data_package" lang="lt" />
+ <output filename="webkit_strings_lv.pak" type="data_package" lang="lv" />
+ <output filename="webkit_strings_ml.pak" type="data_package" lang="ml" />
+ <output filename="webkit_strings_mr.pak" type="data_package" lang="mr" />
+ <output filename="webkit_strings_nl.pak" type="data_package" lang="nl" />
+ <!-- The translation console uses 'no' for Norwegian Bokmål. It should
+ be 'nb'. -->
+ <output filename="webkit_strings_nb.pak" type="data_package" lang="no" />
+ <output filename="webkit_strings_pl.pak" type="data_package" lang="pl" />
+ <output filename="webkit_strings_pt-BR.pak" type="data_package" lang="pt-BR" />
+ <output filename="webkit_strings_pt-PT.pak" type="data_package" lang="pt-PT" />
+ <output filename="webkit_strings_ro.pak" type="data_package" lang="ro" />
+ <output filename="webkit_strings_ru.pak" type="data_package" lang="ru" />
+ <output filename="webkit_strings_sk.pak" type="data_package" lang="sk" />
+ <output filename="webkit_strings_sl.pak" type="data_package" lang="sl" />
+ <output filename="webkit_strings_sr.pak" type="data_package" lang="sr" />
+ <output filename="webkit_strings_sv.pak" type="data_package" lang="sv" />
+ <output filename="webkit_strings_sw.pak" type="data_package" lang="sw" />
+ <output filename="webkit_strings_ta.pak" type="data_package" lang="ta" />
+ <output filename="webkit_strings_te.pak" type="data_package" lang="te" />
+ <output filename="webkit_strings_th.pak" type="data_package" lang="th" />
+ <output filename="webkit_strings_tr.pak" type="data_package" lang="tr" />
+ <output filename="webkit_strings_uk.pak" type="data_package" lang="uk" />
+ <output filename="webkit_strings_vi.pak" type="data_package" lang="vi" />
+ <output filename="webkit_strings_zh-CN.pak" type="data_package" lang="zh-CN" />
+ <output filename="webkit_strings_zh-TW.pak" type="data_package" lang="zh-TW" />
+ </outputs>
+ <translations>
+ <file path="resources/webkit_strings_am.xtb" lang="am" />
+ <file path="resources/webkit_strings_ar.xtb" lang="ar" />
+ <file path="resources/webkit_strings_bg.xtb" lang="bg" />
+ <file path="resources/webkit_strings_bn.xtb" lang="bn" />
+ <file path="resources/webkit_strings_ca.xtb" lang="ca" />
+ <file path="resources/webkit_strings_cs.xtb" lang="cs" />
+ <file path="resources/webkit_strings_da.xtb" lang="da" />
+ <file path="resources/webkit_strings_de.xtb" lang="de" />
+ <file path="resources/webkit_strings_el.xtb" lang="el" />
+ <file path="resources/webkit_strings_en-GB.xtb" lang="en-GB" />
+ <file path="resources/webkit_strings_es.xtb" lang="es" />
+ <file path="resources/webkit_strings_es-419.xtb" lang="es-419" />
+ <file path="resources/webkit_strings_et.xtb" lang="et" />
+ <file path="resources/webkit_strings_fi.xtb" lang="fi" />
+ <file path="resources/webkit_strings_fil.xtb" lang="fil" />
+ <file path="resources/webkit_strings_fr.xtb" lang="fr" />
+ <file path="resources/webkit_strings_gu.xtb" lang="gu" />
+ <file path="resources/webkit_strings_hi.xtb" lang="hi" />
+ <file path="resources/webkit_strings_hr.xtb" lang="hr" />
+ <file path="resources/webkit_strings_hu.xtb" lang="hu" />
+ <file path="resources/webkit_strings_id.xtb" lang="id" />
+ <file path="resources/webkit_strings_it.xtb" lang="it" />
+ <!-- The translation console uses 'iw' for Hebrew, but we use 'he'. -->
+ <file path="resources/webkit_strings_iw.xtb" lang="he" />
+ <file path="resources/webkit_strings_ja.xtb" lang="ja" />
+ <file path="resources/webkit_strings_kn.xtb" lang="kn" />
+ <file path="resources/webkit_strings_ko.xtb" lang="ko" />
+ <file path="resources/webkit_strings_lt.xtb" lang="lt" />
+ <file path="resources/webkit_strings_lv.xtb" lang="lv" />
+ <file path="resources/webkit_strings_ml.xtb" lang="ml" />
+ <file path="resources/webkit_strings_mr.xtb" lang="mr" />
+ <file path="resources/webkit_strings_nl.xtb" lang="nl" />
+ <file path="resources/webkit_strings_no.xtb" lang="no" />
+ <file path="resources/webkit_strings_pl.xtb" lang="pl" />
+ <file path="resources/webkit_strings_pt-BR.xtb" lang="pt-BR" />
+ <file path="resources/webkit_strings_pt-PT.xtb" lang="pt-PT" />
+ <file path="resources/webkit_strings_ro.xtb" lang="ro" />
+ <file path="resources/webkit_strings_ru.xtb" lang="ru" />
+ <file path="resources/webkit_strings_sk.xtb" lang="sk" />
+ <file path="resources/webkit_strings_sl.xtb" lang="sl" />
+ <file path="resources/webkit_strings_sr.xtb" lang="sr" />
+ <file path="resources/webkit_strings_sv.xtb" lang="sv" />
+ <file path="resources/webkit_strings_sw.xtb" lang="sw" />
+ <file path="resources/webkit_strings_ta.xtb" lang="ta" />
+ <file path="resources/webkit_strings_te.xtb" lang="te" />
+ <file path="resources/webkit_strings_th.xtb" lang="th" />
+ <file path="resources/webkit_strings_tr.xtb" lang="tr" />
+ <file path="resources/webkit_strings_uk.xtb" lang="uk" />
+ <file path="resources/webkit_strings_vi.xtb" lang="vi" />
+ <file path="resources/webkit_strings_zh-CN.xtb" lang="zh-CN" />
+ <file path="resources/webkit_strings_zh-TW.xtb" lang="zh-TW" />
+ </translations>
+ <release seq="1" allow_pseudo="false">
+ <messages fallback_to_english="true">
+ <!-- TODO add all of your "string table" messages here. Remember to
+ change nontranslateable parts of the messages into placeholders (using the
+ <ph> element). You can also use the 'grit add' tool to help you identify
+ nontranslateable parts and create placeholders for them. -->
+
+ <message name="IDS_SEARCHABLE_INDEX_INTRO" desc="Text that appears at the start of nearly-obsolete web pages in the form of a 'searchable index'.">
+ This is a searchable index. Enter search keywords: '''
+ </message>
+ <message name="IDS_FORM_SUBMIT_LABEL" desc="Default label for Submit buttons in forms on web pages.">
+ Submit
+ </message>
+ <message name="IDS_FORM_INPUT_ALT" desc="alt text for &lt;input&gt; elements with no alt, title, or value">
+ Submit
+ </message>
+ <message name="IDS_FORM_RESET_LABEL" desc="default label for Reset buttons in forms on web pages">
+ Reset
+ </message>
+ <message name="IDS_FORM_FILE_BUTTON_LABEL" desc="title for file button used in HTML forms">
+ Choose File
+ </message>
+ <message name="IDS_FORM_FILE_NO_FILE_LABEL" desc="text to display in file button used in HTML forms when no file is selected">
+ No file chosen
+ </message>
+ <message name="IDS_FORM_FILE_NO_FILE_DRAG_LABEL" desc="text to display in file button used in HTML forms when no file is selected to indicate that files can be dragged onto the file button">
+ Drag file here
+ </message>
+ <message name="IDS_FORM_FILE_MULTIPLE_UPLOAD" desc="text to display next to file buttons in HTML forms when 2 or more files are selected for uploading. This is not used for a case that just 1 file is selected.">
+ <ph name="NUMBER_OF_FILES">$1<ex>3</ex></ph> files
+ </message>
+
+ <message name="IDS_RECENT_SEARCHES_NONE" desc="Label for only item in menu that appears when clicking on the search field image, when no searches have been performed">
+ No recent searches
+ </message>
+ <message name="IDS_RECENT_SEARCHES" desc="label for first item in the menu that appears when clicking on the search field image, used as embedded menu title">
+ Recent Searches
+ </message>
+ <message name="IDS_RECENT_SEARCHES_CLEAR" desc="menu item in Recent Searches menu that empties menu's contents">
+ Clear Recent Searches
+ </message>
+
+ <message name="IDS_IMAGE_TITLE_FOR_FILENAME" desc="window title for a standalone image (uses mutiplication symbol, not x)">
+ <ph name="FILENAME">%s<ex>My Cool Image.gif</ex></ph><ph name="WIDTH">%d<ex>400</ex></ph>×<ph name="HEIGHT">%d<ex>600</ex></ph>
+ </message>
+
+ <message name="IDS_AX_ROLE_WEB_AREA" desc="accessibility role description for web area">
+ web area
+ </message>
+ <message name="IDS_AX_ROLE_LINK" desc="accessibility role description for link">
+ link
+ </message>
+ <message name="IDS_AX_ROLE_LIST_MARKER" desc="accessibility role description for list marker">
+ list marker
+ </message>
+ <message name="IDS_AX_ROLE_IMAGE_MAP" desc="accessibility role description for image map">
+ image map
+ </message>
+ <message name="IDS_AX_ROLE_HEADING" desc="accessibility role description for headings">
+ heading
+ </message>
+
+ <message name="IDS_AX_BUTTON_ACTION_VERB" desc="Verb stating the action that will occur when a button is pressed, as used by accessibility.">
+ press
+ </message>
+ <message name="IDS_AX_RADIO_BUTTON_ACTION_VERB" desc="Verb stating the action that will occur when a radio button is clicked, as used by accessibility.">
+ select
+ </message>
+ <message name="IDS_AX_TEXT_FIELD_ACTION_VERB" desc="Verb stating the action that will occur when a text field is selected, as used by accessibility.">
+ activate
+ </message>
+ <message name="IDS_AX_CHECKED_CHECK_BOX_ACTION_VERB" desc="Verb stating the action that will occur when a checked checkbox is clicked, as used by accessibility.">
+ uncheck
+ </message>
+ <message name="IDS_AX_UNCHECKED_CHECK_BOX_ACTION_VERB" desc="Verb stating the action that will occur when an unchecked checkbox is clicked, as used by accessibility.">
+ check
+ </message>
+ <message name="IDS_AX_LINK_ACTION_VERB" desc="Verb stating the action that will occur when a link is clicked, as used by accessibility.">
+ jump
+ </message>
+
+ <message name="IDS_KEYGEN_HIGH_GRADE_KEY" desc="High-grade cryptographic key size menu item">
+ 2048 (High Grade)
+ </message>
+ <message name="IDS_KEYGEN_MED_GRADE_KEY" desc="Medium-grade cryptographic key size menu item">
+ 1024 (Medium Grade)
+ </message>
+
+ <message name="IDS_DEFAULT_PLUGIN_GET_PLUGIN_MSG" desc="Message displayed by the default plugin in its main window">
+ <ph name="PLUGIN">$1<ex>Realplayer</ex></ph> plug-in is not installed
+ </message>
+
+ <message name="IDS_DEFAULT_PLUGIN_GET_PLUGIN_MSG_NO_PLUGIN_NAME" desc="Message displayed by the default plugin in its main window when we don't know the plugin name">
+ The required plug-in is not installed
+ </message>
+
+ <message name="IDS_DEFAULT_PLUGIN_GET_PLUGIN_MSG_2" desc="Second Message displayed by the default plugin in its main window">
+ Click here to download plug-in
+ </message>
+
+ <message name="IDS_DEFAULT_PLUGIN_REFRESH_PLUGIN_MSG" desc="Message displayed by the default plugin to refresh the window after installing the required plugin">
+ After installing the plug-in, click here to refresh
+ </message>
+
+ <message name="IDS_DEFAULT_PLUGIN_NO_PLUGIN_AVAILABLE_MSG" desc="Message displayed by the default plugin when no plugin was found for the page.">
+ No plug-in available to display this content
+ </message>
+
+ <message name="IDS_DEFAULT_PLUGIN_DOWNLOADING_PLUGIN_MSG" desc="Message displayed by the default plugin when a download has been initiated for the third party plugin.">
+ Downloading plug-in...
+ </message>
+
+ <message name="IDS_DEFAULT_PLUGIN_GET_THE_PLUGIN_BTN_MSG" desc="Message displayed by the default plugin on the button which the user should click to fetch the plugin.">
+ Get Plug-in
+ </message>
+
+ <message name="IDS_DEFAULT_PLUGIN_CANCEL_PLUGIN_DOWNLOAD_MSG" desc="Message displayed by the default plugin on the button which the user should click to cancel the plugin download.">
+ Cancel
+ </message>
+
+ <message name="IDS_DEFAULT_PLUGIN_CONFIRMATION_DIALOG_TITLE" desc="Default plugin confirmation dialog title.">
+ <ph name="PLUGIN">$1<ex>Realplayer</ex></ph> plug-in needed
+ </message>
+
+ <message name="IDS_DEFAULT_PLUGIN_CONFIRMATION_DIALOG_TITLE_NO_PLUGIN_NAME" desc="Default plugin confirmation dialog title when we don't know the plugin name">
+ Additional plug-in needed
+ </message>
+
+ <message name="IDS_DEFAULT_PLUGIN_USER_OPTION_MSG" desc="Message displayed by the default plugin in the install dialog indicating information on the user action.">
+ Please confirm that you would like to install the <ph name="PLUGIN">$1<ex>realplayer</ex></ph> plug-in. You should only install plug-ins that you trust.
+ </message>
+ <message name="IDS_DEFAULT_PLUGIN_USER_OPTION_MSG_NO_PLUGIN_NAME" desc="Message displayed by the default plugin in the install dialog indicating information on the user action when we don't know the plugin name">
+ Please confirm that you would like to install this plug-in. You should only install plug-ins that you trust.
+ </message>
+
+ <message name="IDS_DEFAULT_PLUGIN_USE_OPTION_CONFIRM" desc="Button to confirm installation of plugin">
+ Install
+ </message>
+ <message name="IDS_DEFAULT_PLUGIN_USE_OPTION_CANCEL" desc="Button to cancel installation of plugin">
+ Cancel
+ </message>
+
+ <message name="IDS_DEFAULT_PLUGIN_DOWNLOAD_FAILED_MSG" desc="Message displayed by the default plugin when download is failed.">
+ Failed to install plug-in from <ph name="URL">$1<ex>http://www.google.com/blablah.exe</ex></ph>
+ </message>
+
+ <message name="IDS_DEFAULT_PLUGIN_INSTALLATION_FAILED_MSG" desc="Message displayed by the default plugin when installation is failed.">
+ Plug-in installation failed
+ </message>
+
+ <message name="IDS_PDF_NEED_PASSWORD" desc="A message asking the user for a password to open a PDF file.">
+ This document is password protected. Please enter a password.
+ </message>
+ </messages>
+ </release>
+</grit>
diff --git a/webkit/glue/webkitclient_impl.cc b/webkit/glue/webkitclient_impl.cc
new file mode 100644
index 0000000..b05e7d9
--- /dev/null
+++ b/webkit/glue/webkitclient_impl.cc
@@ -0,0 +1,437 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/webkitclient_impl.h"
+
+#if defined(OS_LINUX)
+#include <malloc.h>
+#endif
+
+#include <math.h>
+
+#include <vector>
+
+#include "base/lock.h"
+#include "base/message_loop.h"
+#include "base/process_util.h"
+#include "base/platform_file.h"
+#include "base/singleton.h"
+#include "base/stats_counters.h"
+#include "base/time.h"
+#include "base/utf_string_conversions.h"
+#include "base/trace_event.h"
+#include "grit/webkit_resources.h"
+#include "grit/webkit_strings.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebCookie.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebData.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebFrameClient.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebPluginListBuilder.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebScreenInfo.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebString.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebVector.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURL.h"
+#include "webkit/glue/plugins/plugin_instance.h"
+#include "webkit/glue/plugins/webplugininfo.h"
+#include "webkit/glue/webkit_glue.h"
+#include "webkit/glue/websocketstreamhandle_impl.h"
+#include "webkit/glue/weburlloader_impl.h"
+
+#if defined(OS_LINUX)
+#include "v8/include/v8.h"
+#endif
+
+using WebKit::WebCookie;
+using WebKit::WebData;
+using WebKit::WebLocalizedString;
+using WebKit::WebPluginListBuilder;
+using WebKit::WebString;
+using WebKit::WebSocketStreamHandle;
+using WebKit::WebThemeEngine;
+using WebKit::WebURL;
+using WebKit::WebURLLoader;
+using WebKit::WebVector;
+
+namespace {
+
+// A simple class to cache the memory usage for a given amount of time.
+class MemoryUsageCache {
+ public:
+ // Retrieves the Singleton.
+ static MemoryUsageCache* Get() {
+ return Singleton<MemoryUsageCache>::get();
+ }
+
+ MemoryUsageCache() : memory_value_(0) { Init(); }
+ ~MemoryUsageCache() {}
+
+ void Init() {
+ const unsigned int kCacheSeconds = 1;
+ cache_valid_time_ = base::TimeDelta::FromSeconds(kCacheSeconds);
+ }
+
+ // Returns true if the cached value is fresh.
+ // Returns false if the cached value is stale, or if |cached_value| is NULL.
+ bool IsCachedValueValid(size_t* cached_value) {
+ AutoLock scoped_lock(lock_);
+ if (!cached_value)
+ return false;
+ if (base::Time::Now() - last_updated_time_ > cache_valid_time_)
+ return false;
+ *cached_value = memory_value_;
+ return true;
+ };
+
+ // Setter for |memory_value_|, refreshes |last_updated_time_|.
+ void SetMemoryValue(const size_t value) {
+ AutoLock scoped_lock(lock_);
+ memory_value_ = value;
+ last_updated_time_ = base::Time::Now();
+ }
+
+ private:
+ // The cached memory value.
+ size_t memory_value_;
+
+ // How long the cached value should remain valid.
+ base::TimeDelta cache_valid_time_;
+
+ // The last time the cached value was updated.
+ base::Time last_updated_time_;
+
+ Lock lock_;
+};
+
+} // anonymous namespace
+
+namespace webkit_glue {
+
+static int ToMessageID(WebLocalizedString::Name name) {
+ switch (name) {
+ case WebLocalizedString::SubmitButtonDefaultLabel:
+ return IDS_FORM_SUBMIT_LABEL;
+ case WebLocalizedString::InputElementAltText:
+ return IDS_FORM_INPUT_ALT;
+ case WebLocalizedString::ResetButtonDefaultLabel:
+ return IDS_FORM_RESET_LABEL;
+ case WebLocalizedString::FileButtonChooseFileLabel:
+ return IDS_FORM_FILE_BUTTON_LABEL;
+ case WebLocalizedString::FileButtonNoFileSelectedLabel:
+ return IDS_FORM_FILE_NO_FILE_LABEL;
+ case WebLocalizedString::MultipleFileUploadText:
+ return IDS_FORM_FILE_MULTIPLE_UPLOAD;
+ case WebLocalizedString::SearchableIndexIntroduction:
+ return IDS_SEARCHABLE_INDEX_INTRO;
+ case WebLocalizedString::SearchMenuNoRecentSearchesText:
+ return IDS_RECENT_SEARCHES_NONE;
+ case WebLocalizedString::SearchMenuRecentSearchesText:
+ return IDS_RECENT_SEARCHES;
+ case WebLocalizedString::SearchMenuClearRecentSearchesText:
+ return IDS_RECENT_SEARCHES_CLEAR;
+ case WebLocalizedString::AXWebAreaText:
+ return IDS_AX_ROLE_WEB_AREA;
+ case WebLocalizedString::AXLinkText:
+ return IDS_AX_ROLE_LINK;
+ case WebLocalizedString::AXListMarkerText:
+ return IDS_AX_ROLE_LIST_MARKER;
+ case WebLocalizedString::AXImageMapText:
+ return IDS_AX_ROLE_IMAGE_MAP;
+ case WebLocalizedString::AXHeadingText:
+ return IDS_AX_ROLE_HEADING;
+ case WebLocalizedString::AXButtonActionVerb:
+ return IDS_AX_BUTTON_ACTION_VERB;
+ case WebLocalizedString::AXRadioButtonActionVerb:
+ return IDS_AX_RADIO_BUTTON_ACTION_VERB;
+ case WebLocalizedString::AXTextFieldActionVerb:
+ return IDS_AX_TEXT_FIELD_ACTION_VERB;
+ case WebLocalizedString::AXCheckedCheckBoxActionVerb:
+ return IDS_AX_CHECKED_CHECK_BOX_ACTION_VERB;
+ case WebLocalizedString::AXUncheckedCheckBoxActionVerb:
+ return IDS_AX_UNCHECKED_CHECK_BOX_ACTION_VERB;
+ case WebLocalizedString::AXLinkActionVerb:
+ return IDS_AX_LINK_ACTION_VERB;
+ case WebLocalizedString::KeygenMenuHighGradeKeySize:
+ return IDS_KEYGEN_HIGH_GRADE_KEY;
+ case WebLocalizedString::KeygenMenuMediumGradeKeySize:
+ return IDS_KEYGEN_MED_GRADE_KEY;
+ }
+ return -1;
+}
+
+WebKitClientImpl::WebKitClientImpl()
+ : main_loop_(MessageLoop::current()),
+ shared_timer_func_(NULL),
+ shared_timer_fire_time_(0.0),
+ shared_timer_suspended_(0) {
+}
+
+WebThemeEngine* WebKitClientImpl::themeEngine() {
+#if defined(OS_WIN)
+ return &theme_engine_;
+#else
+ return NULL;
+#endif
+}
+
+WebURLLoader* WebKitClientImpl::createURLLoader() {
+ return new WebURLLoaderImpl();
+}
+
+WebSocketStreamHandle* WebKitClientImpl::createSocketStreamHandle() {
+ return new WebSocketStreamHandleImpl();
+}
+
+WebString WebKitClientImpl::userAgent(const WebURL& url) {
+ return WebString::fromUTF8(webkit_glue::GetUserAgent(url));
+}
+
+void WebKitClientImpl::getPluginList(bool refresh,
+ WebPluginListBuilder* builder) {
+ std::vector<WebPluginInfo> plugins;
+ GetPlugins(refresh, &plugins);
+
+ for (size_t i = 0; i < plugins.size(); ++i) {
+ const WebPluginInfo& plugin = plugins[i];
+
+ builder->addPlugin(
+ plugin.name, plugin.desc,
+ FilePathStringToWebString(plugin.path.BaseName().value()));
+
+ for (size_t j = 0; j < plugin.mime_types.size(); ++j) {
+ const WebPluginMimeType& mime_type = plugin.mime_types[j];
+
+ builder->addMediaTypeToLastPlugin(
+ WebString::fromUTF8(mime_type.mime_type), mime_type.description);
+
+ for (size_t k = 0; k < mime_type.file_extensions.size(); ++k) {
+ builder->addFileExtensionToLastMediaType(
+ UTF8ToUTF16(mime_type.file_extensions[k]));
+ }
+ }
+ }
+}
+
+void WebKitClientImpl::decrementStatsCounter(const char* name) {
+ StatsCounter(name).Decrement();
+}
+
+void WebKitClientImpl::incrementStatsCounter(const char* name) {
+ StatsCounter(name).Increment();
+}
+
+void WebKitClientImpl::traceEventBegin(const char* name, void* id,
+ const char* extra) {
+ TRACE_EVENT_BEGIN(name, id, extra);
+}
+
+void WebKitClientImpl::traceEventEnd(const char* name, void* id,
+ const char* extra) {
+ TRACE_EVENT_END(name, id, extra);
+}
+
+WebData WebKitClientImpl::loadResource(const char* name) {
+ struct {
+ const char* name;
+ int id;
+ } resources[] = {
+ { "missingImage", IDR_BROKENIMAGE },
+ { "mediaPause", IDR_MEDIA_PAUSE_BUTTON },
+ { "mediaPlay", IDR_MEDIA_PLAY_BUTTON },
+ { "mediaPlayDisabled", IDR_MEDIA_PLAY_BUTTON_DISABLED },
+ { "mediaSoundDisabled", IDR_MEDIA_SOUND_DISABLED },
+ { "mediaSoundFull", IDR_MEDIA_SOUND_FULL_BUTTON },
+ { "mediaSoundNone", IDR_MEDIA_SOUND_NONE_BUTTON },
+ { "mediaSliderThumb", IDR_MEDIA_SLIDER_THUMB },
+ { "mediaVolumeSliderThumb", IDR_MEDIA_VOLUME_SLIDER_THUMB },
+ { "panIcon", IDR_PAN_SCROLL_ICON },
+ { "searchCancel", IDR_SEARCH_CANCEL },
+ { "searchCancelPressed", IDR_SEARCH_CANCEL_PRESSED },
+ { "searchMagnifier", IDR_SEARCH_MAGNIFIER },
+ { "searchMagnifierResults", IDR_SEARCH_MAGNIFIER_RESULTS },
+ { "textAreaResizeCorner", IDR_TEXTAREA_RESIZER },
+ { "tickmarkDash", IDR_TICKMARK_DASH },
+#if defined(OS_POSIX) && !defined(OS_MACOSX)
+ // TODO(port): rename these to "skia" instead of "Linux".
+ { "linuxCheckboxDisabledIndeterminate",
+ IDR_LINUX_CHECKBOX_DISABLED_INDETERMINATE },
+ { "linuxCheckboxDisabledOff", IDR_LINUX_CHECKBOX_DISABLED_OFF },
+ { "linuxCheckboxDisabledOn", IDR_LINUX_CHECKBOX_DISABLED_ON },
+ { "linuxCheckboxIndeterminate", IDR_LINUX_CHECKBOX_INDETERMINATE },
+ { "linuxCheckboxOff", IDR_LINUX_CHECKBOX_OFF },
+ { "linuxCheckboxOn", IDR_LINUX_CHECKBOX_ON },
+ { "linuxRadioDisabledOff", IDR_LINUX_RADIO_DISABLED_OFF },
+ { "linuxRadioDisabledOn", IDR_LINUX_RADIO_DISABLED_ON },
+ { "linuxRadioOff", IDR_LINUX_RADIO_OFF },
+ { "linuxRadioOn", IDR_LINUX_RADIO_ON },
+ { "linuxProgressBar", IDR_PROGRESS_BAR },
+ { "linuxProgressBorderLeft", IDR_PROGRESS_BORDER_LEFT },
+ { "linuxProgressBorderRight", IDR_PROGRESS_BORDER_RIGHT },
+ { "linuxProgressValue", IDR_PROGRESS_VALUE },
+#endif
+ };
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(resources); ++i) {
+ if (!strcmp(name, resources[i].name)) {
+ base::StringPiece resource = GetDataResource(resources[i].id);
+ return WebData(resource.data(), resource.size());
+ }
+ }
+ NOTREACHED() << "Unknown image resource " << name;
+ return WebData();
+}
+
+WebString WebKitClientImpl::queryLocalizedString(
+ WebLocalizedString::Name name) {
+ int message_id = ToMessageID(name);
+ if (message_id < 0)
+ return WebString();
+ return GetLocalizedString(message_id);
+}
+
+WebString WebKitClientImpl::queryLocalizedString(
+ WebLocalizedString::Name name, int numeric_value) {
+ int message_id = ToMessageID(name);
+ if (message_id < 0)
+ return WebString();
+ return ReplaceStringPlaceholders(GetLocalizedString(message_id),
+ IntToString16(numeric_value),
+ NULL);
+}
+
+double WebKitClientImpl::currentTime() {
+ return base::Time::Now().ToDoubleT();
+}
+
+void WebKitClientImpl::setSharedTimerFiredFunction(void (*func)()) {
+ shared_timer_func_ = func;
+}
+
+void WebKitClientImpl::setSharedTimerFireTime(double fire_time) {
+ shared_timer_fire_time_ = fire_time;
+ if (shared_timer_suspended_)
+ return;
+
+ // By converting between double and int64 representation, we run the risk
+ // of losing precision due to rounding errors. Performing computations in
+ // microseconds reduces this risk somewhat. But there still is the potential
+ // of us computing a fire time for the timer that is shorter than what we
+ // need.
+ // As the event loop will check event deadlines prior to actually firing
+ // them, there is a risk of needlessly rescheduling events and of
+ // needlessly looping if sleep times are too short even by small amounts.
+ // This results in measurable performance degradation unless we use ceil() to
+ // always round up the sleep times.
+ int64 interval = static_cast<int64>(
+ ceil((fire_time - currentTime()) * base::Time::kMicrosecondsPerSecond));
+ if (interval < 0)
+ interval = 0;
+
+ shared_timer_.Stop();
+ shared_timer_.Start(base::TimeDelta::FromMicroseconds(interval), this,
+ &WebKitClientImpl::DoTimeout);
+}
+
+void WebKitClientImpl::stopSharedTimer() {
+ shared_timer_.Stop();
+}
+
+void WebKitClientImpl::callOnMainThread(void (*func)(void*), void* context) {
+ main_loop_->PostTask(FROM_HERE, NewRunnableFunction(func, context));
+}
+
+base::PlatformFile WebKitClientImpl::databaseOpenFile(
+ const WebKit::WebString& vfs_file_name, int desired_flags) {
+ return base::kInvalidPlatformFileValue;
+}
+
+int WebKitClientImpl::databaseDeleteFile(
+ const WebKit::WebString& vfs_file_name, bool sync_dir) {
+ return -1;
+}
+
+long WebKitClientImpl::databaseGetFileAttributes(
+ const WebKit::WebString& vfs_file_name) {
+ return 0;
+}
+
+long long WebKitClientImpl::databaseGetFileSize(
+ const WebKit::WebString& vfs_file_name) {
+ return 0;
+}
+
+WebKit::WebString WebKitClientImpl::signedPublicKeyAndChallengeString(
+ unsigned key_size_index,
+ const WebKit::WebString& challenge,
+ const WebKit::WebURL& url) {
+ NOTREACHED();
+ return WebKit::WebString();
+}
+
+#if defined(OS_LINUX)
+static size_t memoryUsageMBLinux() {
+ struct mallinfo minfo = mallinfo();
+ uint64_t mem_usage =
+#if defined(USE_TCMALLOC)
+ minfo.uordblks
+#else
+ (minfo.hblkhd + minfo.arena)
+#endif
+ >> 20;
+
+ v8::HeapStatistics stat;
+ v8::V8::GetHeapStatistics(&stat);
+ return mem_usage + (static_cast<uint64_t>(stat.total_heap_size()) >> 20);
+}
+#endif
+
+#if defined(OS_MACOSX)
+static size_t memoryUsageMBMac() {
+ using base::ProcessMetrics;
+ static ProcessMetrics* process_metrics =
+ // The default port provider is sufficient to get data for the current
+ // process.
+ ProcessMetrics::CreateProcessMetrics(base::GetCurrentProcessHandle(),
+ NULL);
+ DCHECK(process_metrics);
+ return process_metrics->GetPagefileUsage() >> 20;
+}
+#endif
+
+#if !defined(OS_LINUX) && !defined(OS_MACOSX)
+static size_t memoryUsageMBGeneric() {
+ using base::ProcessMetrics;
+ static ProcessMetrics* process_metrics =
+ ProcessMetrics::CreateProcessMetrics(base::GetCurrentProcessHandle());
+ DCHECK(process_metrics);
+ return process_metrics->GetPagefileUsage() >> 20;
+}
+#endif
+
+size_t WebKitClientImpl::memoryUsageMB() {
+ size_t current_mem_usage = 0;
+ MemoryUsageCache* mem_usage_cache_singleton = MemoryUsageCache::Get();
+ if (mem_usage_cache_singleton->IsCachedValueValid(&current_mem_usage))
+ return current_mem_usage;
+
+ current_mem_usage =
+#if defined(OS_LINUX)
+ memoryUsageMBLinux();
+#elif defined(OS_MACOSX)
+ memoryUsageMBMac();
+#else
+ memoryUsageMBGeneric();
+#endif
+ mem_usage_cache_singleton->SetMemoryValue(current_mem_usage);
+ return current_mem_usage;
+}
+
+void WebKitClientImpl::SuspendSharedTimer() {
+ ++shared_timer_suspended_;
+}
+
+void WebKitClientImpl::ResumeSharedTimer() {
+ // The shared timer may have fired or been adjusted while we were suspended.
+ if (--shared_timer_suspended_ == 0 && !shared_timer_.IsRunning())
+ setSharedTimerFireTime(shared_timer_fire_time_);
+}
+
+} // namespace webkit_glue
diff --git a/webkit/glue/webkitclient_impl.h b/webkit/glue/webkitclient_impl.h
new file mode 100644
index 0000000..bec7a79
--- /dev/null
+++ b/webkit/glue/webkitclient_impl.h
@@ -0,0 +1,80 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_CLIENT_IMPL_H_
+#define WEBKIT_CLIENT_IMPL_H_
+
+#include "base/platform_file.h"
+#include "base/timer.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebKitClient.h"
+#if defined(OS_WIN)
+#include "webkit/glue/webthemeengine_impl_win.h"
+#endif
+
+class MessageLoop;
+
+namespace webkit_glue {
+
+class WebKitClientImpl : public WebKit::WebKitClient {
+ public:
+ WebKitClientImpl();
+ virtual ~WebKitClientImpl() {}
+
+ // WebKitClient methods (partial implementation):
+ virtual WebKit::WebThemeEngine* themeEngine();
+
+ virtual base::PlatformFile databaseOpenFile(
+ const WebKit::WebString& vfs_file_name, int desired_flags);
+ virtual int databaseDeleteFile(const WebKit::WebString& vfs_file_name,
+ bool sync_dir);
+ virtual long databaseGetFileAttributes(
+ const WebKit::WebString& vfs_file_name);
+ virtual long long databaseGetFileSize(const WebKit::WebString& vfs_file_name);
+ virtual WebKit::WebString signedPublicKeyAndChallengeString(
+ unsigned key_size_index, const WebKit::WebString& challenge,
+ const WebKit::WebURL& url);
+ virtual size_t memoryUsageMB();
+ virtual WebKit::WebURLLoader* createURLLoader();
+ virtual WebKit::WebSocketStreamHandle* createSocketStreamHandle();
+ virtual WebKit::WebString userAgent(const WebKit::WebURL& url);
+ virtual void getPluginList(bool refresh, WebKit::WebPluginListBuilder*);
+ virtual void decrementStatsCounter(const char* name);
+ virtual void incrementStatsCounter(const char* name);
+ virtual void traceEventBegin(const char* name, void* id, const char* extra);
+ virtual void traceEventEnd(const char* name, void* id, const char* extra);
+ virtual WebKit::WebData loadResource(const char* name);
+ virtual WebKit::WebString queryLocalizedString(
+ WebKit::WebLocalizedString::Name name);
+ virtual WebKit::WebString queryLocalizedString(
+ WebKit::WebLocalizedString::Name name, int numeric_value);
+ virtual void suddenTerminationChanged(bool enabled) { }
+ virtual double currentTime();
+ virtual void setSharedTimerFiredFunction(void (*func)());
+ virtual void setSharedTimerFireTime(double fireTime);
+ virtual void stopSharedTimer();
+ virtual void callOnMainThread(void (*func)(void*), void* context);
+
+ void SuspendSharedTimer();
+ void ResumeSharedTimer();
+
+ private:
+ void DoTimeout() {
+ if (shared_timer_func_ && !shared_timer_suspended_)
+ shared_timer_func_();
+ }
+
+ MessageLoop* main_loop_;
+ base::OneShotTimer<WebKitClientImpl> shared_timer_;
+ void (*shared_timer_func_)();
+ double shared_timer_fire_time_;
+ int shared_timer_suspended_; // counter
+
+#if defined(OS_WIN)
+ WebThemeEngineImpl theme_engine_;
+#endif
+};
+
+} // namespace webkit_glue
+
+#endif // WEBKIT_CLIENT_IMPL_H_
diff --git a/webkit/glue/webmediaplayer_impl.cc b/webkit/glue/webmediaplayer_impl.cc
new file mode 100644
index 0000000..51d67ab
--- /dev/null
+++ b/webkit/glue/webmediaplayer_impl.cc
@@ -0,0 +1,711 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/webmediaplayer_impl.h"
+
+#include "base/callback.h"
+#include "base/command_line.h"
+#include "media/base/limits.h"
+#include "media/base/media_format.h"
+#include "media/base/media_switches.h"
+#include "media/filters/ffmpeg_audio_decoder.h"
+#include "media/filters/ffmpeg_demuxer.h"
+#include "media/filters/ffmpeg_video_decoder.h"
+#include "media/filters/null_audio_renderer.h"
+#include "skia/ext/platform_canvas.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebRect.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebSize.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURL.h"
+#include "webkit/glue/media/video_renderer_impl.h"
+#include "webkit/glue/media/web_video_renderer.h"
+
+using WebKit::WebCanvas;
+using WebKit::WebRect;
+using WebKit::WebSize;
+
+namespace {
+
+// Limits the maximum outstanding repaints posted on render thread.
+// This number of 50 is a guess, it does not take too much memory on the task
+// queue but gives up a pretty good latency on repaint.
+const int kMaxOutstandingRepaints = 50;
+
+// Limits the range of playback rate.
+//
+// TODO(kylep): Revisit these.
+//
+// Vista has substantially lower performance than XP or Windows7. If you speed
+// up a video too much, it can't keep up, and rendering stops updating except on
+// the time bar. For really high speeds, audio becomes a bottleneck and we just
+// use up the data we have, which may not achieve the speed requested, but will
+// not crash the tab.
+//
+// A very slow speed, ie 0.00000001x, causes the machine to lock up. (It seems
+// like a busy loop). It gets unresponsive, although its not completely dead.
+//
+// Also our timers are not very accurate (especially for ogg), which becomes
+// evident at low speeds and on Vista. Since other speeds are risky and outside
+// the norms, we think 1/16x to 16x is a safe and useful range for now.
+const float kMinRate = 0.0625f;
+const float kMaxRate = 16.0f;
+
+} // namespace
+
+namespace webkit_glue {
+
+/////////////////////////////////////////////////////////////////////////////
+// WebMediaPlayerImpl::Proxy implementation
+
+WebMediaPlayerImpl::Proxy::Proxy(MessageLoop* render_loop,
+ WebMediaPlayerImpl* webmediaplayer)
+ : render_loop_(render_loop),
+ webmediaplayer_(webmediaplayer),
+ outstanding_repaints_(0) {
+ DCHECK(render_loop_);
+ DCHECK(webmediaplayer_);
+}
+
+WebMediaPlayerImpl::Proxy::~Proxy() {
+ Detach();
+}
+
+void WebMediaPlayerImpl::Proxy::Repaint() {
+ AutoLock auto_lock(lock_);
+ if (outstanding_repaints_ < kMaxOutstandingRepaints) {
+ ++outstanding_repaints_;
+
+ render_loop_->PostTask(FROM_HERE,
+ NewRunnableMethod(this, &WebMediaPlayerImpl::Proxy::RepaintTask));
+ }
+}
+
+void WebMediaPlayerImpl::Proxy::SetVideoRenderer(
+ WebVideoRenderer* video_renderer) {
+ video_renderer_ = video_renderer;
+}
+
+void WebMediaPlayerImpl::Proxy::Paint(skia::PlatformCanvas* canvas,
+ const gfx::Rect& dest_rect) {
+ DCHECK(MessageLoop::current() == render_loop_);
+ if (video_renderer_) {
+ video_renderer_->Paint(canvas, dest_rect);
+ }
+}
+
+void WebMediaPlayerImpl::Proxy::SetSize(const gfx::Rect& rect) {
+ DCHECK(MessageLoop::current() == render_loop_);
+ if (video_renderer_) {
+ video_renderer_->SetRect(rect);
+ }
+}
+
+void WebMediaPlayerImpl::Proxy::Detach() {
+ DCHECK(MessageLoop::current() == render_loop_);
+ webmediaplayer_ = NULL;
+ video_renderer_ = NULL;
+}
+
+void WebMediaPlayerImpl::Proxy::PipelineInitializationCallback() {
+ render_loop_->PostTask(FROM_HERE, NewRunnableMethod(this,
+ &WebMediaPlayerImpl::Proxy::PipelineInitializationTask));
+}
+
+void WebMediaPlayerImpl::Proxy::PipelineSeekCallback() {
+ render_loop_->PostTask(FROM_HERE, NewRunnableMethod(this,
+ &WebMediaPlayerImpl::Proxy::PipelineSeekTask));
+}
+
+void WebMediaPlayerImpl::Proxy::PipelineEndedCallback() {
+ render_loop_->PostTask(FROM_HERE, NewRunnableMethod(this,
+ &WebMediaPlayerImpl::Proxy::PipelineEndedTask));
+}
+
+void WebMediaPlayerImpl::Proxy::PipelineErrorCallback() {
+ render_loop_->PostTask(FROM_HERE, NewRunnableMethod(this,
+ &WebMediaPlayerImpl::Proxy::PipelineErrorTask));
+}
+
+void WebMediaPlayerImpl::Proxy::NetworkEventCallback() {
+ render_loop_->PostTask(FROM_HERE, NewRunnableMethod(this,
+ &WebMediaPlayerImpl::Proxy::NetworkEventTask));
+}
+
+void WebMediaPlayerImpl::Proxy::RepaintTask() {
+ DCHECK(MessageLoop::current() == render_loop_);
+ {
+ AutoLock auto_lock(lock_);
+ --outstanding_repaints_;
+ DCHECK_GE(outstanding_repaints_, 0);
+ }
+ if (webmediaplayer_) {
+ webmediaplayer_->Repaint();
+ }
+}
+
+void WebMediaPlayerImpl::Proxy::PipelineInitializationTask() {
+ DCHECK(MessageLoop::current() == render_loop_);
+ if (webmediaplayer_) {
+ webmediaplayer_->OnPipelineInitialize();
+ }
+}
+
+void WebMediaPlayerImpl::Proxy::PipelineSeekTask() {
+ DCHECK(MessageLoop::current() == render_loop_);
+ if (webmediaplayer_) {
+ webmediaplayer_->OnPipelineSeek();
+ }
+}
+
+void WebMediaPlayerImpl::Proxy::PipelineEndedTask() {
+ DCHECK(MessageLoop::current() == render_loop_);
+ if (webmediaplayer_) {
+ webmediaplayer_->OnPipelineEnded();
+ }
+}
+
+void WebMediaPlayerImpl::Proxy::PipelineErrorTask() {
+ DCHECK(MessageLoop::current() == render_loop_);
+ if (webmediaplayer_) {
+ webmediaplayer_->OnPipelineError();
+ }
+}
+
+void WebMediaPlayerImpl::Proxy::NetworkEventTask() {
+ DCHECK(MessageLoop::current() == render_loop_);
+ if (webmediaplayer_) {
+ webmediaplayer_->OnNetworkEvent();
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// WebMediaPlayerImpl implementation
+
+WebMediaPlayerImpl::WebMediaPlayerImpl(WebKit::WebMediaPlayerClient* client,
+ media::FilterFactoryCollection* factory,
+ WebVideoRendererFactoryFactory*
+ video_renderer_factory)
+ : network_state_(WebKit::WebMediaPlayer::Empty),
+ ready_state_(WebKit::WebMediaPlayer::HaveNothing),
+ main_loop_(NULL),
+ filter_factory_(factory),
+ pipeline_thread_("PipelineThread"),
+ paused_(true),
+ playback_rate_(0.0f),
+ client_(client),
+ pipeline_stopped_(false, false) {
+ // Saves the current message loop.
+ DCHECK(!main_loop_);
+ main_loop_ = MessageLoop::current();
+
+ // Make sure this gets deleted.
+ scoped_ptr<WebVideoRendererFactoryFactory>
+ scoped_video_renderer_factory(video_renderer_factory);
+
+ // Create the pipeline and its thread.
+ if (!pipeline_thread_.Start()) {
+ NOTREACHED() << "Could not start PipelineThread";
+ return;
+ }
+
+ pipeline_ = new media::PipelineImpl(pipeline_thread_.message_loop());
+
+ // Also we want to be notified of |main_loop_| destruction.
+ main_loop_->AddDestructionObserver(this);
+
+ // Creates the proxy.
+ proxy_ = new Proxy(main_loop_, this);
+
+ // Set our pipeline callbacks.
+ pipeline_->SetPipelineEndedCallback(NewCallback(proxy_.get(),
+ &WebMediaPlayerImpl::Proxy::PipelineEndedCallback));
+ pipeline_->SetPipelineErrorCallback(NewCallback(proxy_.get(),
+ &WebMediaPlayerImpl::Proxy::PipelineErrorCallback));
+ pipeline_->SetNetworkEventCallback(NewCallback(proxy_.get(),
+ &WebMediaPlayerImpl::Proxy::NetworkEventCallback));
+
+ // Add in the default filter factories.
+ filter_factory_->AddFactory(media::FFmpegDemuxer::CreateFilterFactory());
+ filter_factory_->AddFactory(media::FFmpegAudioDecoder::CreateFactory());
+ filter_factory_->AddFactory(media::FFmpegVideoDecoder::CreateFactory());
+ filter_factory_->AddFactory(media::NullAudioRenderer::CreateFilterFactory());
+ filter_factory_->AddFactory(video_renderer_factory->CreateFactory(proxy_));
+}
+
+WebMediaPlayerImpl::~WebMediaPlayerImpl() {
+ Destroy();
+
+ // Finally tell the |main_loop_| we don't want to be notified of destruction
+ // event.
+ if (main_loop_) {
+ main_loop_->RemoveDestructionObserver(this);
+ }
+}
+
+void WebMediaPlayerImpl::load(const WebKit::WebURL& url) {
+ DCHECK(MessageLoop::current() == main_loop_);
+ DCHECK(proxy_);
+
+ // Handle any volume changes that occured before load().
+ setVolume(GetClient()->volume());
+
+ // Initialize the pipeline.
+ SetNetworkState(WebKit::WebMediaPlayer::Loading);
+ SetReadyState(WebKit::WebMediaPlayer::HaveNothing);
+ pipeline_->Start(
+ filter_factory_.get(),
+ url.spec(),
+ NewCallback(proxy_.get(),
+ &WebMediaPlayerImpl::Proxy::PipelineInitializationCallback));
+}
+
+void WebMediaPlayerImpl::cancelLoad() {
+ DCHECK(MessageLoop::current() == main_loop_);
+}
+
+void WebMediaPlayerImpl::play() {
+ DCHECK(MessageLoop::current() == main_loop_);
+
+ paused_ = false;
+ pipeline_->SetPlaybackRate(playback_rate_);
+}
+
+void WebMediaPlayerImpl::pause() {
+ DCHECK(MessageLoop::current() == main_loop_);
+
+ paused_ = true;
+ pipeline_->SetPlaybackRate(0.0f);
+ paused_time_ = pipeline_->GetCurrentTime();
+}
+
+bool WebMediaPlayerImpl::supportsFullscreen() const {
+ DCHECK(MessageLoop::current() == main_loop_);
+ return true;
+}
+
+bool WebMediaPlayerImpl::supportsSave() const {
+ DCHECK(MessageLoop::current() == main_loop_);
+ return true;
+}
+
+void WebMediaPlayerImpl::seek(float seconds) {
+ DCHECK(MessageLoop::current() == main_loop_);
+
+ // WebKit fires a seek(0) at the very start, however pipeline already does a
+ // seek(0) internally. Avoid doing seek(0) the second time because this will
+ // cause extra pre-rolling and will break servers without range request
+ // support.
+ //
+ // We still have to notify WebKit that time has changed otherwise
+ // HTMLMediaElement gets into an inconsistent state.
+ if (pipeline_->GetCurrentTime().ToInternalValue() == 0 && seconds == 0) {
+ GetClient()->timeChanged();
+ return;
+ }
+
+ // Drop our ready state if the media file isn't fully loaded.
+ if (!pipeline_->IsLoaded()) {
+ SetReadyState(WebKit::WebMediaPlayer::HaveMetadata);
+ }
+
+ // Try to preserve as much accuracy as possible.
+ float microseconds = seconds * base::Time::kMicrosecondsPerSecond;
+ base::TimeDelta seek_time =
+ base::TimeDelta::FromMicroseconds(static_cast<int64>(microseconds));
+
+ // Update our paused time.
+ if (paused_) {
+ paused_time_ = seek_time;
+ }
+
+ // Kick off the asynchronous seek!
+ pipeline_->Seek(
+ seek_time,
+ NewCallback(proxy_.get(),
+ &WebMediaPlayerImpl::Proxy::PipelineSeekCallback));
+}
+
+void WebMediaPlayerImpl::setEndTime(float seconds) {
+ DCHECK(MessageLoop::current() == main_loop_);
+
+ // TODO(hclam): add method call when it has been implemented.
+ return;
+}
+
+void WebMediaPlayerImpl::setRate(float rate) {
+ DCHECK(MessageLoop::current() == main_loop_);
+
+ // TODO(kylep): Remove when support for negatives is added. Also, modify the
+ // following checks so rewind uses reasonable values also.
+ if (rate < 0.0f)
+ return;
+
+ // Limit rates to reasonable values by clamping.
+ if (rate != 0.0f) {
+ if (rate < kMinRate)
+ rate = kMinRate;
+ else if (rate > kMaxRate)
+ rate = kMaxRate;
+ }
+
+ playback_rate_ = rate;
+ if (!paused_) {
+ pipeline_->SetPlaybackRate(rate);
+ }
+}
+
+void WebMediaPlayerImpl::setVolume(float volume) {
+ DCHECK(MessageLoop::current() == main_loop_);
+
+ pipeline_->SetVolume(volume);
+}
+
+void WebMediaPlayerImpl::setVisible(bool visible) {
+ DCHECK(MessageLoop::current() == main_loop_);
+
+ // TODO(hclam): add appropriate method call when pipeline has it implemented.
+ return;
+}
+
+bool WebMediaPlayerImpl::setAutoBuffer(bool autoBuffer) {
+ DCHECK(MessageLoop::current() == main_loop_);
+
+ return false;
+}
+
+bool WebMediaPlayerImpl::totalBytesKnown() {
+ DCHECK(MessageLoop::current() == main_loop_);
+
+ return pipeline_->GetTotalBytes() != 0;
+}
+
+bool WebMediaPlayerImpl::hasVideo() const {
+ DCHECK(MessageLoop::current() == main_loop_);
+
+ return pipeline_->IsRendered(media::mime_type::kMajorTypeVideo);
+}
+
+bool WebMediaPlayerImpl::hasAudio() const {
+ DCHECK(MessageLoop::current() == main_loop_);
+
+ return pipeline_->IsRendered(media::mime_type::kMajorTypeAudio);
+}
+
+WebKit::WebSize WebMediaPlayerImpl::naturalSize() const {
+ DCHECK(MessageLoop::current() == main_loop_);
+
+ size_t width, height;
+ pipeline_->GetVideoSize(&width, &height);
+ return WebKit::WebSize(width, height);
+}
+
+bool WebMediaPlayerImpl::paused() const {
+ DCHECK(MessageLoop::current() == main_loop_);
+
+ return pipeline_->GetPlaybackRate() == 0.0f;
+}
+
+bool WebMediaPlayerImpl::seeking() const {
+ DCHECK(MessageLoop::current() == main_loop_);
+
+ if (ready_state_ == WebKit::WebMediaPlayer::HaveNothing)
+ return false;
+
+ return ready_state_ == WebKit::WebMediaPlayer::HaveMetadata;
+}
+
+float WebMediaPlayerImpl::duration() const {
+ DCHECK(MessageLoop::current() == main_loop_);
+
+ return static_cast<float>(pipeline_->GetMediaDuration().InSecondsF());
+}
+
+float WebMediaPlayerImpl::currentTime() const {
+ DCHECK(MessageLoop::current() == main_loop_);
+
+ if (paused_) {
+ return static_cast<float>(paused_time_.InSecondsF());
+ }
+ return static_cast<float>(pipeline_->GetCurrentTime().InSecondsF());
+}
+
+int WebMediaPlayerImpl::dataRate() const {
+ DCHECK(MessageLoop::current() == main_loop_);
+
+ // TODO(hclam): Add this method call if pipeline has it in the interface.
+ return 0;
+}
+
+const WebKit::WebTimeRanges& WebMediaPlayerImpl::buffered() {
+ DCHECK(MessageLoop::current() == main_loop_);
+
+ // Update buffered_ with the most recent buffered time.
+ if (buffered_.size() > 0) {
+ float buffered_time = static_cast<float>(
+ pipeline_->GetBufferedTime().InSecondsF());
+ if (buffered_time >= buffered_[0].start)
+ buffered_[0].end = buffered_time;
+ }
+
+ return buffered_;
+}
+
+float WebMediaPlayerImpl::maxTimeSeekable() const {
+ DCHECK(MessageLoop::current() == main_loop_);
+
+ // If we are performing streaming, we report that we cannot seek at all.
+ // We are using this flag to indicate if the data source supports seeking
+ // or not. We should be able to seek even if we are performing streaming.
+ // TODO(hclam): We need to update this when we have better caching.
+ if (pipeline_->IsStreaming())
+ return 0.0f;
+ return static_cast<float>(pipeline_->GetMediaDuration().InSecondsF());
+}
+
+unsigned long long WebMediaPlayerImpl::bytesLoaded() const {
+ DCHECK(MessageLoop::current() == main_loop_);
+
+ return pipeline_->GetBufferedBytes();
+}
+
+unsigned long long WebMediaPlayerImpl::totalBytes() const {
+ DCHECK(MessageLoop::current() == main_loop_);
+
+ return pipeline_->GetTotalBytes();
+}
+
+void WebMediaPlayerImpl::setSize(const WebSize& size) {
+ DCHECK(MessageLoop::current() == main_loop_);
+ DCHECK(proxy_);
+
+ proxy_->SetSize(gfx::Rect(0, 0, size.width, size.height));
+}
+
+void WebMediaPlayerImpl::paint(WebCanvas* canvas,
+ const WebRect& rect) {
+ DCHECK(MessageLoop::current() == main_loop_);
+ DCHECK(proxy_);
+
+#if WEBKIT_USING_SKIA
+ proxy_->Paint(canvas, rect);
+#elif WEBKIT_USING_CG
+ // Get the current scaling in X and Y.
+ CGAffineTransform mat = CGContextGetCTM(canvas);
+ float scale_x = sqrt(mat.a * mat.a + mat.b * mat.b);
+ float scale_y = sqrt(mat.c * mat.c + mat.d * mat.d);
+ float inverse_scale_x = SkScalarNearlyZero(scale_x) ? 0.0f : 1.0f / scale_x;
+ float inverse_scale_y = SkScalarNearlyZero(scale_y) ? 0.0f : 1.0f / scale_y;
+ int scaled_width = static_cast<int>(rect.width * fabs(scale_x));
+ int scaled_height = static_cast<int>(rect.height * fabs(scale_y));
+
+ // Make sure we don't create a huge canvas.
+ // TODO(hclam): Respect the aspect ratio.
+ if (scaled_width > static_cast<int>(media::Limits::kMaxCanvas))
+ scaled_width = media::Limits::kMaxCanvas;
+ if (scaled_height > static_cast<int>(media::Limits::kMaxCanvas))
+ scaled_height = media::Limits::kMaxCanvas;
+
+ // If there is no preexisting platform canvas, or if the size has
+ // changed, recreate the canvas. This is to avoid recreating the bitmap
+ // buffer over and over for each frame of video.
+ if (!skia_canvas_.get() ||
+ skia_canvas_->getDevice()->width() != scaled_width ||
+ skia_canvas_->getDevice()->height() != scaled_height) {
+ skia_canvas_.reset(
+ new skia::PlatformCanvas(scaled_width, scaled_height, true));
+ }
+
+ // Draw to our temporary skia canvas.
+ gfx::Rect normalized_rect(scaled_width, scaled_height);
+ proxy_->Paint(skia_canvas_.get(), normalized_rect);
+
+ // The mac coordinate system is flipped vertical from the normal skia
+ // coordinates. During painting of the frame, flip the coordinates
+ // system and, for simplicity, also translate the clip rectangle to
+ // start at 0,0.
+ CGContextSaveGState(canvas);
+ CGContextTranslateCTM(canvas, rect.x, rect.height + rect.y);
+ CGContextScaleCTM(canvas, inverse_scale_x, -inverse_scale_y);
+
+ // We need a local variable CGRect version for DrawToContext.
+ CGRect normalized_cgrect =
+ CGRectMake(normalized_rect.x(), normalized_rect.y(),
+ normalized_rect.width(), normalized_rect.height());
+
+ // Copy the frame rendered to our temporary skia canvas onto the passed in
+ // canvas.
+ skia_canvas_->getTopPlatformDevice().DrawToContext(canvas, 0, 0,
+ &normalized_cgrect);
+
+ CGContextRestoreGState(canvas);
+#else
+ NOTIMPLEMENTED() << "We only support rendering to skia or CG";
+#endif
+}
+
+bool WebMediaPlayerImpl::hasSingleSecurityOrigin() const {
+ // TODO(scherkus): we'll need to do something smarter here if/when we start to
+ // support formats that contain references to external resources (i.e., MP4s
+ // containing links to other MP4s). See http://crbug.com/25432
+ return true;
+}
+
+WebKit::WebMediaPlayer::MovieLoadType
+ WebMediaPlayerImpl::movieLoadType() const {
+ DCHECK(MessageLoop::current() == main_loop_);
+
+ // TODO(hclam): If the pipeline is performing streaming, we say that this is
+ // a live stream. But instead it should be a StoredStream if we have proper
+ // caching.
+ if (pipeline_->IsStreaming())
+ return WebKit::WebMediaPlayer::LiveStream;
+ return WebKit::WebMediaPlayer::Unknown;
+}
+
+void WebMediaPlayerImpl::WillDestroyCurrentMessageLoop() {
+ Destroy();
+ main_loop_ = NULL;
+}
+
+void WebMediaPlayerImpl::Repaint() {
+ DCHECK(MessageLoop::current() == main_loop_);
+ GetClient()->repaint();
+}
+
+void WebMediaPlayerImpl::OnPipelineInitialize() {
+ DCHECK(MessageLoop::current() == main_loop_);
+ if (pipeline_->GetError() == media::PIPELINE_OK) {
+ // Only keep one time range starting from 0.
+ WebKit::WebTimeRanges new_buffered(static_cast<size_t>(1));
+ new_buffered[0].start = 0.0f;
+ new_buffered[0].end =
+ static_cast<float>(pipeline_->GetMediaDuration().InSecondsF());
+ buffered_.swap(new_buffered);
+
+ // Since we have initialized the pipeline, say we have everything otherwise
+ // we'll remain either loading/idle.
+ // TODO(hclam): change this to report the correct status.
+ SetReadyState(WebKit::WebMediaPlayer::HaveMetadata);
+ SetReadyState(WebKit::WebMediaPlayer::HaveEnoughData);
+ if (pipeline_->IsLoaded()) {
+ SetNetworkState(WebKit::WebMediaPlayer::Loaded);
+ }
+ } else {
+ // TODO(hclam): should use pipeline_->GetError() to determine the state
+ // properly and reports error using MediaError.
+ // WebKit uses FormatError to indicate an error for bogus URL or bad file.
+ // Since we are at the initialization stage we can safely treat every error
+ // as format error. Should post a task to call to |webmediaplayer_|.
+ SetNetworkState(WebKit::WebMediaPlayer::FormatError);
+ }
+
+ // Repaint to trigger UI update.
+ Repaint();
+}
+
+void WebMediaPlayerImpl::OnPipelineSeek() {
+ DCHECK(MessageLoop::current() == main_loop_);
+ if (pipeline_->GetError() == media::PIPELINE_OK) {
+ // Update our paused time.
+ if (paused_) {
+ paused_time_ = pipeline_->GetCurrentTime();
+ }
+
+ SetReadyState(WebKit::WebMediaPlayer::HaveEnoughData);
+ GetClient()->timeChanged();
+ }
+}
+
+void WebMediaPlayerImpl::OnPipelineEnded() {
+ DCHECK(MessageLoop::current() == main_loop_);
+ if (pipeline_->GetError() == media::PIPELINE_OK) {
+ GetClient()->timeChanged();
+ }
+}
+
+void WebMediaPlayerImpl::OnPipelineError() {
+ DCHECK(MessageLoop::current() == main_loop_);
+ switch (pipeline_->GetError()) {
+ case media::PIPELINE_OK:
+ case media::PIPELINE_ERROR_INITIALIZATION_FAILED:
+ case media::PIPELINE_ERROR_REQUIRED_FILTER_MISSING:
+ case media::PIPELINE_ERROR_COULD_NOT_RENDER:
+ case media::PIPELINE_ERROR_URL_NOT_FOUND:
+ case media::PIPELINE_ERROR_NETWORK:
+ case media::PIPELINE_ERROR_READ:
+ case media::DEMUXER_ERROR_COULD_NOT_OPEN:
+ case media::DEMUXER_ERROR_COULD_NOT_PARSE:
+ case media::DEMUXER_ERROR_NO_SUPPORTED_STREAMS:
+ case media::DEMUXER_ERROR_COULD_NOT_CREATE_THREAD:
+ // Format error.
+ SetNetworkState(WebMediaPlayer::FormatError);
+ break;
+
+ case media::PIPELINE_ERROR_DECODE:
+ case media::PIPELINE_ERROR_ABORT:
+ case media::PIPELINE_ERROR_OUT_OF_MEMORY:
+ case media::PIPELINE_ERROR_AUDIO_HARDWARE:
+ // Decode error.
+ SetNetworkState(WebMediaPlayer::DecodeError);
+ break;
+ }
+
+ // Repaint to trigger UI update.
+ Repaint();
+}
+
+void WebMediaPlayerImpl::OnNetworkEvent() {
+ DCHECK(MessageLoop::current() == main_loop_);
+ if (pipeline_->GetError() == media::PIPELINE_OK) {
+ if (pipeline_->IsNetworkActive())
+ SetNetworkState(WebKit::WebMediaPlayer::Loading);
+ else
+ SetNetworkState(WebKit::WebMediaPlayer::Idle);
+ }
+}
+
+void WebMediaPlayerImpl::SetNetworkState(
+ WebKit::WebMediaPlayer::NetworkState state) {
+ DCHECK(MessageLoop::current() == main_loop_);
+ // Always notify to ensure client has the latest value.
+ network_state_ = state;
+ GetClient()->networkStateChanged();
+}
+
+void WebMediaPlayerImpl::SetReadyState(
+ WebKit::WebMediaPlayer::ReadyState state) {
+ DCHECK(MessageLoop::current() == main_loop_);
+ // Always notify to ensure client has the latest value.
+ ready_state_ = state;
+ GetClient()->readyStateChanged();
+}
+
+void WebMediaPlayerImpl::Destroy() {
+ DCHECK(MessageLoop::current() == main_loop_);
+
+ // Make sure to kill the pipeline so there's no more media threads running.
+ // Note: stopping the pipeline might block for a long time.
+ pipeline_->Stop(NewCallback(this,
+ &WebMediaPlayerImpl::PipelineStoppedCallback));
+ pipeline_stopped_.Wait();
+ pipeline_thread_.Stop();
+
+ // And then detach the proxy, it may live on the render thread for a little
+ // longer until all the tasks are finished.
+ if (proxy_) {
+ proxy_->Detach();
+ proxy_ = NULL;
+ }
+}
+
+void WebMediaPlayerImpl::PipelineStoppedCallback() {
+ pipeline_stopped_.Signal();
+}
+
+WebKit::WebMediaPlayerClient* WebMediaPlayerImpl::GetClient() {
+ DCHECK(MessageLoop::current() == main_loop_);
+ DCHECK(client_);
+ return client_;
+}
+
+} // namespace webkit_glue
diff --git a/webkit/glue/webmediaplayer_impl.h b/webkit/glue/webmediaplayer_impl.h
new file mode 100644
index 0000000..e8794dc
--- /dev/null
+++ b/webkit/glue/webmediaplayer_impl.h
@@ -0,0 +1,340 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Delegate calls from WebCore::MediaPlayerPrivate to Chrome's video player.
+// It contains PipelineImpl which is the actual media player pipeline, it glues
+// the media player pipeline, data source, audio renderer and renderer.
+// PipelineImpl would creates multiple threads and access some public methods
+// of this class, so we need to be extra careful about concurrent access of
+// methods and members.
+//
+// WebMediaPlayerImpl works with multiple objects, the most important ones are:
+//
+// media::PipelineImpl
+// The media playback pipeline.
+//
+// WebVideoRenderer
+// Video renderer object.
+//
+// WebMediaPlayerImpl::Proxy
+// Proxies methods calls from the media pipeline to WebKit.
+//
+// WebKit::WebMediaPlayerClient
+// WebKit client of this media player object.
+//
+// The following diagram shows the relationship of these objects:
+// (note: ref-counted reference is marked by a "r".)
+//
+// WebMediaPlayerImpl ------> PipelineImpl
+// | ^ | r
+// | | v
+// | | WebVideoRenderer
+// | | ^ r
+// | | |
+// | r | r |
+// .------> Proxy <-----.
+// |
+// |
+// v
+// WebMediaPlayerClient
+//
+// Notice that Proxy and WebVideoRenderer are referencing each other. This
+// interdependency has to be treated carefully.
+//
+// Other issues:
+// During tear down of the whole browser or a tab, the DOM tree may not be
+// destructed nicely, and there will be some dangling media threads trying to
+// the main thread, so we need this class to listen to destruction event of the
+// main thread and cleanup the media threads when the even is received. Also
+// at destruction of this class we will need to unhook it from destruction event
+// list of the main thread.
+
+#ifndef WEBKIT_GLUE_WEBMEDIAPLAYER_IMPL_H_
+#define WEBKIT_GLUE_WEBMEDIAPLAYER_IMPL_H_
+
+#include <vector>
+
+#include "base/lock.h"
+#include "base/message_loop.h"
+#include "base/ref_counted.h"
+#include "base/scoped_ptr.h"
+#include "base/waitable_event.h"
+#include "gfx/rect.h"
+#include "gfx/size.h"
+#include "media/base/filters.h"
+#include "media/base/pipeline_impl.h"
+#include "skia/ext/platform_canvas.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebMediaPlayer.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebMediaPlayerClient.h"
+
+class GURL;
+
+namespace media {
+class FilterFactoryCollection;
+}
+
+namespace webkit_glue {
+
+class WebVideoRenderer;
+class WebVideoRendererFactoryFactory;
+
+class WebMediaPlayerImpl : public WebKit::WebMediaPlayer,
+ public MessageLoop::DestructionObserver {
+ public:
+ // A proxy class that dispatches method calls from the media pipeline to
+ // WebKit. Since there are multiple threads in the media pipeline and there's
+ // need for the media pipeline to call to WebKit, e.g. repaint requests,
+ // initialization events, etc, we have this class to bridge all method calls
+ // from the media pipeline on different threads and serialize these calls
+ // on the render thread.
+ // Because of the nature of this object that it works with different threads,
+ // it is made ref-counted.
+ class Proxy : public base::RefCountedThreadSafe<Proxy> {
+ public:
+ Proxy(MessageLoop* render_loop,
+ WebMediaPlayerImpl* webmediaplayer);
+
+ // Public methods called from the video renderer.
+ void Repaint();
+ void SetVideoRenderer(WebVideoRenderer* video_renderer);
+
+ // Public methods called from WebMediaPlayerImpl.
+ void Paint(skia::PlatformCanvas* canvas, const gfx::Rect& dest_rect);
+ void SetSize(const gfx::Rect& rect);
+ void Detach();
+
+ // Public methods called from the pipeline via callback issued by
+ // WebMediaPlayerImpl.
+ void PipelineInitializationCallback();
+ void PipelineSeekCallback();
+ void PipelineEndedCallback();
+ void PipelineErrorCallback();
+ void NetworkEventCallback();
+
+ // Returns the message loop used by the proxy.
+ MessageLoop* message_loop() { return render_loop_; }
+
+ private:
+ friend class base::RefCountedThreadSafe<Proxy>;
+
+ virtual ~Proxy();
+
+ // Invoke |webmediaplayer_| to perform a repaint.
+ void RepaintTask();
+
+ // Notify |webmediaplayer_| that initialization has finished.
+ void PipelineInitializationTask();
+
+ // Notify |webmediaplayer_| that a seek has finished.
+ void PipelineSeekTask();
+
+ // Notify |webmediaplayer_| that the media has ended.
+ void PipelineEndedTask();
+
+ // Notify |webmediaplayer_| that a pipeline error has been set.
+ void PipelineErrorTask();
+
+ // Notify |webmediaplayer_| that there's a network event.
+ void NetworkEventTask();
+
+ // The render message loop where WebKit lives.
+ MessageLoop* render_loop_;
+ WebMediaPlayerImpl* webmediaplayer_;
+ scoped_refptr<WebVideoRenderer> video_renderer_;
+
+ Lock lock_;
+ int outstanding_repaints_;
+ };
+
+ // Construct a WebMediaPlayerImpl with reference to the client, and media
+ // filter factory collection. By providing the filter factory collection
+ // the implementor can provide more specific media filters that does resource
+ // loading and rendering. |factory| should contain filter factories for:
+ // 1. Data source
+ // 2. Audio renderer
+ // 3. Video renderer (optional)
+ //
+ // There are some default filters provided by this method:
+ // 1. FFmpeg demuxer
+ // 2. FFmpeg audio decoder
+ // 3. FFmpeg video decoder
+ // 4. Video renderer
+ // 5. Null audio renderer
+ // The video renderer provided by this class is using the graphics context
+ // provided by WebKit to perform renderering. The simple data source does
+ // resource loading by loading the whole resource object into memory. Null
+ // audio renderer is a fake audio device that plays silence. Provider of the
+ // |factory| can override the default filters by adding extra filters to
+ // |factory| before calling this method.
+ //
+ // |video_renderer_factory| is used to construct a factory that should create
+ // a subclass of WebVideoRenderer. Is deleted by WebMediaPlayerImpl.
+ WebMediaPlayerImpl(WebKit::WebMediaPlayerClient* client,
+ media::FilterFactoryCollection* factory,
+ WebVideoRendererFactoryFactory* video_renderer_factory);
+ virtual ~WebMediaPlayerImpl();
+
+ virtual void load(const WebKit::WebURL& url);
+ virtual void cancelLoad();
+
+ // Playback controls.
+ virtual void play();
+ virtual void pause();
+ virtual bool supportsFullscreen() const;
+ virtual bool supportsSave() const;
+ virtual void seek(float seconds);
+ virtual void setEndTime(float seconds);
+ virtual void setRate(float rate);
+ virtual void setVolume(float volume);
+ virtual void setVisible(bool visible);
+ virtual bool setAutoBuffer(bool autoBuffer);
+ virtual bool totalBytesKnown();
+ virtual const WebKit::WebTimeRanges& buffered();
+ virtual float maxTimeSeekable() const;
+
+ // Methods for painting.
+ virtual void setSize(const WebKit::WebSize& size);
+
+ virtual void paint(WebKit::WebCanvas* canvas, const WebKit::WebRect& rect);
+
+ // True if the loaded media has a playable video/audio track.
+ virtual bool hasVideo() const;
+ virtual bool hasAudio() const;
+
+ // Dimensions of the video.
+ virtual WebKit::WebSize naturalSize() const;
+
+ // Getters of playback state.
+ virtual bool paused() const;
+ virtual bool seeking() const;
+ virtual float duration() const;
+ virtual float currentTime() const;
+
+ // Get rate of loading the resource.
+ virtual int32 dataRate() const;
+
+ // Internal states of loading and network.
+ // TODO(hclam): Ask the pipeline about the state rather than having reading
+ // them from members which would cause race conditions.
+ virtual WebKit::WebMediaPlayer::NetworkState networkState() const {
+ return network_state_;
+ }
+ virtual WebKit::WebMediaPlayer::ReadyState readyState() const {
+ return ready_state_;
+ }
+
+ virtual unsigned long long bytesLoaded() const;
+ virtual unsigned long long totalBytes() const;
+
+ virtual bool hasSingleSecurityOrigin() const;
+ virtual WebKit::WebMediaPlayer::MovieLoadType movieLoadType() const;
+
+ // As we are closing the tab or even the browser, |main_loop_| is destroyed
+ // even before this object gets destructed, so we need to know when
+ // |main_loop_| is being destroyed and we can stop posting repaint task
+ // to it.
+ virtual void WillDestroyCurrentMessageLoop();
+
+ void Repaint();
+
+ void OnPipelineInitialize();
+
+ void OnPipelineSeek();
+
+ void OnPipelineEnded();
+
+ void OnPipelineError();
+
+ void OnNetworkEvent();
+
+ private:
+ // Helpers that set the network/ready state and notifies the client if
+ // they've changed.
+ void SetNetworkState(WebKit::WebMediaPlayer::NetworkState state);
+ void SetReadyState(WebKit::WebMediaPlayer::ReadyState state);
+
+ // Destroy resources held.
+ void Destroy();
+
+ // Callback executed after |pipeline_| stops which signals Destroy()
+ // to continue.
+ void PipelineStoppedCallback();
+
+ // Getter method to |client_|.
+ WebKit::WebMediaPlayerClient* GetClient();
+
+ // TODO(hclam): get rid of these members and read from the pipeline directly.
+ WebKit::WebMediaPlayer::NetworkState network_state_;
+ WebKit::WebMediaPlayer::ReadyState ready_state_;
+
+ // Keep a list of buffered time ranges.
+ WebKit::WebTimeRanges buffered_;
+
+ // Message loops for posting tasks between Chrome's main thread. Also used
+ // for DCHECKs so methods calls won't execute in the wrong thread.
+ MessageLoop* main_loop_;
+
+ // A collection of factories for creating filters.
+ scoped_refptr<media::FilterFactoryCollection> filter_factory_;
+
+ // The actual pipeline and the thread it runs on.
+ scoped_refptr<media::PipelineImpl> pipeline_;
+ base::Thread pipeline_thread_;
+
+ // Playback state.
+ //
+ // TODO(scherkus): we have these because Pipeline favours the simplicity of a
+ // single "playback rate" over worrying about paused/stopped etc... It forces
+ // all clients to manage the pause+playback rate externally, but is that
+ // really a bad thing?
+ //
+ // TODO(scherkus): since SetPlaybackRate(0) is asynchronous and we don't want
+ // to hang the render thread during pause(), we record the time at the same
+ // time we pause and then return that value in currentTime(). Otherwise our
+ // clock can creep forward a little bit while the asynchronous
+ // SetPlaybackRate(0) is being executed.
+ bool paused_;
+ float playback_rate_;
+ base::TimeDelta paused_time_;
+
+ WebKit::WebMediaPlayerClient* client_;
+
+ scoped_refptr<Proxy> proxy_;
+
+ // Used to block Destroy() until Pipeline::Stop() is completed.
+ base::WaitableEvent pipeline_stopped_;
+
+#if WEBKIT_USING_CG
+ scoped_ptr<skia::PlatformCanvas> skia_canvas_;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(WebMediaPlayerImpl);
+};
+
+// TODO(scherkus): WebMediaPlayerImpl creates and injects its Proxy into a
+// video renderer factory, so we need to (unfortunately) have a factory of a
+// factory so we can receive the proxy pointer without violating the
+// separation of renderer code from webkit glue code. This is part of a
+// longer-term plan to rethink our FilterFactory strategy (refer to
+// http://crbug.com/28207).
+//
+// Either that or we rethink this Proxy business as a short-term solution.
+class WebVideoRendererFactoryFactory {
+ public:
+ WebVideoRendererFactoryFactory() {}
+ virtual ~WebVideoRendererFactoryFactory() {}
+
+ // Creates a FilterFactory which should be capable of creating a
+ // WebVideoRenderer subclass.
+ virtual media::FilterFactory* CreateFactory(
+ WebMediaPlayerImpl::Proxy* proxy) = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(WebVideoRendererFactoryFactory);
+};
+
+
+} // namespace webkit_glue
+
+#endif // WEBKIT_GLUE_WEBMEDIAPLAYER_IMPL_H_
diff --git a/webkit/glue/webmenuitem.h b/webkit/glue/webmenuitem.h
new file mode 100644
index 0000000..4b0fdc3
--- /dev/null
+++ b/webkit/glue/webmenuitem.h
@@ -0,0 +1,39 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBMENUITEM_H_
+#define WEBMENUITEM_H_
+
+#include "base/string16.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebMenuItemInfo.h"
+
+// Container for information about entries in an HTML select popup menu and
+// custom entries of the context menu.
+struct WebMenuItem {
+ enum Type {
+ OPTION = WebKit::WebMenuItemInfo::Option,
+ CHECKABLE_OPTION = WebKit::WebMenuItemInfo::CheckableOption,
+ GROUP = WebKit::WebMenuItemInfo::Group,
+ SEPARATOR = WebKit::WebMenuItemInfo::Separator
+ };
+
+ string16 label;
+ Type type;
+ unsigned action;
+ bool enabled;
+ bool checked;
+
+ WebMenuItem() : type(OPTION), action(0), enabled(false), checked(false) {
+ }
+
+ WebMenuItem(const WebKit::WebMenuItemInfo& item)
+ : label(item.label),
+ type(static_cast<Type>(item.type)),
+ action(item.action),
+ enabled(item.enabled),
+ checked(item.checked) {
+ }
+};
+
+#endif // WEBMENUITEM_H_
diff --git a/webkit/glue/webmenurunner_mac.h b/webkit/glue/webmenurunner_mac.h
new file mode 100644
index 0000000..c56eeeb
--- /dev/null
+++ b/webkit/glue/webmenurunner_mac.h
@@ -0,0 +1,70 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_WEBMENURUNNER_MAC_H_
+#define WEBKIT_GLUE_WEBMENURUNNER_MAC_H_
+
+#import <Cocoa/Cocoa.h>
+
+#include <vector>
+
+#include "base/scoped_nsobject.h"
+#include "webkit/glue/webmenuitem.h"
+
+
+// WebMenuRunner ---------------------------------------------------------------
+// A class for determining whether an item was selected from an HTML select
+// control, or if the menu was dismissed without making a selection. If a menu
+// item is selected, MenuDelegate is informed and sets a flag which can be
+// queried after the menu has finished running.
+
+@interface WebMenuRunner : NSObject {
+ @private
+ // The native menu control.
+ scoped_nsobject<NSMenu> menu_;
+
+ // A flag set to YES if a menu item was chosen, or NO if the menu was
+ // dismissed without selecting an item.
+ BOOL menuItemWasChosen_;
+
+ // The index of the selected menu item.
+ int index_;
+
+ // The font size being used for the menu.
+ CGFloat fontSize_;
+}
+
+// Initializes the MenuDelegate with a list of items sent from WebKit.
+- (id)initWithItems:(const std::vector<WebMenuItem>&)items
+ fontSize:(CGFloat)fontSize
+ rightAligned:(BOOL)rightAligned;
+
+// Returns YES if an item was selected from the menu, NO if the menu was
+// dismissed.
+- (BOOL)menuItemWasChosen;
+
+// Displays and runs a native popup menu.
+- (void)runMenuInView:(NSView*)view
+ withBounds:(NSRect)bounds
+ initialIndex:(int)index;
+
+// Returns the index of selected menu item, or its initial value (-1) if no item
+// was selected.
+- (int)indexOfSelectedItem;
+
+@end // @interface WebMenuRunner
+
+namespace webkit_glue {
+// Helper function for users of WebMenuRunner, for manufacturing input events to
+// send to WebKit. If |item_chosen| is YES, we manufacture a mouse click event
+// that corresponds to the menu item that was selected, |selected_index|, based
+// on the position of the mouse click. Of |item_chosen| is NO, we create a
+// keyboard event that simulates an ESC (menu dismissal) action. The event is
+// designed to be sent to WebKit for processing by the PopupMenu class.
+NSEvent* EventWithMenuAction(BOOL item_chosen, int window_num,
+ int item_height, int selected_index,
+ NSRect menu_bounds, NSRect view_bounds);
+} // namespace webkit_glue
+
+#endif // WEBKIT_GLUE_WEBMENURUNNER_MAC_H_
diff --git a/webkit/glue/webmenurunner_mac.mm b/webkit/glue/webmenurunner_mac.mm
new file mode 100644
index 0000000..e4c8a4b
--- /dev/null
+++ b/webkit/glue/webmenurunner_mac.mm
@@ -0,0 +1,204 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/webmenurunner_mac.h"
+
+#include "base/sys_string_conversions.h"
+
+namespace {
+
+const CGFloat kPopupXOffset = -10.0f;
+BOOL gNewNSMenuAPI;
+
+} // namespace
+
+#if !defined(MAC_OS_X_VERSION_10_6) || \
+ MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6
+@interface NSMenu (SnowLeopardSDKDeclarations)
+- (BOOL)popUpMenuPositioningItem:(NSMenuItem *)item
+ atLocation:(NSPoint)location
+ inView:(NSView *)view;
+- (void)setFont:(NSFont *)font;
+@end
+#endif
+
+@interface WebMenuRunner (PrivateAPI)
+
+// Worker function used during initialization.
+- (void)addItem:(const WebMenuItem&)item
+ withAttributes:(NSDictionary*)attrs;
+
+// A callback for the menu controller object to call when an item is selected
+// from the menu. This is not called if the menu is dismissed without a
+// selection.
+- (void)menuItemSelected:(id)sender;
+
+@end // WebMenuRunner (PrivateAPI)
+
+@implementation WebMenuRunner
+
+- (id)initWithItems:(const std::vector<WebMenuItem>&)items
+ fontSize:(CGFloat)fontSize
+ rightAligned:(BOOL)rightAligned {
+ static BOOL newNSMenuAPIInitialized = NO;
+ if (!newNSMenuAPIInitialized) {
+ newNSMenuAPIInitialized = YES;
+ gNewNSMenuAPI = [NSMenu instancesRespondToSelector:
+ @selector(popUpMenuPositioningItem:atLocation:inView:)] &&
+ [NSMenu instancesRespondToSelector:@selector(setFont:)];
+ }
+
+ if ((self = [super init])) {
+ menu_.reset([[NSMenu alloc] initWithTitle:@""]);
+ if (gNewNSMenuAPI)
+ [menu_ setFont:[NSFont menuFontOfSize:fontSize]];
+ [menu_ setAutoenablesItems:NO];
+ index_ = -1;
+ fontSize_ = fontSize;
+ scoped_nsobject<NSDictionary> attrs;
+ if (rightAligned) {
+ // NB: Right-aligning menu items in this manner is known to not work in
+ // Mac OS X 10.5.
+ scoped_nsobject<NSMutableParagraphStyle> paragraphStyle(
+ [[NSMutableParagraphStyle alloc] init]);
+ [paragraphStyle setAlignment:NSRightTextAlignment];
+ attrs.reset([[NSDictionary alloc] initWithObjectsAndKeys:
+ paragraphStyle, NSParagraphStyleAttributeName, nil]);
+ }
+ for (size_t i = 0; i < items.size(); ++i)
+ [self addItem:items[i] withAttributes:attrs];
+ }
+ return self;
+}
+
+- (void)addItem:(const WebMenuItem&)item
+ withAttributes:(NSDictionary*)attrs {
+ if (item.type == WebMenuItem::SEPARATOR) {
+ [menu_ addItem:[NSMenuItem separatorItem]];
+ return;
+ }
+
+ NSString* title = base::SysUTF16ToNSString(item.label);
+ NSMenuItem* menuItem = [menu_ addItemWithTitle:title
+ action:@selector(menuItemSelected:)
+ keyEquivalent:@""];
+ [menuItem setEnabled:(item.enabled && item.type != WebMenuItem::GROUP)];
+ [menuItem setTarget:self];
+ if (attrs) {
+ scoped_nsobject<NSAttributedString> attrTitle(
+ [[NSAttributedString alloc] initWithString:title
+ attributes:attrs]);
+ [menuItem setAttributedTitle:attrTitle];
+ }
+ if (gNewNSMenuAPI)
+ [menuItem setTag:[menu_ numberOfItems] - 1];
+}
+
+// Reflects the result of the user's interaction with the popup menu. If NO, the
+// menu was dismissed without the user choosing an item, which can happen if the
+// user clicked outside the menu region or hit the escape key. If YES, the user
+// selected an item from the menu.
+- (BOOL)menuItemWasChosen {
+ return menuItemWasChosen_;
+}
+
+- (void)menuItemSelected:(id)sender {
+ menuItemWasChosen_ = YES;
+ if (gNewNSMenuAPI)
+ index_ = [sender tag];
+}
+
+- (void)runMenuInView:(NSView*)view
+ withBounds:(NSRect)bounds
+ initialIndex:(int)index {
+ if (gNewNSMenuAPI) {
+ NSMenuItem* selectedItem = [menu_ itemAtIndex:index];
+ [selectedItem setState:NSOnState];
+ NSPoint anchor = NSMakePoint(NSMinX(bounds) + kPopupXOffset,
+ NSMaxY(bounds));
+ [menu_ popUpMenuPositioningItem:selectedItem
+ atLocation:anchor
+ inView:view];
+ } else {
+ // Set up the button cell, converting to NSView coordinates. The menu is
+ // positioned such that the currently selected menu item appears over the
+ // popup button, which is the expected Mac popup menu behavior.
+ NSPopUpButtonCell* button = [[NSPopUpButtonCell alloc] initTextCell:@""
+ pullsDown:NO];
+ [button autorelease];
+ [button setMenu:menu_];
+ [button selectItemAtIndex:index];
+ [button setFont:[NSFont menuFontOfSize:fontSize_]];
+
+ // Display the menu, and set a flag if a menu item was chosen.
+ [button performClickWithFrame:bounds inView:view];
+
+ if ([self menuItemWasChosen])
+ index_ = [button indexOfSelectedItem];
+ }
+}
+
+- (int)indexOfSelectedItem {
+ return index_;
+}
+
+@end // WebMenuRunner
+
+namespace webkit_glue {
+
+// Helper function for manufacturing input events to send to WebKit.
+NSEvent* EventWithMenuAction(BOOL item_chosen, int window_num,
+ int item_height, int selected_index,
+ NSRect menu_bounds, NSRect view_bounds) {
+ NSEvent* event = nil;
+ double event_time = (double)(AbsoluteToDuration(UpTime())) / 1000.0;
+
+ if (item_chosen) {
+ // Construct a mouse up event to simulate the selection of an appropriate
+ // menu item.
+ NSPoint click_pos;
+ click_pos.x = menu_bounds.size.width / 2;
+
+ // This is going to be hard to calculate since the button is painted by
+ // WebKit, the menu by Cocoa, and we have to translate the selected_item
+ // index to a coordinate that WebKit's PopupMenu expects which uses a
+ // different font *and* expects to draw the menu below the button like we do
+ // on Windows.
+ // The WebKit popup menu thinks it will draw just below the button, so
+ // create the click at the offset based on the selected item's index and
+ // account for the different coordinate system used by NSView.
+ int item_offset = selected_index * item_height + item_height / 2;
+ click_pos.y = view_bounds.size.height - item_offset;
+ event = [NSEvent mouseEventWithType:NSLeftMouseUp
+ location:click_pos
+ modifierFlags:0
+ timestamp:event_time
+ windowNumber:window_num
+ context:nil
+ eventNumber:0
+ clickCount:1
+ pressure:1.0];
+ } else {
+ // Fake an ESC key event (keyCode = 0x1B, from webinputevent_mac.mm) and
+ // forward that to WebKit.
+ NSPoint key_pos;
+ key_pos.x = 0;
+ key_pos.y = 0;
+ NSString* escape_str = [NSString stringWithFormat:@"%c", 0x1B];
+ event = [NSEvent keyEventWithType:NSKeyDown
+ location:key_pos
+ modifierFlags:0
+ timestamp:event_time
+ windowNumber:window_num
+ context:nil
+ characters:@""
+ charactersIgnoringModifiers:escape_str
+ isARepeat:NO
+ keyCode:0x1B];
+ }
+
+ return event;
+}
+
+} // namespace webkit_glue
diff --git a/webkit/glue/webpasswordautocompletelistener_impl.cc b/webkit/glue/webpasswordautocompletelistener_impl.cc
new file mode 100644
index 0000000..6f6e754
--- /dev/null
+++ b/webkit/glue/webpasswordautocompletelistener_impl.cc
@@ -0,0 +1,197 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// This file provides the implementaiton of the password manager's autocomplete
+// component.
+
+#include "webkit/glue/webpasswordautocompletelistener_impl.h"
+
+#include <vector>
+
+#include "base/string_util.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebDocument.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebFrame.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebView.h"
+
+using WebKit::WebFrame;
+using WebKit::WebView;
+
+namespace webkit_glue {
+
+WebInputElementDelegate::WebInputElementDelegate() {
+}
+
+WebInputElementDelegate::WebInputElementDelegate(const WebInputElement& element)
+ : element_(element) {
+}
+
+WebInputElementDelegate::~WebInputElementDelegate() {
+}
+
+bool WebInputElementDelegate::IsEditable() const {
+ return element_.isEnabledFormControl() && !element_.hasAttribute("readonly");
+}
+
+void WebInputElementDelegate::SetValue(const string16& value) {
+ element_.setValue(value);
+}
+
+bool WebInputElementDelegate::IsAutofilled() const {
+ return element_.isAutofilled();
+}
+
+void WebInputElementDelegate::SetAutofilled(bool autofilled) {
+ if (element_.isAutofilled() == autofilled)
+ return;
+ element_.setAutofilled(autofilled);
+ // Notify any changeEvent listeners.
+ element_.dispatchFormControlChangeEvent();
+}
+
+void WebInputElementDelegate::SetSelectionRange(size_t start, size_t end) {
+ element_.setSelectionRange(start, end);
+}
+
+void WebInputElementDelegate::RefreshAutofillPopup(
+ const std::vector<string16>& suggestions) {
+ WebView* webview = element_.document().frame()->view();
+ if (webview) {
+ std::vector<string16> names;
+ std::vector<string16> labels;
+ std::vector<int> unique_ids;
+
+ for (size_t i = 0; i < suggestions.size(); ++i) {
+ names.push_back(suggestions[i]);
+ labels.push_back(string16());
+ unique_ids.push_back(0);
+ }
+
+ webview->applyAutoFillSuggestions(element_, names, labels, unique_ids, -1);
+ }
+}
+
+WebPasswordAutocompleteListenerImpl::WebPasswordAutocompleteListenerImpl(
+ WebInputElementDelegate* username_delegate,
+ WebInputElementDelegate* password_delegate,
+ const PasswordFormFillData& data)
+ : password_delegate_(password_delegate),
+ username_delegate_(username_delegate),
+ data_(data) {
+}
+
+void WebPasswordAutocompleteListenerImpl::didBlurInputElement(
+ const WebString& user_input) {
+ // If this listener exists, its because the password manager had more than
+ // one match for the password form, which implies it had at least one
+ // [preferred] username/password pair.
+// DCHECK(data_.basic_data.values.size() == 2);
+
+ if (!password_delegate_->IsEditable())
+ return;
+
+ string16 user_input16 = user_input;
+
+ // If enabled, set the password field to match the current username.
+ if (data_.basic_data.fields[0].value() == user_input16) {
+ // Preferred username/login is selected.
+ password_delegate_->SetValue(data_.basic_data.fields[1].value());
+ password_delegate_->SetAutofilled(true);
+ } else if (data_.additional_logins.find(user_input16) !=
+ data_.additional_logins.end()) {
+ // One of the extra username/logins is selected.
+ password_delegate_->SetValue(data_.additional_logins[user_input16]);
+ password_delegate_->SetAutofilled(true);
+ }
+}
+
+void WebPasswordAutocompleteListenerImpl::performInlineAutocomplete(
+ const WebString& user_input,
+ bool backspace_or_delete_pressed,
+ bool show_suggestions) {
+ // If wait_for_username is true, we only autofill the password when the
+ // username field is blurred (i.e not inline) with a matching username string
+ // entered.
+ if (data_.wait_for_username)
+ return;
+
+ string16 user_input16 = user_input;
+
+ // The input text is being changed, so any autofilled password is now
+ // outdated.
+ username_delegate_->SetAutofilled(false);
+ if (password_delegate_->IsAutofilled()) {
+ password_delegate_->SetValue(string16());
+ password_delegate_->SetAutofilled(false);
+ }
+
+ if (show_suggestions)
+ showSuggestionPopup(user_input16);
+
+ if (backspace_or_delete_pressed)
+ return; // Don't inline autocomplete when the user deleted something.
+
+ // Look for any suitable matches to current field text.
+ // TODO(timsteele): The preferred login (in basic_data.values) and additional
+ // logins could be bundled into the same data structure (possibly even as
+ // WebCore strings) upon construction of the PasswordAutocompleteListenerImpl
+ // to simplify lookup and save string conversions (see SetValue) on each
+ // successful call to OnInlineAutocompleteNeeded.
+ if (TryToMatch(user_input16,
+ data_.basic_data.fields[0].value(),
+ data_.basic_data.fields[1].value())) {
+ return;
+ }
+
+ // Scan additional logins for a match.
+ for (PasswordFormFillData::LoginCollection::iterator it =
+ data_.additional_logins.begin();
+ it != data_.additional_logins.end();
+ ++it) {
+ if (TryToMatch(user_input16, it->first, it->second))
+ return;
+ }
+}
+
+bool WebPasswordAutocompleteListenerImpl::showSuggestionPopup(
+ const WebString& value) {
+ std::vector<string16> suggestions;
+ GetSuggestions(value, &suggestions);
+ if (suggestions.empty())
+ return false;
+
+ username_delegate_->RefreshAutofillPopup(suggestions);
+ return true;
+}
+
+bool WebPasswordAutocompleteListenerImpl::TryToMatch(const string16& input,
+ const string16& username,
+ const string16& password) {
+ if (!StartsWith(username, input, false))
+ return false;
+
+ // Input matches the username, fill in required values.
+ username_delegate_->SetValue(username);
+ username_delegate_->SetSelectionRange(input.length(), username.length());
+ username_delegate_->SetAutofilled(true);
+ if (password_delegate_->IsEditable())
+ password_delegate_->SetValue(password);
+ password_delegate_->SetAutofilled(true);
+ return true;
+}
+
+void WebPasswordAutocompleteListenerImpl::GetSuggestions(
+ const string16& input, std::vector<string16>* suggestions) {
+ if (StartsWith(data_.basic_data.fields[0].value(), input, false))
+ suggestions->push_back(data_.basic_data.fields[0].value());
+
+ for (PasswordFormFillData::LoginCollection::iterator it =
+ data_.additional_logins.begin();
+ it != data_.additional_logins.end();
+ ++it) {
+ if (StartsWith(it->first, input, false))
+ suggestions->push_back(it->first);
+ }
+}
+
+} // namespace webkit_glue
diff --git a/webkit/glue/webpasswordautocompletelistener_impl.h b/webkit/glue/webpasswordautocompletelistener_impl.h
new file mode 100644
index 0000000..4fea455
--- /dev/null
+++ b/webkit/glue/webpasswordautocompletelistener_impl.h
@@ -0,0 +1,86 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// A concrete definition of the DOM autocomplete framework defined by
+// autocomplete_input_listener.h, for the password manager.
+
+#ifndef WEBKIT_GLUE_PASSWORDAUTOCOMPLETELISTENER_IMPL_H_
+#define WEBKIT_GLUE_PASSWORDAUTOCOMPLETELISTENER_IMPL_H_
+
+#include "base/basictypes.h"
+#include "base/scoped_ptr.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebInputElement.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebPasswordAutocompleteListener.h"
+#include "webkit/glue/password_form_dom_manager.h"
+
+using WebKit::WebInputElement;
+using WebKit::WebString;
+
+namespace webkit_glue {
+
+// A proxy interface to a WebInputElement for inline autocomplete. The proxy
+// is overridden by webpasswordautocompletelistener_unittest.
+class WebInputElementDelegate {
+ public:
+ WebInputElementDelegate();
+ WebInputElementDelegate(const WebInputElement& element);
+ virtual ~WebInputElementDelegate();
+
+ // These are virtual to support unit testing.
+ virtual bool IsEditable() const;
+ virtual void SetValue(const string16& value);
+ virtual bool IsAutofilled() const;
+ virtual void SetAutofilled(bool autofilled);
+ virtual void SetSelectionRange(size_t start, size_t end);
+ virtual void RefreshAutofillPopup(const std::vector<string16>& suggestions);
+
+ private:
+ // The underlying DOM element we're wrapping.
+ WebInputElement element_;
+
+ DISALLOW_COPY_AND_ASSIGN(WebInputElementDelegate);
+};
+
+class WebPasswordAutocompleteListenerImpl :
+ public WebKit::WebPasswordAutocompleteListener {
+ public:
+ WebPasswordAutocompleteListenerImpl(
+ WebInputElementDelegate* username_element,
+ WebInputElementDelegate* password_element,
+ const PasswordFormFillData& data);
+ ~WebPasswordAutocompleteListenerImpl() {
+ }
+
+ // WebKit::PasswordAutocompleteListener methods:
+ virtual void didBlurInputElement(const WebString& user_input);
+ virtual void performInlineAutocomplete(const WebString& user_input,
+ bool backspace_or_delete_pressed,
+ bool show_suggestions);
+ virtual bool showSuggestionPopup(const WebString& value);
+
+ private:
+ // Check if the input string resembles a potential matching login
+ // (username/password) and if so, match them up by autocompleting the edit
+ // delegates.
+ bool TryToMatch(const string16& input,
+ const string16& username,
+ const string16& password);
+
+ // Scan |data_| for prefix matches of |input| and add each to |suggestions|.
+ void GetSuggestions(const string16& input,
+ std::vector<string16>* suggestions);
+
+ // Access to password field to autocomplete on blur/username updates.
+ scoped_ptr<WebInputElementDelegate> password_delegate_;
+ scoped_ptr<WebInputElementDelegate> username_delegate_;
+
+ // Contains the extra logins for matching on delta/blur.
+ PasswordFormFillData data_;
+
+ DISALLOW_COPY_AND_ASSIGN(WebPasswordAutocompleteListenerImpl);
+};
+
+} // webkit_glue
+
+#endif // WEBKIT_GLUE_PASSWORD_AUTOCOMPLETE_LISTENER_H_
diff --git a/webkit/glue/webpasswordautocompletelistener_unittest.cc b/webkit/glue/webpasswordautocompletelistener_unittest.cc
new file mode 100644
index 0000000..5a05d46
--- /dev/null
+++ b/webkit/glue/webpasswordautocompletelistener_unittest.cc
@@ -0,0 +1,291 @@
+// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// The PasswordManagerAutocompleteTests in this file test only the
+// PasswordAutocompleteListener class implementation (and not any of the
+// higher level dom autocomplete framework).
+
+#include <string>
+
+#include "base/string_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/glue/form_field.h"
+#include "webkit/glue/webpasswordautocompletelistener_impl.h"
+
+using WebKit::WebString;
+using webkit_glue::FormField;
+using webkit_glue::PasswordFormDomManager;
+using webkit_glue::PasswordFormFillData;
+using webkit_glue::WebInputElementDelegate;
+using webkit_glue::WebPasswordAutocompleteListenerImpl;
+
+class TestWebInputElementDelegate : public WebInputElementDelegate {
+ public:
+ TestWebInputElementDelegate()
+ : WebInputElementDelegate(),
+ selection_start_(0),
+ selection_end_(0),
+ is_editable_(true),
+ is_autofilled_(false) {
+ }
+
+ // Override those methods we implicitly invoke in the tests.
+ virtual bool IsEditable() const {
+ return is_editable_;
+ }
+
+ virtual void SetValue(const string16& value) {
+ value_ = value;
+ }
+
+ virtual bool IsAutofilled() const { return is_autofilled_; }
+
+ virtual void SetAutofilled(bool autofilled) {
+ is_autofilled_ = autofilled;
+ }
+
+ virtual void SetSelectionRange(size_t start, size_t end) {
+ selection_start_ = start;
+ selection_end_ = end;
+ }
+
+ // Testing-only methods.
+ void set_is_editable(bool editable) {
+ is_editable_ = editable;
+ }
+
+ string16 value() const {
+ return value_;
+ }
+
+ size_t selection_start() const {
+ return selection_start_;
+ }
+
+ size_t selection_end() const {
+ return selection_end_;
+ }
+
+ private:
+ string16 value_;
+ size_t selection_start_;
+ size_t selection_end_;
+ bool is_editable_;
+ bool is_autofilled_;
+};
+
+namespace {
+class PasswordManagerAutocompleteTests : public testing::Test {
+ public:
+ PasswordManagerAutocompleteTests()
+ : username_delegate_(NULL),
+ password_delegate_(NULL) {
+ }
+
+ virtual void SetUp() {
+ // Add a preferred login and an additional login to the FillData.
+ username1_ = ASCIIToUTF16("alice");
+ password1_ = ASCIIToUTF16("password");
+ username2_ = ASCIIToUTF16("bob");
+ password2_ = ASCIIToUTF16("bobsyouruncle");
+ data_.basic_data.fields.push_back(FormField(string16(),
+ string16(),
+ username1_,
+ string16(),
+ 0));
+ data_.basic_data.fields.push_back(FormField(string16(),
+ string16(),
+ password1_,
+ string16(),
+ 0));
+ data_.additional_logins[username2_] = password2_;
+
+ username_delegate_ = new TestWebInputElementDelegate();
+ password_delegate_ = new TestWebInputElementDelegate();
+
+ testing::Test::SetUp();
+ }
+
+ protected:
+ // Create the WebPasswordAutocompleteListener associated with
+ // username_delegate_ and password_delegate_, should be called only once at
+ // the begining of the test.
+ WebKit::WebPasswordAutocompleteListener* CreateListener(
+ bool wait_for_username) {
+ DCHECK(!listener_.get());
+ data_.wait_for_username = wait_for_username;
+ listener_.reset(new WebPasswordAutocompleteListenerImpl(username_delegate_,
+ password_delegate_,
+ data_));
+ return listener_.get();
+ }
+
+ string16 username1_;
+ string16 password1_;
+ string16 username2_;
+ string16 password2_;
+ PasswordFormFillData data_;
+ TestWebInputElementDelegate* username_delegate_;
+ TestWebInputElementDelegate* password_delegate_;
+
+ private:
+ scoped_ptr<WebPasswordAutocompleteListenerImpl> listener_;
+};
+
+TEST_F(PasswordManagerAutocompleteTests, OnBlur) {
+ WebKit::WebPasswordAutocompleteListener* listener = CreateListener(false);
+
+ // Make the password field read-only.
+ password_delegate_->set_is_editable(false);
+ // Simulate a blur event on the username field, but r/o password won't fill.
+ listener->didBlurInputElement(username1_);
+ EXPECT_TRUE(password_delegate_->value().empty());
+ EXPECT_FALSE(password_delegate_->IsAutofilled());
+ password_delegate_->set_is_editable(true);
+
+ // Simulate a blur event on the username field and expect a password autofill.
+ listener->didBlurInputElement(username1_);
+ EXPECT_EQ(password1_, password_delegate_->value());
+ EXPECT_TRUE(password_delegate_->IsAutofilled());
+
+ // Now the user goes back and changes the username to something we don't
+ // have saved. The password should remain unchanged.
+ listener->didBlurInputElement(ASCIIToUTF16("blahblahblah"));
+ EXPECT_EQ(password1_, password_delegate_->value());
+ EXPECT_TRUE(password_delegate_->IsAutofilled());
+
+ // Now they type in the additional login username.
+ listener->didBlurInputElement(username2_);
+ EXPECT_EQ(password2_, password_delegate_->value());
+ EXPECT_TRUE(password_delegate_->IsAutofilled());
+}
+
+TEST_F(PasswordManagerAutocompleteTests, OnInlineAutocompleteNeeded) {
+ WebKit::WebPasswordAutocompleteListener* listener = CreateListener(false);
+
+ // Simulate the user typing in the first letter of 'alice', a stored username.
+ listener->performInlineAutocomplete(ASCIIToUTF16("a"), false, false);
+ // Both the username and password delegates should reflect selection
+ // of the stored login.
+ EXPECT_EQ(username1_, username_delegate_->value());
+ EXPECT_TRUE(username_delegate_->IsAutofilled());
+ EXPECT_EQ(password1_, password_delegate_->value());
+ EXPECT_TRUE(password_delegate_->IsAutofilled());
+ // And the selection should have been set to 'lice', the last 4 letters.
+ EXPECT_EQ(1U, username_delegate_->selection_start());
+ EXPECT_EQ(username1_.length(), username_delegate_->selection_end());
+ // And both fields should have the autofill style.
+ EXPECT_TRUE(username_delegate_->IsAutofilled());
+ EXPECT_TRUE(password_delegate_->IsAutofilled());
+
+ // Now the user types the next letter of the same username, 'l'.
+ listener->performInlineAutocomplete(ASCIIToUTF16("al"), false, false);
+ // Now the fields should have the same value, but the selection should have a
+ // different start value.
+ EXPECT_EQ(username1_, username_delegate_->value());
+ EXPECT_TRUE(username_delegate_->IsAutofilled());
+ EXPECT_EQ(password1_, password_delegate_->value());
+ EXPECT_TRUE(password_delegate_->IsAutofilled());
+ EXPECT_EQ(2U, username_delegate_->selection_start());
+ EXPECT_EQ(username1_.length(), username_delegate_->selection_end());
+
+ // Now lets say the user goes astray from the stored username and types
+ // the letter 'f', spelling 'alf'. We don't know alf (that's just sad),
+ // so in practice the username should no longer be 'alice' and the selected
+ // range should be empty. In our case, when the autocomplete code doesn't
+ // know the text, it won't set the value or the selection and hence our
+ // delegate methods won't get called. The WebCore::HTMLInputElement's value
+ // and selection would be set directly by WebCore in practice.
+
+ // Reset the delegate's test state so we can determine what, if anything,
+ // was set during performInlineAutocomplete.
+ username_delegate_->SetValue(string16());
+ username_delegate_->SetSelectionRange(0, 0);
+ listener->performInlineAutocomplete(ASCIIToUTF16("alf"), false, false);
+ EXPECT_EQ(0U, username_delegate_->selection_start());
+ EXPECT_EQ(0U, username_delegate_->selection_end());
+ // Username should not have been filled by us.
+ EXPECT_TRUE(username_delegate_->value().empty());
+ EXPECT_FALSE(username_delegate_->IsAutofilled());
+ EXPECT_TRUE(password_delegate_->value().empty());
+ EXPECT_FALSE(password_delegate_->IsAutofilled());
+
+ // Ok, so now the user removes all the text and enters the letter 'b'.
+ listener->performInlineAutocomplete(ASCIIToUTF16("b"), false, false);
+ // The username and password fields should match the 'bob' entry.
+ EXPECT_EQ(username2_, username_delegate_->value());
+ EXPECT_TRUE(username_delegate_->IsAutofilled());
+ EXPECT_EQ(password2_, password_delegate_->value());
+ EXPECT_TRUE(password_delegate_->IsAutofilled());
+ EXPECT_EQ(1U, username_delegate_->selection_start());
+ EXPECT_EQ(username2_.length(), username_delegate_->selection_end());
+}
+
+TEST_F(PasswordManagerAutocompleteTests, TestWaitUsername) {
+ // If we had an action authority mismatch (for example), we don't want to
+ // automatically autofill anything without some user interaction first.
+ // We require an explicit blur on the username field, and that a valid
+ // matching username is in the field, before we autofill passwords.
+ WebKit::WebPasswordAutocompleteListener* listener = CreateListener(true);
+
+ // In all cases, username_delegate should remain empty because we should
+ // never modify it when wait_for_username is true; only the user can by
+ // typing into (in real life) the HTMLInputElement.
+ password_delegate_->SetValue(string16());
+ listener->performInlineAutocomplete(ASCIIToUTF16("a"), false, false);
+ EXPECT_TRUE(username_delegate_->value().empty());
+ EXPECT_FALSE(username_delegate_->IsAutofilled());
+ EXPECT_TRUE(password_delegate_->value().empty());
+ EXPECT_FALSE(password_delegate_->IsAutofilled());
+ listener->performInlineAutocomplete(ASCIIToUTF16("al"), false, false);
+ EXPECT_TRUE(username_delegate_->value().empty());
+ EXPECT_FALSE(username_delegate_->IsAutofilled());
+ EXPECT_TRUE(password_delegate_->value().empty());
+ EXPECT_FALSE(password_delegate_->IsAutofilled());
+ listener->performInlineAutocomplete(ASCIIToUTF16("alice"), false, false);
+ EXPECT_TRUE(username_delegate_->value().empty());
+ EXPECT_FALSE(username_delegate_->IsAutofilled());
+ EXPECT_TRUE(password_delegate_->value().empty());
+ EXPECT_FALSE(password_delegate_->IsAutofilled());
+
+ listener->didBlurInputElement(ASCIIToUTF16("a"));
+ EXPECT_TRUE(username_delegate_->value().empty());
+ EXPECT_FALSE(username_delegate_->IsAutofilled());
+ EXPECT_TRUE(password_delegate_->value().empty());
+ EXPECT_FALSE(password_delegate_->IsAutofilled());
+ listener->didBlurInputElement(ASCIIToUTF16("ali"));
+ EXPECT_TRUE(username_delegate_->value().empty());
+ EXPECT_FALSE(username_delegate_->IsAutofilled());
+ EXPECT_TRUE(password_delegate_->value().empty());
+ EXPECT_FALSE(password_delegate_->IsAutofilled());
+
+ // Blur with 'alice' should allow password autofill.
+ listener->didBlurInputElement(ASCIIToUTF16("alice"));
+ EXPECT_TRUE(username_delegate_->value().empty());
+ EXPECT_FALSE(username_delegate_->IsAutofilled());
+ EXPECT_EQ(password1_, password_delegate_->value());
+ EXPECT_TRUE(password_delegate_->IsAutofilled());
+}
+
+// Tests that editing the password clears the autofilled password field.
+TEST_F(PasswordManagerAutocompleteTests, TestPasswordClearOnEdit) {
+ WebKit::WebPasswordAutocompleteListener* listener = CreateListener(false);
+
+ // User enters a known login.
+ listener->performInlineAutocomplete(ASCIIToUTF16("alice"), false, false);
+ // We are autofilled.
+ EXPECT_TRUE(username_delegate_->IsAutofilled());
+ EXPECT_EQ(password1_, password_delegate_->value());
+ EXPECT_TRUE(password_delegate_->IsAutofilled());
+
+ // User modifies the login name to an unknown one.
+ listener->performInlineAutocomplete(ASCIIToUTF16("alicia"), false, false);
+ // We should not be autofilled anymore and the password should have been
+ // cleared.
+ EXPECT_FALSE(username_delegate_->IsAutofilled());
+ EXPECT_FALSE(password_delegate_->IsAutofilled());
+ EXPECT_TRUE(password_delegate_->value().empty());
+}
+
+} // namespace
diff --git a/webkit/glue/webpreferences.cc b/webkit/glue/webpreferences.cc
new file mode 100644
index 0000000..987cef4
--- /dev/null
+++ b/webkit/glue/webpreferences.cc
@@ -0,0 +1,110 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/webpreferences.h"
+
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebRuntimeFeatures.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebKit.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebSettings.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebString.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURL.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebView.h"
+#include "webkit/glue/webkit_glue.h"
+
+using WebKit::WebRuntimeFeatures;
+using WebKit::WebSettings;
+using WebKit::WebString;
+using WebKit::WebURL;
+using WebKit::WebView;
+
+void WebPreferences::Apply(WebView* web_view) const {
+ WebSettings* settings = web_view->settings();
+ settings->setStandardFontFamily(WideToUTF16Hack(standard_font_family));
+ settings->setFixedFontFamily(WideToUTF16Hack(fixed_font_family));
+ settings->setSerifFontFamily(WideToUTF16Hack(serif_font_family));
+ settings->setSansSerifFontFamily(WideToUTF16Hack(sans_serif_font_family));
+ settings->setCursiveFontFamily(WideToUTF16Hack(cursive_font_family));
+ settings->setFantasyFontFamily(WideToUTF16Hack(fantasy_font_family));
+ settings->setDefaultFontSize(default_font_size);
+ settings->setDefaultFixedFontSize(default_fixed_font_size);
+ settings->setMinimumFontSize(minimum_font_size);
+ settings->setMinimumLogicalFontSize(minimum_logical_font_size);
+ settings->setDefaultTextEncodingName(ASCIIToUTF16(default_encoding));
+ settings->setJavaScriptEnabled(javascript_enabled);
+ settings->setWebSecurityEnabled(web_security_enabled);
+ settings->setJavaScriptCanOpenWindowsAutomatically(
+ javascript_can_open_windows_automatically);
+ settings->setLoadsImagesAutomatically(loads_images_automatically);
+ settings->setPluginsEnabled(plugins_enabled);
+ settings->setDOMPasteAllowed(dom_paste_enabled);
+ settings->setDeveloperExtrasEnabled(developer_extras_enabled);
+ settings->setNeedsSiteSpecificQuirks(site_specific_quirks_enabled);
+ settings->setShrinksStandaloneImagesToFit(shrinks_standalone_images_to_fit);
+ settings->setUsesEncodingDetector(uses_universal_detector);
+ settings->setTextAreasAreResizable(text_areas_are_resizable);
+ settings->setAllowScriptsToCloseWindows(allow_scripts_to_close_windows);
+ if (user_style_sheet_enabled)
+ settings->setUserStyleSheetLocation(user_style_sheet_location);
+ else
+ settings->setUserStyleSheetLocation(WebURL());
+ settings->setAuthorAndUserStylesEnabled(author_and_user_styles_enabled);
+ settings->setUsesPageCache(uses_page_cache);
+ settings->setDownloadableBinaryFontsEnabled(remote_fonts_enabled);
+ settings->setJavaScriptCanAccessClipboard(javascript_can_access_clipboard);
+ settings->setXSSAuditorEnabled(xss_auditor_enabled);
+ settings->setLocalStorageEnabled(local_storage_enabled);
+ WebRuntimeFeatures::enableDatabase(
+ WebRuntimeFeatures::isDatabaseEnabled() || databases_enabled);
+ settings->setOfflineWebApplicationCacheEnabled(application_cache_enabled);
+ settings->setHTML5ParserEnabled(enable_html5_parser);
+
+ // This setting affects the behavior of links in an editable region:
+ // clicking the link should select it rather than navigate to it.
+ // Safari uses the same default. It is unlikley an embedder would want to
+ // change this, since it would break existing rich text editors.
+ settings->setEditableLinkBehaviorNeverLive();
+
+ settings->setFontRenderingModeNormal();
+ settings->setJavaEnabled(java_enabled);
+
+ // Turn this on to cause WebCore to paint the resize corner for us.
+ settings->setShouldPaintCustomScrollbars(true);
+
+ // By default, allow_universal_access_from_file_urls is set to false and thus
+ // we mitigate attacks from local HTML files by not granting file:// URLs
+ // universal access. Only test shell will enable this.
+ settings->setAllowUniversalAccessFromFileURLs(
+ allow_universal_access_from_file_urls);
+ settings->setAllowFileAccessFromFileURLs(allow_file_access_from_file_urls);
+
+ // We prevent WebKit from checking if it needs to add a "text direction"
+ // submenu to a context menu. it is not only because we don't need the result
+ // but also because it cause a possible crash in Editor::hasBidiSelection().
+ settings->setTextDirectionSubmenuInclusionBehaviorNeverIncluded();
+
+ // Enable experimental WebGL support if requested on command line
+ // and support is compiled in.
+ settings->setExperimentalWebGLEnabled(experimental_webgl_enabled);
+
+ // Display colored borders around composited render layers if requested
+ // on command line.
+ settings->setShowDebugBorders(show_composited_layer_borders);
+
+ // Enable gpu-accelerated compositing if requested on the command line.
+ settings->setAcceleratedCompositingEnabled(accelerated_compositing_enabled);
+
+ // Enable memory info reporting to page if requested on the command line.
+ settings->setMemoryInfoEnabled(memory_info_enabled);
+
+ for (WebInspectorPreferences::const_iterator it = inspector_settings.begin();
+ it != inspector_settings.end(); ++it)
+ web_view->setInspectorSetting(WebString::fromUTF8(it->first),
+ WebString::fromUTF8(it->second));
+
+ // Tabs to link is not part of the settings. WebCore calls
+ // ChromeClient::tabsToLinks which is part of the glue code.
+ web_view->setTabsToLinks(tabs_to_links);
+}
diff --git a/webkit/glue/webpreferences.h b/webkit/glue/webpreferences.h
new file mode 100644
index 0000000..3e4f926
--- /dev/null
+++ b/webkit/glue/webpreferences.h
@@ -0,0 +1,120 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// A struct for managing webkit's settings.
+//
+// Adding new values to this class probably involves updating
+// WebKit::WebSettings, common/render_messages.h, and
+// browser/profile.cc.
+
+#ifndef WEBKIT_GLUE_WEBPREFERENCES_H__
+#define WEBKIT_GLUE_WEBPREFERENCES_H__
+
+#include <string>
+#include <vector>
+#include "googleurl/src/gurl.h"
+
+namespace WebKit {
+class WebView;
+}
+
+struct WebPreferences {
+ std::wstring standard_font_family;
+ std::wstring fixed_font_family;
+ std::wstring serif_font_family;
+ std::wstring sans_serif_font_family;
+ std::wstring cursive_font_family;
+ std::wstring fantasy_font_family;
+ int default_font_size;
+ int default_fixed_font_size;
+ int minimum_font_size;
+ int minimum_logical_font_size;
+ std::string default_encoding;
+ bool javascript_enabled;
+ bool web_security_enabled;
+ bool javascript_can_open_windows_automatically;
+ bool loads_images_automatically;
+ bool plugins_enabled;
+ bool dom_paste_enabled;
+ bool developer_extras_enabled;
+ typedef std::vector<std::pair<std::string, std::string> >
+ WebInspectorPreferences;
+ WebInspectorPreferences inspector_settings;
+ bool site_specific_quirks_enabled;
+ bool shrinks_standalone_images_to_fit;
+ bool uses_universal_detector;
+ bool text_areas_are_resizable;
+ bool java_enabled;
+ bool allow_scripts_to_close_windows;
+ bool uses_page_cache;
+ bool remote_fonts_enabled;
+ bool javascript_can_access_clipboard;
+ bool xss_auditor_enabled;
+ bool local_storage_enabled;
+ bool databases_enabled;
+ bool application_cache_enabled;
+ bool tabs_to_links;
+
+ bool user_style_sheet_enabled;
+ GURL user_style_sheet_location;
+ bool author_and_user_styles_enabled;
+ bool allow_universal_access_from_file_urls;
+ bool allow_file_access_from_file_urls;
+ bool experimental_webgl_enabled;
+ bool show_composited_layer_borders;
+ bool accelerated_compositing_enabled;
+ bool enable_html5_parser;
+ bool memory_info_enabled;
+
+ // We try to keep the default values the same as the default values in
+ // chrome, except for the cases where it would require lots of extra work for
+ // the embedder to use the same default value.
+ WebPreferences()
+ : standard_font_family(L"Times New Roman"),
+ fixed_font_family(L"Courier New"),
+ serif_font_family(L"Times New Roman"),
+ sans_serif_font_family(L"Arial"),
+ cursive_font_family(L"Script"),
+ fantasy_font_family(), // Not sure what to use on Windows.
+ default_font_size(16),
+ default_fixed_font_size(13),
+ minimum_font_size(1),
+ minimum_logical_font_size(6),
+ default_encoding("ISO-8859-1"),
+ javascript_enabled(true),
+ web_security_enabled(true),
+ javascript_can_open_windows_automatically(true),
+ loads_images_automatically(true),
+ plugins_enabled(true),
+ dom_paste_enabled(false), // enables execCommand("paste")
+ developer_extras_enabled(false), // Requires extra work by embedder
+ site_specific_quirks_enabled(false),
+ shrinks_standalone_images_to_fit(true),
+ uses_universal_detector(false), // Disabled: page cycler regression
+ text_areas_are_resizable(true),
+ java_enabled(true),
+ allow_scripts_to_close_windows(false),
+ uses_page_cache(false),
+ remote_fonts_enabled(true),
+ javascript_can_access_clipboard(false),
+ xss_auditor_enabled(false),
+ local_storage_enabled(false),
+ databases_enabled(false),
+ application_cache_enabled(false),
+ tabs_to_links(true),
+ user_style_sheet_enabled(false),
+ author_and_user_styles_enabled(true),
+ allow_universal_access_from_file_urls(false),
+ allow_file_access_from_file_urls(false),
+ experimental_webgl_enabled(false),
+ show_composited_layer_borders(false),
+ accelerated_compositing_enabled(false),
+ enable_html5_parser(true),
+ memory_info_enabled(false) {
+ }
+
+ void Apply(WebKit::WebView* web_view) const;
+};
+
+#endif // WEBKIT_GLUE_WEBPREFERENCES_H__
diff --git a/webkit/glue/websocketstreamhandle_bridge.h b/webkit/glue/websocketstreamhandle_bridge.h
new file mode 100644
index 0000000..ee897fd
--- /dev/null
+++ b/webkit/glue/websocketstreamhandle_bridge.h
@@ -0,0 +1,47 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_WEBSOCKETSTREAMHANDLE_BRIDGE_H_
+#define WEBKIT_GLUE_WEBSOCKETSTREAMHANDLE_BRIDGE_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/ref_counted.h"
+
+class GURL;
+
+namespace WebKit {
+class WebSocketStreamHandle;
+}
+
+namespace webkit_glue {
+
+class WebSocketStreamHandleDelegate;
+
+class WebSocketStreamHandleBridge
+ : public base::RefCountedThreadSafe<WebSocketStreamHandleBridge> {
+ public:
+ static WebSocketStreamHandleBridge* Create(
+ WebKit::WebSocketStreamHandle* handle,
+ WebSocketStreamHandleDelegate* delegate);
+
+ virtual void Connect(const GURL& url) = 0;
+
+ virtual bool Send(const std::vector<char>& data) = 0;
+
+ virtual void Close() = 0;
+
+ protected:
+ friend class base::RefCountedThreadSafe<WebSocketStreamHandleBridge>;
+ WebSocketStreamHandleBridge() {}
+ virtual ~WebSocketStreamHandleBridge() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(WebSocketStreamHandleBridge);
+};
+
+} // namespace webkit_glue
+
+#endif // WEBKIT_GLUE_WEBSOCKETSTREAMHANDLE_BRIDGE_H_
diff --git a/webkit/glue/websocketstreamhandle_delegate.h b/webkit/glue/websocketstreamhandle_delegate.h
new file mode 100644
index 0000000..2148699
--- /dev/null
+++ b/webkit/glue/websocketstreamhandle_delegate.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_WEBSOCKETSTREAMHANDLE_DELEGATE_H_
+#define WEBKIT_GLUE_WEBSOCKETSTREAMHANDLE_DELEGATE_H_
+
+class GURL;
+
+namespace WebKit {
+class WebSocketStreamHandle;
+}
+
+namespace webkit_glue {
+
+class WebSocketStreamHandleDelegate {
+ public:
+ WebSocketStreamHandleDelegate() {}
+ virtual ~WebSocketStreamHandleDelegate() {}
+
+ virtual void WillOpenStream(WebKit::WebSocketStreamHandle* handle,
+ const GURL& url) {}
+ virtual void WillSendData(WebKit::WebSocketStreamHandle* handle,
+ const char* data, int len) {}
+
+ virtual void DidOpenStream(WebKit::WebSocketStreamHandle* handle,
+ int max_amount_send_allowed) {}
+ virtual void DidSendData(WebKit::WebSocketStreamHandle* handle,
+ int amount_sent) {}
+ virtual void DidReceiveData(WebKit::WebSocketStreamHandle* handle,
+ const char* data, int len) {}
+ virtual void DidClose(WebKit::WebSocketStreamHandle*) {}
+};
+
+} // namespace webkit_glue
+
+#endif // WEBKIT_GLUE_WEBSOCKETSTREAMHANDLE_DELEGATE_H_
diff --git a/webkit/glue/websocketstreamhandle_impl.cc b/webkit/glue/websocketstreamhandle_impl.cc
new file mode 100644
index 0000000..60faea2
--- /dev/null
+++ b/webkit/glue/websocketstreamhandle_impl.cc
@@ -0,0 +1,169 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// An implementation of WebSocketStreamHandle.
+
+#include "webkit/glue/websocketstreamhandle_impl.h"
+
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/ref_counted.h"
+#include "base/scoped_ptr.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebData.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURL.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebSocketStreamHandleClient.h"
+#include "webkit/glue/websocketstreamhandle_bridge.h"
+#include "webkit/glue/websocketstreamhandle_delegate.h"
+
+namespace webkit_glue {
+
+// WebSocketStreamHandleImpl::Context -----------------------------------------
+
+class WebSocketStreamHandleImpl::Context
+ : public base::RefCounted<Context>,
+ public WebSocketStreamHandleDelegate {
+ public:
+ explicit Context(WebSocketStreamHandleImpl* handle);
+
+ WebKit::WebSocketStreamHandleClient* client() const { return client_; }
+ void set_client(WebKit::WebSocketStreamHandleClient* client) {
+ client_ = client;
+ }
+
+ void Connect(const WebKit::WebURL& url);
+ bool Send(const WebKit::WebData& data);
+ void Close();
+
+ // Must be called before |handle_| or |client_| is deleted.
+ // Once detached, it never calls |client_| back.
+ void Detach();
+
+ // WebSocketStreamHandleDelegate methods:
+ virtual void DidOpenStream(WebKit::WebSocketStreamHandle*, int);
+ virtual void DidSendData(WebKit::WebSocketStreamHandle*, int);
+ virtual void DidReceiveData(
+ WebKit::WebSocketStreamHandle*, const char*, int);
+ virtual void DidClose(WebKit::WebSocketStreamHandle*);
+
+ private:
+ friend class base::RefCounted<Context>;
+ ~Context() {
+ DCHECK(!handle_);
+ DCHECK(!client_);
+ DCHECK(!bridge_);
+ }
+
+ WebSocketStreamHandleImpl* handle_;
+ WebKit::WebSocketStreamHandleClient* client_;
+ // |bridge_| is alive from Connect to DidClose, so Context must be alive
+ // in the time period.
+ scoped_refptr<WebSocketStreamHandleBridge> bridge_;
+
+ DISALLOW_COPY_AND_ASSIGN(Context);
+};
+
+WebSocketStreamHandleImpl::Context::Context(WebSocketStreamHandleImpl* handle)
+ : handle_(handle),
+ client_(NULL),
+ bridge_(NULL) {
+}
+
+void WebSocketStreamHandleImpl::Context::Connect(const WebKit::WebURL& url) {
+ LOG(INFO) << "Connect url=" << url;
+ DCHECK(!bridge_);
+ bridge_ = WebSocketStreamHandleBridge::Create(handle_, this);
+ AddRef(); // Will be released by DidClose().
+ bridge_->Connect(url);
+}
+
+bool WebSocketStreamHandleImpl::Context::Send(const WebKit::WebData& data) {
+ LOG(INFO) << "Send data.size=" << data.size();
+ DCHECK(bridge_);
+ return bridge_->Send(
+ std::vector<char>(data.data(), data.data() + data.size()));
+}
+
+void WebSocketStreamHandleImpl::Context::Close() {
+ LOG(INFO) << "Close";
+ if (bridge_)
+ bridge_->Close();
+}
+
+void WebSocketStreamHandleImpl::Context::Detach() {
+ handle_ = NULL;
+ client_ = NULL;
+ // If Connect was called, |bridge_| is not NULL, so that this Context closes
+ // the |bridge_| here. Then |bridge_| will call back DidClose, and will
+ // be released by itself.
+ // Otherwise, |bridge_| is NULL.
+ if (bridge_)
+ bridge_->Close();
+}
+
+void WebSocketStreamHandleImpl::Context::DidOpenStream(
+ WebKit::WebSocketStreamHandle* web_handle, int max_amount_send_allowed) {
+ LOG(INFO) << "DidOpen";
+ if (client_)
+ client_->didOpenStream(handle_, max_amount_send_allowed);
+}
+
+void WebSocketStreamHandleImpl::Context::DidSendData(
+ WebKit::WebSocketStreamHandle* web_handle, int amount_sent) {
+ if (client_)
+ client_->didSendData(handle_, amount_sent);
+}
+
+void WebSocketStreamHandleImpl::Context::DidReceiveData(
+ WebKit::WebSocketStreamHandle* web_handle, const char* data, int size) {
+ if (client_)
+ client_->didReceiveData(handle_, WebKit::WebData(data, size));
+}
+
+void WebSocketStreamHandleImpl::Context::DidClose(
+ WebKit::WebSocketStreamHandle* web_handle) {
+ LOG(INFO) << "DidClose";
+ bridge_ = NULL;
+ WebSocketStreamHandleImpl* handle = handle_;
+ handle_ = NULL;
+ if (client_) {
+ WebKit::WebSocketStreamHandleClient* client = client_;
+ client_ = NULL;
+ client->didClose(handle);
+ }
+ Release();
+}
+
+// WebSocketStreamHandleImpl ------------------------------------------------
+
+WebSocketStreamHandleImpl::WebSocketStreamHandleImpl()
+ : ALLOW_THIS_IN_INITIALIZER_LIST(context_(new Context(this))) {
+}
+
+WebSocketStreamHandleImpl::~WebSocketStreamHandleImpl() {
+ // We won't receive any events from |context_|.
+ // |context_| is ref counted, and will be released when it received
+ // DidClose.
+ context_->Detach();
+}
+
+void WebSocketStreamHandleImpl::connect(
+ const WebKit::WebURL& url, WebKit::WebSocketStreamHandleClient* client) {
+ LOG(INFO) << "connect url=" << url;
+ DCHECK(!context_->client());
+ context_->set_client(client);
+
+ context_->Connect(url);
+}
+
+bool WebSocketStreamHandleImpl::send(const WebKit::WebData& data) {
+ return context_->Send(data);
+}
+
+void WebSocketStreamHandleImpl::close() {
+ context_->Close();
+}
+
+} // namespace webkit_glue
diff --git a/webkit/glue/websocketstreamhandle_impl.h b/webkit/glue/websocketstreamhandle_impl.h
new file mode 100644
index 0000000..f8a08f4
--- /dev/null
+++ b/webkit/glue/websocketstreamhandle_impl.h
@@ -0,0 +1,34 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_WEBSOCKETSTREAMHANDLE_IMPL_H_
+#define WEBKIT_GLUE_WEBSOCKETSTREAMHANDLE_IMPL_H_
+
+#include "base/ref_counted.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebSocketStreamHandle.h"
+
+namespace webkit_glue {
+
+class WebSocketStreamHandleImpl : public WebKit::WebSocketStreamHandle {
+ public:
+ WebSocketStreamHandleImpl();
+ virtual ~WebSocketStreamHandleImpl();
+
+ // WebSocketStreamHandle methods:
+ virtual void connect(
+ const WebKit::WebURL& url,
+ WebKit::WebSocketStreamHandleClient* client);
+ virtual bool send(const WebKit::WebData& data);
+ virtual void close();
+
+ private:
+ class Context;
+ scoped_refptr<Context> context_;
+
+ DISALLOW_COPY_AND_ASSIGN(WebSocketStreamHandleImpl);
+};
+
+} // namespace webkit_glue
+
+#endif // WEBKIT_GLUE_WEBSOCKETSTREAMHANDLE_IMPL_H_
diff --git a/webkit/glue/webthemeengine_impl_win.cc b/webkit/glue/webthemeengine_impl_win.cc
new file mode 100644
index 0000000..85dc113
--- /dev/null
+++ b/webkit/glue/webthemeengine_impl_win.cc
@@ -0,0 +1,142 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/webthemeengine_impl_win.h"
+
+#include "gfx/native_theme_win.h"
+#include "skia/ext/platform_canvas.h"
+#include "skia/ext/skia_utils_win.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebRect.h"
+
+using WebKit::WebCanvas;
+using WebKit::WebColor;
+using WebKit::WebRect;
+
+namespace webkit_glue {
+
+static RECT WebRectToRECT(const WebRect& rect) {
+ RECT result;
+ result.left = rect.x;
+ result.top = rect.y;
+ result.right = rect.x + rect.width;
+ result.bottom = rect.y + rect.height;
+ return result;
+}
+
+void WebThemeEngineImpl::paintButton(
+ WebCanvas* canvas, int part, int state, int classic_state,
+ const WebRect& rect) {
+ HDC hdc = canvas->beginPlatformPaint();
+
+ RECT native_rect = WebRectToRECT(rect);
+ gfx::NativeTheme::instance()->PaintButton(
+ hdc, part, state, classic_state, &native_rect);
+
+ canvas->endPlatformPaint();
+}
+
+void WebThemeEngineImpl::paintMenuList(
+ WebCanvas* canvas, int part, int state, int classic_state,
+ const WebRect& rect) {
+ HDC hdc = canvas->beginPlatformPaint();
+
+ RECT native_rect = WebRectToRECT(rect);
+ gfx::NativeTheme::instance()->PaintMenuList(
+ hdc, part, state, classic_state, &native_rect);
+
+ canvas->endPlatformPaint();
+}
+
+void WebThemeEngineImpl::paintScrollbarArrow(
+ WebCanvas* canvas, int state, int classic_state,
+ const WebRect& rect) {
+ HDC hdc = canvas->beginPlatformPaint();
+
+ RECT native_rect = WebRectToRECT(rect);
+ gfx::NativeTheme::instance()->PaintScrollbarArrow(
+ hdc, state, classic_state, &native_rect);
+
+ canvas->endPlatformPaint();
+}
+
+void WebThemeEngineImpl::paintScrollbarThumb(
+ WebCanvas* canvas, int part, int state, int classic_state,
+ const WebRect& rect) {
+ HDC hdc = canvas->beginPlatformPaint();
+
+ RECT native_rect = WebRectToRECT(rect);
+ gfx::NativeTheme::instance()->PaintScrollbarThumb(
+ hdc, part, state, classic_state, &native_rect);
+
+ canvas->endPlatformPaint();
+}
+
+void WebThemeEngineImpl::paintScrollbarTrack(
+ WebCanvas* canvas, int part, int state, int classic_state,
+ const WebRect& rect, const WebRect& align_rect) {
+ HDC hdc = canvas->beginPlatformPaint();
+
+ RECT native_rect = WebRectToRECT(rect);
+ RECT native_align_rect = WebRectToRECT(align_rect);
+ gfx::NativeTheme::instance()->PaintScrollbarTrack(
+ hdc, part, state, classic_state, &native_rect, &native_align_rect,
+ canvas);
+
+ canvas->endPlatformPaint();
+}
+
+void WebThemeEngineImpl::paintSpinButton(
+ WebCanvas* canvas, int part, int state, int classic_state,
+ const WebRect& rect) {
+ HDC hdc = canvas->beginPlatformPaint();
+
+ RECT native_rect = WebRectToRECT(rect);
+ gfx::NativeTheme::instance()->PaintSpinButton(
+ hdc, part, state, classic_state, &native_rect);
+
+ canvas->endPlatformPaint();
+}
+
+void WebThemeEngineImpl::paintTextField(
+ WebCanvas* canvas, int part, int state, int classic_state,
+ const WebRect& rect, WebColor color, bool fill_content_area,
+ bool draw_edges) {
+ HDC hdc = canvas->beginPlatformPaint();
+
+ RECT native_rect = WebRectToRECT(rect);
+ COLORREF c = skia::SkColorToCOLORREF(color);
+
+ gfx::NativeTheme::instance()->PaintTextField(
+ hdc, part, state, classic_state, &native_rect, c, fill_content_area,
+ draw_edges);
+
+ canvas->endPlatformPaint();
+}
+
+void WebThemeEngineImpl::paintTrackbar(
+ WebCanvas* canvas, int part, int state, int classic_state,
+ const WebRect& rect) {
+ HDC hdc = canvas->beginPlatformPaint();
+
+ RECT native_rect = WebRectToRECT(rect);
+ gfx::NativeTheme::instance()->PaintTrackbar(
+ hdc, part, state, classic_state, &native_rect, canvas);
+
+ canvas->endPlatformPaint();
+}
+
+void WebThemeEngineImpl::paintProgressBar(
+ WebCanvas* canvas, const WebRect& barRect, const WebRect& valueRect,
+ bool determinate, double animatedSeconds)
+{
+ HDC hdc = canvas->beginPlatformPaint();
+ RECT native_bar_rect = WebRectToRECT(barRect);
+ RECT native_value_rect = WebRectToRECT(valueRect);
+ gfx::NativeTheme::instance()->PaintProgressBar(
+ hdc, &native_bar_rect,
+ &native_value_rect, determinate, animatedSeconds, canvas);
+ canvas->endPlatformPaint();
+}
+
+} // namespace webkit_glue
diff --git a/webkit/glue/webthemeengine_impl_win.h b/webkit/glue/webthemeengine_impl_win.h
new file mode 100644
index 0000000..533ea19
--- /dev/null
+++ b/webkit/glue/webthemeengine_impl_win.h
@@ -0,0 +1,48 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBTHEMEENGINE_IMPL_WIN_H_
+#define WEBTHEMEENGINE_IMPL_WIN_H_
+
+#include "third_party/WebKit/WebKit/chromium/public/WebThemeEngine.h"
+
+namespace webkit_glue {
+
+class WebThemeEngineImpl : public WebKit::WebThemeEngine {
+ public:
+ // WebThemeEngine methods:
+ virtual void paintButton(
+ WebKit::WebCanvas*, int part, int state, int classic_state,
+ const WebKit::WebRect&);
+ virtual void paintMenuList(
+ WebKit::WebCanvas*, int part, int state, int classic_state,
+ const WebKit::WebRect&);
+ virtual void paintScrollbarArrow(
+ WebKit::WebCanvas*, int state, int classic_state,
+ const WebKit::WebRect&);
+ virtual void paintScrollbarThumb(
+ WebKit::WebCanvas*, int part, int state, int classic_state,
+ const WebKit::WebRect&);
+ virtual void paintScrollbarTrack(
+ WebKit::WebCanvas*, int part, int state, int classic_state,
+ const WebKit::WebRect&, const WebKit::WebRect& align_rect);
+ virtual void paintSpinButton(
+ WebKit::WebCanvas*, int part, int state, int classic_state,
+ const WebKit::WebRect&);
+ virtual void paintTextField(
+ WebKit::WebCanvas*, int part, int state, int classic_state,
+ const WebKit::WebRect&, WebKit::WebColor, bool fill_content_area,
+ bool draw_edges);
+ virtual void paintTrackbar(
+ WebKit::WebCanvas*, int part, int state, int classic_state,
+ const WebKit::WebRect&);
+ virtual void paintProgressBar(
+ WebKit::WebCanvas*, const WebKit::WebRect& barRect,
+ const WebKit::WebRect& valueRect, bool determinate,
+ double animatedSeconds);
+};
+
+} // namespace webkit_glue
+
+#endif // WEBTHEMEENGINE_IMPL_WIN_H_
diff --git a/webkit/glue/weburlloader_impl.cc b/webkit/glue/weburlloader_impl.cc
new file mode 100644
index 0000000..f235734
--- /dev/null
+++ b/webkit/glue/weburlloader_impl.cc
@@ -0,0 +1,688 @@
+// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// An implementation of WebURLLoader in terms of ResourceLoaderBridge.
+
+#include "webkit/glue/weburlloader_impl.h"
+
+#include "base/file_path.h"
+#include "base/message_loop.h"
+#include "base/process_util.h"
+#include "base/scoped_ptr.h"
+#include "base/string_util.h"
+#include "base/time.h"
+#include "net/base/data_url.h"
+#include "net/base/load_flags.h"
+#include "net/base/net_errors.h"
+#include "net/base/net_util.h"
+#include "net/http/http_response_headers.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebHTTPHeaderVisitor.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebSecurityPolicy.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURL.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURLError.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURLLoadTiming.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURLLoaderClient.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURLRequest.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURLResponse.h"
+#include "webkit/glue/ftp_directory_listing_response_delegate.h"
+#include "webkit/glue/multipart_response_delegate.h"
+#include "webkit/glue/resource_loader_bridge.h"
+#include "webkit/glue/site_isolation_metrics.h"
+#include "webkit/glue/webkit_glue.h"
+
+using base::Time;
+using base::TimeDelta;
+using WebKit::WebData;
+using WebKit::WebHTTPBody;
+using WebKit::WebHTTPHeaderVisitor;
+using WebKit::WebSecurityPolicy;
+using WebKit::WebString;
+using WebKit::WebURL;
+using WebKit::WebURLError;
+using WebKit::WebURLLoadTiming;
+using WebKit::WebURLLoader;
+using WebKit::WebURLLoaderClient;
+using WebKit::WebURLRequest;
+using WebKit::WebURLResponse;
+
+namespace webkit_glue {
+
+// Utilities ------------------------------------------------------------------
+
+namespace {
+
+class HeaderFlattener : public WebHTTPHeaderVisitor {
+ public:
+ explicit HeaderFlattener(int load_flags)
+ : load_flags_(load_flags),
+ has_accept_header_(false) {
+ }
+
+ virtual void visitHeader(const WebString& name, const WebString& value) {
+ // TODO(darin): is UTF-8 really correct here? It is if the strings are
+ // already ASCII (i.e., if they are already escaped properly).
+ const std::string& name_utf8 = name.utf8();
+ const std::string& value_utf8 = value.utf8();
+
+ // Skip over referrer headers found in the header map because we already
+ // pulled it out as a separate parameter. We likewise prune the UA since
+ // that will be added back by the network layer.
+ if (LowerCaseEqualsASCII(name_utf8, "referer") ||
+ LowerCaseEqualsASCII(name_utf8, "user-agent"))
+ return;
+
+ // Skip over "Cache-Control: max-age=0" header if the corresponding
+ // load flag is already specified. FrameLoader sets both the flag and
+ // the extra header -- the extra header is redundant since our network
+ // implementation will add the necessary headers based on load flags.
+ // See http://code.google.com/p/chromium/issues/detail?id=3434.
+ if ((load_flags_ & net::LOAD_VALIDATE_CACHE) &&
+ LowerCaseEqualsASCII(name_utf8, "cache-control") &&
+ LowerCaseEqualsASCII(value_utf8, "max-age=0"))
+ return;
+
+ if (LowerCaseEqualsASCII(name_utf8, "accept"))
+ has_accept_header_ = true;
+
+ if (!buffer_.empty())
+ buffer_.append("\r\n");
+ buffer_.append(name_utf8 + ": " + value_utf8);
+ }
+
+ const std::string& GetBuffer() {
+ // In some cases, WebKit doesn't add an Accept header, but not having the
+ // header confuses some web servers. See bug 808613.
+ if (!has_accept_header_) {
+ if (!buffer_.empty())
+ buffer_.append("\r\n");
+ buffer_.append("Accept: */*");
+ has_accept_header_ = true;
+ }
+ return buffer_;
+ }
+
+ private:
+ int load_flags_;
+ std::string buffer_;
+ bool has_accept_header_;
+};
+
+ResourceType::Type FromTargetType(WebURLRequest::TargetType type) {
+ switch (type) {
+ case WebURLRequest::TargetIsMainFrame:
+ return ResourceType::MAIN_FRAME;
+ case WebURLRequest::TargetIsSubframe:
+ return ResourceType::SUB_FRAME;
+ case WebURLRequest::TargetIsSubresource:
+ return ResourceType::SUB_RESOURCE;
+ case WebURLRequest::TargetIsStyleSheet:
+ return ResourceType::STYLESHEET;
+ case WebURLRequest::TargetIsScript:
+ return ResourceType::SCRIPT;
+ case WebURLRequest::TargetIsFontResource:
+ return ResourceType::FONT_RESOURCE;
+ case WebURLRequest::TargetIsImage:
+ return ResourceType::IMAGE;
+ case WebURLRequest::TargetIsObject:
+ return ResourceType::OBJECT;
+ case WebURLRequest::TargetIsMedia:
+ return ResourceType::MEDIA;
+ case WebURLRequest::TargetIsWorker:
+ return ResourceType::WORKER;
+ case WebURLRequest::TargetIsSharedWorker:
+ return ResourceType::SHARED_WORKER;
+ default:
+ NOTREACHED();
+ return ResourceType::SUB_RESOURCE;
+ }
+}
+
+// Extracts the information from a data: url.
+bool GetInfoFromDataURL(const GURL& url,
+ ResourceLoaderBridge::ResponseInfo* info,
+ std::string* data, URLRequestStatus* status) {
+ std::string mime_type;
+ std::string charset;
+ if (net::DataURL::Parse(url, &mime_type, &charset, data)) {
+ *status = URLRequestStatus(URLRequestStatus::SUCCESS, 0);
+ info->request_time = Time::Now();
+ info->response_time = Time::Now();
+ info->headers = NULL;
+ info->mime_type.swap(mime_type);
+ info->charset.swap(charset);
+ info->security_info.clear();
+ info->content_length = -1;
+
+ return true;
+ }
+
+ *status = URLRequestStatus(URLRequestStatus::FAILED, net::ERR_INVALID_URL);
+ return false;
+}
+
+void PopulateURLResponse(
+ const GURL& url,
+ const ResourceLoaderBridge::ResponseInfo& info,
+ WebURLResponse* response) {
+ response->setURL(url);
+ response->setResponseTime(info.response_time.ToDoubleT());
+ response->setMIMEType(WebString::fromUTF8(info.mime_type));
+ response->setTextEncodingName(WebString::fromUTF8(info.charset));
+ response->setExpectedContentLength(info.content_length);
+ response->setSecurityInfo(info.security_info);
+ response->setAppCacheID(info.appcache_id);
+ response->setAppCacheManifestURL(info.appcache_manifest_url);
+ response->setWasCached(!info.load_timing.base_time.is_null() &&
+ info.response_time < info.load_timing.base_time);
+ response->setWasFetchedViaSPDY(info.was_fetched_via_spdy);
+ response->setWasNpnNegotiated(info.was_npn_negotiated);
+ response->setWasAlternateProtocolAvailable(
+ info.was_alternate_protocol_available);
+ response->setWasFetchedViaProxy(info.was_fetched_via_proxy);
+ response->setConnectionID(info.connection_id);
+ response->setConnectionReused(info.connection_reused);
+
+ WebURLLoadTiming timing;
+ timing.initialize();
+ const ResourceLoaderBridge::LoadTimingInfo& timing_info = info.load_timing;
+ timing.setRequestTime(timing_info.base_time.ToDoubleT());
+ timing.setProxyStart(timing_info.proxy_start);
+ timing.setProxyEnd(timing_info.proxy_end);
+ timing.setDNSStart(timing_info.dns_start);
+ timing.setDNSEnd(timing_info.dns_end);
+ timing.setConnectStart(timing_info.connect_start);
+ timing.setConnectEnd(timing_info.connect_end);
+ timing.setSSLStart(timing_info.ssl_start);
+ timing.setSSLEnd(timing_info.ssl_end);
+ timing.setSendStart(timing_info.send_start);
+ timing.setSendEnd(timing_info.send_end);
+ timing.setReceiveHeadersEnd(timing_info.receive_headers_end);
+ response->setLoadTiming(timing);
+
+ const net::HttpResponseHeaders* headers = info.headers;
+ if (!headers)
+ return;
+
+ response->setHTTPStatusCode(headers->response_code());
+ response->setHTTPStatusText(WebString::fromUTF8(headers->GetStatusText()));
+
+ // TODO(darin): We should leverage HttpResponseHeaders for this, and this
+ // should be using the same code as ResourceDispatcherHost.
+ // TODO(jungshik): Figure out the actual value of the referrer charset and
+ // pass it to GetSuggestedFilename.
+ std::string value;
+ if (headers->EnumerateHeader(NULL, "content-disposition", &value)) {
+ response->setSuggestedFileName(webkit_glue::FilePathToWebString(
+ net::GetSuggestedFilename(url, value, "", FilePath())));
+ }
+
+ Time time_val;
+ if (headers->GetLastModifiedValue(&time_val))
+ response->setLastModifiedDate(time_val.ToDoubleT());
+
+ // Build up the header map.
+ void* iter = NULL;
+ std::string name;
+ while (headers->EnumerateHeaderLines(&iter, &name, &value)) {
+ response->addHTTPHeaderField(WebString::fromUTF8(name),
+ WebString::fromUTF8(value));
+ }
+}
+
+} // namespace
+
+// WebURLLoaderImpl::Context --------------------------------------------------
+
+// This inner class exists since the WebURLLoader may be deleted while inside a
+// call to WebURLLoaderClient. The bridge requires its Peer to stay alive
+// until it receives OnCompletedRequest.
+class WebURLLoaderImpl::Context : public base::RefCounted<Context>,
+ public ResourceLoaderBridge::Peer {
+ public:
+ explicit Context(WebURLLoaderImpl* loader);
+
+ WebURLLoaderClient* client() const { return client_; }
+ void set_client(WebURLLoaderClient* client) { client_ = client; }
+
+ void Cancel();
+ void SetDefersLoading(bool value);
+ void Start(
+ const WebURLRequest& request,
+ ResourceLoaderBridge::SyncLoadResponse* sync_load_response);
+
+ // ResourceLoaderBridge::Peer methods:
+ virtual void OnUploadProgress(uint64 position, uint64 size);
+ virtual bool OnReceivedRedirect(
+ const GURL& new_url,
+ const ResourceLoaderBridge::ResponseInfo& info,
+ bool* has_new_first_party_for_cookies,
+ GURL* new_first_party_for_cookies);
+ virtual void OnReceivedResponse(
+ const ResourceLoaderBridge::ResponseInfo& info, bool content_filtered);
+ virtual void OnReceivedData(const char* data, int len);
+ virtual void OnReceivedCachedMetadata(const char* data, int len);
+ virtual void OnCompletedRequest(
+ const URLRequestStatus& status, const std::string& security_info);
+ virtual GURL GetURLForDebugging() const;
+
+ private:
+ friend class base::RefCounted<Context>;
+ ~Context() {}
+
+ void HandleDataURL();
+
+ WebURLLoaderImpl* loader_;
+ WebURLRequest request_;
+ WebURLLoaderClient* client_;
+ scoped_ptr<ResourceLoaderBridge> bridge_;
+ scoped_ptr<FtpDirectoryListingResponseDelegate> ftp_listing_delegate_;
+ scoped_ptr<MultipartResponseDelegate> multipart_delegate_;
+
+ // TODO(japhet): Storing this is a temporary hack for site isolation logging.
+ WebURL response_url_;
+};
+
+WebURLLoaderImpl::Context::Context(WebURLLoaderImpl* loader)
+ : loader_(loader),
+ client_(NULL) {
+}
+
+void WebURLLoaderImpl::Context::Cancel() {
+ // The bridge will still send OnCompletedRequest, which will Release() us, so
+ // we don't do that here.
+ if (bridge_.get())
+ bridge_->Cancel();
+
+ // Ensure that we do not notify the multipart delegate anymore as it has
+ // its own pointer to the client.
+ if (multipart_delegate_.get())
+ multipart_delegate_->Cancel();
+
+ // Do not make any further calls to the client.
+ client_ = NULL;
+ loader_ = NULL;
+}
+
+void WebURLLoaderImpl::Context::SetDefersLoading(bool value) {
+ if (bridge_.get())
+ bridge_->SetDefersLoading(value);
+}
+
+void WebURLLoaderImpl::Context::Start(
+ const WebURLRequest& request,
+ ResourceLoaderBridge::SyncLoadResponse* sync_load_response) {
+ DCHECK(!bridge_.get());
+
+ request_ = request; // Save the request.
+
+ GURL url = request.url();
+ if (url.SchemeIs("data")) {
+ if (sync_load_response) {
+ // This is a sync load. Do the work now.
+ sync_load_response->url = url;
+ std::string data;
+ GetInfoFromDataURL(sync_load_response->url, sync_load_response,
+ &sync_load_response->data,
+ &sync_load_response->status);
+ } else {
+ AddRef(); // Balanced in OnCompletedRequest
+ MessageLoop::current()->PostTask(FROM_HERE,
+ NewRunnableMethod(this, &Context::HandleDataURL));
+ }
+ return;
+ }
+
+ GURL referrer_url(
+ request.httpHeaderField(WebString::fromUTF8("Referer")).utf8());
+ const std::string& method = request.httpMethod().utf8();
+
+ int load_flags = net::LOAD_NORMAL;
+ switch (request.cachePolicy()) {
+ case WebURLRequest::ReloadIgnoringCacheData:
+ // Required by LayoutTests/http/tests/misc/refresh-headers.php
+ load_flags |= net::LOAD_VALIDATE_CACHE;
+ break;
+ case WebURLRequest::ReturnCacheDataElseLoad:
+ load_flags |= net::LOAD_PREFERRING_CACHE;
+ break;
+ case WebURLRequest::ReturnCacheDataDontLoad:
+ load_flags |= net::LOAD_ONLY_FROM_CACHE;
+ break;
+ case WebURLRequest::UseProtocolCachePolicy:
+ break;
+ }
+
+ if (request.reportUploadProgress())
+ load_flags |= net::LOAD_ENABLE_UPLOAD_PROGRESS;
+ if (request.reportLoadTiming())
+ load_flags |= net::LOAD_ENABLE_LOAD_TIMING;
+
+ if (!request.allowCookies() || !request.allowStoredCredentials()) {
+ load_flags |= net::LOAD_DO_NOT_SAVE_COOKIES;
+ load_flags |= net::LOAD_DO_NOT_SEND_COOKIES;
+ }
+
+ if (!request.allowStoredCredentials())
+ load_flags |= net::LOAD_DO_NOT_SEND_AUTH_DATA;
+
+
+ // TODO(jcampan): in the non out-of-process plugin case the request does not
+ // have a requestor_pid. Find a better place to set this.
+ int requestor_pid = request.requestorProcessID();
+ if (requestor_pid == 0)
+ requestor_pid = base::GetCurrentProcId();
+
+ HeaderFlattener flattener(load_flags);
+ request.visitHTTPHeaderFields(&flattener);
+
+ // TODO(abarth): These are wrong! I need to figure out how to get the right
+ // strings here. See: http://crbug.com/8706
+ std::string frame_origin = request.firstPartyForCookies().spec();
+ std::string main_frame_origin = request.firstPartyForCookies().spec();
+
+ // TODO(brettw) this should take parameter encoding into account when
+ // creating the GURLs.
+
+ webkit_glue::ResourceLoaderBridge::RequestInfo request_info;
+ request_info.method = method;
+ request_info.url = url;
+ request_info.first_party_for_cookies = request.firstPartyForCookies();
+ request_info.referrer = referrer_url;
+ request_info.frame_origin = frame_origin;
+ request_info.main_frame_origin = main_frame_origin;
+ request_info.headers = flattener.GetBuffer();
+ request_info.load_flags = load_flags;
+ request_info.requestor_pid = requestor_pid;
+ request_info.request_type = FromTargetType(request.targetType());
+ request_info.appcache_host_id = request.appCacheHostID();
+ request_info.routing_id = request.requestorID();
+ bridge_.reset(ResourceLoaderBridge::Create(request_info));
+
+ if (!request.httpBody().isNull()) {
+ // GET and HEAD requests shouldn't have http bodies.
+ DCHECK(method != "GET" && method != "HEAD");
+ const WebHTTPBody& httpBody = request.httpBody();
+ size_t i = 0;
+ WebHTTPBody::Element element;
+ while (httpBody.elementAt(i++, element)) {
+ switch (element.type) {
+ case WebHTTPBody::Element::TypeData:
+ if (!element.data.isEmpty()) {
+ // WebKit sometimes gives up empty data to append. These aren't
+ // necessary so we just optimize those out here.
+ bridge_->AppendDataToUpload(
+ element.data.data(), static_cast<int>(element.data.size()));
+ }
+ break;
+ case WebHTTPBody::Element::TypeFile:
+ if (element.fileLength == -1) {
+ bridge_->AppendFileToUpload(
+ WebStringToFilePath(element.filePath));
+ } else {
+ bridge_->AppendFileRangeToUpload(
+ WebStringToFilePath(element.filePath),
+ static_cast<uint64>(element.fileStart),
+ static_cast<uint64>(element.fileLength),
+ base::Time::FromDoubleT(element.fileInfo.modificationTime));
+ }
+ break;
+ default:
+ NOTREACHED();
+ }
+ }
+ bridge_->SetUploadIdentifier(request.httpBody().identifier());
+ }
+
+ if (sync_load_response) {
+ bridge_->SyncLoad(sync_load_response);
+ return;
+ }
+
+ if (bridge_->Start(this)) {
+ AddRef(); // Balanced in OnCompletedRequest
+ } else {
+ bridge_.reset();
+ }
+}
+
+void WebURLLoaderImpl::Context::OnUploadProgress(uint64 position, uint64 size) {
+ if (client_)
+ client_->didSendData(loader_, position, size);
+}
+
+bool WebURLLoaderImpl::Context::OnReceivedRedirect(
+ const GURL& new_url,
+ const ResourceLoaderBridge::ResponseInfo& info,
+ bool* has_new_first_party_for_cookies,
+ GURL* new_first_party_for_cookies) {
+ if (!client_)
+ return false;
+
+ WebURLResponse response;
+ response.initialize();
+ PopulateURLResponse(request_.url(), info, &response);
+
+ // TODO(darin): We lack sufficient information to construct the actual
+ // request that resulted from the redirect.
+ WebURLRequest new_request(new_url);
+ new_request.setFirstPartyForCookies(request_.firstPartyForCookies());
+
+ WebString referrer_string = WebString::fromUTF8("Referer");
+ WebString referrer = request_.httpHeaderField(referrer_string);
+ if (!WebSecurityPolicy::shouldHideReferrer(new_url, referrer))
+ new_request.setHTTPHeaderField(referrer_string, referrer);
+
+ if (response.httpStatusCode() == 307)
+ new_request.setHTTPMethod(request_.httpMethod());
+
+ client_->willSendRequest(loader_, new_request, response);
+ request_ = new_request;
+ *has_new_first_party_for_cookies = true;
+ *new_first_party_for_cookies = request_.firstPartyForCookies();
+
+ // Only follow the redirect if WebKit left the URL unmodified.
+ if (new_url == GURL(new_request.url()))
+ return true;
+
+ // We assume that WebKit only changes the URL to suppress a redirect, and we
+ // assume that it does so by setting it to be invalid.
+ DCHECK(!new_request.url().isValid());
+ return false;
+}
+
+void WebURLLoaderImpl::Context::OnReceivedResponse(
+ const ResourceLoaderBridge::ResponseInfo& info,
+ bool content_filtered) {
+ if (!client_)
+ return;
+
+ WebURLResponse response;
+ response.initialize();
+ PopulateURLResponse(request_.url(), info, &response);
+ response.setIsContentFiltered(content_filtered);
+
+ bool show_raw_listing = (GURL(request_.url()).query() == "raw");
+
+ if (info.mime_type == "text/vnd.chromium.ftp-dir") {
+ if (show_raw_listing) {
+ // Set the MIME type to plain text to prevent any active content.
+ response.setMIMEType("text/plain");
+ } else {
+ // We're going to produce a parsed listing in HTML.
+ response.setMIMEType("text/html");
+ }
+ }
+
+ client_->didReceiveResponse(loader_, response);
+
+ // We may have been cancelled after didReceiveResponse, which would leave us
+ // without a client and therefore without much need to do further handling.
+ if (!client_)
+ return;
+
+ DCHECK(!ftp_listing_delegate_.get());
+ DCHECK(!multipart_delegate_.get());
+ if (info.headers && info.mime_type == "multipart/x-mixed-replace") {
+ std::string content_type;
+ info.headers->EnumerateHeader(NULL, "content-type", &content_type);
+
+ std::string boundary = net::GetHeaderParamValue(content_type, "boundary");
+ TrimString(boundary, " \"", &boundary);
+
+ // If there's no boundary, just handle the request normally. In the gecko
+ // code, nsMultiMixedConv::OnStartRequest throws an exception.
+ if (!boundary.empty()) {
+ multipart_delegate_.reset(
+ new MultipartResponseDelegate(client_, loader_, response, boundary));
+ }
+ } else if (info.mime_type == "text/vnd.chromium.ftp-dir" &&
+ !show_raw_listing) {
+ ftp_listing_delegate_.reset(
+ new FtpDirectoryListingResponseDelegate(client_, loader_, response));
+ }
+
+ response_url_ = response.url();
+}
+
+void WebURLLoaderImpl::Context::OnReceivedData(const char* data, int len) {
+ if (!client_)
+ return;
+
+ // Temporary logging, see site_isolation_metrics.h/cc.
+ SiteIsolationMetrics::SniffCrossOriginHTML(response_url_, data, len);
+
+ if (ftp_listing_delegate_.get()) {
+ // The FTP listing delegate will make the appropriate calls to
+ // client_->didReceiveData and client_->didReceiveResponse.
+ ftp_listing_delegate_->OnReceivedData(data, len);
+ } else if (multipart_delegate_.get()) {
+ // The multipart delegate will make the appropriate calls to
+ // client_->didReceiveData and client_->didReceiveResponse.
+ multipart_delegate_->OnReceivedData(data, len);
+ } else {
+ client_->didReceiveData(loader_, data, len);
+ }
+}
+
+void WebURLLoaderImpl::Context::OnReceivedCachedMetadata(
+ const char* data, int len) {
+ if (client_)
+ client_->didReceiveCachedMetadata(loader_, data, len);
+}
+
+void WebURLLoaderImpl::Context::OnCompletedRequest(
+ const URLRequestStatus& status,
+ const std::string& security_info) {
+ if (ftp_listing_delegate_.get()) {
+ ftp_listing_delegate_->OnCompletedRequest();
+ ftp_listing_delegate_.reset(NULL);
+ } else if (multipart_delegate_.get()) {
+ multipart_delegate_->OnCompletedRequest();
+ multipart_delegate_.reset(NULL);
+ }
+
+ // Prevent any further IPC to the browser now that we're complete.
+ bridge_.reset();
+
+ if (client_) {
+ if (status.status() != URLRequestStatus::SUCCESS) {
+ int error_code;
+ if (status.status() == URLRequestStatus::HANDLED_EXTERNALLY) {
+ // By marking this request as aborted we insure that we don't navigate
+ // to an error page.
+ error_code = net::ERR_ABORTED;
+ } else {
+ error_code = status.os_error();
+ }
+ WebURLError error;
+ error.domain = WebString::fromUTF8(net::kErrorDomain);
+ error.reason = error_code;
+ error.unreachableURL = request_.url();
+ client_->didFail(loader_, error);
+ } else {
+ client_->didFinishLoading(loader_);
+ }
+ }
+
+ // Temporary logging, see site_isolation_metrics.h/cc
+ SiteIsolationMetrics::RemoveCompletedResponse(response_url_);
+
+ // We are done with the bridge now, and so we need to release the reference
+ // to ourselves that we took on behalf of the bridge. This may cause our
+ // destruction.
+ Release();
+}
+
+GURL WebURLLoaderImpl::Context::GetURLForDebugging() const {
+ return request_.url();
+}
+
+void WebURLLoaderImpl::Context::HandleDataURL() {
+ ResourceLoaderBridge::ResponseInfo info;
+ URLRequestStatus status;
+ std::string data;
+
+ if (GetInfoFromDataURL(request_.url(), &info, &data, &status)) {
+ OnReceivedResponse(info, false);
+ if (!data.empty())
+ OnReceivedData(data.data(), data.size());
+ }
+
+ OnCompletedRequest(status, info.security_info);
+}
+
+// WebURLLoaderImpl -----------------------------------------------------------
+
+WebURLLoaderImpl::WebURLLoaderImpl()
+ : ALLOW_THIS_IN_INITIALIZER_LIST(context_(new Context(this))) {
+}
+
+WebURLLoaderImpl::~WebURLLoaderImpl() {
+ cancel();
+}
+
+void WebURLLoaderImpl::loadSynchronously(const WebURLRequest& request,
+ WebURLResponse& response,
+ WebURLError& error,
+ WebData& data) {
+ ResourceLoaderBridge::SyncLoadResponse sync_load_response;
+ context_->Start(request, &sync_load_response);
+
+ const GURL& final_url = sync_load_response.url;
+
+ // TODO(tc): For file loads, we may want to include a more descriptive
+ // status code or status text.
+ const URLRequestStatus::Status& status = sync_load_response.status.status();
+ if (status != URLRequestStatus::SUCCESS &&
+ status != URLRequestStatus::HANDLED_EXTERNALLY) {
+ response.setURL(final_url);
+ error.domain = WebString::fromUTF8(net::kErrorDomain);
+ error.reason = sync_load_response.status.os_error();
+ error.unreachableURL = final_url;
+ return;
+ }
+
+ PopulateURLResponse(final_url, sync_load_response, &response);
+
+ data.assign(sync_load_response.data.data(),
+ sync_load_response.data.size());
+}
+
+void WebURLLoaderImpl::loadAsynchronously(const WebURLRequest& request,
+ WebURLLoaderClient* client) {
+ DCHECK(!context_->client());
+
+ context_->set_client(client);
+ context_->Start(request, NULL);
+}
+
+void WebURLLoaderImpl::cancel() {
+ context_->Cancel();
+}
+
+void WebURLLoaderImpl::setDefersLoading(bool value) {
+ context_->SetDefersLoading(value);
+}
+
+} // namespace webkit_glue
diff --git a/webkit/glue/weburlloader_impl.h b/webkit/glue/weburlloader_impl.h
new file mode 100644
index 0000000..f9e5a0b
--- /dev/null
+++ b/webkit/glue/weburlloader_impl.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved. Use of this
+// source code is governed by a BSD-style license that can be found in the
+// LICENSE file.
+
+#ifndef WEBKIT_GLUE_WEBURLLOADER_IMPL_H_
+#define WEBKIT_GLUE_WEBURLLOADER_IMPL_H_
+
+#include "base/ref_counted.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURLLoader.h"
+
+namespace webkit_glue {
+
+class WebURLLoaderImpl : public WebKit::WebURLLoader {
+ public:
+ WebURLLoaderImpl();
+ ~WebURLLoaderImpl();
+
+ // WebURLLoader methods:
+ virtual void loadSynchronously(
+ const WebKit::WebURLRequest& request,
+ WebKit::WebURLResponse& response,
+ WebKit::WebURLError& error,
+ WebKit::WebData& data);
+ virtual void loadAsynchronously(
+ const WebKit::WebURLRequest& request,
+ WebKit::WebURLLoaderClient* client);
+ virtual void cancel();
+ virtual void setDefersLoading(bool value);
+
+ private:
+ class Context;
+ scoped_refptr<Context> context_;
+};
+
+} // namespace webkit_glue
+
+#endif // WEBKIT_GLUE_WEBURLLOADER_IMPL_H_
diff --git a/webkit/glue/webview_unittest.cc b/webkit/glue/webview_unittest.cc
new file mode 100644
index 0000000..22c9cea
--- /dev/null
+++ b/webkit/glue/webview_unittest.cc
@@ -0,0 +1,28 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebView.h"
+#include "webkit/tools/test_shell/test_shell_test.h"
+
+using WebKit::WebView;
+
+class WebViewTest : public TestShellTest {
+};
+
+TEST_F(WebViewTest, ActiveState) {
+ WebView* view = test_shell_->webView();
+ ASSERT_TRUE(view != 0);
+
+ view->setIsActive(true);
+ EXPECT_TRUE(view->isActive());
+
+ view->setIsActive(false);
+ EXPECT_FALSE(view->isActive());
+
+ view->setIsActive(true);
+ EXPECT_TRUE(view->isActive());
+}
+
+// TODO(viettrungluu): add more tests
diff --git a/webkit/glue/window_open_disposition.cc b/webkit/glue/window_open_disposition.cc
new file mode 100644
index 0000000..6a117d3
--- /dev/null
+++ b/webkit/glue/window_open_disposition.cc
@@ -0,0 +1,30 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/window_open_disposition.h"
+
+#include "base/logging.h"
+
+WindowOpenDisposition NavigationPolicyToDisposition(
+ WebKit::WebNavigationPolicy policy) {
+ switch (policy) {
+ case WebKit::WebNavigationPolicyIgnore:
+ return IGNORE_ACTION;
+ case WebKit::WebNavigationPolicyDownload:
+ return SAVE_TO_DISK;
+ case WebKit::WebNavigationPolicyCurrentTab:
+ return CURRENT_TAB;
+ case WebKit::WebNavigationPolicyNewBackgroundTab:
+ return NEW_BACKGROUND_TAB;
+ case WebKit::WebNavigationPolicyNewForegroundTab:
+ return NEW_FOREGROUND_TAB;
+ case WebKit::WebNavigationPolicyNewWindow:
+ return NEW_WINDOW;
+ case WebKit::WebNavigationPolicyNewPopup:
+ return NEW_POPUP;
+ default:
+ NOTREACHED() << "Unexpected WebNavigationPolicy";
+ return IGNORE_ACTION;
+ }
+}
diff --git a/webkit/glue/window_open_disposition.h b/webkit/glue/window_open_disposition.h
new file mode 100644
index 0000000..24495f1
--- /dev/null
+++ b/webkit/glue/window_open_disposition.h
@@ -0,0 +1,28 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_WINDOW_OPEN_DISPOSITION_H_
+#define WEBKIT_GLUE_WINDOW_OPEN_DISPOSITION_H_
+
+#include "third_party/WebKit/WebKit/chromium/public/WebNavigationPolicy.h"
+
+enum WindowOpenDisposition {
+ SUPPRESS_OPEN,
+ CURRENT_TAB,
+ // Indicates that only one tab with the url should exist in the same window.
+ SINGLETON_TAB,
+ NEW_FOREGROUND_TAB,
+ NEW_BACKGROUND_TAB,
+ NEW_POPUP,
+ NEW_WINDOW,
+ SAVE_TO_DISK,
+ OFF_THE_RECORD,
+ IGNORE_ACTION
+};
+
+// Conversion function:
+WindowOpenDisposition NavigationPolicyToDisposition(
+ WebKit::WebNavigationPolicy policy);
+
+#endif // WEBKIT_GLUE_WINDOW_OPEN_DISPOSITION_H_